DrawingTest.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  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 = 10;
  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 readonly static ExpectedResultsHash ExpectedResults = new ExpectedResultsHash ();
  151. protected readonly static Cache cache = new Cache ();
  152. public Graphics Graphics {get {return _graphics;}}
  153. public Bitmap Bitmap {get { return _bitmap; }}
  154. public static bool ShowForms {
  155. get {return _showForms;}
  156. set {_showForms = value;}
  157. }
  158. public static bool CreateResults {
  159. get {return _createResults;}
  160. set {_createResults = value;}
  161. }
  162. protected DrawingTest() {}
  163. private void Init (int width, int height) {
  164. Init (new Bitmap (width, height));
  165. }
  166. private void Init (Bitmap bitmap) {
  167. _bitmap = bitmap;
  168. _graphics = Graphics.FromImage (_bitmap);
  169. }
  170. protected abstract string DetermineCallingFunction ();
  171. protected interface IMyForm {
  172. void Show ();
  173. }
  174. protected abstract IMyForm CreateForm (string title);
  175. public void Show () {
  176. CheckCounter ();
  177. if (!ShowForms)
  178. return;
  179. IMyForm form = CreateForm(_callingFunction + _mpFuncCount[_callingFunction]);
  180. form.Show ();
  181. }
  182. static protected string TestName {
  183. get {
  184. return _callingFunction + ":" + _mpFuncCount[_callingFunction]/* + ".dat"*/;
  185. }
  186. }
  187. #region GetImageFFTArray
  188. private static ComplexF[] GetImageFFTArray(Bitmap bitmap) {
  189. float scale = 1F / (float) System.Math.Sqrt(bitmap.Width * bitmap.Height);
  190. ComplexF[] data = new ComplexF [bitmap.Width * bitmap.Height * 4];
  191. int offset = 0;
  192. for( int y = 0; y < bitmap.Height; y ++ )
  193. for( int x = 0; x < bitmap.Width; x ++ ) {
  194. Color c = bitmap.GetPixel (x, y);
  195. float s = 1F;
  196. if( (( x + y ) & 0x1 ) != 0 ) {
  197. s = -1F;
  198. }
  199. data [offset++] = new ComplexF( c.A * s / 256F, 0);
  200. data [offset++] = new ComplexF( c.R * s / -256F, 0);
  201. data [offset++] = new ComplexF( c.G * s / 256F, 0);
  202. data [offset++] = new ComplexF( c.B * s / -256F, 0);
  203. }
  204. Fourier.FFT3( data, 4, bitmap.Width, bitmap.Height, FourierDirection.Forward );
  205. for( int i = 0; i < data.Length; i ++ ) {
  206. data[i] *= scale;
  207. }
  208. return data;
  209. }
  210. #endregion
  211. abstract public string CalculateSHA1 ();
  212. public static double CalculateNorm (Bitmap bitmap) {
  213. ComplexF[] matrix = GetImageFFTArray(bitmap);
  214. double norm = 0;
  215. int size_x = 4; //ARGB values
  216. int size_y = bitmap.Width;
  217. int size_z = bitmap.Height;
  218. for (int x=1; x<=size_x; x++) {
  219. double norm_y = 0;
  220. for (int y=1; y<=size_y; y++) {
  221. double norm_z = 0;
  222. for (int z=1; z<=size_z; z++) {
  223. ComplexF cur = matrix[(size_x-x)+size_x*(size_y-y)+size_x*size_y*(size_z-z)];
  224. norm_z += cur.GetModulusSquared ();// * z;
  225. }
  226. norm_y += norm_z;// * y;
  227. }
  228. norm += norm_y;// * x;
  229. }
  230. return norm;
  231. }
  232. public double GetNorm () {
  233. string sha1 = CalculateSHA1 ();
  234. double norm = cache.GetNorm (sha1);
  235. if (double.IsNaN (norm)) {
  236. norm = CalculateNorm (_bitmap);
  237. cache.Add (TestName, sha1, norm);
  238. }
  239. return norm;
  240. }
  241. protected abstract double GetExpectedNorm (double myNorm);
  242. private void CheckCounter () {
  243. string callFunc = DetermineCallingFunction ();
  244. _callingFunction = callFunc;
  245. if (!_mpFuncCount.Contains(_callingFunction)) {
  246. _mpFuncCount[_callingFunction] = 1;
  247. }
  248. else {
  249. int counter = (int)_mpFuncCount[_callingFunction];
  250. counter ++;
  251. _mpFuncCount[_callingFunction] = counter;
  252. }
  253. }
  254. public static void AssertAlmostEqual (float expected, float actual)
  255. {
  256. AssertAlmostEqual (expected, actual, DEFAULT_FLOAT_TOLERANCE);
  257. }
  258. public static void AssertAlmostEqual (float expected, float actual, float tolerance)
  259. {
  260. string msg = String.Format("\nExpected : {0} \nActual : {1}",expected.ToString(),actual.ToString());
  261. AssertAlmostEqual (expected, actual, tolerance, msg);
  262. }
  263. private static void AssertAlmostEqual (float expected, float actual, float tolerance, string message)
  264. {
  265. float error = Math.Abs (expected - actual) / (expected + actual + float.Epsilon);
  266. Assert.IsTrue (error < tolerance, message);
  267. }
  268. public static void AssertAlmostEqual (PointF expected, PointF actual)
  269. {
  270. string msg = String.Format("\nExpected : {0} \n Actual : {1}",expected.ToString(),actual.ToString());
  271. AssertAlmostEqual (expected.X, actual.X, DEFAULT_FLOAT_TOLERANCE, msg);
  272. AssertAlmostEqual (expected.Y, actual.Y, DEFAULT_FLOAT_TOLERANCE, msg);
  273. }
  274. public bool Compare () {
  275. CheckCounter ();
  276. return (CompareToExpectedInternal()*100) < DEFAULT_IMAGE_TOLERANCE;
  277. }
  278. /// <summary>
  279. /// Checks that the given bitmap norm is similar to expected
  280. /// </summary>
  281. /// <param name="tolerance">tolerance in percents (0..100)</param>
  282. /// <returns></returns>
  283. ///
  284. public bool Compare (double tolerance) {
  285. CheckCounter ();
  286. return (CompareToExpectedInternal()*100) < tolerance;
  287. }
  288. public void AssertCompare () {
  289. CheckCounter ();
  290. Assert.IsTrue ((CompareToExpectedInternal () * 100) < DEFAULT_IMAGE_TOLERANCE);
  291. }
  292. public void AssertCompare (double tolerance) {
  293. CheckCounter ();
  294. Assert.IsTrue ((CompareToExpectedInternal () * 100) < tolerance);
  295. }
  296. public double CompareToExpected () {
  297. CheckCounter ();
  298. return CompareToExpectedInternal ();
  299. }
  300. double CompareToExpectedInternal () {
  301. if (ShowForms)
  302. return 0;
  303. double norm = GetNorm ();
  304. double expNorm = GetExpectedNorm (norm);
  305. return System.Math.Abs (norm-expNorm)/(norm+expNorm+double.Epsilon);
  306. }
  307. public static DrawingTest Create (int width, int height) {
  308. DrawingTest test;
  309. #if TARGET_JVM
  310. test = new JavaDrawingTest ();
  311. #else
  312. test = new NetDrawingTest ();
  313. #endif
  314. test.Init (width, height);
  315. return test;
  316. }
  317. }
  318. #if TARGET_JVM
  319. internal class JavaDrawingTest:DrawingTest {
  320. java.awt.image.BufferedImage _image;
  321. java.awt.image.BufferedImage Image {
  322. get {
  323. if (_image != null)
  324. return _image;
  325. Type imageType = typeof (Image);
  326. PropertyInfo prop = imageType.GetProperty ("NativeObject",
  327. BindingFlags.NonPublic | BindingFlags.Instance);
  328. MethodInfo method = prop.GetGetMethod (true);
  329. _image = (java.awt.image.BufferedImage) method.Invoke (_bitmap, new object [0]);
  330. return _image;
  331. }
  332. }
  333. public JavaDrawingTest () {}
  334. protected override double GetExpectedNorm (double myNorm) {
  335. return ExpectedResults.GetNorm(TestName);
  336. }
  337. private class JavaForm:java.awt.Dialog,IMyForm {
  338. class EventListener : [email protected] {
  339. #region WindowListener Members
  340. public void windowOpened([email protected] arg_0) {
  341. // TODO: Add ttt.windowOpened implementation
  342. }
  343. public void windowActivated([email protected] arg_0) {
  344. // TODO: Add ttt.windowActivated implementation
  345. }
  346. public void windowClosed([email protected] arg_0) {
  347. // TODO: Add ttt.windowClosed implementation
  348. }
  349. public void windowDeiconified([email protected] arg_0) {
  350. // TODO: Add ttt.windowDeiconified implementation
  351. }
  352. public void windowIconified([email protected] arg_0) {
  353. // TODO: Add ttt.windowIconified implementation
  354. }
  355. public void windowClosing([email protected] arg_0) {
  356. // TODO: Add ttt.windowClosing implementation
  357. java.awt.Window w = arg_0.getWindow();
  358. java.awt.Window par = w.getOwner ();
  359. w.dispose();
  360. par.dispose ();
  361. }
  362. public void windowDeactivated([email protected] arg_0) {
  363. // TODO: Add ttt.windowDeactivated implementation
  364. }
  365. #endregion
  366. }
  367. java.awt.Image _image;
  368. Size _s;
  369. public JavaForm (string title, java.awt.Image anImage, Size s)
  370. : base(new java.awt.Frame(), title, true) {
  371. _image = anImage;
  372. _s = s;
  373. addWindowListener(new EventListener());
  374. }
  375. public override void paint (java.awt.Graphics g) {
  376. base.paint (g);
  377. awt.Insets insets = this.getInsets ();
  378. g.drawImage (_image, insets.left, insets.top, null);
  379. }
  380. void IMyForm.Show () {
  381. awt.Insets insets = this.getInsets ();
  382. base.setSize (_s.Width + insets.left + insets.right,
  383. _s.Width + insets.top + insets.bottom);
  384. this.show ();
  385. //save the image
  386. //ImageIO.write((java.awt.image.RenderedImage)_image, "png", new java.io.File("test.java.png"));
  387. }
  388. }
  389. protected override IMyForm CreateForm(string title) {
  390. return new JavaForm (title, Image, _bitmap.Size);
  391. }
  392. protected override string DetermineCallingFunction() {
  393. System.Exception e = new System.Exception ();
  394. java.lang.Class c = vmw.common.TypeUtils.ToClass (e);
  395. java.lang.reflect.Method m = c.getMethod ("getStackTrace",
  396. new java.lang.Class [0]);
  397. java.lang.StackTraceElement [] els = (java.lang.StackTraceElement [])
  398. m.invoke (e, new object [0]);
  399. java.lang.StackTraceElement el = els [4];
  400. return el.getClassName () + "." + el.getMethodName ();
  401. }
  402. public override string CalculateSHA1() {
  403. MessageDigest md = MessageDigest.getInstance ("SHA");
  404. DataBufferInt dbi = (DataBufferInt) Image.getRaster ().getDataBuffer ();
  405. for (int i=0; i<dbi.getNumBanks (); i++) {
  406. int [] curBank = dbi.getData (i);
  407. for (int j=0; j<curBank.Length; j++) {
  408. int x = curBank[j];
  409. md.update ((sbyte) (x & 0xFF));
  410. md.update ((sbyte) ((x>>8) & 0xFF));
  411. md.update ((sbyte) ((x>>16) & 0xFF));
  412. md.update ((sbyte) ((x>>24) & 0xFF));
  413. }
  414. }
  415. return md.ToString ();
  416. }
  417. }
  418. #else
  419. internal class NetDrawingTest:DrawingTest {
  420. public NetDrawingTest () {}
  421. protected override double GetExpectedNorm (double myNorm) {
  422. if (CreateResults)
  423. ExpectedResults.WriteNorm (TestName, myNorm);
  424. return myNorm;
  425. }
  426. private class NetForm:Form,IMyForm {
  427. Image image;
  428. public NetForm(string title, Image anImage):base() {
  429. base.Text = title;
  430. image = anImage;
  431. }
  432. protected override void OnPaint(PaintEventArgs e) {
  433. e.Graphics.DrawImageUnscaled (image, 0, 0);
  434. }
  435. void IMyForm.Show () {
  436. this.Size = image.Size;
  437. this.ShowDialog ();
  438. this.image.Save("test.net.png");
  439. }
  440. }
  441. protected override IMyForm CreateForm(string title) {
  442. return new NetForm (title, _bitmap);
  443. }
  444. protected override string DetermineCallingFunction() {
  445. StackFrame sf = new StackFrame (3, true);
  446. MethodBase mb = sf.GetMethod ();
  447. string name = mb.DeclaringType.FullName + "." + mb.Name;
  448. return name;
  449. }
  450. public override string CalculateSHA1() {
  451. Rectangle r = new Rectangle(0, 0, _bitmap.Width, _bitmap.Height);
  452. BitmapData data = _bitmap.LockBits (r, ImageLockMode.ReadOnly,
  453. _bitmap.PixelFormat);
  454. int dataSize = data.Stride * data.Height;
  455. byte [] bdata = new byte [dataSize];
  456. Marshal.Copy (data.Scan0, bdata, 0, dataSize);
  457. _bitmap.UnlockBits (data);
  458. SHA1 sha1 = new SHA1CryptoServiceProvider ();
  459. byte [] resdata = sha1.ComputeHash (bdata);
  460. return Convert.ToBase64String (resdata);
  461. }
  462. }
  463. #endif
  464. }