I decided to try something myself. I came up with a simple GDI + code that uses tiles that I already have. I'm just filtering out parts that are relevant to the current clipping area. It works like magic! Please find my code below. (Double buffered form settings for best results)
protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics dc = e.Graphics; dc.ScaleTransform(1.0F, 1.0F); Size scrollOffset = new Size(AutoScrollPosition); int start_x = Math.Min(matrix_x_size, (e.ClipRectangle.Left - scrollOffset.Width) / 256); int start_y = Math.Min(matrix_y_size, (e.ClipRectangle.Top - scrollOffset.Height) / 256); int end_x = Math.Min(matrix_x_size, (e.ClipRectangle.Right - scrollOffset.Width + 255) / 256); int end_y = Math.Min(matrix_y_size, (e.ClipRectangle.Bottom - scrollOffset.Height + 255) / 256);
To test this, I created an 80x80 matrix of 256 fragments (420 MPixel). Of course, I will have to add some delayed loading in real life. I can leave tiles (empty) if they are not already loaded. In fact, I asked my client to insert 8 GByte into his machine, so I donβt have to worry too much about performance. Once downloaded tiles can remain in memory.
public partial class Form1 : Form { bool dragging = false; float Zoom = 1.0F; Point lastMouse; PointF viewPortCenter; private readonly Brush solidYellowBrush = new SolidBrush(Color.Yellow); private readonly Brush solidBlueBrush = new SolidBrush(Color.LightBlue); const int matrix_x_size = 80; const int matrix_y_size = 80; private Bitmap[,] BmpMatrix = new Bitmap[matrix_x_size, matrix_y_size]; public Form1() { InitializeComponent(); Font font = new Font("Times New Roman", 10, FontStyle.Regular); StringFormat strFormat = new StringFormat(); strFormat.Alignment = StringAlignment.Center; strFormat.LineAlignment = StringAlignment.Center; for (int y = 0; y < matrix_y_size; y++) for (int x = 0; x < matrix_x_size; x++) { BmpMatrix[y, x] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
It turned out to be the easy part. Getting asynchronous multi-threaded I / O in the background was much more difficult. However, it works for me as described here. The issues that need to be addressed are related to most of the threading of .NET / Form than to this topic.
In pseudocode, it works as follows:
after onPaint (and on Tick) check if tiles on display need to be retrieved from disc if so: post them to an async io queue if not: check if tiles close to display area are already loaded if not: post them to an async io/queue check if bitmaps have arrived from io thread if so: updat them on screen, and force repaint if visible
Result: I now have my own user control that uses approximately 50 megabytes for very quick access to tiled files of arbitrary size (tiled).
Adriaan
source share