I have a DrawingContext (part of a Visual or a DrawingGroup), where I draw a bunch of rectangles and/or 1-bit images on top of each other. Think of it as a masking 1-bit image. I would like to convert this into a bitmap image file.
Using RenderTargetBitmap is not an option because it can only render at 32bit pixel format, so if I have to render a 20MB 1-bit image, I will end up with a 640MB (20*32) of memory on my heap. This of course creates magnificent LOH fragmentation, and the application runs out-of-memory on the second shot.
So, I basically need a way to write a 1-bit bitmap file from a drawing context efficiently. Any ideas/suggestions/alternate methods would be appreciated.
A number of ideas, some are a bit convoluted...
Print to XPS then extract Bitmap
You could print the Visual to an XPS Document.
If you're lucky then it will combine the 1bit images that you drew in in the DrawingContext and produce a single bitmap in the XPS file.
For the Rectangles it might keep the Rectangles as vector based information in the XPS (the "Shape" based Rectangle or DrawingContext DrawRectangle might both do this)....if that happens then create a bitmap into which your draw the rectangle part and draw that bitmap into the context.
Once you have the XPS files, you could then parse it and extract the "image" if you are lucky that's what it produced inside. (XPS is just a ZIP file that uses XAML to describe content and uses subdirectories to store bitmap image data files, fonts, etc).
(I think you can use the Package class to access the raw parts of an XPS document, or XpsDocument for a higher level interpretation).
Or just display the XPS in an XPS viewer if your intention is just to provide a way to allow your combined 1bit images to be viewed in a different context.
Convert WPF (XAML) Control to XPS Document
How to create BIG bitmaps with RenderTargetBitmap?
Printing a WPF BitmapImage
Use a Printer Driver that provides Print to Image functionality
This isn't ideal...not least because you have to install a Printer Driver on the machine.....but you might be able to use a "Print to Image" printer driver and then use that to produce your bitmap. Here are some suggestions:
http://sourceforge.net/projects/imageprinter/
http://www.zan1011.com/
Create a GDI+ HBITMAP and do the drawing using GDI+ calls...then wrap it for use by WPF...or save out to disk.
[1] First create a GDI+ Bitmap large enough to hold your composed rendering
(there are a few different ways to do this...one way is to use a WriteableBitmap to provide the backbuffer bits/store...that's if you wanted access to the bits in memory...in your case I don't think you do/need to).
var bmp = new System.Drawing.Bitmap( pixelwidth, pixelheight, System.Drawing.Imaging.Format1bppIndexed );
Or this way if you want WriteableBitmap access.
Getting a DrawingContext for a wpf WriteableBitmap
[2] Convert any WPF BitmapSources to GDI+ Bitmaps so you can draw them onto the GDI+ Graphics context using DrawImage.
You can use CopyPixels to do that on your BitmapSources.
Is there a good way to convert between BitmapSource and Bitmap?
For your rectangles you can just use the GDI+ DrawRectangle rendering command.
[3] Save the GDI+ Bitmap to disk
Use the .Save method on System.Image.Bitmap to save it as a Bitmap.
[4] Use the saved image as the Source to an Image element
(note this will probably use masses of memory when it loads your image even though it's 1bpp...because the WPF rendering path is all 32bpp).
[4] OR wrap the GDI+ Bitmap for use in WPF
You can use InteropBitmap to wrap the GDI+ based bitmap so you can use it as a BitmapSource in WPF. (note it may have to be a 32bpp one....if it has to...then you are back at square 1...but try anyway).
http://arbel.net/2008/10/22/improper-use-of-interopbitmap-can-cause-a-memory-leak/
http://arbel.net/2008/10/22/improper-use-of-interopbitmap-can-cause-a-memory-leak/
Create a Bitmap "Service"
Create another process which acts as a service (doesn't have to be an NT service...could just be a child process you start) to render your combined 1bpp images....there are various ways to communicate with it to give it the rendering commands.
When memory consumption gets too high/the LOH gets fragmented, then you could restart this service.
Other Ideas
You could render using OpenGL (e.g. use OpenTK or SharpGL), or render using DirectX...via the 3D path with D3DImage or Direct2D (whether this behaves the same as RenderTargetBitmap in terms of memory usage...that's to find out).
http://www.codeproject.com/Articles/265903/Using-OpenGL-in-a-WPF-Application
http://www.codeproject.com/Articles/113991/Using-Direct2D-with-WPF
http://www.codeproject.com/Articles/28526/Introduction-to-D3DImage
Try out NET 4.5 as there have been a number of improvements in the LOH Heap:
http://blogs.msdn.com/b/dotnet/archive/2011/10/04/large-object-heap-improvements-in-net-4-5.aspx
http://blogs.msdn.com/b/dotnet/archive/2012/07/20/the-net-framework-4-5-includes-new-garbage-collector-enhancements-for-client-and-server-apps.aspx
I dont think you can do anything better. Since RenderTargetBitmap uses MILcore which you can't access. And I don't think there is any other way to copy Visual. However I think there is one option more. It won't be one-line but I think it will be good enough.
Basically you will render visual block by block(PGBRA32) and convert it into BlackWhite on the fly, and then concat it with Blackwhite buffer. I've started a little example code but in halfway decided that it's not gonna be so easy, but you can finish it.
/// <summary>
/// Renders only part of the visual and returns byte[] array
/// which holds only black/white information.
/// </summary>
/// <param name="oVisual"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns>black/white pixel information. one pixel=one bit. 1=white, 0=black</returns>
public static byte[] RenderPartially(Visual oVisual,
int x, int y, double width, double height)
{
int nWidth = (int)Math.Ceiling(width);
int nHeight = (int)Math.Ceiling(height);
RenderTargetBitmap oTargetBitmap = new RenderTargetBitmap(
nWidth,
nHeight,
96,
96,
PixelFormats.Pbgra32
);
DrawingVisual oDrawingVisual = new DrawingVisual();
using (DrawingContext oDrawingContext = oDrawingVisual.RenderOpen())
{
VisualBrush oVisualBrush = new VisualBrush(oVisual);
oDrawingContext.DrawRectangle(
oVisualBrush,
null,
new Rect(
new Point(x, y),
new Size(nWidth, nHeight)
)
);
oDrawingContext.Close();
oTargetBitmap.Render(oDrawingVisual);
}
//Pbgra32 == 32 bits ber pixel?!(4bytes)
// Calculate stride of source and copy it over into new array.
int bytesPerPixel = oTargetBitmap.Format.BitsPerPixel / 8;
int stride = oTargetBitmap.PixelWidth * bytesPerPixel;
byte[] data = new byte[stride * oTargetBitmap.PixelHeight];
oTargetBitmap.CopyPixels(data, stride, 0);
//assume pixels in byte[] are stored as PBGRA32 which means that 4 bytes form single PIXEL.
//so the layout is like:
// R1, G1, B1, A1, R2, G2, B2, A2, R3, G3, B3, A3, and so on.
byte [] bitBufferBlackWhite = new byte[oTargetBitmap.PixelWidth
* oTargetBitmap.PixelHeight / bytesPerPixel];
for(int row = 0; row < oTargetBitmap.PixelHeight; row++)
{
for(int col = 0; col < oTargetBitmap.PixelWidth; col++)
{
//calculate concrete pixel from PBGRA32
int index = stride * row + bytesPerPixel * col;
int r = data[index];
int g = data[index + 1];
int b = data[index + 2];
int transparency = data[index + 3];
//determine whenever pixel is black or white.
//note that I dont know the exact process how one converts
//from PBGRA32 to BlackWhite format. But you should get the idea.
bool isBlack = r + g + b + transparency == 0;
//calculate target index and USE bitwise operators in order to
//set right bits.
}
}
return bitBufferBlackWhite;
}
So essentially, set up new WriteableBitmap with BlackWhite format, and then call this function like this:
WriteableBitmap blackWhiteFullBuffer = new WriteableBItmap(....., PIxelFormats.BlackWhite);
for(int x = 0; x < Visual.Width; x += <some uniform amount that divides correctly>)
{
for(int y = 0; y < VIsual.Height; y+= <some uniform amount that divides correctly>)
{
byte[] partialBuffer = PartialRenderer.RenderPartially(Visual, x, y, <uniform amX>,
<uniform amY>);
//concat that partial blackWhite buffer with other blackWhite buffer.
//you do this as long as needed and memory wont grow too much
//hopefully it will be fast enough too.
PartialRenderer.ConcateBuffers(blackWhiteFullBuffer, partialBuffer);
}
}
//then save blackWhiteFullBuffer to HDD if needed.
What about PixelFormats
Edit: (thanks to Anders Gustafsson)
The lower is PixelFormats.BlackWhite, with 1bit per pixel.
So this way, you can convert any BitmapImage to a FormatConvertedBitmap, where you can modify the format to a lower bpp.
Related
I have two monochromatic images as byte[] taken from cameras. I want to combine these images and write the combined image into a writeable bitmap.
Merging images: OpenCV
Using openCV, I create Mat objects (of type CV_8UC1) from the byte arrays (greenMat for the green and redMat for the red color channel image) and merge them via Cv2.Merge
Mat mergedMat = new Mat(greenMat.Height, greenMat.Width, MatType.CV_8UC3);
Mat emptyMat = Mat.Zeros(greenMat.Height, greenMat.Width, MatType.CV_8UC1);
Cv2.Merge(new[]
{
redMat, greenMat, emptyMat
}, mergedMat);
The mergedMat.ToBytes() now retuns a buffer of size 2175338. Why is it this size? Merging three one-channel matrices (CV_8UC1) if size 2448x2048 into one three-channel matrix(CV_8UC3) should yield to a buffer of size 15040512, or am I missing something here?
Display merged image: WriteableBitmap
I want to display the merged image by writing it into an existing WriteableBitmap, that is initialized via
ImageSource = new WriteableBitmap(
width, //of the green image
height, //of the green image
96, //dpiX: does not affect image as far as I tested
96, //dpiY
PixelFormats.Rgb24, //since the combined image is a three channel color image
null); //no bitmap palette needed for used PixelFormat
Is this initialization correct? I'm unsure about the PixelFormat here. The dpiX and dpiY values also seem to have no effect. What is their use?
The tricky part I can't get to work properly now is to write the image data into the WriteableBitmap.
Using the following code to obtain the byte array of the
merged image and using WriteableBitmaps' WritePixels method, with
stride according to the ImageSources' PixelFormat, yields an
System.ArgumentException: 'Buffer size is not sufficient.'
var mergedBuffer = mergedMat.ToBytes();
var bytesPerPixel = (PixelFormats.Rgb24.BitsPerPixel + 7) / 8;
var stride = bytesPerPixel * width;
((WriteableBitmap)ImageSource).WritePixels(
new Int32Rect(0, 0, width, height),
mergedBuffer,
stride,
0)
Edit: Stride is calculated according to this answer, although it's not neccessary with a fixed PixelFormat like here.
Why is the buffer too small? I did initialize the WriteableBitmap
with the correct width and height and PixelFormats.Rgb24. Am I
calculating my stride correctly?
Solution
Thanks to #Micka s suggestions in the comments I realized the mergedMat.ToBytes method seems not to do what I expceted. Instead, I tried to use mergedMat.Data pointer like so:
var mergedBufferSize = (int)(mergedMat.Total() * mergedMat.Channels());
byte[] mergedBuffer = new byte[mergedBufferSize];
System.Runtime.InteropServices.Marshal.Copy(
mergedMat.Data,
mergedBuffer,
0,
mergedBufferSize);
Everything works fine this way.
I'm using Bitmap.GetHbitmap for passing image to C++ dll from c# like below.
Bitmap img = Bitmap.FromFile(PATH); // this is 24bppRGB bitmap.
IntPtr hBit = img.GetHbitmap(); // Make hbitmap for c++ dll.
Here's problem:
Bitmap temp = Bitmap.FromHbitmap(hBit); // It changes to 32bppRGB.
I need 24bpp bitmap for c++ dll methods, but GetHbitmap() changes the bitcount.
How can I make a 24bpp HBITMAP?
Short Version
Use Bitmap.LockBits and CreateDIBSection to manually create your desired HBITMAP.
Long Version
Bitmap.GetHBITMAP will always return a 32bpp HBITMAP; no matter what the format of the image you actually loaded is. This is because it is the format the image is internally stored as. You will need to manually create an HBITMAP yourself.
You can use CreateDIBSection to create your own 24bpp bitmap. The function returns a pointer where you can place the raw pixel data.
Fortunately GDI+ lets you obtain pixel data in whatever format you want, by calling LockBits and specifying the pixel format you want (i.e. PixelFormat24bppRGB). Then you can just copy the pixel data. There may be issues with -Stride, and top-down or bottom-up bitmaps.
Define a BITMAPINFO for the bitmap we want to create:
BITMAPINFO bm;
bm.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bm.bmiHeader.biWidth = img.Width;
bm.bmiHeader.biHeight = -img.Height; // oriented top-down
bm.bmiHeader.biPlanes = 1;
bm.bmiHeader.biBitCount = 24; // 24bpp
bm.bmiHeader.biCompression = BI_RGB; // no compression
bm.bmiHeader.biSizeImage = 0; // let Windows determine size
bm.bmiHeader.biXPelsPerMeter = 0; // Not used by CreateDIBSection
bm.bmiHeader.biYPelsPerMeter = 0; // Not used by CreateDIBSection
Create a DIBSection based on the bitmap info, and get the buffer where we place our pixel data
Pointer dibPixels; //will receive a point where we can stuff our pixel data
HBITMAP bmp = CreateDIBSection(0, bmInfo, DIB_RGB_COLORS, out dibPixels, 0, 0);
Use Bitmap.LockBits to obtain a pointer to pixel data in the format you want:
BitmapData bitmapData = img.LockBits(
img.Bounds, //get entire image
ImageLockModeRead,
PixelFormat24bppRGB //we want the pixel data in 24bpp format
);
Copy pixel data from our bitmapData source image into the pixel buffer returned by CreateDIBSectin:
int stride = bitmapData.Stride;
int bufferSize = stride * bmData.Height;
CopyMemory(bitmapData.Scan0, dibPixels, bufferSize);
Unlock the bits:
img.UnlockBits(bitmapData);
Now you have your HBITMAP bitmap ready to pass to your dll seven years later.
Bonus Reading
The Supercomputing Blog: Using LockBits in GDI+ archive
When building Bitmaps and collectively building a few Bitmaps and combining them into one Bitmap does it help to .MakeTransparent().
And or, before I send the Bitmap to the requesting client if I .MakeTransparent() will it become smaller in size? not width or height, buy in bytes?
In other words will .MakeTransparent() optimize the Bitmap, and if not does anyone get any suggestions on how to optmize a Bitmap before sending to the requesting client over the wire via internet?
The code in question is sheet.MakeTransparent()
internal static Task<Bitmap> GetDoorSecheduleSheetAsync(ShopDrawing.DoorSchedules schedules, RotateFlipType rotate, byte schedulesPerSheet, byte currentI)
{
return Task.Run(() =>
{
var sheet = new Bitmap(DrawingOptions.PAGE_HEIGHT_SIZE, DrawingOptions.PAGE_WIDTH_SIZE);
sheet.SetResolution(150, 150);
byte scheduleCnt = 0;
float prevWidth = 0;
using (Graphics dc = Graphics.FromImage(sheet))
{
dc.Clear(Color.White);
using (Pen pen = new Pen(Color.FromArgb(80, Color.Black), 4))
{
for (; currentI < schedules.Count(); currentI++)
{
if (scheduleCnt > 0)
{
dc.DrawLine(pen, prevWidth, 380/*need constant for start height*/, prevWidth, sheet.Height);
};
using (var doorSchedule = schedules[currentI].Door)
{
dc.DrawImage(doorSchedule, prevWidth + 50, 380/*need constant for start height*/);
prevWidth += doorSchedule.Width + 50;
scheduleCnt++;
}
if (scheduleCnt == schedulesPerSheet)
{
sheet.RotateFlip(rotate);
sheet.MakeTransparent();
return sheet;
}
};
};
};
sheet.MakeTransparent();
sheet.RotateFlip(rotate);
return sheet;
});
}
Thank you!
BMP is uncompressed format with essentially raw bytes (or palette indexes) for each pixel - there is no transformation that will change size of resulting file except changing bit-per-pixel count.
Don't send uncompressed bmp over network - use either loss-less PNG/GIF or (if it works for you) JPG.
As said by Alexei Levenkov sending the raw BMP-Format over the wire is the worst choice.
Calling MakeTransparent() would (if it's not already the case) convert the image format to 32bit (= with alpha cannel), which is most byte hungry - transparency has its costs.
You should save the Bitmap as a i.e. jpeg (if you don't really need transparency) or png (if you really need it) - they're both much more efficient.
Look at this answered question about it: High Quality Image Scaling Library
It shows how to use the .NET built-in image encoders.
When you call the MakeTransparent method the bitmap will be converted to the Format32bppArgb format, as this format supports an alpha channel.
The Format32bppArgb is a format what uses 32 bits per pixel; 8 bits each are used for the alpha, red, green, and blue components. Therefore it could mean a change in size.
But just to have control over what really happens you could zip the bitmap or compress it bye converting it to other formats as PNG o JPG as Alexei Levenkov suggested
Here you can find and example of how to convert PNG to BMP, you can use the same code but exchanging formats
How to convert PNG to BMP at runtime?
How to load a part of a *.tif image without loading this image into memory.
I have to work with big TIFF files. (> 4 GB). I tried to read this file using BinaryReader, and using BitMiracle.LibTiff.Classic to convert bytes into image. But I didn't find example for how to read a specific pixel in a TIFF file.
Maybe be you have some solution for this task.
Lets say i have a BigScan.tif file and it is always:
Image Compression - NONE
Pixel Order - Interleaved (RGBRGB)
Byte Order - IBM PC
I have some variable:
ImagePart with User Defined Width
ImagePart with User Define Height
ImagePArt with User Defined Location
The question is, how could I get ImagePart from BigScan.tif?
But it would be best to have the ability to read information of the pixel in "BigScan.tif" with (x,y) coorinates.
I need to read a pixel from the BigScan.tif in specified place, with such function:
public Color GetPixelColorFromTiffImage(string TiffFileName, int PixelPositionX, int PixelPositionY)
{
//Some Code
return returnedColor;
}
Very strange but the support did`t unswer my quastion. May be somebody knows it. Why dose this part of the code from BitMiracle Samples wrote to 'raster' array numbers like "-11512229", "-11838376" and so on.
using (Tiff image = Tiff.Open(fullImageLocation, "r"))
{
// Find the width and height of the image
FieldValue[] value = image.GetField(TiffTag.IMAGEWIDTH);
width = value[0].ToInt();
value = image.GetField(TiffTag.IMAGELENGTH);
height = value[0].ToInt();
int imageSize = height * width;
int[] raster = new int[imageSize];
// Read the image into the memory buffer
if (!image.ReadRGBAImage(width, height, raster))
{
MessageBox.Show("Could not read image");
}
using (Bitmap btm = new Bitmap(200, 200))
{
for (int i = 0; i < btm.Width; ++i)
for (int j = 0; j < btm.Height; ++j)
btm.SetPixel(i, j, getSample(i + 330, j + 30, raster, width, height));
ReternedBitmap = btm;
}
}//using
Your question is unclear (you ask at least two different questions).
If you need to crop a part of a larger image then you need to:
read each relevant scanline of a source image
copy part of that scanline to a new image.
If you need to get color value of a single pixel in a given location than again you need to:
read relevant scanline
find relevant bytes in that scanline
pack those bytes into a Color structure or whatever else
You didn't specify what are Photometric, BitsPerSample and SamplesPerPixel values for your image, so it's hard to tell what exactly are you dealing with.
Most probably, you are faced with geo-images. If so, they are probably RGB, 24bit, tiled images.
For tiled images it is possible to read only small part (say, 256 x 256 pixels) of the image at once. But even if they are stripped, one scanline of such an image will take only about 1 MB of memory (219 000 pixels * 3 bytes per pixel). It's nothing if you are really need to process such big images.
I wouldn't recommend you to try developing your own parser. It's not that easy if you know only basics about TIFF format.
In my project, I'm using (uncompressed 16-bit grayscale) gigapixel images which come from a high resolution scanner for measurement purposes. Since these bitmaps can not be loaded in memory (mainly due to memory fragmentation) I'm using tiles (and tiled TIFF on disc). (see StackOverflow topic on this)
I need to implement panning/zooming in a way like Google Maps or DeepZoom. I have to apply image processing on the fly before presenting it on screen, so I can not use a precooked library which directly accesses an image file. For zooming I intend to keep a multi-resolution image in my file (pyramid storage). The most useful steps seem to be +200%, 50% and show all.
My code base is currently C# and .NET 3.5. Currently I assume Forms type, unless WPF gives me great advantage in this area. I have got a method which can return any (processed) part of the underlying image.
Specific issues:
hints or references on how to implement this pan/zoom with on-demand generation of image parts
any code which could be used as a basis (preferably commercial or LGPL/BSD like licenses)
can DeepZoom be used for this (i.e. is there a way that I can provide a function to provide a tile at the right resulution for the current zoom level?) ( I need to have pixel accurate addressing still)
This CodeProject article: Generate...DeepZoom Image Collection might be a useful read since it talks about generating a DeepZoom image source.
This MSDN article has a section Dynamic Deep Zoom: Supplying Image Pixels at Run Time and links to this Mandelbrot Explorer which 'kinda' sounds similar to what you're trying to do (ie. he is generating specific parts of the mandelbrot set on-demand; you want to retrieve specific parts of your gigapixel image on-demand).
I think the answer to "can DeepZoom be used for this?" is probably "Yes", however as it is only available in Silverlight you will have to do some tricks with an embedded web browser control if you need a WinForms/WPF client app.
Sorry I can't provide more specific answers - hope those links help.
p.s. I'm not sure if Silverlight supports TIFF images - that might be an issue unless you convert to another format.
I decided to try something myself. I came up with a straightforward GDI+ code, which uses the tiles I've already got. I just filter out the parts which are relevant for current clipping region. It works like magic! Please find my code below.
(Form settings double buffering for the 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);
// start * contain the first and last tile x/y which are on screen
// and which need to be redrawn.
// now iterate trough all tiles which need an update
for (int y = start_y; y < end_y; y++)
for (int x = start_x; x < end_x; x++)
{ // draw bitmap with gdi+ at calculated position.
dc.DrawImage(BmpMatrix[y, x],
new Point(x * 256 + scrollOffset.Width,
y * 256 + scrollOffset.Height));
}
}
To test it, I've created a matrix of 80x80 of 256 tiles (420 MPixel). Of course I'll have to add some deferred loading in real life. I can leave tiles out (empty) if they are not yet loaded. In fact, I've asked my client to stick 8 GByte in his machine so I don't have to bother about performance too much. Once loaded tiles can stay 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);
// BmpMatrix[y, x].Palette.Entries[0] = (x+y)%1==0?Color.Blue:Color.White;
using (Graphics g = Graphics.FromImage(BmpMatrix[y, x]))
{
g.FillRectangle(((x + y) % 2 == 0) ? solidBlueBrush : solidYellowBrush, new Rectangle(new Point(0, 0), new Size(256, 256)));
g.DrawString("hello world\n[" + x.ToString() + "," + y.ToString() + "]", new Font("Tahoma", 8), Brushes.Black,
new RectangleF(0, 0, 256, 256), strFormat);
g.DrawImage(BmpMatrix[y, x], Point.Empty);
}
}
BackColor = Color.White;
Size = new Size(300, 300);
Text = "Scroll Shapes Correct";
AutoScrollMinSize = new Size(256 * matrix_x_size, 256 * matrix_y_size);
}
Turned out this was the easy part. Getting async multithreaded i/o done in the background was a lot harder to acchieve. Still, I've got it working in the way described here. The issues to resolve were more .NET/Form multithreading related than to this topic.
In pseudo code it works like this:
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 Custom control which uses roughly 50 MByte for very fast access to arbitrary size (tiled) TIFF files.
I guess you can address this issue following the steps below:
Image generation:
segment your image in multiple subimages (tiles) of a small resolution, for instace, 500x500. These images are depth 0
combine a series of tiles with depth 0 (4x4 or 6x6), resize the combination generating a new tile with 500x500 pixels in depth 1.
continue with this approach until get the entire image using only a few tiles.
Image visualization
Start from the highest depth
When user drags the image, load the tiles dynamically
When the user zoom a region of the image, decrease the depth, loading the tiles for that region in a higher resolution.
The final result is similar to Google Maps.