DrawingTest.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. using System;
  2. using System.Drawing;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Text;
  6. using Exocortex.DSP;
  7. using System.Reflection;
  8. using System.Xml.Serialization;
  9. using System.Collections;
  10. using System.Security.Cryptography;
  11. #if TARGET_JVM
  12. using awt = java.awt;
  13. using javax.imageio;
  14. using java.lang;
  15. using java.security;
  16. using java.awt.image;
  17. #else
  18. using System.Windows.Forms;
  19. using System.Drawing.Imaging;
  20. using System.Runtime.InteropServices;
  21. #endif
  22. using NUnit.Framework;
  23. namespace DrawingTestHelper
  24. {
  25. #region Results serialization classes
  26. public sealed class ExpectedResult {
  27. public ExpectedResult(){}
  28. public ExpectedResult(string testName, double norm) {
  29. TestName = testName;
  30. Norm = norm;
  31. }
  32. public string TestName;
  33. public double Norm;
  34. }
  35. public sealed class ExpectedResults {
  36. [XmlArrayItem(typeof(ExpectedResult))]
  37. public ArrayList Tests = new ArrayList();
  38. }
  39. public sealed class ExpectedResultsHash {
  40. Hashtable _hash;
  41. ExpectedResults _suite;
  42. public ExpectedResultsHash () {
  43. try {
  44. using (StreamReader s = new StreamReader (FileName)) {
  45. _suite = (ExpectedResults)TestSuiteSerializer.Deserialize(s);
  46. }
  47. }
  48. catch {
  49. _suite = new ExpectedResults ();
  50. }
  51. _hash = new Hashtable(_suite.Tests.Count);
  52. foreach (ExpectedResult res in _suite.Tests)
  53. _hash[res.TestName] = res.Norm;
  54. }
  55. public const string FileName = "ExpectedResults.xml";
  56. public readonly static XmlSerializer TestSuiteSerializer = new XmlSerializer(typeof(ExpectedResults));
  57. public double GetNorm(string testName) {
  58. object res = _hash[testName];
  59. if (res != null)
  60. return (double)res;
  61. return double.NaN;
  62. }
  63. public void WriteNorm (string testName, double myNorm) {
  64. if (_hash.Contains (testName)) {
  65. for (int i = 0; i < _suite.Tests.Count; i++) {
  66. ExpectedResult cur = (ExpectedResult) _suite.Tests[i];
  67. if (cur.TestName == testName) {
  68. cur.Norm = myNorm;
  69. break;
  70. }
  71. }
  72. }
  73. else
  74. _suite.Tests.Add(new ExpectedResult(testName, myNorm));
  75. _hash[testName] = myNorm;
  76. using(StreamWriter w = new StreamWriter(FileName))
  77. TestSuiteSerializer.Serialize(w, _suite);
  78. }
  79. }
  80. public sealed class CachedResult {
  81. public CachedResult (){}
  82. public CachedResult (string testName, string sha1, double norm) {
  83. TestName = testName;
  84. SHA1 = sha1;
  85. Norm = norm;
  86. DateTime = DateTime.Now;
  87. }
  88. public string TestName;
  89. public string SHA1;
  90. public double Norm;
  91. public DateTime DateTime;
  92. }
  93. public sealed class CachedResults {
  94. [XmlArrayItem(typeof(CachedResult))]
  95. public ArrayList Tests = new ArrayList();
  96. }
  97. public class Cache {
  98. Hashtable _hash;
  99. CachedResults _results;
  100. #if TARGET_JVM
  101. public const string FileName = "CachedResults.xml";
  102. public const string NewFileName = "NewCachedResults.xml";
  103. #else
  104. public const string FileName = "dotnet.CachedResults.xml";
  105. public const string NewFileName = "dotnet.NewCachedResults.xml";
  106. #endif
  107. public readonly static XmlSerializer TestSuiteSerializer =
  108. new XmlSerializer(typeof(CachedResults));
  109. public Cache () {
  110. try {
  111. using (StreamReader r = new StreamReader(FileName))
  112. _results = (CachedResults)TestSuiteSerializer.Deserialize(r);
  113. }
  114. catch {
  115. _results = new CachedResults ();
  116. }
  117. _hash = new Hashtable(_results.Tests.Count);
  118. foreach (CachedResult res in _results.Tests)
  119. _hash[res.SHA1] = res.Norm;
  120. }
  121. public double GetNorm (string sha1) {
  122. if (_hash.ContainsKey (sha1))
  123. return (double)_hash[sha1];
  124. else
  125. return double.NaN;
  126. }
  127. public void Add (string testName, string sha1, double norm) {
  128. if (_hash.ContainsKey (sha1))
  129. throw new ArgumentException ("This SHA1 is already in the cache", "sha1");
  130. _results.Tests.Add (new CachedResult(testName, sha1, norm));
  131. _hash.Add (sha1, norm);
  132. using(StreamWriter w = new StreamWriter(NewFileName))
  133. TestSuiteSerializer.Serialize(w, _results);
  134. }
  135. }
  136. #endregion
  137. /// <summary>
  138. /// Summary description for DrawingTest.
  139. /// </summary>
  140. public abstract class DrawingTest : IDisposable {
  141. public const float DEFAULT_FLOAT_TOLERANCE = 1e-5f;
  142. public const int DEFAULT_IMAGE_TOLERANCE = 2;
  143. Graphics _graphics;
  144. protected Bitmap _bitmap;
  145. static string _callingFunction;
  146. //static int _counter;
  147. static Hashtable _mpFuncCount = new Hashtable();
  148. static bool _showForms = false;
  149. static bool _createResults = true;
  150. protected string _ownerClass = "";
  151. protected Hashtable _specialTolerance = null;
  152. protected readonly static ExpectedResultsHash ExpectedResults = new ExpectedResultsHash ();
  153. protected readonly static Cache cache = new Cache ();
  154. public Graphics Graphics {get {return _graphics;}}
  155. public Bitmap Bitmap {get { return _bitmap; }}
  156. public Hashtable SpecialTolerance
  157. {
  158. get {return _specialTolerance;}
  159. set {_specialTolerance = value;}
  160. }
  161. public string OwnerClass
  162. {
  163. get {return _ownerClass;}
  164. set {_ownerClass = value;}
  165. }
  166. public static bool ShowForms
  167. {
  168. get {return _showForms;}
  169. set {_showForms = value;}
  170. }
  171. public static bool CreateResults {
  172. get {return _createResults;}
  173. set {_createResults = value;}
  174. }
  175. protected DrawingTest() {}
  176. private void Init (int width, int height) {
  177. Init (new Bitmap (width, height));
  178. }
  179. private void Init (Bitmap bitmap) {
  180. _bitmap = bitmap;
  181. _graphics = Graphics.FromImage (_bitmap);
  182. }
  183. protected abstract string DetermineCallingFunction ();
  184. protected interface IMyForm {
  185. void Show ();
  186. }
  187. protected abstract IMyForm CreateForm (string title);
  188. public void Show () {
  189. CheckCounter ();
  190. if (!ShowForms)
  191. return;
  192. IMyForm form = CreateForm(_callingFunction + _mpFuncCount[_callingFunction]);
  193. form.Show ();
  194. }
  195. static protected string TestName {
  196. get {
  197. return _callingFunction + ":" + _mpFuncCount[_callingFunction]/* + ".dat"*/;
  198. }
  199. }
  200. #region GetImageFFTArray
  201. private static ComplexF[] GetImageFFTArray(Bitmap bitmap) {
  202. float scale = 1F / (float) System.Math.Sqrt(bitmap.Width * bitmap.Height);
  203. ComplexF[] data = new ComplexF [bitmap.Width * bitmap.Height * 4];
  204. int offset = 0;
  205. for( int y = 0; y < bitmap.Height; y ++ )
  206. for( int x = 0; x < bitmap.Width; x ++ ) {
  207. Color c = bitmap.GetPixel (x, y);
  208. float s = 1F;
  209. if( (( x + y ) & 0x1 ) != 0 ) {
  210. s = -1F;
  211. }
  212. data [offset++] = new ComplexF( c.A * s / 256F, 0);
  213. data [offset++] = new ComplexF( c.R * s / -256F, 0);
  214. data [offset++] = new ComplexF( c.G * s / 256F, 0);
  215. data [offset++] = new ComplexF( c.B * s / -256F, 0);
  216. }
  217. Fourier.FFT3( data, 4, bitmap.Width, bitmap.Height, FourierDirection.Forward );
  218. for( int i = 0; i < data.Length; i ++ ) {
  219. data[i] *= scale;
  220. }
  221. return data;
  222. }
  223. #endregion
  224. abstract public string CalculateSHA1 ();
  225. public static double CalculateNorm (Bitmap bitmap) {
  226. ComplexF[] matrix = GetImageFFTArray(bitmap);
  227. double norm = 0;
  228. int size_x = 4; //ARGB values
  229. int size_y = bitmap.Width;
  230. int size_z = bitmap.Height;
  231. for (int x=1; x<=size_x; x++) {
  232. double norm_y = 0;
  233. for (int y=1; y<=size_y; y++) {
  234. double norm_z = 0;
  235. for (int z=1; z<=size_z; z++) {
  236. ComplexF cur = matrix[(size_x-x)+size_x*(size_y-y)+size_x*size_y*(size_z-z)];
  237. norm_z += cur.GetModulusSquared ();// * z;
  238. }
  239. norm_y += norm_z;// * y;
  240. }
  241. norm += norm_y;// * x;
  242. }
  243. return norm;
  244. }
  245. public double GetNorm () {
  246. string sha1 = CalculateSHA1 ();
  247. double norm = cache.GetNorm (sha1);
  248. if (double.IsNaN (norm)) {
  249. norm = CalculateNorm (_bitmap);
  250. cache.Add (TestName, sha1, norm);
  251. //_bitmap.Save(TestName.Replace(":", "_"));
  252. }
  253. return norm;
  254. }
  255. protected abstract double GetExpectedNorm (double myNorm);
  256. private void CheckCounter () {
  257. string callFunc = DetermineCallingFunction ();
  258. _callingFunction = callFunc;
  259. if (!_mpFuncCount.Contains(_callingFunction)) {
  260. _mpFuncCount[_callingFunction] = 1;
  261. }
  262. else {
  263. int counter = (int)_mpFuncCount[_callingFunction];
  264. counter ++;
  265. _mpFuncCount[_callingFunction] = counter;
  266. }
  267. }
  268. public static void AssertAlmostEqual (float expected, float actual)
  269. {
  270. AssertAlmostEqual (expected, actual, DEFAULT_FLOAT_TOLERANCE);
  271. }
  272. public static void AssertAlmostEqual (float expected, float actual, float tolerance)
  273. {
  274. string msg = String.Format("\nExpected : {0} \nActual : {1}",expected.ToString(),actual.ToString());
  275. AssertAlmostEqual (expected, actual, tolerance, msg);
  276. }
  277. private static void AssertAlmostEqual (float expected, float actual, float tolerance, string message)
  278. {
  279. float error = System.Math.Abs ((expected - actual) / (expected + actual + float.Epsilon));
  280. Assert.IsTrue (error < tolerance, message);
  281. }
  282. public static void AssertAlmostEqual (PointF expected, PointF actual)
  283. {
  284. string msg = String.Format("\nExpected : {0} \n Actual : {1}",expected.ToString(),actual.ToString());
  285. AssertAlmostEqual (expected.X, actual.X, DEFAULT_FLOAT_TOLERANCE, msg);
  286. AssertAlmostEqual (expected.Y, actual.Y, DEFAULT_FLOAT_TOLERANCE, msg);
  287. }
  288. /// <summary>
  289. /// Checks that the given bitmap norm is similar to expected
  290. /// </summary>
  291. /// <param name="tolerance">tolerance in percents (0..100)</param>
  292. /// <returns></returns>
  293. ///
  294. public bool Compare (double tolerance) {
  295. CheckCounter ();
  296. double error = CompareToExpectedInternal()*100;
  297. if (SpecialTolerance != null)
  298. return error <= GetSpecialTolerance(TestName);
  299. return error <= tolerance;
  300. }
  301. public bool PDCompare (double tolerance) {
  302. Bitmap ri = GetReferenceImage(TestName);
  303. if (ri == null)
  304. return true;
  305. double error = PDComparer.Compare(ri, _bitmap);
  306. return error <= tolerance;
  307. }
  308. public bool Compare () {
  309. CheckCounter ();
  310. double error = CompareToExpectedInternal()*100;
  311. if (SpecialTolerance != null)
  312. return error <= GetSpecialTolerance(TestName);
  313. return error <= DEFAULT_IMAGE_TOLERANCE;
  314. }
  315. public bool PDCompare () {
  316. Bitmap ri = GetReferenceImage(TestName);
  317. if (ri == null)
  318. return true;
  319. double error = PDComparer.Compare(ri, _bitmap);
  320. return error <= DEFAULT_IMAGE_TOLERANCE;
  321. }
  322. protected abstract Bitmap GetReferenceImage(string testName);
  323. protected double GetSpecialTolerance(string testName) {
  324. try {
  325. string shortTestName = testName.Substring( testName.LastIndexOf(".") + 1 );
  326. object o = SpecialTolerance[shortTestName];
  327. if (o == null)
  328. return DEFAULT_IMAGE_TOLERANCE;
  329. return Convert.ToDouble(o);
  330. }
  331. catch (System.Exception) {
  332. return DEFAULT_IMAGE_TOLERANCE;
  333. }
  334. }
  335. public void AssertCompare () {
  336. CheckCounter ();
  337. Assert.IsTrue ((CompareToExpectedInternal () * 100) < DEFAULT_IMAGE_TOLERANCE);
  338. }
  339. public void AssertCompare (double tolerance) {
  340. CheckCounter ();
  341. Assert.IsTrue ((CompareToExpectedInternal () * 100) < tolerance);
  342. }
  343. public double CompareToExpected () {
  344. CheckCounter ();
  345. return CompareToExpectedInternal ();
  346. }
  347. double CompareToExpectedInternal () {
  348. if (ShowForms)
  349. return 0;
  350. double norm = GetNorm ();
  351. double expNorm = GetExpectedNorm (norm);
  352. return System.Math.Abs (norm-expNorm)/(norm+expNorm+double.Epsilon);
  353. }
  354. public static DrawingTest Create (int width, int height) {
  355. return Create(width, height, "GraphicsFixture");
  356. }
  357. public static DrawingTest Create (int width, int height, string ownerClass) {
  358. DrawingTest test;
  359. #if TARGET_JVM
  360. test = new JavaDrawingTest ();
  361. #else
  362. test = new NetDrawingTest ();
  363. #endif
  364. test.Init (width, height);
  365. test.OwnerClass = ownerClass;
  366. return test;
  367. }
  368. #region IDisposable Members
  369. public void Dispose()
  370. {
  371. // TODO: Add DrawingTest.Dispose implementation
  372. if (_graphics != null) {
  373. _graphics.Dispose();
  374. _graphics = null;
  375. }
  376. }
  377. #endregion
  378. }
  379. #if TARGET_JVM
  380. internal class JavaDrawingTest:DrawingTest {
  381. java.awt.image.BufferedImage _image;
  382. java.awt.image.BufferedImage Image {
  383. get {
  384. if (_image != null)
  385. return _image;
  386. Type imageType = typeof (Bitmap);
  387. PropertyInfo [] props = imageType.GetProperties (
  388. BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  389. PropertyInfo prop = null;
  390. foreach (PropertyInfo p in props) {
  391. if (p.Name == "NativeObject")
  392. if (p.PropertyType == typeof(java.awt.image.BufferedImage))
  393. prop = p;
  394. }
  395. MethodInfo method = prop.GetGetMethod (true);
  396. _image = (java.awt.image.BufferedImage) method.Invoke (_bitmap, new object [0]);
  397. return _image;
  398. }
  399. }
  400. public JavaDrawingTest () {}
  401. protected override double GetExpectedNorm (double myNorm) {
  402. return ExpectedResults.GetNorm(TestName);
  403. }
  404. protected override Bitmap GetReferenceImage(string testName) {
  405. try{
  406. string dotNetResultsFolder = @"";
  407. string fileName = dotNetResultsFolder + testName.Replace(":", "_") + ".png";
  408. return new Bitmap(fileName);
  409. }
  410. catch(System.Exception e) {
  411. throw new System.Exception("Error creating .Net reference image");
  412. }
  413. }
  414. private class JavaForm:java.awt.Dialog,IMyForm {
  415. class EventListener : [email protected] {
  416. #region WindowListener Members
  417. public void windowOpened([email protected] arg_0) {
  418. // TODO: Add ttt.windowOpened implementation
  419. }
  420. public void windowActivated([email protected] arg_0) {
  421. // TODO: Add ttt.windowActivated implementation
  422. }
  423. public void windowClosed([email protected] arg_0) {
  424. // TODO: Add ttt.windowClosed implementation
  425. }
  426. public void windowDeiconified([email protected] arg_0) {
  427. // TODO: Add ttt.windowDeiconified implementation
  428. }
  429. public void windowIconified([email protected] arg_0) {
  430. // TODO: Add ttt.windowIconified implementation
  431. }
  432. public void windowClosing([email protected] arg_0) {
  433. // TODO: Add ttt.windowClosing implementation
  434. java.awt.Window w = arg_0.getWindow();
  435. java.awt.Window par = w.getOwner ();
  436. w.dispose();
  437. par.dispose ();
  438. }
  439. public void windowDeactivated([email protected] arg_0) {
  440. // TODO: Add ttt.windowDeactivated implementation
  441. }
  442. #endregion
  443. }
  444. java.awt.Image _image;
  445. Size _s;
  446. public JavaForm (string title, java.awt.Image anImage, Size s)
  447. : base(new java.awt.Frame(), title, true) {
  448. _image = anImage;
  449. _s = s;
  450. addWindowListener(new EventListener());
  451. }
  452. public override void paint (java.awt.Graphics g) {
  453. base.paint (g);
  454. awt.Insets insets = this.getInsets ();
  455. g.drawImage (_image, insets.left, insets.top, null);
  456. }
  457. void IMyForm.Show () {
  458. awt.Insets insets = this.getInsets ();
  459. base.setSize (_s.Width + insets.left + insets.right,
  460. _s.Width + insets.top + insets.bottom);
  461. this.show ();
  462. //save the image
  463. //ImageIO.write((java.awt.image.RenderedImage)_image, "png", new java.io.File("test.java.png"));
  464. }
  465. }
  466. protected override IMyForm CreateForm(string title) {
  467. return new JavaForm (title, Image, _bitmap.Size);
  468. }
  469. protected override string DetermineCallingFunction() {
  470. System.Exception e = new System.Exception ();
  471. java.lang.Class c = vmw.common.TypeUtils.ToClass (e);
  472. java.lang.reflect.Method m = c.getMethod ("getStackTrace",
  473. new java.lang.Class [0]);
  474. java.lang.StackTraceElement [] els = (java.lang.StackTraceElement [])
  475. m.invoke (e, new object [0]);
  476. java.lang.StackTraceElement el = els [4];
  477. return el.getClassName () + "." + _ownerClass + "." + el.getMethodName ();
  478. }
  479. public override string CalculateSHA1() {
  480. MessageDigest md = MessageDigest.getInstance ("SHA");
  481. DataBufferInt dbi = (DataBufferInt) Image.getRaster ().getDataBuffer ();
  482. for (int i=0; i<dbi.getNumBanks (); i++) {
  483. int [] curBank = dbi.getData (i);
  484. for (int j=0; j<curBank.Length; j++) {
  485. int x = curBank[j];
  486. md.update ((sbyte) (x & 0xFF));
  487. md.update ((sbyte) ((x>>8) & 0xFF));
  488. md.update ((sbyte) ((x>>16) & 0xFF));
  489. md.update ((sbyte) ((x>>24) & 0xFF));
  490. }
  491. }
  492. byte [] resdata = (byte[])vmw.common.TypeUtils.ToByteArray(md.digest());
  493. return Convert.ToBase64String (resdata);
  494. }
  495. }
  496. #else
  497. internal class NetDrawingTest:DrawingTest {
  498. public NetDrawingTest () {}
  499. protected override double GetExpectedNorm (double myNorm) {
  500. if (CreateResults)
  501. ExpectedResults.WriteNorm (TestName, myNorm);
  502. return myNorm;
  503. }
  504. protected override Bitmap GetReferenceImage(string testName) {
  505. try{
  506. string fileName = testName.Replace(":", "_") + ".png";
  507. _bitmap.Save( fileName );
  508. GC.Collect();
  509. return null;
  510. }
  511. catch(System.Exception e) {
  512. throw new System.Exception("Error creating .Net reference image");
  513. }
  514. }
  515. private class NetForm:Form,IMyForm {
  516. Image image;
  517. public NetForm(string title, Image anImage):base() {
  518. base.Text = title;
  519. image = anImage;
  520. }
  521. protected override void OnPaint(PaintEventArgs e) {
  522. e.Graphics.DrawImageUnscaled (image, 0, 0);
  523. }
  524. void IMyForm.Show () {
  525. this.Size = image.Size;
  526. this.ShowDialog ();
  527. this.image.Save("test.net.png");
  528. }
  529. }
  530. protected override IMyForm CreateForm(string title) {
  531. return new NetForm (title, _bitmap);
  532. }
  533. protected override string DetermineCallingFunction() {
  534. StackFrame sf = new StackFrame (3, true);
  535. MethodBase mb = sf.GetMethod ();
  536. string name = mb.DeclaringType.FullName + "." + _ownerClass + "." + mb.Name;
  537. return name;
  538. }
  539. public override string CalculateSHA1() {
  540. Rectangle r = new Rectangle(0, 0, _bitmap.Width, _bitmap.Height);
  541. BitmapData data = _bitmap.LockBits (r, ImageLockMode.ReadOnly,
  542. _bitmap.PixelFormat);
  543. int dataSize = data.Stride * data.Height;
  544. byte [] bdata = new byte [dataSize];
  545. Marshal.Copy (data.Scan0, bdata, 0, dataSize);
  546. _bitmap.UnlockBits (data);
  547. SHA1 sha1 = new SHA1CryptoServiceProvider ();
  548. byte [] resdata = sha1.ComputeHash (bdata);
  549. return Convert.ToBase64String (resdata);
  550. }
  551. }
  552. #endif
  553. }