DrawingTest.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  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 {
  141. public const float DEFAULT_FLOAT_TOLERANCE = 1e-5f;
  142. public const int DEFAULT_IMAGE_TOLERANCE = 3;
  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. public bool Compare () {
  289. CheckCounter ();
  290. double error = CompareToExpectedInternal()*100;
  291. if (SpecialTolerance != null)
  292. return error <= GetSpecialTolerance(TestName);
  293. return error <= DEFAULT_IMAGE_TOLERANCE;
  294. }
  295. protected double GetSpecialTolerance(string testName) {
  296. try {
  297. string shortTestName = testName.Substring( testName.LastIndexOf(".") + 1 );
  298. object o = SpecialTolerance[shortTestName];
  299. if (o == null)
  300. return DEFAULT_IMAGE_TOLERANCE;
  301. return Convert.ToDouble(o);
  302. }
  303. catch (System.Exception) {
  304. return DEFAULT_IMAGE_TOLERANCE;
  305. }
  306. }
  307. /// <summary>
  308. /// Checks that the given bitmap norm is similar to expected
  309. /// </summary>
  310. /// <param name="tolerance">tolerance in percents (0..100)</param>
  311. /// <returns></returns>
  312. ///
  313. public bool Compare (double tolerance) {
  314. CheckCounter ();
  315. double error = CompareToExpectedInternal()*100;
  316. return error <= tolerance;
  317. }
  318. public void AssertCompare () {
  319. CheckCounter ();
  320. Assert.IsTrue ((CompareToExpectedInternal () * 100) < DEFAULT_IMAGE_TOLERANCE);
  321. }
  322. public void AssertCompare (double tolerance) {
  323. CheckCounter ();
  324. Assert.IsTrue ((CompareToExpectedInternal () * 100) < tolerance);
  325. }
  326. public double CompareToExpected () {
  327. CheckCounter ();
  328. return CompareToExpectedInternal ();
  329. }
  330. double CompareToExpectedInternal () {
  331. if (ShowForms)
  332. return 0;
  333. double norm = GetNorm ();
  334. double expNorm = GetExpectedNorm (norm);
  335. return System.Math.Abs (norm-expNorm)/(norm+expNorm+double.Epsilon);
  336. }
  337. public static DrawingTest Create (int width, int height) {
  338. return Create(width, height, "GraphicsFixture");
  339. }
  340. public static DrawingTest Create (int width, int height, string ownerClass) {
  341. DrawingTest test;
  342. #if TARGET_JVM
  343. test = new JavaDrawingTest ();
  344. #else
  345. test = new NetDrawingTest ();
  346. #endif
  347. test.Init (width, height);
  348. test.OwnerClass = ownerClass;
  349. return test;
  350. }
  351. }
  352. #if TARGET_JVM
  353. internal class JavaDrawingTest:DrawingTest {
  354. java.awt.image.BufferedImage _image;
  355. java.awt.image.BufferedImage Image {
  356. get {
  357. if (_image != null)
  358. return _image;
  359. Type imageType = typeof (Image);
  360. PropertyInfo prop = imageType.GetProperty ("NativeObject",
  361. BindingFlags.NonPublic | BindingFlags.Instance);
  362. MethodInfo method = prop.GetGetMethod (true);
  363. _image = (java.awt.image.BufferedImage) method.Invoke (_bitmap, new object [0]);
  364. return _image;
  365. }
  366. }
  367. public JavaDrawingTest () {}
  368. protected override double GetExpectedNorm (double myNorm) {
  369. return ExpectedResults.GetNorm(TestName);
  370. }
  371. private class JavaForm:java.awt.Dialog,IMyForm {
  372. class EventListener : [email protected] {
  373. #region WindowListener Members
  374. public void windowOpened([email protected] arg_0) {
  375. // TODO: Add ttt.windowOpened implementation
  376. }
  377. public void windowActivated([email protected] arg_0) {
  378. // TODO: Add ttt.windowActivated implementation
  379. }
  380. public void windowClosed([email protected] arg_0) {
  381. // TODO: Add ttt.windowClosed implementation
  382. }
  383. public void windowDeiconified([email protected] arg_0) {
  384. // TODO: Add ttt.windowDeiconified implementation
  385. }
  386. public void windowIconified([email protected] arg_0) {
  387. // TODO: Add ttt.windowIconified implementation
  388. }
  389. public void windowClosing([email protected] arg_0) {
  390. // TODO: Add ttt.windowClosing implementation
  391. java.awt.Window w = arg_0.getWindow();
  392. java.awt.Window par = w.getOwner ();
  393. w.dispose();
  394. par.dispose ();
  395. }
  396. public void windowDeactivated([email protected] arg_0) {
  397. // TODO: Add ttt.windowDeactivated implementation
  398. }
  399. #endregion
  400. }
  401. java.awt.Image _image;
  402. Size _s;
  403. public JavaForm (string title, java.awt.Image anImage, Size s)
  404. : base(new java.awt.Frame(), title, true) {
  405. _image = anImage;
  406. _s = s;
  407. addWindowListener(new EventListener());
  408. }
  409. public override void paint (java.awt.Graphics g) {
  410. base.paint (g);
  411. awt.Insets insets = this.getInsets ();
  412. g.drawImage (_image, insets.left, insets.top, null);
  413. }
  414. void IMyForm.Show () {
  415. awt.Insets insets = this.getInsets ();
  416. base.setSize (_s.Width + insets.left + insets.right,
  417. _s.Width + insets.top + insets.bottom);
  418. this.show ();
  419. //save the image
  420. //ImageIO.write((java.awt.image.RenderedImage)_image, "png", new java.io.File("test.java.png"));
  421. }
  422. }
  423. protected override IMyForm CreateForm(string title) {
  424. return new JavaForm (title, Image, _bitmap.Size);
  425. }
  426. protected override string DetermineCallingFunction() {
  427. System.Exception e = new System.Exception ();
  428. java.lang.Class c = vmw.common.TypeUtils.ToClass (e);
  429. java.lang.reflect.Method m = c.getMethod ("getStackTrace",
  430. new java.lang.Class [0]);
  431. java.lang.StackTraceElement [] els = (java.lang.StackTraceElement [])
  432. m.invoke (e, new object [0]);
  433. java.lang.StackTraceElement el = els [4];
  434. return el.getClassName () + "." + _ownerClass + "." + el.getMethodName ();
  435. }
  436. public override string CalculateSHA1() {
  437. MessageDigest md = MessageDigest.getInstance ("SHA");
  438. DataBufferInt dbi = (DataBufferInt) Image.getRaster ().getDataBuffer ();
  439. for (int i=0; i<dbi.getNumBanks (); i++) {
  440. int [] curBank = dbi.getData (i);
  441. for (int j=0; j<curBank.Length; j++) {
  442. int x = curBank[j];
  443. md.update ((sbyte) (x & 0xFF));
  444. md.update ((sbyte) ((x>>8) & 0xFF));
  445. md.update ((sbyte) ((x>>16) & 0xFF));
  446. md.update ((sbyte) ((x>>24) & 0xFF));
  447. }
  448. }
  449. byte [] resdata = (byte[])vmw.common.TypeUtils.ToByteArray(md.digest());
  450. return Convert.ToBase64String (resdata);
  451. }
  452. }
  453. #else
  454. internal class NetDrawingTest:DrawingTest {
  455. public NetDrawingTest () {}
  456. protected override double GetExpectedNorm (double myNorm) {
  457. if (CreateResults)
  458. ExpectedResults.WriteNorm (TestName, myNorm);
  459. return myNorm;
  460. }
  461. private class NetForm:Form,IMyForm {
  462. Image image;
  463. public NetForm(string title, Image anImage):base() {
  464. base.Text = title;
  465. image = anImage;
  466. }
  467. protected override void OnPaint(PaintEventArgs e) {
  468. e.Graphics.DrawImageUnscaled (image, 0, 0);
  469. }
  470. void IMyForm.Show () {
  471. this.Size = image.Size;
  472. this.ShowDialog ();
  473. this.image.Save("test.net.png");
  474. }
  475. }
  476. protected override IMyForm CreateForm(string title) {
  477. return new NetForm (title, _bitmap);
  478. }
  479. protected override string DetermineCallingFunction() {
  480. StackFrame sf = new StackFrame (3, true);
  481. MethodBase mb = sf.GetMethod ();
  482. string name = mb.DeclaringType.FullName + "." + _ownerClass + "." + mb.Name;
  483. return name;
  484. }
  485. public override string CalculateSHA1() {
  486. Rectangle r = new Rectangle(0, 0, _bitmap.Width, _bitmap.Height);
  487. BitmapData data = _bitmap.LockBits (r, ImageLockMode.ReadOnly,
  488. _bitmap.PixelFormat);
  489. int dataSize = data.Stride * data.Height;
  490. byte [] bdata = new byte [dataSize];
  491. Marshal.Copy (data.Scan0, bdata, 0, dataSize);
  492. _bitmap.UnlockBits (data);
  493. SHA1 sha1 = new SHA1CryptoServiceProvider ();
  494. byte [] resdata = sha1.ComputeHash (bdata);
  495. return Convert.ToBase64String (resdata);
  496. }
  497. }
  498. #endif
  499. }