/////////////////////////////////////////////////////////////////////////////////
// Paint.NET //
// Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
// Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
// See src/Resources/Files/License.txt for full licensing and attribution //
// details. //
// . //
/////////////////////////////////////////////////////////////////////////////////
using Microsoft.Win32;
using PaintDotNet.Threading;
using PaintDotNet.SystemLayer;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace PaintDotNet{
/// <summary>
/// Defines miscellaneous constants and static functions.
/// </summary>
/// // TODO: refactor into mini static classes
public sealed class Utility
{
private Utility()
{
}
internal static bool IsNumber(float x)
{
return x >= float.MinValue && x <= float.MaxValue;
}
internal static bool IsNumber(double x)
{
return x >= double.MinValue && x <= double.MaxValue;
}
internal static int Min(int val0, params int[] vals)
{
int min = val0;
for (int i = 0; i < vals.Length; ++i)
{
if (vals[i] < min)
{
min = vals[i];
}
}
return min;
}
internal static int Max(int val0, params int[] vals)
{
int max = val0;
for (int i = 0; i < vals.Length; ++i)
{
if (vals[i] > max)
{
max = vals[i];
}
}
return max;
}
public static PointF[] GetRgssOffsets(int quality)
{
unsafe
{
int sampleCount = quality * quality;
PointF[] samplesArray = new PointF[sampleCount];
fixed (PointF* pSamplesArray = samplesArray)
{
GetRgssOffsets(pSamplesArray, sampleCount, quality);
}
return samplesArray;
}
}
public static unsafe void GetRgssOffsets(PointF* samplesArray, int sampleCount, int quality)
{
if (sampleCount < 1)
{
throw new ArgumentOutOfRangeException("sampleCount", "sampleCount must be [0, int.MaxValue]");
}
if (sampleCount != quality * quality)
{
throw new ArgumentOutOfRangeException("sampleCount != (quality * quality)");
}
if (sampleCount == 1)
{
samplesArray[0] = new PointF(0.0f, 0.0f);
}
else
{
for (int i = 0; i < sampleCount; ++i)
{
double y = (i + 1d) / (sampleCount + 1d);
double x = y * quality;
x -= (int)x;
samplesArray[i] = new PointF((float)(x - 0.5d), (float)(y - 0.5d));
}
}
}
public static bool IsObsolete(Type type, bool inherit)
{
object[] attrs = type.GetCustomAttributes(typeof(ObsoleteAttribute), inherit);
return (attrs.Length != 0);
}
public static void DrawDropShadow1px(Graphics g, Rectangle rect)
{
Brush b0 = new SolidBrush(Color.FromArgb(15, Color.Black));
Brush b1 = new SolidBrush(Color.FromArgb(47, Color.Black));
Pen p2 = new Pen(Color.FromArgb(63, Color.Black));
g.FillRectangle(b0, rect.Left, rect.Top, 1, 1);
g.FillRectangle(b1, rect.Left + 1, rect.Top, 1, 1);
g.FillRectangle(b1, rect.Left, rect.Top + 1, 1, 1);
g.FillRectangle(b0, rect.Right - 1, rect.Top, 1, 1);
g.FillRectangle(b1, rect.Right - 2, rect.Top, 1, 1);
g.FillRectangle(b1, rect.Right - 1, rect.Top + 1, 1, 1);
g.FillRectangle(b0, rect.Left, rect.Bottom - 1, 1, 1);
g.FillRectangle(b1, rect.Left + 1, rect.Bottom - 1, 1, 1);
g.FillRectangle(b1, rect.Left, rect.Bottom - 2, 1, 1);
g.FillRectangle(b0, rect.Right - 1, rect.Bottom - 1, 1, 1);
g.FillRectangle(b1, rect.Right - 2, rect.Bottom - 1, 1, 1);
g.FillRectangle(b1, rect.Right - 1, rect.Bottom - 2, 1, 1);
g.DrawLine(p2, rect.Left + 2, rect.Top, rect.Right - 3, rect.Top);
g.DrawLine(p2, rect.Left, rect.Top + 2, rect.Left, rect.Bottom - 3);
g.DrawLine(p2, rect.Left + 2, rect.Bottom - 1, rect.Right - 3, rect.Bottom - 1);
g.DrawLine(p2, rect.Right - 1, rect.Top + 2, rect.Right - 1, rect.Bottom - 3);
b0.Dispose();
b0 = null;
b1.Dispose();
b1 = null;
p2.Dispose();
p2 = null;
}
public static Keys LetterOrDigitCharToKeys(char c)
{
if (c >= 'a' && c <= 'z')
{
return (Keys)((int)(c - 'a') + (int)Keys.A);
}
else if (c >= 'A' && c <= 'Z')
{
return (Keys)((int)(c - 'A') + (int)Keys.A);
}
else if (c >= '0' && c <= '9')
{
return (Keys)((int)(c - '0') + (int)Keys.D0);
}
else
{
return Keys.None;
}
}
public static Control FindFocus()
{
foreach (Form form in Application.OpenForms)
{
Control focused = FindFocus(form);
if (focused != null)
{
return focused;
}
}
return null;
}
private static Control FindFocus(Control c)
{
if (c.Focused)
{
return c;
}
foreach (Control child in c.Controls)
{
Control f = FindFocus(child);
if (f != null)
{
return f;
}
}
return null;
}
public static void DrawColorRectangle(Graphics g, Rectangle rect, Color color, bool drawBorder)
{
int inflateAmt = drawBorder ? -2 : 0;
Rectangle colorRectangle = Rectangle.Inflate(rect, inflateAmt, inflateAmt);
Brush colorBrush = new LinearGradientBrush(colorRectangle, Color.FromArgb(255, color), color, 90.0f, false);
HatchBrush backgroundBrush = new HatchBrush(HatchStyle.LargeCheckerBoard, Color.FromArgb(191, 191, 191), Color.FromArgb(255, 255, 255));
if (drawBorder)
{
g.DrawRectangle(Pens.Black, rect.Left, rect.Top, rect.Width - 1, rect.Height - 1);
g.DrawRectangle(Pens.White, rect.Left + 1, rect.Top + 1, rect.Width - 3, rect.Height - 3);
}
PixelOffsetMode oldPOM = g.PixelOffsetMode;
g.PixelOffsetMode = PixelOffsetMode.Half;
g.FillRectangle(backgroundBrush, colorRectangle);
g.FillRectangle(colorBrush, colorRectangle);
g.PixelOffsetMode = oldPOM;
backgroundBrush.Dispose();
colorBrush.Dispose();
}
public static Size ComputeThumbnailSize(Size originalSize, int maxEdgeLength)
{
Size thumbSize;
if (originalSize.Width > originalSize.Height)
{
int longSide = Math.Min(originalSize.Width, maxEdgeLength);
thumbSize = new Size(longSide, Math.Max(1, (originalSize.Height * longSide) / originalSize.Width));
}
else if (originalSize.Height > originalSize.Width)
{
int longSide = Math.Min(originalSize.Height, maxEdgeLength);
thumbSize = new Size(Math.Max(1, (originalSize.Width * longSide) / originalSize.Height), longSide);
}
else // if (docSize.Width == docSize.Height)
{
int longSide = Math.Min(originalSize.Width, maxEdgeLength);
thumbSize = new Size(longSide, longSide);
}
return thumbSize;
}
public static bool IsClipboardImageAvailable()
{
try
{
return System.Windows.Forms.Clipboard.ContainsImage() ||
System.Windows.Forms.Clipboard.ContainsData(DataFormats.EnhancedMetafile);
}
catch (ExternalException)
{
return false;
}
}
public static Font CreateFont(string name, float size, FontStyle style)
{
Font returnFont;
try
{
returnFont = new Font(name, size, style);
}
catch (Exception)
{
returnFont = new Font(FontFamily.GenericSansSerif, size);
}
return returnFont;
}
public static Font CreateFont(string name, float size, string backupName, float backupSize, FontStyle style)
{
Font returnFont;
try
{
returnFont = new Font(name, size, style);
}
catch (Exception)
{
returnFont = CreateFont(backupName, backupSize, style);
}
return returnFont;
}
public static readonly Color TransparentKey = Color.FromArgb(192, 192, 192);
public static string WebExceptionToErrorMessage(WebException wex)
{
string errorMessage;
switch (wex.Status)
{
case WebExceptionStatus.ProtocolError:
string format = PdnResources.GetString("WebExceptionStatus.ProtocolError.Format");
HttpStatusCode statusCode = ((HttpWebResponse)wex.Response).StatusCode;
errorMessage = string.Format(format, statusCode.ToString(), (int)statusCode);
break;
default:
string stringName = "WebExceptionStatus." + wex.Status.ToString();
errorMessage = PdnResources.GetString(stringName);
break;
}
return errorMessage;
}
private static bool allowGCFullCollect = true;
public static bool AllowGCFullCollect
{
get
{
return allowGCFullCollect;
}
set
{
allowGCFullCollect = value;
}
}
public static void GCFullCollect()
{
if (AllowGCFullCollect)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
private static int defaultSimplificationFactor = 50;
public static int DefaultSimplificationFactor
{
get
{
return defaultSimplificationFactor;
}
set
{
defaultSimplificationFactor = value;
}
}
public static bool IsArrowKey(Keys keyData)
{
Keys key = keyData & Keys.KeyCode;
if (key == Keys.Up || key == Keys.Down || key == Keys.Left || key == Keys.Right)
{
return true;
}
else
{
return false;
}
}
public static bool DoesControlHaveMouseCaptured(Control control)
{
bool result = false;
result |= control.Capture;
foreach (Control c in control.Controls)
{
result |= DoesControlHaveMouseCaptured(c);
}
return result;
}
public static void SplitRectangle(Rectangle rect, Rectangle[] rects)
{
int height = rect.Height;
for (int i = 0; i < rects.Length; ++i)
{
Rectangle newRect = Rectangle.FromLTRB(rect.Left,
rect.Top + ((height * i) / rects.Length),
rect.Right,
rect.Top + ((height * (i + 1)) / rects.Length));
rects[i] = newRect;
}
}
public static long TicksToMs(long ticks)
{
return ticks / 10000;
}
public static string GetStaticName(Type type)
{
PropertyInfo pi = type.GetProperty("StaticName", BindingFlags.Static | BindingFlags.Public | BindingFlags.GetProperty);
return (string)pi.GetValue(null, null);
}
public static readonly float[][] Identity5x5F = new float[][] {
new float[] { 1, 0, 0, 0, 0 },
new float[] { 0, 1, 0, 0, 0 },
new float[] { 0, 0, 1, 0, 0 },
new float[] { 0, 0, 0, 1, 0 },
new float[] { 0, 0, 0, 0, 1 }
};
public static readonly ColorMatrix IdentityColorMatrix = new ColorMatrix(Identity5x5F);
[ThreadStatic]
private static Matrix identityMatrix = null;
public static Matrix IdentityMatrix
{
get
{
if (identityMatrix == null)
{
identityMatrix = new Matrix();
identityMatrix.Reset();
}
return identityMatrix;
}
}
/// <summary>
/// Rounds an integer to the smallest power of 2 that is greater
/// than or equal to it.
/// </summary>
public static int Log2RoundUp(int x)
{
if (x == 0)
{
return 1;
}
if (x == 1)
{
return 1;
}
return 1 << (1 + HighestBit(x - 1));
}
private static int HighestBit(int x)
{
if (x == 0)
{
return 0;
}
int b = 0;
int hi = 0;
while (b <= 30)
{
if ((x & (1 << b)) != 0)
{
hi = b;
}
++b;
}
return hi;
}
private int CountBits(int x)
{
uint y = (uint)x;
int count = 0;
for (int bit = 0; bit < 32; ++bit)
{
if ((y & ((uint)1 << bit)) != 0)
{
++count;
}
}
return count;
}
public static string RemoveSpaces(string s)
{
StringBuilder sb = new StringBuilder();
foreach (char c in s)
{
if (!char.IsWhiteSpace(c))
{
sb.Append(c);
}
}
return sb.ToString();
}
public static int Max(int[,] array)
{
int max = int.MinValue;
for (int i = array.GetLowerBound(0); i <= array.GetUpperBound(0); ++i)
{
for (int j = array.GetLowerBound(1); j <= array.GetUpperBound(1); ++j)
{
if (array[i,j] > max)
{
max = array[i,j];
}
}
}
return max;
}
public static int Sum(int[][] array)
{
int sum = 0;
for (int i = 0; i < array.Length; ++i)
{
int[] row = array[i];
for (int j = 0; j < row.Length; ++j)
{
sum += row[j];
}
}
return sum;
}
// TODO: obsolete these NUD funcitons, move them into PdnNumericUpDown
public static void ClipNumericUpDown(NumericUpDown upDown)
{
if (upDown.Value < upDown.Minimum)
{
upDown.Value = upDown.Minimum;
}
else if (upDown.Value > upDown.Maximum)
{
upDown.Value = upDown.Maximum;
}
}
public static bool GetUpDownValueFromText(NumericUpDown nud, out double val)
{
if (nud.Text == string.Empty)
{
val = 0;
return false;
}
else
{
try
{
if (nud.DecimalPlaces == 0)
{
val = (double)int.Parse(nud.Text);
}
else
{
val = double.Parse(nud.Text);
}
}
catch
{
val = 0;
return false;
}
return true;
}
}
public static bool CheckNumericUpDown(NumericUpDown upDown)
{
int a;
bool result = int.TryParse(upDown.Text, out a);
if (result && (a <= (int)upDown.Maximum) && (a >= (int)upDown.Minimum))
{
return true;
}
else
{
return false;
}
}
public static void SetNumericUpDownValue(NumericUpDown upDown, decimal newValue)
{
if (upDown.Value != newValue)
{
upDown.Value = newValue;
}
}
public static void SetNumericUpDownValue(NumericUpDown upDown, int newValue)
{
SetNumericUpDownValue(upDown, (decimal)newValue);
}
public static string SizeStringFromBytes(long bytes)
{
double bytesDouble = (double)bytes;
string toStringFormat;
string formatString;
if (bytesDouble > (1024 * 1024 * 1024))
{
// Gigs
bytesDouble /= 1024 * 1024 * 1024;
toStringFormat = "F1";
formatString = PdnResources.GetString("Utility.SizeStringFromBytes.GBFormat");
}
else if (bytesDouble > (1024 * 1024))
{
// Megs
bytesDouble /= 1024 * 1024;
toStringFormat = "F1";
formatString = PdnResources.GetString("Utility.SizeStringFromBytes.MBFormat");
}
else if (bytesDouble > (1024))
{
// K
bytesDouble /= 1024;
toStringFormat = "F1";
formatString = PdnResources.GetString("Utility.SizeStringFromBytes.KBFormat");
}
else
{
// Bytes
toStringFormat = "F0";
formatString = PdnResources.GetString("Utility.SizeStringFromBytes.BytesFormat");
}
string bytesString = bytesDouble.ToString(toStringFormat);
string sizeString = string.Format(formatString, bytesString);
return sizeString;
}
public static void ShowWiaError(IWin32Window owner)
{
// WIA requires Windows XP SP1 or later, or Windows Server 2003
// So if we know they're on WS2k3, we tell them to enable WIA.
// If they're on XP or later, tell them that WIA isn't available.
// Otherwise we tell them they need XP SP1 (for the Win2K folks).
if (OS.Type == OSType.Server)
{
Utility.ErrorBox(owner, PdnResources.GetString("WIA.Error.EnableMe"));
}
else
{
Utility.ErrorBox(owner, PdnResources.GetString("WIA.Error.UnableToLoad"));
}
}
public static void ShowNonAdminErrorBox(IWin32Window parent)
{
ErrorBox(parent, PdnResources.GetString("NonAdminErrorBox.Message"));
}
public static void ErrorBox(IWin32Window parent, string message)
{
MessageBox.Show(parent, message, PdnInfo.GetBareProductName(), MessageBoxButtons.OK, MessageBoxIcon.Error);
}
public static DialogResult ErrorBoxOKCancel(IWin32Window parent, string message)
{
return MessageBox.Show(parent, message, PdnInfo.GetBareProductName(), MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
}
public static void InfoBox(IWin32Window parent, string message)
{
MessageBox.Show(parent, message, PdnInfo.GetBareProductName(), MessageBoxButtons.OK, MessageBoxIcon.Information);
}
public static DialogResult InfoBoxOKCancel(IWin32Window parent, string message)
{
return MessageBox.Show(parent, message, PdnInfo.GetBareProductName(), MessageBoxButtons.OKCancel, MessageBoxIcon.Information);
}
public static DialogResult AskOKCancel(IWin32Window parent, string question)
{
return MessageBox.Show(parent, question, PdnInfo.GetBareProductName(), MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
}
public static DialogResult AskYesNo(IWin32Window parent, string question)
{
return MessageBox.Show(parent, question, PdnInfo.GetBareProductName(), MessageBoxButtons.YesNo, MessageBoxIcon.Question);
}
public static DialogResult AskYesNoCancel(IWin32Window parent, string question)
{
return MessageBox.Show(parent, question, PdnInfo.GetBareProductName(), MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
}
public static Icon ImageToIcon(Image image)
{
return ImageToIcon(image, Utility.TransparentKey);
}
public static Icon ImageToIcon(Image image, bool disposeImage)
{
return ImageToIcon(image, Utility.TransparentKey, disposeImage);
}
public static Icon ImageToIcon(Image image, Color seeThru)
{
return ImageToIcon(image, seeThru, false);
}
/// <summary>
/// Converts an Image to an Icon.
/// </summary>
/// <param name="image">The Image to convert to an icon. Must be an appropriate icon size (32x32, 16x16, etc).</param>
/// <param name="seeThru">The color that will be treated as transparent in the icon.</param>
/// <param name="disposeImage">Whether or not to dispose the passed-in Image.</param>
/// <returns>An Icon representation of the Image.</returns>
public static Icon ImageToIcon(Image image, Color seeThru, bool disposeImage)
{
Bitmap bitmap = new Bitmap(image);
for (int y = 0; y < bitmap.Height; ++y)
{
for (int x = 0; x < bitmap.Width; ++x)
{
if (bitmap.GetPixel(x, y) == seeThru)
{
bitmap.SetPixel(x, y, Color.FromArgb(0));
}
}
}
Icon icon = Icon.FromHandle(bitmap.GetHicon());
bitmap.Dispose();
if (disposeImage)
{
image.Dispose();
}
return icon;
}
public static Icon BitmapToIcon(Bitmap bitmap, bool disposeBitmap)
{
Icon icon = Icon.FromHandle(bitmap.GetHicon());
if (disposeBitmap)
{
bitmap.Dispose();
}
return icon;
}
public static Icon SurfaceToIcon(Surface surface, bool disposeSurface)
{
Bitmap bitmap = surface.CreateAliasedBitmap();
Icon icon = Icon.FromHandle(bitmap.GetHicon());
bitmap.Dispose();
if (disposeSurface)
{
surface.Dispose();
}
return icon;
}
public static Point GetRectangleCenter(Rectangle rect)
{
return new Point((rect.Left + rect.Right) / 2, (rect.Top + rect.Bottom) / 2);
}
public static PointF GetRectangleCenter(RectangleF rect)
{
return new PointF((rect.Left + rect.Right) / 2, (rect.Top + rect.Bottom) / 2);
}
public static Scanline[] GetRectangleScans(Rectangle rect)
{
Scanline[] scans = new Scanline[rect.Height];
for (int y = 0; y < rect.Height; ++y)
{
scans[y] = new Scanline(rect.X, rect.Y + y, rect.Width);
}
return scans;
}
public static Scanline[] GetRegionScans(Rectangle[] region)
{
int scanCount = 0;
for (int i = 0; i < region.Length; ++i)
{
scanCount += region[i].Height;
}
Scanline[] scans = new Scanline[scanCount];
int scanIndex = 0;
foreach (Rectangle rect in region)
{
for (int y = 0; y < rect.Height; ++y)
{
scans[scanIndex] = new Scanline(rect.X, rect.Y + y, rect.Width);
++scanIndex;
}
}
return scans;
}
public static Rectangle[] ScanlinesToRectangles(Scanline[] scans)
{
return ScanlinesToRectangles(scans, 0, scans.Length);
}
public static Rectangle[] ScanlinesToRectangles(Scanline[] scans, int startIndex, int length)
{
Rectangle[] rects = new Rectangle[length];
for (int i = 0; i < length; ++i)
{
Scanline scan = scans[i + startIndex];
rects[i] = new Rectangle(scan.X, scan.Y, scan.Length, 1);
}
return rects;
}
/// <summary>
/// Found on Google Groups when searching for "Region.Union" while looking
/// for bugs:
/// ---
/// Hello,
///
/// I did not run your code, but I know Region.Union is flawed in both 1.0 and
/// 1.1, so I assume it is in the gdi+ unmanged code dll. The best workaround,
/// in terms of speed, is to use a PdnGraphicsPath, but it must be a path with
/// FillMode = FillMode.Winding. You add the rectangles to the path, then you do
/// union onto an empty region with the path. The important point is to do only
/// one union call on a given empty region. We created a "super region" object
/// to hide all these bugs and optimize clipping operations. In fact, it is much
/// faster to use the path than to call Region.Union for each rectangle.
///
/// Too bad about Region.Union. A lot of people will hit this bug, as it is
/// essential in high-performance animation.
///
/// Regards,
/// Frank Hileman
/// Prodige Software Corporation
/// ---
/// </summary>
/// <param name="rectsF"></param>
/// <param name="startIndex"></param>
/// <param name="length"></param>
/// <returns></returns>
public static PdnRegion RectanglesToRegion(RectangleF[] rectsF, int startIndex, int length)
{
PdnRegion region;
if (rectsF == null || rectsF.Length == 0 || length == 0)
{
region = PdnRegion.CreateEmpty();
}
else
{
using (PdnGraphicsPath path = new PdnGraphicsPath())
{
path.FillMode = FillMode.Winding;
if (startIndex == 0 && length == rectsF.Length)
{
path.AddRectangles(rectsF);
}
else
{
for (int i = startIndex; i < startIndex + length; ++i)
{
path.AddRectangle(rectsF[i]);
}
}
region = new PdnRegion(path);
}
}
return region;
}
public static PdnRegion RectanglesToRegion(RectangleF[] rectsF)
{
return RectanglesToRegion(rectsF, 0, rectsF != null ? rectsF.Length : 0);
}
public static PdnRegion RectanglesToRegion(RectangleF[] rectsF1, RectangleF[] rectsF2, params RectangleF[][] rectsFA)
{
using (PdnGraphicsPath path = new PdnGraphicsPath())
{
path.FillMode = FillMode.Winding;
if (rectsF1 != null && rectsF1.Length > 0)
{
path.AddRectangles(rectsF1);
}
if (rectsF2 != null && rectsF2.Length > 0)
{
path.AddRectangles(rectsF2);
}
foreach (RectangleF[] rectsF in rectsFA)
{
if (rectsF != null && rectsF.Length > 0)
{
path.AddRectangles(rectsF);
}
}
return new PdnRegion(path);
}
}
public static PdnRegion RectanglesToRegion(Rectangle[] rects, int startIndex, int length)
{
PdnRegion region;
if (length == 0)
{
region = PdnRegion.CreateEmpty();
}
else
{
using (PdnGraphicsPath path = new PdnGraphicsPath())
{
path.FillMode = FillMode.Winding;
if (startIndex == 0 && length == rects.Length)
{
path.AddRectangles(rects);
}
else
{
for (int i = startIndex; i < startIndex + length; ++i)
{
path.AddRectangle(rects[i]);
}
}
region = new PdnRegion(path);
path.Dispose();
}
}
return region;
}
public static PdnRegion RectanglesToRegion(Rectangle[] rects)
{
return RectanglesToRegion(rects, 0, rects.Length);
}
public static int GetRegionArea(RectangleF[] rectsF)
{
int area = 0;
foreach (RectangleF rectF in rectsF)
{
Rectangle rect = Rectangle.Truncate(rectF);
area += rect.Width * rect.Height;
}
return area;
}
public static RectangleF RectangleFromCenter(PointF center, float halfSize)
{
RectangleF ret = new RectangleF(center.X, center.Y, 0, 0);
ret.Inflate(halfSize, halfSize);
return ret;
}
public static List<PointF> PointListToPointFList(List<Point> ptList)
{
List<PointF> ret = new List<PointF>(ptList.Count);
for (int i = 0; i < ptList.Count; ++i)
{
ret.Add((PointF)ptList[i]);
}
return ret;
}
public static PointF[] PointArrayToPointFArray(Point[] ptArray)
{
PointF[] ret = new PointF[ptArray.Length];
for (int i = 0; i < ret.Length; ++i)
{
ret[i] = (PointF)ptArray[i];
}
return ret;
}
public static Rectangle[] InflateRectangles(Rectangle[] rects, int amount)
{
Rectangle[] inflated = new Rectangle[rects.Length];
for (int i = 0; i < rects.Length; ++i)
{
inflated[i] = Rectangle.Inflate(rects[i], amount, amount);
}
return inflated;
}
public static void InflateRectanglesInPlace(Rectangle[] rects, int amount)
{
for (int i = 0; i < rects.Length; ++i)
{
rects[i].Inflate(amount, amount);
}
}
public static RectangleF[] InflateRectangles(RectangleF[] rectsF, int amount)
{
RectangleF[] inflated = new RectangleF[rectsF.Length];
for (int i = 0; i < rectsF.Length; ++i)
{
inflated[i] = RectangleF.Inflate(rectsF[i], amount, amount);
}
return inflated;
}
public static void InflateRectanglesInPlace(RectangleF[] rectsF, float amount)
{
for (int i = 0; i < rectsF.Length; ++i)
{
rectsF[i].Inflate(amount, amount);
}
}
public static Rectangle PointsToConstrainedRectangle(Point a, Point b)
{
Rectangle rect = Utility.PointsToRectangle(a, b);
int minWH = Math.Min(rect.Width, rect.Height);
rect.Width = minWH;
rect.Height = minWH;
if (rect.Y != a.Y)
{
rect.Location = new Point(rect.X, a.Y - minWH);
}
if (rect.X != a.X)
{
rect.Location = new Point(a.X - minWH, rect.Y);
}
return rect;
}
public static RectangleF PointsToConstrainedRectangle(PointF a, PointF b)
{
RectangleF rect = Utility.PointsToRectangle(a, b);
float minWH = Math.Min(rect.Width, rect.Height);
rect.Width = minWH;
rect.Height = minWH;
if (rect.Y != a.Y)
{
rect.Location = new PointF(rect.X, a.Y - minWH);
}
if (rect.X != a.X)
{
rect.Location = new PointF(a.X - minWH, rect.Y);
}
return rect;
}
/// <summary>
/// Takes two points and creates a bounding rectangle from them.
/// </summary>
/// <param name="a">One corner of the rectangle.</param>
/// <param name="b">The other corner of the rectangle.</param>
/// <returns>A Rectangle instance that bounds the two points.</returns>
public static Rectangle PointsToRectangle(Point a, Point b)
{
int x = Math.Min(a.X, b.X);
int y = Math.Min(a.Y, b.Y);
int width = Math.Abs(a.X - b.X) + 1;
int height = Math.Abs(a.Y - b.Y) + 1;
return new Rectangle(x, y, width, height);
}
public static RectangleF PointsToRectangle(PointF a, PointF b)
{
float x = Math.Min(a.X, b.X);
float y = Math.Min(a.Y, b.Y);
float width = Math.Abs(a.X - b.X) + 1;
float height = Math.Abs(a.Y - b.Y) + 1;
return new RectangleF(x, y, width, height);
}
public static Rectangle PointsToRectangleExclusive(Point a, Point b)
{
int x = Math.Min(a.X, b.X);
int y = Math.Min(a.Y, b.Y);
int width = Math.Abs(a.X - b.X);
int height = Math.Abs(a.Y - b.Y);
return new Rectangle(x, y, width, height);
}
public static RectangleF PointsToRectangleExclusive(PointF a, PointF b)
{
float x = Math.Min(a.X, b.X);
float y = Math.Min(a.Y, b.Y);
float width = Math.Abs(a.X - b.X);
float height = Math.Abs(a.Y - b.Y);
return new RectangleF(x, y, width, height);
}
public static RectangleF[] PointsToRectangles(PointF[] pointsF)
{
if (pointsF.Length == 0)
{
return new RectangleF[] { };
}
if (pointsF.Length == 1)
{
return new RectangleF[] { new RectangleF(pointsF[0].X, pointsF[0].Y, 1, 1) };
}
RectangleF[] rectsF = new RectangleF[pointsF.Length - 1];
for (int i = 0; i < pointsF.Length - 1; ++i)
{
rectsF[i] = PointsToRectangle(pointsF[i], pointsF[i + 1]);
}
return rectsF;
}
public static Rectangle[] PointsToRectangles(Point[] points)
{
if (points.Length == 0)
{
return new Rectangle[] { };
}
if (points.Length == 1)
{
return new Rectangle[] { new Rectangle(points[0].X, points[0].Y, 1, 1) };
}
Rectangle[] rects = new Rectangle[points.Length - 1];
for (int i = 0; i < points.Length - 1; ++i)
{
rects[i] = PointsToRectangle(points[i], points[i + 1]);
}
return rects;
}
/// <summary>
/// Converts a RectangleF to RectangleF by rounding down the Location and rounding
/// up the Size.
/// </summary>
public static Rectangle RoundRectangle(RectangleF rectF)
{
float left = (float)Math.Floor(rectF.Left);
float top = (float)Math.Floor(rectF.Top);
float right = (float)Math.Ceiling(rectF.Right);
float bottom = (float)Math.Ceiling(rectF.Bottom);
return Rectangle.Truncate(RectangleF.FromLTRB(left, top, right, bottom));
}
public static Stack Reverse(Stack reverseMe)
{
Stack reversed = new Stack();
foreach (object o in reverseMe)
{
reversed.Push(o);
}
return reversed;
}
public static void SerializeObjectToStream(object graph, Stream stream)
{
new BinaryFormatter().Serialize(stream, graph);
}
public static object DeserializeObjectFromStream(Stream stream)
{
return new BinaryFormatter().Deserialize(stream);
}
[Obsolete("Use rect.Contains() instead", true)]
public static bool IsPointInRectangle(Point pt, Rectangle rect)
{
return rect.Contains(pt);
}
[Obsolete("Use rect.Contains() instead", true)]
public static bool IsPointInRectangle(int x, int y, Rectangle rect)
{
return rect.Contains(x, y);
}
public static Bitmap FullCloneBitmap(Bitmap cloneMe)
{
Bitmap bitmap = new Bitmap(cloneMe.Width, cloneMe.Height, cloneMe.PixelFormat);
using (Graphics g = Graphics.FromImage(bitmap))
{
g.DrawImage(cloneMe, 0, 0, cloneMe.Width, cloneMe.Height);
}
return bitmap;
}
/// <summary>
/// Allows you to find the bounding box for a Region object without requiring
/// the presence of a Graphics object.
/// (Region.GetBounds takes a Graphics instance as its only parameter.)
/// </summary>
/// <param name="region">The region you want to find a bounding box for.</param>
/// <returns>A RectangleF structure that surrounds the Region.</returns>
public static Rectangle GetRegionBounds(PdnRegion region)
{
Rectangle[] rects = region.GetRegionScansReadOnlyInt();
return GetRegionBounds(rects, 0, rects.Length);
}
/// <summary>
/// Allows you to find the bounding box for a "region" that is described as an
/// array of bounding boxes.
/// </summary>
/// <param name="rectsF">The "region" you want to find a bounding box for.</param>
/// <returns>A RectangleF structure that surrounds the Region.</returns>
public static RectangleF GetRegionBounds(RectangleF[] rectsF, int startIndex, int length)
{
if (rectsF.Length == 0)
{
return RectangleF.Empty;
}
float left = rectsF[startIndex].Left;
float top = rectsF[startIndex].Top;
float right = rectsF[startIndex].Right;
float bottom = rectsF[startIndex].Bottom;
for (int i = startIndex + 1; i < startIndex + length; ++i)
{
RectangleF rectF = rectsF[i];
if (rectF.Left < left)
{
left = rectF.Left;
}
if (rectF.Top < top)
{
top = rectF.Top;
}
if (rectF.Right > right)
{
right = rectF.Right;
}
if (rectF.Bottom > bottom)
{
bottom = rectF.Bottom;
}
}
return RectangleF.FromLTRB(left, top, right, bottom);
}
public static RectangleF GetTraceBounds(PointF[] pointsF, int startIndex, int length)
{
if (pointsF.Length == 0)
{
return RectangleF.Empty;
}
float left = pointsF[startIndex].X;
float top = pointsF[startIndex].Y;
float right = 1 + pointsF[startIndex].X;
float bottom = 1 + pointsF[startIndex].Y;
for (int i = startIndex + 1; i < startIndex + length; ++i)
{
PointF pointF = pointsF[i];
if (pointF.X < left)
{
left = pointF.X;
}
if (pointF.Y < top)
{
top = pointF.Y;
}
if (pointF.X > right)
{
right = pointF.X;
}
if (pointF.Y > bottom)
{
bottom = pointF.Y;
}
}
return RectangleF.FromLTRB(left, top, right, bottom);
}
public static Rectangle GetTraceBounds(Point[] points, int startIndex, int length)
{
if (points.Length == 0)
{
return Rectangle.Empty;
}
int left = points[startIndex].X;
int top = points[startIndex].Y;
int right = 1 + points[startIndex].X;
int bottom = 1 + points[startIndex].Y;
for (int i = startIndex + 1; i < startIndex + length; ++i)
{
Point point = points[i];
if (point.X < left)
{
left = point.X;
}
if (point.Y < top)
{
top = point.Y;
}
if (point.X > right)
{
right = point.X;
}
if (point.Y > bottom)
{
bottom = point.Y;
}
}
return Rectangle.FromLTRB(left, top, right, bottom);
}
/// <summary>
/// Allows you to find the bounding box for a "region" that is described as an
/// array of bounding boxes.
/// </summary>
/// <param name="rectsF">The "region" you want to find a bounding box for.</param>
/// <returns>A RectangleF structure that surrounds the Region.</returns>
public static Rectangle GetRegionBounds(Rectangle[] rects, int startIndex, int length)
{
if (rects.Length == 0)
{
return Rectangle.Empty;
}
int left = rects[startIndex].Left;
int top = rects[startIndex].Top;
int right = rects[startIndex].Right;
int bottom = rects[startIndex].Bottom;
for (int i = startIndex + 1; i < startIndex + length; ++i)
{
Rectangle rect = rects[i];
if (rect.Left < left)
{
left = rect.Left;
}
if (rect.Top < top)
{
top = rect.Top;
}
if (rect.Right > right)
{
right = rect.Right;
}
if (rect.Bottom > bottom)
{
bottom = rect.Bottom;
}
}
return Rectangle.FromLTRB(left, top, right, bottom);
}
public static RectangleF GetRegionBounds(RectangleF[] rectsF)
{
return GetRegionBounds(rectsF, 0, rectsF.Length);
}
public static Rectangle GetRegionBounds(Rectangle[] rects)
{
return GetRegionBounds(rects, 0, rects.Length);
}
private static float DistanceSquared(RectangleF[] rectsF, int indexA, int indexB)
{
PointF centerA = new PointF(rectsF[indexA].Left + (rectsF[indexA].Width / 2), rectsF[indexA].Top + (rectsF[indexA].Height / 2));
PointF centerB = new PointF(rectsF[indexB].Left + (rectsF[indexB].Width / 2), rectsF[indexB].Top + (rectsF[indexB].Height / 2));
return ((centerA.X - centerB.X) * (centerA.X - centerB.X)) +
((centerA.Y - centerB.Y) * (centerA.Y - centerB.Y));
}
/// <summary>
/// Simplifies a Region into N number of bounding boxes.
/// </summary>
/// <param name="region">The Region to simplify.</param>
/// <param name="complexity">The maximum number of bounding boxes to return, or 0 for however many are necessary (equivalent to using Region.GetRegionScans).</param>
/// <returns></returns>
public static Rectangle[] SimplifyRegion(PdnRegion region, int complexity)
{
Rectangle[] rects = region.GetRegionScansReadOnlyInt();
return SimplifyRegion(rects, complexity);
}
public static Rectangle[] SimplifyRegion(Rectangle[] rects, int complexity)
{
if (complexity == 0 || rects.Length < complexity)
{
return (Rectangle[])rects.Clone();
}
Rectangle[] boxes = new Rectangle[complexity];
for (int i = 0; i < complexity; ++i)
{
int startIndex = (i * rects.Length) / complexity;
int length = Math.Min(rects.Length, ((i + 1) * rects.Length) / complexity) - startIndex;
boxes[i] = GetRegionBounds(rects, startIndex, length);
}
return boxes;
}
public static RectangleF[] SimplifyTrace(PointF[] pointsF, int complexity)
{
if (complexity == 0 ||
(pointsF.Length - 1) < complexity)
{
return PointsToRectangles(pointsF);
}
RectangleF[] boxes = new RectangleF[complexity];
int parLength = pointsF.Length - 1; // "(points as Rectangles).Length"
for (int i = 0; i < complexity; ++i)
{
int startIndex = (i * parLength) / complexity;
int length = Math.Min(parLength, ((i + 1) * parLength) / complexity) - startIndex;
boxes[i] = GetTraceBounds(pointsF, startIndex, length + 1);
}
return boxes;
}
public static Rectangle[] SimplifyTrace(PdnGraphicsPath trace, int complexity)
{
return SimplifyRegion(TraceToRectangles(trace), complexity);
}
public static Rectangle[] SimplifyTrace(PdnGraphicsPath trace)
{
return SimplifyTrace(trace, DefaultSimplificationFactor);
}
public static Rectangle[] TraceToRectangles(PdnGraphicsPath trace, int complexity)
{
int pointCount = trace.PointCount;
if (pointCount == 0)
{
return new Rectangle[0];
}
PointF[] pathPoints = trace.PathPoints;
byte[] pathTypes = trace.PathTypes;
int figureStart = 0;
// first get count of rectangles we'll need
Rectangle[] rects = new Rectangle[pointCount];
for (int i = 0; i < pointCount; ++i)
{
byte type = pathTypes[i];
Point a = Point.Truncate(pathPoints[i]);
Point b;
if ((type & (byte)PathPointType.CloseSubpath) != 0)
{
b = Point.Truncate(pathPoints[figureStart]);
figureStart = i + 1;
}
else
{
b = Point.Truncate(pathPoints[i + 1]);
}
rects[i] = Utility.PointsToRectangle(a, b);
}
return rects;
}
public static Rectangle[] TraceToRectangles(PdnGraphicsPath trace)
{
return TraceToRectangles(trace, DefaultSimplificationFactor);
}
public static RectangleF[] SimplifyTrace(PointF[] pointsF)
{
return SimplifyTrace(pointsF, defaultSimplificationFactor);
}
public static Rectangle[] SimplifyAndInflateRegion(Rectangle[] rects, int complexity, int inflationAmount)
{
Rectangle[] simplified = SimplifyRegion(rects, complexity);
for (int i = 0; i < simplified.Length; ++i)
{
simplified[i].Inflate(inflationAmount, inflationAmount);
}
return simplified;
}
public static Rectangle[] SimplifyAndInflateRegion(Rectangle[] rects)
{
return SimplifyAndInflateRegion(rects, defaultSimplificationFactor, 1);
}
public static PdnRegion SimplifyAndInflateRegion(PdnRegion region, int complexity, int inflationAmount)
{
Rectangle[] rectRegion = SimplifyRegion(region, complexity);
for (int i = 0; i < rectRegion.Length; ++i)
{
rectRegion[i].Inflate(inflationAmount, inflationAmount);
}
return RectanglesToRegion(rectRegion);
}
public static PdnRegion SimplifyAndInflateRegion(PdnRegion region)
{
return SimplifyAndInflateRegion(region, defaultSimplificationFactor, 1);
}
public static RectangleF[] TranslateRectangles(RectangleF[] rectsF, PointF offset)
{
RectangleF[] retRectsF = new RectangleF[rectsF.Length];
int i = 0;
foreach (RectangleF rectF in rectsF)
{
retRectsF[i] = new RectangleF(rectF.X + offset.X, rectF.Y + offset.Y, rectF.Width, rectF.Height);
++i;
}
return retRectsF;
}
public static Rectangle[] TranslateRectangles(Rectangle[] rects, int dx, int dy)
{
Rectangle[] retRects = new Rectangle[rects.Length];
for (int i = 0; i < rects.Length; ++i)
{
retRects[i] = new Rectangle(rects[i].X + dx, rects[i].Y + dy, rects[i].Width, rects[i].Height);
}
return retRects;
}
public static void TranslatePointsInPlace(PointF[] ptsF, float dx, float dy)
{
for (int i = 0; i < ptsF.Length; ++i)
{
ptsF[i].X += dx;
ptsF[i].Y += dy;
}
}
public static void TranslatePointsInPlace(Point[] pts, int dx, int dy)
{
for (int i = 0; i < pts.Length; ++i)
{
pts[i].X += dx;
pts[i].Y += dy;
}
}
public static Rectangle[] TruncateRectangles(RectangleF[] rectsF)
{
Rectangle[] rects = new Rectangle[rectsF.Length];
for (int i = 0; i < rectsF.Length; ++i)
{
rects[i] = Rectangle.Truncate(rectsF[i]);
}
return rects;
}
public static Point[] TruncatePoints(PointF[] pointsF)
{
Point[] points = new Point[pointsF.Length];
for (int i = 0; i < pointsF.Length; ++i)
{
points[i] = Point.Truncate(pointsF[i]);
}
return points;
}
public static Point[] RoundPoints(PointF[] pointsF)
{
Point[] points = new Point[pointsF.Length];
for (int i = 0; i < pointsF.Length; ++i)
{
points[i] = Point.Round(pointsF[i]);
}
return points;
}
/// <summary>
/// The Sutherland-Hodgman clipping alrogithm.
/// http://ezekiel.vancouver.wsu.edu/~cs442/lectures/clip/clip/index.html
///
/// # Clipping a convex polygon to a convex region (e.g., rectangle) will always produce a convex polygon (or no polygon if completely outside the clipping region).
/// # Clipping a concave polygon to a rectangle may produce several polygons (see figure above) or, as the following algorithm does, produce a single, possibly degenerate, polygon.
/// # Divide and conquer: Clip entire polygon against a single edge (i.e., half-plane). Repeat for each edge in the clipping region.
///
/// The input is a sequence of vertices: {v0, v1, ... vn} given as an array of Points
/// the result is a sequence of vertices, given as an array of Points. This result may have
/// less than, equal, more than, or 0 vertices.
/// </summary>
/// <param name="vertices"></param>
/// <returns></returns>
public static List<PointF> SutherlandHodgman(RectangleF bounds, List<PointF> v)
{
List<PointF> p1 = SutherlandHodgmanOneAxis(bounds, RectangleEdge.Left, v);
List<PointF> p2 = SutherlandHodgmanOneAxis(bounds, RectangleEdge.Right, p1);
List<PointF> p3 = SutherlandHodgmanOneAxis(bounds, RectangleEdge.Top, p2);
List<PointF> p4 = SutherlandHodgmanOneAxis(bounds, RectangleEdge.Bottom, p3);
return p4;
}
private enum RectangleEdge
{
Left,
Right,
Top,
Bottom
}
private static List<PointF> SutherlandHodgmanOneAxis(RectangleF bounds, RectangleEdge edge, List<PointF> v)
{
if (v.Count == 0)
{
return new List<PointF>();
}
List<PointF> polygon = new List<PointF>();
PointF s = v[v.Count - 1];
for (int i = 0; i < v.Count; ++i)
{
PointF p = v[i];
bool pIn = IsInside(bounds, edge, p);
bool sIn = IsInside(bounds, edge, s);
if (sIn && pIn)
{
// case 1: inside -> inside
polygon.Add(p);
}
else if (sIn && !pIn)
{
// case 2: inside -> outside
polygon.Add(LineIntercept(bounds, edge, s, p));
}
else if (!sIn && !pIn)
{
// case 3: outside -> outside
// emit nothing
}
else if (!sIn && pIn)
{
// case 4: outside -> inside
polygon.Add(LineIntercept(bounds, edge, s, p));
polygon.Add(p);
}
s = p;
}
return polygon;
}
private static bool IsInside(RectangleF bounds, RectangleEdge edge, PointF p)
{
switch (edge)
{
case RectangleEdge.Left:
return !(p.X < bounds.Left);
case RectangleEdge.Right:
return !(p.X >= bounds.Right);
case RectangleEdge.Top:
return !(p.Y < bounds.Top);
case RectangleEdge.Bottom:
return !(p.Y >= bounds.Bottom);
default:
throw new InvalidEnumArgumentException("edge");
}
}
private static Point LineIntercept(Rectangle bounds, RectangleEdge edge, Point a, Point b)
{
if (a == b)
{
return a;
}
switch (edge)
{
case RectangleEdge.Bottom:
if (b.Y == a.Y)
{
throw new ArgumentException("no intercept found");
}
return new Point(a.X + (((b.X - a.X) * (bounds.Bottom - a.Y)) / (b.Y - a.Y)), bounds.Bottom);
case RectangleEdge.Left:
if (b.X == a.X)
{
throw new ArgumentException("no intercept found");
}
return new Point(bounds.Left, a.Y + (((b.Y - a.Y) * (bounds.Left - a.X)) / (b.X - a.X)));
case RectangleEdge.Right:
if (b.X == a.X)
{
throw new ArgumentException("no intercept found");
}
return new Point(bounds.Right, a.Y + (((b.Y - a.Y) * (bounds.Right - a.X)) / (b.X - a.X)));
case RectangleEdge.Top:
if (b.Y == a.Y)
{
throw new ArgumentException("no intercept found");
}
return new Point(a.X + (((b.X - a.X) * (bounds.Top - a.Y)) / (b.Y - a.Y)), bounds.Top);
}
throw new ArgumentException("no intercept found");
}
private static PointF LineIntercept(RectangleF bounds, RectangleEdge edge, PointF a, PointF b)
{
if (a == b)
{
return a;
}
switch (edge)
{
case RectangleEdge.Bottom:
if (b.Y == a.Y)
{
throw new ArgumentException("no intercept found");
}
return new PointF(a.X + (((b.X - a.X) * (bounds.Bottom - a.Y)) / (b.Y - a.Y)), bounds.Bottom);
case RectangleEdge.Left:
if (b.X == a.X)
{
throw new ArgumentException("no intercept found");
}
return new PointF(bounds.Left, a.Y + (((b.Y - a.Y) * (bounds.Left - a.X)) / (b.X - a.X)));
case RectangleEdge.Right:
if (b.X == a.X)
{
throw new ArgumentException("no intercept found");
}
return new PointF(bounds.Right, a.Y + (((b.Y - a.Y) * (bounds.Right - a.X)) / (b.X - a.X)));
case RectangleEdge.Top:
if (b.Y == a.Y)
{
throw new ArgumentException("no intercept found");
}
return new PointF(a.X + (((b.X - a.X) * (bounds.Top - a.Y)) / (b.Y - a.Y)), bounds.Top);
}
throw new ArgumentException("no intercept found");
}
public static Point[] GetLinePoints(Point first, Point second)
{
Point[] coords = null;
int x1 = first.X;
int y1 = first.Y;
int x2 = second.X;
int y2 = second.Y;
int dx = x2 - x1;
int dy = y2 - y1;
int dxabs = Math.Abs(dx);
int dyabs = Math.Abs(dy);
int px = x1;
int py = y1;
int sdx = Math.Sign(dx);
int sdy = Math.Sign(dy);
int x = 0;
int y = 0;
if (dxabs > dyabs)
{
coords = new Point[dxabs + 1];
for (int i = 0; i <= dxabs; i++)
{
y += dyabs;
if (y >= dxabs)
{
y -= dxabs;
py += sdy;
}
coords[i] = new Point(px, py);
px += sdx;
}
}
else
// had to add in this cludge for slopes of 1 ... wasn't drawing half the line
if (dxabs == dyabs)
{
coords = new Point[dxabs + 1];
for (int i = 0; i <= dxabs; i++)
{
coords[i] = new Point(px, py);
px += sdx;
py += sdy;
}
}
else
{
coords = new Point[dyabs + 1];
for (int i = 0; i <= dyabs; i++)
{
x += dxabs;
if (x >= dyabs)
{
x -= dyabs;
px += sdx;
}
coords[i] = new Point(px, py);
py += sdy;
}
}
return coords;
}
public static long GetTimeMs()
{
return Utility.TicksToMs(DateTime.Now.Ticks);
}
/// <summary>
/// Returns the Distance between two points
/// </summary>
public static float Distance(PointF a, PointF b)
{
return Magnitude(new PointF(a.X - b.X, a.Y - b.Y));
}
/// <summary>
/// Returns the Magnitude (distance to origin) of a point
/// </summary>
// TODO: In v4.0 codebase, turn this into an extension method
public static float Magnitude(PointF p)
{
return (float)Math.Sqrt(p.X * p.X + p.Y * p.Y);
}
// TODO: In v4.0 codebase, turn this into an extension method
public static double Clamp(double x, double min, double max)
{
if (x < min)
{
return min;
}
else if (x > max)
{
return max;
}
else
{
return x;
}
}
// TODO: In v4.0 codebase, turn this into an extension method
public static float Clamp(float x, float min, float max)
{
if (x < min)
{
return min;
}
else if (x > max)
{
return max;
}
else
{
return x;
}
}
// TODO: In v4.0 codebase, turn this into an extension method
public static int Clamp(int x, int min, int max)
{
if (x < min)
{
return min;
}
else if (x > max)
{
return max;
}
else
{
return x;
}
}
public static byte ClampToByte(double x)
{
if (x > 255)
{
return 255;
}
else if (x < 0)
{
return 0;
}
else
{
return (byte)x;
}
}
public static byte ClampToByte(float x)
{
if (x > 255)
{
return 255;
}
else if (x < 0)
{
return 0;
}
else
{
return (byte)x;
}
}
public static byte ClampToByte(int x)
{
if (x > 255)
{
return 255;
}
else if (x < 0)
{
return 0;
}
else
{
return (byte)x;
}
}
public static float Lerp(float from, float to, float frac)
{
return (from + frac * (to - from));
}
public static double Lerp(double from, double to, double frac)
{
return (from + frac * (to - from));
}
public static PointF Lerp(PointF from, PointF to, float frac)
{
return new PointF(Lerp(from.X, to.X, frac), Lerp(from.Y, to.Y, frac));
}
public static int ColorDifference(ColorBgra a, ColorBgra b)
{
return (int)Math.Ceiling(Math.Sqrt(ColorDifferenceSquared(a, b)));
}
public static int ColorDifferenceSquared(ColorBgra a, ColorBgra b)
{
int diffSq = 0, tmp;
tmp = a.R - b.R;
diffSq += tmp * tmp;
tmp = a.G - b.G;
diffSq += tmp * tmp;
tmp = a.B - b.B;
diffSq += tmp * tmp;
return diffSq / 3;
}
public static DialogResult ShowDialog(Form showMe, IWin32Window owner)
{
DialogResult dr;
if (showMe is PdnBaseForm)
{
PdnBaseForm showMe2 = (PdnBaseForm)showMe;
double oldOpacity = showMe2.Opacity;
showMe2.Opacity = 0.9;
dr = showMe2.ShowDialog(owner);
showMe2.Opacity = oldOpacity;
}
else
{
double oldOpacity = showMe.Opacity;
showMe.Opacity = 0.9;
dr = showMe.ShowDialog(owner);
showMe.Opacity = oldOpacity;
}
Control control = owner as Control;
if (control != null)
{
Form form = control.FindForm();
if (form != null)
{
form.Activate();
}
control.Update();
}
return dr;
}
public static void ShowHelp(Control parent)
{
string helpFileUrlFormat = PdnResources.GetString("HelpFile.Url.Format");
string baseSiteUrl = InvariantStrings.WebsiteUrl;
string helpFileUrl = string.Format(helpFileUrlFormat, baseSiteUrl);
PdnInfo.OpenUrl(parent, helpFileUrl);
}
/// <summary>
/// Reads a 16-bit unsigned integer from a Stream in little-endian format.
/// </summary>
/// <param name="stream"></param>
/// <returns>-1 on failure, else the 16-bit unsigned integer that was read.</returns>
public static int ReadUInt16(Stream stream)
{
int byte1 = stream.ReadByte();
if (byte1 == -1)
{
return -1;
}
int byte2 = stream.ReadByte();
if (byte2 == -1)
{
return -1;
}
return byte1 + (byte2 << 8);
}
public static void WriteUInt16(Stream stream, UInt16 word)
{
stream.WriteByte((byte)(word & 0xff));
stream.WriteByte((byte)(word >> 8));
}
public static void WriteUInt24(Stream stream, int uint24)
{
stream.WriteByte((byte)(uint24 & 0xff));
stream.WriteByte((byte)((uint24 >> 8) & 0xff));
stream.WriteByte((byte)((uint24 >> 16) & 0xff));
}
public static void WriteUInt32(Stream stream, UInt32 uint32)
{
stream.WriteByte((byte)(uint32 & 0xff));
stream.WriteByte((byte)((uint32 >> 8) & 0xff));
stream.WriteByte((byte)((uint32 >> 16) & 0xff));
stream.WriteByte((byte)((uint32 >> 24) & 0xff));
}
/// <summary>
/// Reads a 24-bit unsigned integer from a Stream in little-endian format.
/// </summary>
/// <param name="stream"></param>
/// <returns>-1 on failure, else the 24-bit unsigned integer that was read.</returns>
public static int ReadUInt24(Stream stream)
{
int byte1 = stream.ReadByte();
if (byte1 == -1)
{
return -1;
}
int byte2 = stream.ReadByte();
if (byte2 == -1)
{
return -1;
}
int byte3 = stream.ReadByte();
if (byte3 == -1)
{
return -1;
}
return byte1 + (byte2 << 8) + (byte3 << 16);
}
/// <summary>
/// Reads a 32-bit unsigned integer from a Stream in little-endian format.
/// </summary>
/// <param name="stream"></param>
/// <returns>-1 on failure, else the 32-bit unsigned integer that was read.</returns>
public static long ReadUInt32(Stream stream)
{
int byte1 = stream.ReadByte();
if (byte1 == -1)
{
return -1;
}
int byte2 = stream.ReadByte();
if (byte2 == -1)
{
return -1;
}
int byte3 = stream.ReadByte();
if (byte3 == -1)
{
return -1;
}
int byte4 = stream.ReadByte();
if (byte4 == -1)
{
return -1;
}
return unchecked((long)((uint)(byte1 + (byte2 << 8) + (byte3 << 16) + (byte4 << 24))));
}
public static int ReadFromStream(Stream input, byte[] buffer, int offset, int count)
{
int totalBytesRead = 0;
while (totalBytesRead < count)
{
int bytesRead = input.Read(buffer, offset + totalBytesRead, count - totalBytesRead);
if (bytesRead == 0)
{
throw new IOException("ran out of data");
}
totalBytesRead += bytesRead;
}
return totalBytesRead;
}
public static long CopyStream(Stream input, Stream output, long maxBytes)
{
long bytesCopied = 0;
byte[] buffer = new byte[4096];
while (true)
{
int bytesRead = input.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
else
{
int bytesToCopy;
if (maxBytes != -1 && (bytesCopied + bytesRead) > maxBytes)
{
bytesToCopy = (int)(maxBytes - bytesCopied);
}
else
{
bytesToCopy = bytesRead;
}
output.Write(buffer, 0, bytesRead);
bytesCopied += bytesToCopy;
if (bytesToCopy != bytesRead)
{
break;
}
}
}
return bytesCopied;
}
public static long CopyStream(Stream input, Stream output)
{
return CopyStream(input, output, -1);
}
private struct Edge
{
public int miny; // int
public int maxy; // int
public int x; // fixed point: 24.8
public int dxdy; // fixed point: 24.8
public Edge(int miny, int maxy, int x, int dxdy)
{
this.miny = miny;
this.maxy = maxy;
this.x = x;
this.dxdy = dxdy;
}
}
public static Scanline[] GetScans(Point[] vertices)
{
return GetScans(vertices, 0, vertices.Length);
}
public static Scanline[] GetScans(Point[] vertices, int startIndex, int length)
{
if (length > vertices.Length - startIndex)
{
throw new ArgumentException("out of bounds: length > vertices.Length - startIndex");
}
int ymax = 0;
// Build edge table
Edge[] edgeTable = new Edge[length];
int edgeCount = 0;
for (int i = startIndex; i < startIndex + length; ++i)
{
Point top = vertices[i];
Point bottom = vertices[(((i + 1) - startIndex) % length) + startIndex];
int dy;
if (top.Y > bottom.Y)
{
Point temp = top;
top = bottom;
bottom = temp;
}
dy = bottom.Y - top.Y;
if (dy != 0)
{
edgeTable[edgeCount] = new Edge(top.Y, bottom.Y, top.X << 8, (((bottom.X - top.X) << 8) / dy));
ymax = Math.Max(ymax, bottom.Y);
++edgeCount;
}
}
// Sort edge table by miny
for (int i = 0; i < edgeCount - 1; ++i)
{
int min = i;
for (int j = i + 1; j < edgeCount; ++j)
{
if (edgeTable[j].miny < edgeTable[min].miny)
{
min = j;
}
}
if (min != i)
{
Edge temp = edgeTable[min];
edgeTable[min] = edgeTable[i];
edgeTable[i] = temp;
}
}
// Compute how many scanlines we will be emitting
int scanCount = 0;
int activeLow = 0;
int activeHigh = 0;
int yscan1 = edgeTable[0].miny;
// we assume that edgeTable[0].miny == yscan
while (activeHigh < edgeCount - 1 &&
edgeTable[activeHigh + 1].miny == yscan1)
{
++activeHigh;
}
while (yscan1 <= ymax)
{
// Find new edges where yscan == miny
while (activeHigh < edgeCount - 1 &&
edgeTable[activeHigh + 1].miny == yscan1)
{
++activeHigh;
}
int count = 0;
for (int i = activeLow; i <= activeHigh; ++i)
{
if (edgeTable[i].maxy > yscan1)
{
++count;
}
}
scanCount += count / 2;
++yscan1;
// Remove edges where yscan == maxy
while (activeLow < edgeCount - 1 &&
edgeTable[activeLow].maxy <= yscan1)
{
++activeLow;
}
if (activeLow > activeHigh)
{
activeHigh = activeLow;
}
}
// Allocate scanlines that we'll return
Scanline[] scans = new Scanline[scanCount];
// Active Edge Table (AET): it is indices into the Edge Table (ET)
int[] active = new int[edgeCount];
int activeCount = 0;
int yscan2 = edgeTable[0].miny;
int scansIndex = 0;
// Repeat until both the ET and AET are empty
while (yscan2 <= ymax)
{
// Move any edges from the ET to the AET where yscan == miny
for (int i = 0; i < edgeCount; ++i)
{
if (edgeTable[i].miny == yscan2)
{
active[activeCount] = i;
++activeCount;
}
}
// Sort the AET on x
for (int i = 0; i < activeCount - 1; ++i)
{
int min = i;
for (int j = i + 1; j < activeCount; ++j)
{
if (edgeTable[active[j]].x < edgeTable[active[min]].x)
{
min = j;
}
}
if (min != i)
{
int temp = active[min];
active[min] = active[i];
active[i] = temp;
}
}
// For each pair of entries in the AET, fill in pixels between their info
for (int i = 0; i < activeCount; i += 2)
{
Edge el = edgeTable[active[i]];
Edge er = edgeTable[active[i + 1]];
int startx = (el.x + 0xff) >> 8; // ceil(x)
int endx = er.x >> 8; // floor(x)
scans[scansIndex] = new Scanline(startx, yscan2, endx - startx);
++scansIndex;
}
++yscan2;
// Remove from the AET any edge where yscan == maxy
int k = 0;
while (k < activeCount && activeCount > 0)
{
if (edgeTable[active[k]].maxy == yscan2)
{
// remove by shifting everything down one
for (int j = k + 1; j < activeCount; ++j)
{
active[j - 1] = active[j];
}
--activeCount;
}
else
{
++k;
}
}
// Update x for each entry in AET
for (int i = 0; i < activeCount; ++i)
{
edgeTable[active[i]].x += edgeTable[active[i]].dxdy;
}
}
return scans;
}
public static PointF TransformOnePoint(Matrix matrix, PointF ptF)
{
PointF[] ptFs = new PointF[1] { ptF };
matrix.TransformPoints(ptFs);
return ptFs[0];
}
public static PointF TransformOneVector(Matrix matrix, PointF ptF)
{
PointF[] ptFs = new PointF[1] { ptF };
matrix.TransformVectors(ptFs);
return ptFs[0];
}
public static PointF NormalizeVector(PointF vecF)
{
float magnitude = Magnitude(vecF);
vecF.X /= magnitude;
vecF.Y /= magnitude;
return vecF;
}
public static PointF NormalizeVector2(PointF vecF)
{
float magnitude = Magnitude(vecF);
if (magnitude == 0)
{
vecF.X = 0;
vecF.Y = 0;
}
else
{
vecF.X /= magnitude;
vecF.Y /= magnitude;
}
return vecF;
}
public static void NormalizeVectors(PointF[] vecsF)
{
for (int i = 0; i < vecsF.Length; ++i)
{
vecsF[i] = NormalizeVector(vecsF[i]);
}
}
public static PointF RotateVector(PointF vecF, float angleDelta)
{
angleDelta *= (float)( Math.PI / 180.0);
float vecFLen = Magnitude(vecF);
float vecFAngle = angleDelta + (float)Math.Atan2(vecF.Y, vecF.X);
vecF.X = (float)Math.Cos(vecFAngle);
vecF.Y = (float)Math.Sin(vecFAngle);
return vecF;
}
public static void RotateVectors(PointF[] vecFs, float angleDelta)
{
for (int i = 0; i < vecFs.Length; ++i)
{
vecFs[i] = RotateVector(vecFs[i], angleDelta);
}
}
public static PointF MultiplyVector(PointF vecF, float scalar)
{
return new PointF(vecF.X * scalar, vecF.Y * scalar);
}
public static PointF AddVectors(PointF a, PointF b)
{
return new PointF(a.X + b.X, a.Y + b.Y);
}
public static PointF SubtractVectors(PointF lhs, PointF rhs)
{
return new PointF(lhs.X - rhs.X, lhs.Y - rhs.Y);
}
public static PointF NegateVector(PointF v)
{
return new PointF(-v.X, -v.Y);
}
public static float GetAngleOfTransform(Matrix matrix)
{
PointF[] pts = new PointF[] { new PointF(1.0f, 0.0f) };
matrix.TransformVectors(pts);
double atan2 = Math.Atan2(pts[0].Y, pts[0].X);
double angle = atan2 * (180.0f / Math.PI);
return (float)angle;
}
public static bool IsTransformFlipped(Matrix matrix)
{
PointF ptX = new PointF(1.0f, 0.0f);
PointF ptXT = Utility.TransformOneVector(matrix, ptX);
double atan2X = Math.Atan2(ptXT.Y, ptXT.X);
double angleX = atan2X * (180.0 / Math.PI);
PointF ptY = new PointF(0.0f, 1.0f);
PointF ptYT = Utility.TransformOneVector(matrix, ptY);
double atan2Y = Math.Atan2(ptYT.Y, ptYT.X);
double angleY = (atan2Y * (180.0 / Math.PI)) - 90.0;
while (angleX < 0)
{
angleX += 360;
}
while (angleY < 0)
{
angleY += 360;
}
double angleDelta = Math.Abs(angleX - angleY);
return angleDelta > 1.0 && angleDelta < 359.0;
}
/// <summary>
/// Calculates the dot product of two vectors.
/// </summary>
public static float DotProduct(PointF lhs, PointF rhs)
{
return lhs.X * rhs.X + lhs.Y * rhs.Y;
}
/// <summary>
/// Calculates the orthogonal projection of y on to u.
/// yhat = u * ((y dot u) / (u dot u))
/// z = y - yhat
/// Section 6.2 (pg. 381) of Linear Algebra and its Applications, Second Edition, by David C. Lay
/// </summary>
/// <param name="y">The vector to decompose</param>
/// <param name="u">The non-zero vector to project y on to</param>
/// <param name="yhat">The orthogonal projection of y onto u</param>
/// <param name="yhatLen">The length of yhat such that yhat = yhatLen * u</param>
/// <param name="z">The component of y orthogonal to u</param>
/// <remarks>
/// As a special case, if u=(0,0) the results are all zero.
/// </remarks>
public static void GetProjection(PointF y, PointF u, out PointF yhat, out float yhatLen, out PointF z)
{
if (u.X == 0 && u.Y == 0)
{
yhat = new PointF(0, 0);
yhatLen = 0;
z = new PointF(0, 0);
}
else
{
float yDotU = DotProduct(y, u);
float uDotU = DotProduct(u, u);
yhatLen = yDotU / uDotU;
yhat = MultiplyVector(u, yhatLen);
z = SubtractVectors(y, yhat);
}
}
public static int GreatestCommonDivisor(int a, int b)
{
int r;
if (a < b)
{
r = a;
a = b;
b = r;
}
do
{
r = a % b;
a = b;
b = r;
} while (r != 0);
return a;
}
public static void Swap(ref int a, ref int b)
{
int t;
t = a;
a = b;
b = t;
}
public static void Swap<T>(ref T a, ref T b)
{
T t;
t = a;
a = b;
b = t;
}
private static byte[] DownloadSmallFile(Uri uri, WebProxy proxy)
{
WebRequest request = WebRequest.Create(uri);
if (proxy != null)
{
request.Proxy = proxy;
}
request.Timeout = 5000;
WebResponse response = request.GetResponse();
Stream stream = response.GetResponseStream();
try
{
byte[] buffer = new byte[8192];
int offset = 0;
while (offset < buffer.Length)
{
int bytesRead = stream.Read(buffer, offset, buffer.Length - offset);
if (bytesRead == 0)
{
byte[] smallerBuffer = new byte[offset + bytesRead];
for (int i = 0; i < offset + bytesRead; ++i)
{
smallerBuffer[i] = buffer[i];
}
buffer = smallerBuffer;
}
offset += bytesRead;
}
return buffer;
}
finally
{
if (stream != null)
{
stream.Close();
stream = null;
}
if (response != null)
{
response.Close();
response = null;
}
}
}
public static T[] RepeatArray<T>(T[] array, int repeatCount)
{
T[] returnArray = new T[repeatCount * array.Length];
for (int i = 0; i < repeatCount; ++i)
{
for (int j = 0; j < array.Length; ++j)
{
int index = (i * array.Length) + j;
returnArray[index] = array[j];
}
}
return returnArray;
}
/// <summary>
/// Downloads a small file (max 8192 bytes) and returns it as a byte array.
/// </summary>
/// <returns>The contents of the file if downloaded successfully.</returns>
public static byte[] DownloadSmallFile(Uri uri)
{
byte[] bytes = null;
Exception exception = null;
WebProxy[] proxiesPre = Network.GetProxyList();
WebProxy[] proxies = RepeatArray(proxiesPre, 2); // see bug #1942
foreach (WebProxy proxy in proxies)
{
try
{
bytes = DownloadSmallFile(uri, proxy);
exception = null;
}
catch (Exception ex)
{
exception = ex;
bytes = null;
}
if (bytes != null)
{
break;
}
}
if (exception != null)
{
WebException we = exception as WebException;
if (we != null)
{
throw new WebException(null, we, we.Status, we.Response);
}
else
{
throw new ApplicationException("An exception occurred while trying to download '" + uri.ToString() + "'", exception);
}
}
return bytes;
}
private static void DownloadFile(Uri uri, Stream output, WebProxy proxy, ProgressEventHandler progressCallback)
{
WebRequest request = WebRequest.Create(uri);
if (proxy != null)
{
request.Proxy = proxy;
}
request.Timeout = 5000;
WebResponse response = request.GetResponse();
Stream stream = null;
SiphonStream siphonOutputStream = null;
try
{
stream = response.GetResponseStream();
siphonOutputStream = new SiphonStream(output, 1024); // monitor the completion of writes to 'output'
siphonOutputStream.IOFinished +=
delegate(object sender, IOEventArgs e)
{
if (progressCallback != null)
{
double percent = 100.0 * Utility.Clamp(((double)e.Position / (double)response.ContentLength), 0, 100);
progressCallback(uri, new ProgressEventArgs(percent));
}
};
Utility.CopyStream(stream, siphonOutputStream, 128 * 1024 * 1024); // cap at 128mb
siphonOutputStream.Flush();
}
finally
{
if (siphonOutputStream != null)
{
siphonOutputStream.Close();
siphonOutputStream = null;
}
if (stream != null)
{
stream.Close();
stream = null;
}
if (response != null)
{
response.Close();
response = null;
}
}
}
/// <summary>
/// Download a file (max 128MB) and saves it to the given Stream.
/// </summary>
public static void DownloadFile(Uri uri, Stream output, ProgressEventHandler progressCallback)
{
long startPosition = output.Position;
Exception exception = null;
WebProxy[] proxies = Network.GetProxyList();
foreach (WebProxy proxy in proxies)
{
bool success = false;
try
{
DownloadFile(uri, output, proxy, progressCallback);
exception = null;
success = true;
}
catch (Exception ex)
{
exception = ex;
}
// If the output stream was written to, then we know
// that we were either successful in downloading the
// file, or there was an error unrelated to using the
// proxy (maybe they unplugged the network cable, who
// knows!)
if (output.Position != startPosition || success)
{
break;
}
}
if (exception != null)
{
WebException we = exception as WebException;
if (we != null)
{
throw new WebException(null, we, we.Status, we.Response);
}
else
{
throw new ApplicationException("An exception occurred while trying to download '" + uri.ToString() + "'", exception);
}
}
}
public static byte FastScaleByteByByte(byte a, byte b)
{
int r1 = a * b + 0x80;
int r2 = ((r1 >> 8) + r1) >> 8;
return (byte)r2;
}
public static int FastDivideShortByByte(ushort n, byte d)
{
int i = d * 3;
uint m = masTable[i];
uint a = masTable[i + 1];
uint s = masTable[i + 2];
uint nTimesMPlusA = unchecked((n * m) + a);
uint shifted = nTimesMPlusA >> (int)s;
int r = (int)shifted;
return r;
}
// i = z * 3;
// (x / z) = ((x * masTable[i]) + masTable[i + 1]) >> masTable[i + 2)
private static readonly uint[] masTable =
{
0x00000000, 0x00000000, 0, // 0
0x00000001, 0x00000000, 0, // 1
0x00000001, 0x00000000, 1, // 2
0xAAAAAAAB, 0x00000000, 33, // 3
0x00000001, 0x00000000, 2, // 4
0xCCCCCCCD, 0x00000000, 34, // 5
0xAAAAAAAB, 0x00000000, 34, // 6
0x49249249, 0x49249249, 33, // 7
0x00000001, 0x00000000, 3, // 8
0x38E38E39, 0x00000000, 33, // 9
0xCCCCCCCD, 0x00000000, 35, // 10
0xBA2E8BA3, 0x00000000, 35, // 11
0xAAAAAAAB, 0x00000000, 35, // 12
0x4EC4EC4F, 0x00000000, 34, // 13
0x49249249, 0x49249249, 34, // 14
0x88888889, 0x00000000, 35, // 15
0x00000001, 0x00000000, 4, // 16
0xF0F0F0F1, 0x00000000, 36, // 17
0x38E38E39, 0x00000000, 34, // 18
0xD79435E5, 0xD79435E5, 36, // 19
0xCCCCCCCD, 0x00000000, 36, // 20
0xC30C30C3, 0xC30C30C3, 36, // 21
0xBA2E8BA3, 0x00000000, 36, // 22
0xB21642C9, 0x00000000, 36, // 23
0xAAAAAAAB, 0x00000000, 36, // 24
0x51EB851F, 0x00000000, 35, // 25
0x4EC4EC4F, 0x00000000, 35, // 26
0x97B425ED, 0x97B425ED, 36, // 27
0x49249249, 0x49249249, 35, // 28
0x8D3DCB09, 0x00000000, 36, // 29
0x88888889, 0x00000000, 36, // 30
0x42108421, 0x42108421, 35, // 31
0x00000001, 0x00000000, 5, // 32
0x3E0F83E1, 0x00000000, 35, // 33
0xF0F0F0F1, 0x00000000, 37, // 34
0x75075075, 0x75075075, 36, // 35
0x38E38E39, 0x00000000, 35, // 36
0x6EB3E453, 0x6EB3E453, 36, // 37
0xD79435E5, 0xD79435E5, 37, // 38
0x69069069, 0x69069069, 36, // 39
0xCCCCCCCD, 0x00000000, 37, // 40
0xC7CE0C7D, 0x00000000, 37, // 41
0xC30C30C3, 0xC30C30C3, 37, // 42
0x2FA0BE83, 0x00000000, 35, // 43
0xBA2E8BA3, 0x00000000, 37, // 44
0x5B05B05B, 0x5B05B05B, 36, // 45
0xB21642C9, 0x00000000, 37, // 46
0xAE4C415D, 0x00000000, 37, // 47
0xAAAAAAAB, 0x00000000, 37, // 48
0x5397829D, 0x00000000, 36, // 49
0x51EB851F, 0x00000000, 36, // 50
0xA0A0A0A1, 0x00000000, 37, // 51
0x4EC4EC4F, 0x00000000, 36, // 52
0x9A90E7D9, 0x9A90E7D9, 37, // 53
0x97B425ED, 0x97B425ED, 37, // 54
0x94F2094F, 0x94F2094F, 37, // 55
0x49249249, 0x49249249, 36, // 56
0x47DC11F7, 0x47DC11F7, 36, // 57
0x8D3DCB09, 0x00000000, 37, // 58
0x22B63CBF, 0x00000000, 35, // 59
0x88888889, 0x00000000, 37, // 60
0x4325C53F, 0x00000000, 36, // 61
0x42108421, 0x42108421, 36, // 62
0x41041041, 0x41041041, 36, // 63
0x00000001, 0x00000000, 6, // 64
0xFC0FC0FD, 0x00000000, 38, // 65
0x3E0F83E1, 0x00000000, 36, // 66
0x07A44C6B, 0x00000000, 33, // 67
0xF0F0F0F1, 0x00000000, 38, // 68
0x76B981DB, 0x00000000, 37, // 69
0x75075075, 0x75075075, 37, // 70
0xE6C2B449, 0x00000000, 38, // 71
0x38E38E39, 0x00000000, 36, // 72
0x381C0E07, 0x381C0E07, 36, // 73
0x6EB3E453, 0x6EB3E453, 37, // 74
0x1B4E81B5, 0x00000000, 35, // 75
0xD79435E5, 0xD79435E5, 38, // 76
0x3531DEC1, 0x00000000, 36, // 77
0x69069069, 0x69069069, 37, // 78
0xCF6474A9, 0x00000000, 38, // 79
0xCCCCCCCD, 0x00000000, 38, // 80
0xCA4587E7, 0x00000000, 38, // 81
0xC7CE0C7D, 0x00000000, 38, // 82
0x3159721F, 0x00000000, 36, // 83
0xC30C30C3, 0xC30C30C3, 38, // 84
0xC0C0C0C1, 0x00000000, 38, // 85
0x2FA0BE83, 0x00000000, 36, // 86
0x2F149903, 0x00000000, 36, // 87
0xBA2E8BA3, 0x00000000, 38, // 88
0xB81702E1, 0x00000000, 38, // 89
0x5B05B05B, 0x5B05B05B, 37, // 90
0x2D02D02D, 0x2D02D02D, 36, // 91
0xB21642C9, 0x00000000, 38, // 92
0xB02C0B03, 0x00000000, 38, // 93
0xAE4C415D, 0x00000000, 38, // 94
0x2B1DA461, 0x2B1DA461, 36, // 95
0xAAAAAAAB, 0x00000000, 38, // 96
0xA8E83F57, 0xA8E83F57, 38, // 97
0x5397829D, 0x00000000, 37, // 98
0xA57EB503, 0x00000000, 38, // 99
0x51EB851F, 0x00000000, 37, // 100
0xA237C32B, 0xA237C32B, 38, // 101
0xA0A0A0A1, 0x00000000, 38, // 102
0x9F1165E7, 0x9F1165E7, 38, // 103
0x4EC4EC4F, 0x00000000, 37, // 104
0x27027027, 0x27027027, 36, // 105
0x9A90E7D9, 0x9A90E7D9, 38, // 106
0x991F1A51, 0x991F1A51, 38, // 107
0x97B425ED, 0x97B425ED, 38, // 108
0x2593F69B, 0x2593F69B, 36, // 109
0x94F2094F, 0x94F2094F, 38, // 110
0x24E6A171, 0x24E6A171, 36, // 111
0x49249249, 0x49249249, 37, // 112
0x90FDBC09, 0x90FDBC09, 38, // 113
0x47DC11F7, 0x47DC11F7, 37, // 114
0x8E78356D, 0x8E78356D, 38, // 115
0x8D3DCB09, 0x00000000, 38, // 116
0x23023023, 0x23023023, 36, // 117
0x22B63CBF, 0x00000000, 36, // 118
0x44D72045, 0x00000000, 37, // 119
0x88888889, 0x00000000, 38, // 120
0x8767AB5F, 0x8767AB5F, 38, // 121
0x4325C53F, 0x00000000, 37, // 122
0x85340853, 0x85340853, 38, // 123
0x42108421, 0x42108421, 37, // 124
0x10624DD3, 0x00000000, 35, // 125
0x41041041, 0x41041041, 37, // 126
0x10204081, 0x10204081, 35, // 127
0x00000001, 0x00000000, 7, // 128
0x0FE03F81, 0x00000000, 35, // 129
0xFC0FC0FD, 0x00000000, 39, // 130
0xFA232CF3, 0x00000000, 39, // 131
0x3E0F83E1, 0x00000000, 37, // 132
0xF6603D99, 0x00000000, 39, // 133
0x07A44C6B, 0x00000000, 34, // 134
0xF2B9D649, 0x00000000, 39, // 135
0xF0F0F0F1, 0x00000000, 39, // 136
0x077975B9, 0x00000000, 34, // 137
0x76B981DB, 0x00000000, 38, // 138
0x75DED953, 0x00000000, 38, // 139
0x75075075, 0x75075075, 38, // 140
0x3A196B1F, 0x00000000, 37, // 141
0xE6C2B449, 0x00000000, 39, // 142
0xE525982B, 0x00000000, 39, // 143
0x38E38E39, 0x00000000, 37, // 144
0xE1FC780F, 0x00000000, 39, // 145
0x381C0E07, 0x381C0E07, 37, // 146
0xDEE95C4D, 0x00000000, 39, // 147
0x6EB3E453, 0x6EB3E453, 38, // 148
0xDBEB61EF, 0x00000000, 39, // 149
0x1B4E81B5, 0x00000000, 36, // 150
0x36406C81, 0x00000000, 37, // 151
0xD79435E5, 0xD79435E5, 39, // 152
0xD62B80D7, 0x00000000, 39, // 153
0x3531DEC1, 0x00000000, 37, // 154
0xD3680D37, 0x00000000, 39, // 155
0x69069069, 0x69069069, 38, // 156
0x342DA7F3, 0x00000000, 37, // 157
0xCF6474A9, 0x00000000, 39, // 158
0xCE168A77, 0xCE168A77, 39, // 159
0xCCCCCCCD, 0x00000000, 39, // 160
0xCB8727C1, 0x00000000, 39, // 161
0xCA4587E7, 0x00000000, 39, // 162
0xC907DA4F, 0x00000000, 39, // 163
0xC7CE0C7D, 0x00000000, 39, // 164
0x634C0635, 0x00000000, 38, // 165
0x3159721F, 0x00000000, 37, // 166
0x621B97C3, 0x00000000, 38, // 167
0xC30C30C3, 0xC30C30C3, 39, // 168
0x60F25DEB, 0x00000000, 38, // 169
0xC0C0C0C1, 0x00000000, 39, // 170
0x17F405FD, 0x17F405FD, 36, // 171
0x2FA0BE83, 0x00000000, 37, // 172
0xBD691047, 0xBD691047, 39, // 173
0x2F149903, 0x00000000, 37, // 174
0x5D9F7391, 0x00000000, 38, // 175
0xBA2E8BA3, 0x00000000, 39, // 176
0x5C90A1FD, 0x5C90A1FD, 38, // 177
0xB81702E1, 0x00000000, 39, // 178
0x5B87DDAD, 0x5B87DDAD, 38, // 179
0x5B05B05B, 0x5B05B05B, 38, // 180
0xB509E68B, 0x00000000, 39, // 181
0x2D02D02D, 0x2D02D02D, 37, // 182
0xB30F6353, 0x00000000, 39, // 183
0xB21642C9, 0x00000000, 39, // 184
0x1623FA77, 0x1623FA77, 36, // 185
0xB02C0B03, 0x00000000, 39, // 186
0xAF3ADDC7, 0x00000000, 39, // 187
0xAE4C415D, 0x00000000, 39, // 188
0x15AC056B, 0x15AC056B, 36, // 189
0x2B1DA461, 0x2B1DA461, 37, // 190
0xAB8F69E3, 0x00000000, 39, // 191
0xAAAAAAAB, 0x00000000, 39, // 192
0x15390949, 0x00000000, 36, // 193
0xA8E83F57, 0xA8E83F57, 39, // 194
0x15015015, 0x15015015, 36, // 195
0x5397829D, 0x00000000, 38, // 196
0xA655C439, 0xA655C439, 39, // 197
0xA57EB503, 0x00000000, 39, // 198
0x5254E78F, 0x00000000, 38, // 199
0x51EB851F, 0x00000000, 38, // 200
0x028C1979, 0x00000000, 33, // 201
0xA237C32B, 0xA237C32B, 39, // 202
0xA16B312F, 0x00000000, 39, // 203
0xA0A0A0A1, 0x00000000, 39, // 204
0x4FEC04FF, 0x00000000, 38, // 205
0x9F1165E7, 0x9F1165E7, 39, // 206
0x27932B49, 0x00000000, 37, // 207
0x4EC4EC4F, 0x00000000, 38, // 208
0x9CC8E161, 0x00000000, 39, // 209
0x27027027, 0x27027027, 37, // 210
0x9B4C6F9F, 0x00000000, 39, // 211
0x9A90E7D9, 0x9A90E7D9, 39, // 212
0x99D722DB, 0x00000000, 39, // 213
0x991F1A51, 0x991F1A51, 39, // 214
0x4C346405, 0x00000000, 38, // 215
0x97B425ED, 0x97B425ED, 39, // 216
0x4B809701, 0x4B809701, 38, // 217
0x2593F69B, 0x2593F69B, 37, // 218
0x12B404AD, 0x12B404AD, 36, // 219
0x94F2094F, 0x94F2094F, 39, // 220
0x25116025, 0x25116025, 37, // 221
0x24E6A171, 0x24E6A171, 37, // 222
0x24BC44E1, 0x24BC44E1, 37, // 223
0x49249249, 0x49249249, 38, // 224
0x91A2B3C5, 0x00000000, 39, // 225
0x90FDBC09, 0x90FDBC09, 39, // 226
0x905A3863, 0x905A3863, 39, // 227
0x47DC11F7, 0x47DC11F7, 38, // 228
0x478BBCED, 0x00000000, 38, // 229
0x8E78356D, 0x8E78356D, 39, // 230
0x46ED2901, 0x46ED2901, 38, // 231
0x8D3DCB09, 0x00000000, 39, // 232
0x2328A701, 0x2328A701, 37, // 233
0x23023023, 0x23023023, 37, // 234
0x45B81A25, 0x45B81A25, 38, // 235
0x22B63CBF, 0x00000000, 37, // 236
0x08A42F87, 0x08A42F87, 35, // 237
0x44D72045, 0x00000000, 38, // 238
0x891AC73B, 0x00000000, 39, // 239
0x88888889, 0x00000000, 39, // 240
0x10FEF011, 0x00000000, 36, // 241
0x8767AB5F, 0x8767AB5F, 39, // 242
0x86D90545, 0x00000000, 39, // 243
0x4325C53F, 0x00000000, 38, // 244
0x85BF3761, 0x85BF3761, 39, // 245
0x85340853, 0x85340853, 39, // 246
0x10953F39, 0x10953F39, 36, // 247
0x42108421, 0x42108421, 38, // 248
0x41CC9829, 0x41CC9829, 38, // 249
0x10624DD3, 0x00000000, 36, // 250
0x828CBFBF, 0x00000000, 39, // 251
0x41041041, 0x41041041, 38, // 252
0x81848DA9, 0x00000000, 39, // 253
0x10204081, 0x10204081, 36, // 254
0x80808081, 0x00000000, 39 // 255
};
}
}
|