using System;
using System.Drawing;
using System.Diagnostics;
using System.IO;
using System.Text;
using Exocortex.DSP;
using System.Reflection;
using System.Xml.Serialization;
using System.Collections;
using System.Security.Cryptography;
#if TARGET_JVM
using awt = java.awt;
using javax.imageio;
using java.lang;
using java.security;
using java.awt.image;
#else
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
#endif
using NUnit.Framework;
namespace DrawingTestHelper
{
#region Results serialization classes
public sealed class ExpectedResult {
public ExpectedResult(){}
public ExpectedResult(string testName, double norm) {
TestName = testName;
Norm = norm;
}
public string TestName;
public double Norm;
}
public sealed class ExpectedResults {
[XmlArrayItem(typeof(ExpectedResult))]
public ArrayList Tests = new ArrayList();
}
public sealed class ExpectedResultsHash {
Hashtable _hash;
ExpectedResults _suite;
public ExpectedResultsHash () {
try {
using (StreamReader s = new StreamReader (FileName)) {
_suite = (ExpectedResults)TestSuiteSerializer.Deserialize(s);
}
}
catch {
_suite = new ExpectedResults ();
}
_hash = new Hashtable(_suite.Tests.Count);
foreach (ExpectedResult res in _suite.Tests)
_hash[res.TestName] = res.Norm;
}
public const string FileName = "ExpectedResults.xml";
public readonly static XmlSerializer TestSuiteSerializer = new XmlSerializer(typeof(ExpectedResults));
public double GetNorm(string testName) {
object res = _hash[testName];
if (res != null)
return (double)res;
return double.NaN;
}
public void WriteNorm (string testName, double myNorm) {
if (_hash.Contains (testName)) {
for (int i = 0; i < _suite.Tests.Count; i++) {
ExpectedResult cur = (ExpectedResult) _suite.Tests[i];
if (cur.TestName == testName) {
cur.Norm = myNorm;
break;
}
}
}
else
_suite.Tests.Add(new ExpectedResult(testName, myNorm));
_hash[testName] = myNorm;
using(StreamWriter w = new StreamWriter(FileName))
TestSuiteSerializer.Serialize(w, _suite);
}
}
public sealed class CachedResult {
public CachedResult (){}
public CachedResult (string testName, string sha1, double norm) {
TestName = testName;
SHA1 = sha1;
Norm = norm;
DateTime = DateTime.Now;
}
public string TestName;
public string SHA1;
public double Norm;
public DateTime DateTime;
}
public sealed class CachedResults {
[XmlArrayItem(typeof(CachedResult))]
public ArrayList Tests = new ArrayList();
}
public class Cache {
Hashtable _hash;
CachedResults _results;
#if TARGET_JVM
public const string FileName = "CachedResults.xml";
public const string NewFileName = "NewCachedResults.xml";
#else
public const string FileName = "dotnet.CachedResults.xml";
public const string NewFileName = "dotnet.NewCachedResults.xml";
#endif
public readonly static XmlSerializer TestSuiteSerializer =
new XmlSerializer(typeof(CachedResults));
public Cache () {
try {
using (StreamReader r = new StreamReader(FileName))
_results = (CachedResults)TestSuiteSerializer.Deserialize(r);
}
catch {
_results = new CachedResults ();
}
_hash = new Hashtable(_results.Tests.Count);
foreach (CachedResult res in _results.Tests)
_hash[res.SHA1] = res.Norm;
}
public double GetNorm (string sha1) {
if (_hash.ContainsKey (sha1))
return (double)_hash[sha1];
else
return double.NaN;
}
public void Add (string testName, string sha1, double norm) {
if (_hash.ContainsKey (sha1))
throw new ArgumentException ("This SHA1 is already in the cache", "sha1");
_results.Tests.Add (new CachedResult(testName, sha1, norm));
_hash.Add (sha1, norm);
using(StreamWriter w = new StreamWriter(NewFileName))
TestSuiteSerializer.Serialize(w, _results);
}
}
#endregion
///
/// Summary description for DrawingTest.
///
public abstract class DrawingTest {
public const float DEFAULT_FLOAT_TOLERANCE = 1e-5f;
public const int DEFAULT_IMAGE_TOLERANCE = 10;
Graphics _graphics;
protected Bitmap _bitmap;
static string _callingFunction;
//static int _counter;
static Hashtable _mpFuncCount = new Hashtable();
static bool _showForms = false;
static bool _createResults = true;
protected readonly static ExpectedResultsHash ExpectedResults = new ExpectedResultsHash ();
protected readonly static Cache cache = new Cache ();
public Graphics Graphics {get {return _graphics;}}
public Bitmap Bitmap {get { return _bitmap; }}
public static bool ShowForms {
get {return _showForms;}
set {_showForms = value;}
}
public static bool CreateResults {
get {return _createResults;}
set {_createResults = value;}
}
protected DrawingTest() {}
private void Init (int width, int height) {
Init (new Bitmap (width, height));
}
private void Init (Bitmap bitmap) {
_bitmap = bitmap;
_graphics = Graphics.FromImage (_bitmap);
}
protected abstract string DetermineCallingFunction ();
protected interface IMyForm {
void Show ();
}
protected abstract IMyForm CreateForm (string title);
public void Show () {
CheckCounter ();
if (!ShowForms)
return;
IMyForm form = CreateForm(_callingFunction + _mpFuncCount[_callingFunction]);
form.Show ();
}
static protected string TestName {
get {
return _callingFunction + ":" + _mpFuncCount[_callingFunction]/* + ".dat"*/;
}
}
#region GetImageFFTArray
private static ComplexF[] GetImageFFTArray(Bitmap bitmap) {
float scale = 1F / (float) System.Math.Sqrt(bitmap.Width * bitmap.Height);
ComplexF[] data = new ComplexF [bitmap.Width * bitmap.Height * 4];
int offset = 0;
for( int y = 0; y < bitmap.Height; y ++ )
for( int x = 0; x < bitmap.Width; x ++ ) {
Color c = bitmap.GetPixel (x, y);
float s = 1F;
if( (( x + y ) & 0x1 ) != 0 ) {
s = -1F;
}
data [offset++] = new ComplexF( c.A * s / 256F, 0);
data [offset++] = new ComplexF( c.R * s / -256F, 0);
data [offset++] = new ComplexF( c.G * s / 256F, 0);
data [offset++] = new ComplexF( c.B * s / -256F, 0);
}
Fourier.FFT3( data, 4, bitmap.Width, bitmap.Height, FourierDirection.Forward );
for( int i = 0; i < data.Length; i ++ ) {
data[i] *= scale;
}
return data;
}
#endregion
abstract public string CalculateSHA1 ();
public static double CalculateNorm (Bitmap bitmap) {
ComplexF[] matrix = GetImageFFTArray(bitmap);
double norm = 0;
int size_x = 4; //ARGB values
int size_y = bitmap.Width;
int size_z = bitmap.Height;
for (int x=1; x<=size_x; x++) {
double norm_y = 0;
for (int y=1; y<=size_y; y++) {
double norm_z = 0;
for (int z=1; z<=size_z; z++) {
ComplexF cur = matrix[(size_x-x)+size_x*(size_y-y)+size_x*size_y*(size_z-z)];
norm_z += cur.GetModulusSquared ();// * z;
}
norm_y += norm_z;// * y;
}
norm += norm_y;// * x;
}
return norm;
}
public double GetNorm () {
string sha1 = CalculateSHA1 ();
double norm = cache.GetNorm (sha1);
if (double.IsNaN (norm)) {
norm = CalculateNorm (_bitmap);
cache.Add (TestName, sha1, norm);
}
return norm;
}
protected abstract double GetExpectedNorm (double myNorm);
private void CheckCounter () {
string callFunc = DetermineCallingFunction ();
_callingFunction = callFunc;
if (!_mpFuncCount.Contains(_callingFunction)) {
_mpFuncCount[_callingFunction] = 1;
}
else {
int counter = (int)_mpFuncCount[_callingFunction];
counter ++;
_mpFuncCount[_callingFunction] = counter;
}
}
public static void AssertAlmostEqual (float expected, float actual)
{
AssertAlmostEqual (expected, actual, DEFAULT_FLOAT_TOLERANCE);
}
public static void AssertAlmostEqual (float expected, float actual, float tolerance)
{
string msg = String.Format("\nExpected : {0} \nActual : {1}",expected.ToString(),actual.ToString());
AssertAlmostEqual (expected, actual, tolerance, msg);
}
private static void AssertAlmostEqual (float expected, float actual, float tolerance, string message)
{
float error = Math.Abs (expected - actual) / (expected + actual + float.Epsilon);
Assert.IsTrue (error < tolerance, message);
}
public static void AssertAlmostEqual (PointF expected, PointF actual)
{
string msg = String.Format("\nExpected : {0} \n Actual : {1}",expected.ToString(),actual.ToString());
AssertAlmostEqual (expected.X, actual.X, DEFAULT_FLOAT_TOLERANCE, msg);
AssertAlmostEqual (expected.Y, actual.Y, DEFAULT_FLOAT_TOLERANCE, msg);
}
public bool Compare () {
CheckCounter ();
return (CompareToExpectedInternal()*100) < DEFAULT_IMAGE_TOLERANCE;
}
///
/// Checks that the given bitmap norm is similar to expected
///
/// tolerance in percents (0..100)
///
///
public bool Compare (double tolerance) {
CheckCounter ();
return (CompareToExpectedInternal()*100) < tolerance;
}
public void AssertCompare () {
CheckCounter ();
Assert.IsTrue ((CompareToExpectedInternal () * 100) < DEFAULT_IMAGE_TOLERANCE);
}
public void AssertCompare (double tolerance) {
CheckCounter ();
Assert.IsTrue ((CompareToExpectedInternal () * 100) < tolerance);
}
public double CompareToExpected () {
CheckCounter ();
return CompareToExpectedInternal ();
}
double CompareToExpectedInternal () {
if (ShowForms)
return 0;
double norm = GetNorm ();
double expNorm = GetExpectedNorm (norm);
return System.Math.Abs (norm-expNorm)/(norm+expNorm+double.Epsilon);
}
public static DrawingTest Create (int width, int height) {
DrawingTest test;
#if TARGET_JVM
test = new JavaDrawingTest ();
#else
test = new NetDrawingTest ();
#endif
test.Init (width, height);
return test;
}
}
#if TARGET_JVM
internal class JavaDrawingTest:DrawingTest {
java.awt.image.BufferedImage _image;
java.awt.image.BufferedImage Image {
get {
if (_image != null)
return _image;
Type imageType = typeof (Image);
PropertyInfo prop = imageType.GetProperty ("NativeObject",
BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo method = prop.GetGetMethod (true);
_image = (java.awt.image.BufferedImage) method.Invoke (_bitmap, new object [0]);
return _image;
}
}
public JavaDrawingTest () {}
protected override double GetExpectedNorm (double myNorm) {
return ExpectedResults.GetNorm(TestName);
}
private class JavaForm:java.awt.Dialog,IMyForm {
class EventListener : java.awt.@event.WindowListener {
#region WindowListener Members
public void windowOpened(java.awt.@event.WindowEvent arg_0) {
// TODO: Add ttt.windowOpened implementation
}
public void windowActivated(java.awt.@event.WindowEvent arg_0) {
// TODO: Add ttt.windowActivated implementation
}
public void windowClosed(java.awt.@event.WindowEvent arg_0) {
// TODO: Add ttt.windowClosed implementation
}
public void windowDeiconified(java.awt.@event.WindowEvent arg_0) {
// TODO: Add ttt.windowDeiconified implementation
}
public void windowIconified(java.awt.@event.WindowEvent arg_0) {
// TODO: Add ttt.windowIconified implementation
}
public void windowClosing(java.awt.@event.WindowEvent arg_0) {
// TODO: Add ttt.windowClosing implementation
java.awt.Window w = arg_0.getWindow();
java.awt.Window par = w.getOwner ();
w.dispose();
par.dispose ();
}
public void windowDeactivated(java.awt.@event.WindowEvent arg_0) {
// TODO: Add ttt.windowDeactivated implementation
}
#endregion
}
java.awt.Image _image;
Size _s;
public JavaForm (string title, java.awt.Image anImage, Size s)
: base(new java.awt.Frame(), title, true) {
_image = anImage;
_s = s;
addWindowListener(new EventListener());
}
public override void paint (java.awt.Graphics g) {
base.paint (g);
awt.Insets insets = this.getInsets ();
g.drawImage (_image, insets.left, insets.top, null);
}
void IMyForm.Show () {
awt.Insets insets = this.getInsets ();
base.setSize (_s.Width + insets.left + insets.right,
_s.Width + insets.top + insets.bottom);
this.show ();
//save the image
//ImageIO.write((java.awt.image.RenderedImage)_image, "png", new java.io.File("test.java.png"));
}
}
protected override IMyForm CreateForm(string title) {
return new JavaForm (title, Image, _bitmap.Size);
}
protected override string DetermineCallingFunction() {
System.Exception e = new System.Exception ();
java.lang.Class c = vmw.common.TypeUtils.ToClass (e);
java.lang.reflect.Method m = c.getMethod ("getStackTrace",
new java.lang.Class [0]);
java.lang.StackTraceElement [] els = (java.lang.StackTraceElement [])
m.invoke (e, new object [0]);
java.lang.StackTraceElement el = els [4];
return el.getClassName () + "." + el.getMethodName ();
}
public override string CalculateSHA1() {
MessageDigest md = MessageDigest.getInstance ("SHA");
DataBufferInt dbi = (DataBufferInt) Image.getRaster ().getDataBuffer ();
for (int i=0; i>8) & 0xFF));
md.update ((sbyte) ((x>>16) & 0xFF));
md.update ((sbyte) ((x>>24) & 0xFF));
}
}
return md.ToString ();
}
}
#else
internal class NetDrawingTest:DrawingTest {
public NetDrawingTest () {}
protected override double GetExpectedNorm (double myNorm) {
if (CreateResults)
ExpectedResults.WriteNorm (TestName, myNorm);
return myNorm;
}
private class NetForm:Form,IMyForm {
Image image;
public NetForm(string title, Image anImage):base() {
base.Text = title;
image = anImage;
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.DrawImageUnscaled (image, 0, 0);
}
void IMyForm.Show () {
this.Size = image.Size;
this.ShowDialog ();
this.image.Save("test.net.png");
}
}
protected override IMyForm CreateForm(string title) {
return new NetForm (title, _bitmap);
}
protected override string DetermineCallingFunction() {
StackFrame sf = new StackFrame (3, true);
MethodBase mb = sf.GetMethod ();
string name = mb.DeclaringType.FullName + "." + mb.Name;
return name;
}
public override string CalculateSHA1() {
Rectangle r = new Rectangle(0, 0, _bitmap.Width, _bitmap.Height);
BitmapData data = _bitmap.LockBits (r, ImageLockMode.ReadOnly,
_bitmap.PixelFormat);
int dataSize = data.Stride * data.Height;
byte [] bdata = new byte [dataSize];
Marshal.Copy (data.Scan0, bdata, 0, dataSize);
_bitmap.UnlockBits (data);
SHA1 sha1 = new SHA1CryptoServiceProvider ();
byte [] resdata = sha1.ComputeHash (bdata);
return Convert.ToBase64String (resdata);
}
}
#endif
}