2 using System.Drawing;
\r
3 using System.Diagnostics;
\r
7 using System.Reflection;
\r
8 using System.Xml.Serialization;
\r
9 using System.Collections;
\r
10 using System.Security.Cryptography;
\r
11 using System.Runtime.InteropServices;
\r
12 using System.Drawing.Imaging;
\r
16 using System.Windows.Forms;
\r
19 using NUnit.Framework;
\r
21 namespace DrawingTestHelper
\r
23 #region Results serialization classes
\r
24 public sealed class ExpectedResult {
\r
25 public ExpectedResult(){}
\r
26 public ExpectedResult(string testName, double norm) {
\r
27 TestName = testName;
\r
30 public string TestName;
\r
34 public sealed class ExpectedResults {
\r
35 [XmlArrayItem(typeof(ExpectedResult))]
\r
36 public ArrayList Tests = new ArrayList();
\r
39 public sealed class ExpectedResultsHash {
\r
41 ExpectedResults _suite;
\r
43 public ExpectedResultsHash () {
\r
45 using (StreamReader s = new StreamReader (FileName)) {
\r
46 _suite = (ExpectedResults)TestSuiteSerializer.Deserialize(s);
\r
50 _suite = new ExpectedResults ();
\r
52 _hash = new Hashtable(_suite.Tests.Count);
\r
53 foreach (ExpectedResult res in _suite.Tests)
\r
54 _hash[res.TestName] = res.Norm;
\r
57 public const string FileName = "ExpectedResults.xml";
\r
58 public readonly static XmlSerializer TestSuiteSerializer = new XmlSerializer(typeof(ExpectedResults));
\r
60 public double GetNorm(string testName) {
\r
61 object res = _hash[testName];
\r
67 public void WriteNorm (string testName, double myNorm) {
\r
68 if (_hash.Contains (testName)) {
\r
69 for (int i = 0; i < _suite.Tests.Count; i++) {
\r
70 ExpectedResult cur = (ExpectedResult) _suite.Tests[i];
\r
71 if (cur.TestName == testName) {
\r
78 _suite.Tests.Add(new ExpectedResult(testName, myNorm));
\r
80 _hash[testName] = myNorm;
\r
81 using(StreamWriter w = new StreamWriter(FileName))
\r
82 TestSuiteSerializer.Serialize(w, _suite);
\r
86 public sealed class CachedResult {
\r
87 public CachedResult (){}
\r
88 public CachedResult (string testName, string sha1, double norm) {
\r
89 TestName = testName;
\r
92 DateTime = DateTime.Now;
\r
95 public string TestName;
\r
98 public DateTime DateTime;
\r
101 public sealed class CachedResults {
\r
102 [XmlArrayItem(typeof(CachedResult))]
\r
103 public ArrayList Tests = new ArrayList();
\r
106 public class Cache {
\r
108 CachedResults _results;
\r
110 public const string FileName = "dotnet.CachedResults.xml";
\r
111 public const string NewFileName = "dotnet.NewCachedResults.xml";
\r
112 public readonly static XmlSerializer TestSuiteSerializer =
\r
113 new XmlSerializer(typeof(CachedResults));
\r
117 using (StreamReader r = new StreamReader(FileName))
\r
118 _results = (CachedResults)TestSuiteSerializer.Deserialize(r);
\r
121 _results = new CachedResults ();
\r
124 _hash = new Hashtable(_results.Tests.Count);
\r
125 foreach (CachedResult res in _results.Tests)
\r
126 _hash[res.SHA1] = res.Norm;
\r
129 public double GetNorm (string sha1) {
\r
130 if (_hash.ContainsKey (sha1))
\r
131 return (double)_hash[sha1];
\r
136 public void Add (string testName, string sha1, double norm) {
\r
137 if (_hash.ContainsKey (sha1))
\r
138 throw new ArgumentException ("This SHA1 is already in the cache", "sha1");
\r
140 _results.Tests.Add (new CachedResult(testName, sha1, norm));
\r
141 _hash.Add (sha1, norm);
\r
143 using(StreamWriter w = new StreamWriter(NewFileName))
\r
144 TestSuiteSerializer.Serialize(w, _results);
\r
150 /// Summary description for DrawingTest.
\r
152 public abstract class DrawingTest : IDisposable {
\r
154 public const float DEFAULT_FLOAT_TOLERANCE = 1e-5f;
\r
155 public const int DEFAULT_IMAGE_TOLERANCE = 2;
\r
157 Graphics _graphics;
\r
158 protected Bitmap _bitmap;
\r
159 static string _callingFunction;
\r
160 //static int _counter;
\r
161 static Hashtable _mpFuncCount = new Hashtable();
\r
162 static bool _showForms = false;
\r
163 static bool _createResults = true;
\r
164 protected string _ownerClass = "";
\r
165 protected Hashtable _specialTolerance = null;
\r
167 protected readonly static ExpectedResultsHash ExpectedResults = new ExpectedResultsHash ();
\r
168 protected readonly static Cache cache = new Cache ();
\r
170 public Graphics Graphics {get {return _graphics;}}
\r
171 public Bitmap Bitmap {get { return _bitmap; }}
\r
173 public Hashtable SpecialTolerance
\r
175 get {return _specialTolerance;}
\r
176 set {_specialTolerance = value;}
\r
179 public string OwnerClass
\r
181 get {return _ownerClass;}
\r
182 set {_ownerClass = value;}
\r
185 public static bool ShowForms
\r
187 get {return _showForms;}
\r
188 set {_showForms = value;}
\r
191 public static bool CreateResults {
\r
192 get {return _createResults;}
\r
193 set {_createResults = value;}
\r
196 protected DrawingTest() {}
\r
198 private void Init (int width, int height) {
\r
199 Init (new Bitmap (width, height));
\r
202 private void Init (Bitmap bitmap) {
\r
204 _graphics = Graphics.FromImage (_bitmap);
\r
207 protected abstract string DetermineCallingFunction ();
\r
209 protected interface IMyForm {
\r
213 protected abstract IMyForm CreateForm (string title);
\r
215 public void Show () {
\r
219 IMyForm form = CreateForm(_callingFunction + _mpFuncCount[_callingFunction]);
\r
223 static protected string TestName {
\r
225 return _callingFunction + ":" + _mpFuncCount[_callingFunction]/* + ".dat"*/;
\r
229 #region GetImageFFTArray
\r
230 private static ComplexF[] GetImageFFTArray(Bitmap bitmap) {
\r
231 float scale = 1F / (float) System.Math.Sqrt(bitmap.Width * bitmap.Height);
\r
232 ComplexF[] data = new ComplexF [bitmap.Width * bitmap.Height * 4];
\r
235 for( int y = 0; y < bitmap.Height; y ++ )
\r
236 for( int x = 0; x < bitmap.Width; x ++ ) {
\r
237 Color c = bitmap.GetPixel (x, y);
\r
239 if( (( x + y ) & 0x1 ) != 0 ) {
\r
243 data [offset++] = new ComplexF( c.A * s / 256F, 0);
\r
244 data [offset++] = new ComplexF( c.R * s / -256F, 0);
\r
245 data [offset++] = new ComplexF( c.G * s / 256F, 0);
\r
246 data [offset++] = new ComplexF( c.B * s / -256F, 0);
\r
250 Fourier.FFT3( data, 4, bitmap.Width, bitmap.Height, FourierDirection.Forward );
\r
252 for( int i = 0; i < data.Length; i ++ ) {
\r
260 abstract public string CalculateSHA1 ();
\r
262 public static double CalculateNorm (Bitmap bitmap) {
\r
263 ComplexF[] matrix = GetImageFFTArray(bitmap);
\r
266 int size_x = 4; //ARGB values
\r
267 int size_y = bitmap.Width;
\r
268 int size_z = bitmap.Height;
\r
269 for (int x=1; x<=size_x; x++) {
\r
271 for (int y=1; y<=size_y; y++) {
\r
273 for (int z=1; z<=size_z; z++) {
\r
274 ComplexF cur = matrix[(size_x-x)+size_x*(size_y-y)+size_x*size_y*(size_z-z)];
\r
275 norm_z += cur.GetModulusSquared ();// * z;
\r
277 norm_y += norm_z;// * y;
\r
279 norm += norm_y;// * x;
\r
284 public double GetNorm () {
\r
285 string sha1 = CalculateSHA1 ();
\r
287 double norm = cache.GetNorm (sha1);
\r
288 if (double.IsNaN (norm)) {
\r
289 norm = CalculateNorm (_bitmap);
\r
290 cache.Add (TestName, sha1, norm);
\r
291 //_bitmap.Save(TestName.Replace(":", "_"));
\r
296 protected abstract double GetExpectedNorm (double myNorm);
\r
298 private void CheckCounter () {
\r
299 string callFunc = DetermineCallingFunction ();
\r
300 _callingFunction = callFunc;
\r
301 if (!_mpFuncCount.Contains(_callingFunction)) {
\r
303 _mpFuncCount[_callingFunction] = 1;
\r
306 int counter = (int)_mpFuncCount[_callingFunction];
\r
308 _mpFuncCount[_callingFunction] = counter;
\r
312 public static void AssertAlmostEqual (float expected, float actual)
\r
314 AssertAlmostEqual (expected, actual, DEFAULT_FLOAT_TOLERANCE);
\r
317 public static void AssertAlmostEqual (float expected, float actual, float tolerance)
\r
319 string msg = String.Format("\nExpected : {0} \nActual : {1}",expected.ToString(),actual.ToString());
\r
320 AssertAlmostEqual (expected, actual, tolerance, msg);
\r
323 private static void AssertAlmostEqual (float expected, float actual, float tolerance, string message)
\r
325 float error = System.Math.Abs ((expected - actual) / (expected + actual + float.Epsilon));
\r
326 Assert.That (error < tolerance, Is.True, message);
\r
329 public static void AssertAlmostEqual (PointF expected, PointF actual)
\r
331 string msg = String.Format("\nExpected : {0} \n Actual : {1}",expected.ToString(),actual.ToString());
\r
332 AssertAlmostEqual (expected.X, actual.X, DEFAULT_FLOAT_TOLERANCE, msg);
\r
333 AssertAlmostEqual (expected.Y, actual.Y, DEFAULT_FLOAT_TOLERANCE, msg);
\r
337 /// Checks that the given bitmap norm is similar to expected
\r
339 /// <param name="tolerance">tolerance in percents (0..100)</param>
\r
340 /// <returns></returns>
\r
342 public bool Compare (double tolerance) {
\r
345 double error = CompareToExpectedInternal()*100;
\r
347 if (SpecialTolerance != null)
\r
348 return error <= GetSpecialTolerance(TestName);
\r
350 return error <= tolerance;
\r
353 public bool PDCompare (double tolerance) {
\r
354 Bitmap ri = GetReferenceImage(TestName);
\r
358 double error = PDComparer.Compare(ri, _bitmap);
\r
359 return error <= tolerance;
\r
362 public bool Compare () {
\r
365 double error = CompareToExpectedInternal()*100;
\r
367 if (SpecialTolerance != null)
\r
368 return error <= GetSpecialTolerance(TestName);
\r
370 return error <= DEFAULT_IMAGE_TOLERANCE;
\r
373 public bool PDCompare () {
\r
374 Bitmap ri = GetReferenceImage(TestName);
\r
378 double error = PDComparer.Compare(ri, _bitmap);
\r
379 return error <= DEFAULT_IMAGE_TOLERANCE;
\r
382 protected abstract Bitmap GetReferenceImage(string testName);
\r
384 protected double GetSpecialTolerance(string testName) {
\r
386 string shortTestName = testName.Substring( testName.LastIndexOf(".") + 1 );
\r
387 object o = SpecialTolerance[shortTestName];
\r
389 return DEFAULT_IMAGE_TOLERANCE;
\r
391 return Convert.ToDouble(o);
\r
393 catch (System.Exception) {
\r
394 return DEFAULT_IMAGE_TOLERANCE;
\r
398 public void AssertCompare () {
\r
400 Assert.That ((CompareToExpectedInternal () * 100) < DEFAULT_IMAGE_TOLERANCE, Is.True);
\r
403 public void AssertCompare (double tolerance) {
\r
405 Assert.That ((CompareToExpectedInternal () * 100) < tolerance, Is.True);
\r
408 public double CompareToExpected () {
\r
410 return CompareToExpectedInternal ();
\r
413 double CompareToExpectedInternal () {
\r
417 double norm = GetNorm ();
\r
418 double expNorm = GetExpectedNorm (norm);
\r
419 return System.Math.Abs (norm-expNorm)/(norm+expNorm+double.Epsilon);
\r
422 public static DrawingTest Create (int width, int height) {
\r
423 return Create(width, height, "GraphicsFixture");
\r
425 public static DrawingTest Create (int width, int height, string ownerClass) {
\r
427 test = new NetDrawingTest ();
\r
428 test.Init (width, height);
\r
429 test.OwnerClass = ownerClass;
\r
432 #region IDisposable Members
\r
434 public void Dispose()
\r
436 // TODO: Add DrawingTest.Dispose implementation
\r
437 if (_graphics != null) {
\r
438 _graphics.Dispose();
\r
446 internal class NetDrawingTest:DrawingTest {
\r
447 public NetDrawingTest () {}
\r
449 protected override double GetExpectedNorm (double myNorm) {
\r
451 ExpectedResults.WriteNorm (TestName, myNorm);
\r
456 protected override Bitmap GetReferenceImage(string testName) {
\r
457 string fileName = testName.Replace(":", "_") + ".png";
\r
460 return new Bitmap("/Developer/MonoTouch/Source/mono/mcs/class/System.Drawing/Test/DrawingTest/Test/PNGs/" + fileName);
\r
462 _bitmap.Save( fileName );
\r
467 catch(System.Exception e) {
\r
468 throw new System.Exception("Error loading .Net reference image: " + fileName);
\r
473 private class NetForm:MonoTouch.UIKit.UIViewController,IMyForm {
\r
475 public NetForm(string title, Image anImage):base() {
\r
476 //base.Text = title;
\r
479 void IMyForm.Show () {
\r
480 this.image.Save("test.net.png");
\r
484 private class NetForm:Form,IMyForm {
\r
486 public NetForm(string title, Image anImage):base() {
\r
490 protected override void OnPaint(PaintEventArgs e) {
\r
491 e.Graphics.DrawImageUnscaled (image, 0, 0);
\r
493 void IMyForm.Show () {
\r
494 this.Size = image.Size;
\r
495 this.ShowDialog ();
\r
496 this.image.Save("test.net.png");
\r
500 protected override IMyForm CreateForm(string title) {
\r
501 return new NetForm (title, _bitmap);
\r
504 protected override string DetermineCallingFunction() {
\r
505 StackFrame sf = new StackFrame (3, true);
\r
506 MethodBase mb = sf.GetMethod ();
\r
508 string name = mb.DeclaringType.FullName + "." + _ownerClass + "." + mb.Name;
\r
512 public override string CalculateSHA1() {
\r
513 Rectangle r = new Rectangle(0, 0, _bitmap.Width, _bitmap.Height);
\r
515 BitmapData data = _bitmap.LockBits (r, ImageLockMode.ReadOnly,
\r
516 _bitmap.PixelFormat);
\r
517 int dataSize = data.Stride * data.Height;
\r
518 byte [] bdata = new byte [dataSize];
\r
519 Marshal.Copy (data.Scan0, bdata, 0, dataSize);
\r
520 _bitmap.UnlockBits (data);
\r
522 SHA1 sha1 = new SHA1CryptoServiceProvider ();
\r
523 byte [] resdata = sha1.ComputeHash (bdata);
\r
524 return Convert.ToBase64String (resdata);
\r