Hi, Narcís
Thank you for quick answer.
I've made a simple test of different ways to fill Bitmap:
Code: Select all
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
public class ConsoleApp
{
private static void SetPixelFill(Bitmap bitmap)
{
int width = bitmap.Width;
int height = bitmap.Height;
Color color = Color.Purple;
for(int j = 0; j < height; ++j)
for(int i = 0; i < width; ++i)
bitmap.SetPixel(i, j, color);
}
private static void MarshalFill(Bitmap bitmap)
{
int width = bitmap.Width;
int height = bitmap.Height;
Color color = Color.Purple;
BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
IntPtr ptr = bmData.Scan0;
for(int j = 0; j < height; ++j)
for(int i = 0; i < width; ++i)
Marshal.WriteInt32(ptr, (i + j * width) * 4, color.ToArgb());
bitmap.UnlockBits(bmData);
}
private static void DirectFill(Bitmap bitmap)
{
int width = bitmap.Width;
int height = bitmap.Height;
Color color = Color.Purple;
BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
unsafe
{
int *ptr = (int*)bmData.Scan0.ToPointer();
for(int j = 0; j < height; ++j)
for(int i = 0; i < width; ++i)
*(ptr + i + j * width) = color.ToArgb();
}
bitmap.UnlockBits(bmData);
}
private static void DirectFillOptimized(Bitmap bitmap)
{
int width = bitmap.Width;
int height = bitmap.Height;
Color color = Color.Purple;
int argb = color.ToArgb();
BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
unsafe
{
int *ptr = (int*)bmData.Scan0.ToPointer();
for(int j = 0; j < height; ++j)
for(int i = 0; i < width; ++i)
*ptr++ = argb;
}
bitmap.UnlockBits(bmData);
}
private delegate void TestInvoker(Bitmap bitmap);
private static void RunTest(string testName, TestInvoker test, Bitmap originalBitmap)
{
Bitmap bitmap = (Bitmap)originalBitmap.Clone();
System.Console.Write("\nRunning \"{0}\" test... ", testName);
DateTime start = DateTime.Now;
test.Invoke(bitmap);
System.Console.WriteLine("{0} ms", (DateTime.Now - start).TotalMilliseconds);
bitmap.Save(testName + ".png");
}
public static void Main(string[] args)
{
Bitmap originalBitmap = new Bitmap(1600, 1200, PixelFormat.Format32bppArgb);
RunTest("SetPixel fill", new TestInvoker(SetPixelFill), originalBitmap);
RunTest("marshal fill", new TestInvoker(MarshalFill), originalBitmap);
RunTest("direct fill", new TestInvoker(DirectFill), originalBitmap);
RunTest("direct fill optimized", new TestInvoker(DirectFillOptimized), originalBitmap);
}
}
You can compile it with 'csc /unsafe /o+ test.cs'.
This test outputs the following results on my pc:
Code: Select all
Running "SetPixel fill" test... 4453.125 ms
Running "marshal fill" test... 93.75 ms
Running "direct fill" test... 78.125 ms
Running "direct fill optimized" test... 31.25 ms
You see, that the method you've suggested (with Marshal.WriteInt32) makes bitmap filling about 50 times faster. But execution time can be made 3 more times shorter by using unsafe code properly.
I don't know your reasons of being afraid to add unsafe code to TeeChart. Probably, you want to keep your library at the highest level of compatibility and safety - that's fine. As for me, I don't need TeeChart.dll to be CLSCompliant or to be 100% verified code. I would be glad if you made a separate TeeChart.dll version with maximum optimizations, which probably will involve unsafe code. And
ColorGrid is not the only place which you could optimize this way. I think this could be useful to many of your customers.
And what about
FastDraw option, which should disable value-to-color mapping and
IrregularGrid? As I wrote earlier, this option can be implemented very fast in case of regular grids. You could bypass indexing by x- and z- axis and use
Series.YValues.Value to retrieve values and use this values as RGB colors. This should increase speed seriously and wouldn't require any unsafe code.