DrawingTest.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  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. namespace DrawingTestHelper
  23. {
  24. #region Results serialization classes
  25. public sealed class ExpectedResult {
  26. public ExpectedResult(){}
  27. public ExpectedResult(string testName, double norm) {
  28. TestName = testName;
  29. Norm = norm;
  30. }
  31. public string TestName;
  32. public double Norm;
  33. }
  34. public sealed class ExpectedResults {
  35. [XmlArrayItem(typeof(ExpectedResult))]
  36. public ArrayList Tests = new ArrayList();
  37. }
  38. public sealed class ExpectedResultsHash {
  39. Hashtable _hash;
  40. ExpectedResults _suite;
  41. public ExpectedResultsHash () {
  42. try {
  43. using (StreamReader s = new StreamReader (FileName)) {
  44. _suite = (ExpectedResults)TestSuiteSerializer.Deserialize(s);
  45. }
  46. }
  47. catch {
  48. _suite = new ExpectedResults ();
  49. }
  50. _hash = new Hashtable(_suite.Tests.Count);
  51. foreach (ExpectedResult res in _suite.Tests)
  52. _hash[res.TestName] = res.Norm;
  53. }
  54. public const string FileName = "ExpectedResults.xml";
  55. public readonly static XmlSerializer TestSuiteSerializer = new XmlSerializer(typeof(ExpectedResults));
  56. public double GetNorm(string testName) {
  57. object res = _hash[testName];
  58. if (res != null)
  59. return (double)res;
  60. return double.NaN;
  61. }
  62. public void WriteNorm (string testName, double myNorm) {
  63. if (_hash.Contains (testName)) {
  64. for (int i = 0; i < _suite.Tests.Count; i++) {
  65. ExpectedResult cur = (ExpectedResult) _suite.Tests[i];
  66. if (cur.TestName == testName) {
  67. cur.Norm = myNorm;
  68. break;
  69. }
  70. }
  71. }
  72. else
  73. _suite.Tests.Add(new ExpectedResult(testName, myNorm));
  74. _hash[testName] = myNorm;
  75. using(StreamWriter w = new StreamWriter(FileName))
  76. TestSuiteSerializer.Serialize(w, _suite);
  77. }
  78. }
  79. public sealed class CachedResult {
  80. public CachedResult (){}
  81. public CachedResult (string testName, string sha1, double norm) {
  82. TestName = testName;
  83. SHA1 = sha1;
  84. Norm = norm;
  85. DateTime = DateTime.Now;
  86. }
  87. public string TestName;
  88. public string SHA1;
  89. public double Norm;
  90. public DateTime DateTime;
  91. }
  92. public sealed class CachedResults {
  93. [XmlArrayItem(typeof(CachedResult))]
  94. public ArrayList Tests = new ArrayList();
  95. }
  96. public class Cache {
  97. Hashtable _hash;
  98. CachedResults _results;
  99. #if TARGET_JVM
  100. public const string FileName = "CachedResults.xml";
  101. public const string NewFileName = "NewCachedResults.xml";
  102. #else
  103. public const string FileName = "dotnet.CachedResults.xml";
  104. public const string NewFileName = "dotnet.NewCachedResults.xml";
  105. #endif
  106. public readonly static XmlSerializer TestSuiteSerializer =
  107. new XmlSerializer(typeof(CachedResults));
  108. public Cache () {
  109. try {
  110. using (StreamReader r = new StreamReader(FileName))
  111. _results = (CachedResults)TestSuiteSerializer.Deserialize(r);
  112. }
  113. catch {
  114. _results = new CachedResults ();
  115. }
  116. _hash = new Hashtable(_results.Tests.Count);
  117. foreach (CachedResult res in _results.Tests)
  118. _hash[res.SHA1] = res.Norm;
  119. }
  120. public double GetNorm (string sha1) {
  121. if (_hash.ContainsKey (sha1))
  122. return (double)_hash[sha1];
  123. else
  124. return double.NaN;
  125. }
  126. public void Add (string testName, string sha1, double norm) {
  127. if (_hash.ContainsKey (sha1))
  128. throw new ArgumentException ("This SHA1 is already in the cache", "sha1");
  129. _results.Tests.Add (new CachedResult(testName, sha1, norm));
  130. _hash.Add (sha1, norm);
  131. using(StreamWriter w = new StreamWriter(NewFileName))
  132. TestSuiteSerializer.Serialize(w, _results);
  133. }
  134. }
  135. #endregion
  136. /// <summary>
  137. /// Summary description for DrawingTest.
  138. /// </summary>
  139. public abstract class DrawingTest {
  140. Graphics _graphics;
  141. protected Bitmap _bitmap;
  142. static string _callingFunction;
  143. //static int _counter;
  144. static Hashtable _mpFuncCount = new Hashtable();
  145. static bool _showForms = false;
  146. static bool _createResults = true;
  147. protected readonly static ExpectedResultsHash ExpectedResults = new ExpectedResultsHash ();
  148. protected readonly static Cache cache = new Cache ();
  149. public Graphics Graphics {get {return _graphics;}}
  150. public Bitmap Bitmap {get { return _bitmap; }}
  151. public static bool ShowForms {
  152. get {return _showForms;}
  153. set {_showForms = value;}
  154. }
  155. public static bool CreateResults {
  156. get {return _createResults;}
  157. set {_createResults = value;}
  158. }
  159. protected DrawingTest() {}
  160. private void Init (int width, int height) {
  161. Init (new Bitmap (width, height));
  162. }
  163. private void Init (Bitmap bitmap) {
  164. _bitmap = bitmap;
  165. _graphics = Graphics.FromImage (_bitmap);
  166. }
  167. protected abstract string DetermineCallingFunction ();
  168. protected interface IMyForm {
  169. void Show ();
  170. }
  171. protected abstract IMyForm CreateForm (string title);
  172. public void Show () {
  173. CheckCounter ();
  174. if (!ShowForms)
  175. return;
  176. IMyForm form = CreateForm(_callingFunction + _mpFuncCount[_callingFunction]);
  177. form.Show ();
  178. }
  179. static protected string TestName {
  180. get {
  181. return _callingFunction + ":" + _mpFuncCount[_callingFunction]/* + ".dat"*/;
  182. }
  183. }
  184. #region GetImageFFTArray
  185. private static ComplexF[] GetImageFFTArray(Bitmap bitmap) {
  186. float scale = 1F / (float) System.Math.Sqrt(bitmap.Width * bitmap.Height);
  187. ComplexF[] data = new ComplexF [bitmap.Width * bitmap.Height * 4];
  188. int offset = 0;
  189. for( int y = 0; y < bitmap.Height; y ++ )
  190. for( int x = 0; x < bitmap.Width; x ++ ) {
  191. Color c = bitmap.GetPixel (x, y);
  192. float s = 1F;
  193. if( (( x + y ) & 0x1 ) != 0 ) {
  194. s = -1F;
  195. }
  196. data [offset++] = new ComplexF( c.A * s / 256F, 0);
  197. data [offset++] = new ComplexF( c.R * s / -256F, 0);
  198. data [offset++] = new ComplexF( c.G * s / 256F, 0);
  199. data [offset++] = new ComplexF( c.B * s / -256F, 0);
  200. }
  201. Fourier.FFT3( data, 4, bitmap.Width, bitmap.Height, FourierDirection.Forward );
  202. for( int i = 0; i < data.Length; i ++ ) {
  203. data[i] *= scale;
  204. }
  205. return data;
  206. }
  207. #endregion
  208. abstract public string CalculateSHA1 ();
  209. public static double CalculateNorm (Bitmap bitmap) {
  210. ComplexF[] matrix = GetImageFFTArray(bitmap);
  211. double norm = 0;
  212. int size_x = 4; //ARGB values
  213. int size_y = bitmap.Width;
  214. int size_z = bitmap.Height;
  215. for (int x=1; x<=size_x; x++) {
  216. double norm_y = 0;
  217. for (int y=1; y<=size_y; y++) {
  218. double norm_z = 0;
  219. for (int z=1; z<=size_z; z++) {
  220. ComplexF cur = matrix[(size_x-x)+size_x*(size_y-y)+size_x*size_y*(size_z-z)];
  221. norm_z += cur.GetModulusSquared ();// * z;
  222. }
  223. norm_y += norm_z;// * y;
  224. }
  225. norm += norm_y;// * x;
  226. }
  227. return norm;
  228. }
  229. public double GetNorm () {
  230. string sha1 = CalculateSHA1 ();
  231. double norm = cache.GetNorm (sha1);
  232. if (double.IsNaN (norm)) {
  233. norm = CalculateNorm (_bitmap);
  234. cache.Add (TestName, sha1, norm);
  235. }
  236. return norm;
  237. }
  238. protected abstract double GetExpectedNorm (double myNorm);
  239. private void CheckCounter () {
  240. string callFunc = DetermineCallingFunction ();
  241. _callingFunction = callFunc;
  242. if (!_mpFuncCount.Contains(_callingFunction)) {
  243. _mpFuncCount[_callingFunction] = 1;
  244. }
  245. else {
  246. int counter = (int)_mpFuncCount[_callingFunction];
  247. counter ++;
  248. _mpFuncCount[_callingFunction] = counter;
  249. }
  250. }
  251. /// <summary>
  252. /// Checks that the given bitmap norm is similar to expected
  253. /// </summary>
  254. /// <param name="tolerance">tolerance in percents (0..100)</param>
  255. /// <returns></returns>
  256. public bool Compare (double tolerance) {
  257. CheckCounter ();
  258. return (CompareToExpectedInternal()*100) < tolerance;
  259. }
  260. public double CompareToExpected () {
  261. CheckCounter ();
  262. return CompareToExpectedInternal ();
  263. }
  264. double CompareToExpectedInternal () {
  265. if (ShowForms)
  266. return 0;
  267. double norm = GetNorm ();
  268. double expNorm = GetExpectedNorm (norm);
  269. return System.Math.Abs (norm-expNorm)/(norm+expNorm+double.Epsilon);
  270. }
  271. public static DrawingTest Create (int width, int height) {
  272. DrawingTest test;
  273. #if TARGET_JVM
  274. test = new JavaDrawingTest ();
  275. #else
  276. test = new NetDrawingTest ();
  277. #endif
  278. test.Init (width, height);
  279. return test;
  280. }
  281. }
  282. #if TARGET_JVM
  283. internal class JavaDrawingTest:DrawingTest {
  284. java.awt.image.BufferedImage _image;
  285. java.awt.image.BufferedImage Image {
  286. get {
  287. if (_image != null)
  288. return _image;
  289. Type imageType = typeof (Image);
  290. PropertyInfo prop = imageType.GetProperty ("NativeObject",
  291. BindingFlags.NonPublic | BindingFlags.Instance);
  292. MethodInfo method = prop.GetGetMethod (true);
  293. _image = (java.awt.image.BufferedImage) method.Invoke (_bitmap, new object [0]);
  294. return _image;
  295. }
  296. }
  297. public JavaDrawingTest () {}
  298. protected override double GetExpectedNorm (double myNorm) {
  299. return ExpectedResults.GetNorm(TestName);
  300. }
  301. private class JavaForm:java.awt.Dialog,IMyForm {
  302. class EventListener : [email protected] {
  303. #region WindowListener Members
  304. public void windowOpened([email protected] arg_0) {
  305. // TODO: Add ttt.windowOpened implementation
  306. }
  307. public void windowActivated([email protected] arg_0) {
  308. // TODO: Add ttt.windowActivated implementation
  309. }
  310. public void windowClosed([email protected] arg_0) {
  311. // TODO: Add ttt.windowClosed implementation
  312. }
  313. public void windowDeiconified([email protected] arg_0) {
  314. // TODO: Add ttt.windowDeiconified implementation
  315. }
  316. public void windowIconified([email protected] arg_0) {
  317. // TODO: Add ttt.windowIconified implementation
  318. }
  319. public void windowClosing([email protected] arg_0) {
  320. // TODO: Add ttt.windowClosing implementation
  321. java.awt.Window w = arg_0.getWindow();
  322. java.awt.Window par = w.getOwner ();
  323. w.dispose();
  324. par.dispose ();
  325. }
  326. public void windowDeactivated([email protected] arg_0) {
  327. // TODO: Add ttt.windowDeactivated implementation
  328. }
  329. #endregion
  330. }
  331. java.awt.Image _image;
  332. Size _s;
  333. public JavaForm (string title, java.awt.Image anImage, Size s)
  334. : base(new java.awt.Frame(), title, true) {
  335. _image = anImage;
  336. _s = s;
  337. addWindowListener(new EventListener());
  338. }
  339. public override void paint (java.awt.Graphics g) {
  340. base.paint (g);
  341. awt.Insets insets = this.getInsets ();
  342. g.drawImage (_image, insets.left, insets.top, null);
  343. }
  344. void IMyForm.Show () {
  345. awt.Insets insets = this.getInsets ();
  346. base.setSize (_s.Width + insets.left + insets.right,
  347. _s.Width + insets.top + insets.bottom);
  348. this.show ();
  349. //save the image
  350. //ImageIO.write((java.awt.image.RenderedImage)_image, "png", new java.io.File("test.java.png"));
  351. }
  352. }
  353. protected override IMyForm CreateForm(string title) {
  354. return new JavaForm (title, Image, _bitmap.Size);
  355. }
  356. protected override string DetermineCallingFunction() {
  357. System.Exception e = new System.Exception ();
  358. java.lang.Class c = vmw.common.TypeUtils.ToClass (e);
  359. java.lang.reflect.Method m = c.getMethod ("getStackTrace",
  360. new java.lang.Class [0]);
  361. java.lang.StackTraceElement [] els = (java.lang.StackTraceElement [])
  362. m.invoke (e, new object [0]);
  363. java.lang.StackTraceElement el = els [4];
  364. return el.getClassName () + "." + el.getMethodName ();
  365. }
  366. public override string CalculateSHA1() {
  367. MessageDigest md = MessageDigest.getInstance ("SHA");
  368. DataBufferInt dbi = (DataBufferInt) Image.getRaster ().getDataBuffer ();
  369. for (int i=0; i<dbi.getNumBanks (); i++) {
  370. int [] curBank = dbi.getData (i);
  371. for (int j=0; j<curBank.Length; j++) {
  372. int x = curBank[j];
  373. md.update ((sbyte) (x & 0xFF));
  374. md.update ((sbyte) ((x>>8) & 0xFF));
  375. md.update ((sbyte) ((x>>16) & 0xFF));
  376. md.update ((sbyte) ((x>>24) & 0xFF));
  377. }
  378. }
  379. return md.ToString ();
  380. }
  381. }
  382. #else
  383. internal class NetDrawingTest:DrawingTest {
  384. public NetDrawingTest () {}
  385. protected override double GetExpectedNorm (double myNorm) {
  386. if (CreateResults)
  387. ExpectedResults.WriteNorm (TestName, myNorm);
  388. return myNorm;
  389. }
  390. private class NetForm:Form,IMyForm {
  391. Image image;
  392. public NetForm(string title, Image anImage):base() {
  393. base.Text = title;
  394. image = anImage;
  395. }
  396. protected override void OnPaint(PaintEventArgs e) {
  397. e.Graphics.DrawImageUnscaled (image, 0, 0);
  398. }
  399. void IMyForm.Show () {
  400. this.Size = image.Size;
  401. this.ShowDialog ();
  402. this.image.Save("test.net.png");
  403. }
  404. }
  405. protected override IMyForm CreateForm(string title) {
  406. return new NetForm (title, _bitmap);
  407. }
  408. protected override string DetermineCallingFunction() {
  409. StackFrame sf = new StackFrame (3, true);
  410. MethodBase mb = sf.GetMethod ();
  411. string name = mb.DeclaringType.FullName + "." + mb.Name;
  412. return name;
  413. }
  414. public override string CalculateSHA1() {
  415. Rectangle r = new Rectangle(0, 0, _bitmap.Width, _bitmap.Height);
  416. BitmapData data = _bitmap.LockBits (r, ImageLockMode.ReadOnly,
  417. _bitmap.PixelFormat);
  418. int dataSize = data.Stride * data.Height;
  419. byte [] bdata = new byte [dataSize];
  420. Marshal.Copy (data.Scan0, bdata, 0, dataSize);
  421. _bitmap.UnlockBits (data);
  422. SHA1 sha1 = new SHA1CryptoServiceProvider ();
  423. byte [] resdata = sha1.ComputeHash (bdata);
  424. return Convert.ToBase64String (resdata);
  425. }
  426. }
  427. #endif
  428. }