Display image from unmanaged memory in canvas - c#

I've got many images from an external source (umnanaged dll) which are to be displayed on a WPF canvas. The external source renders the images and passed back a pointer (IntPtr) to umnanaged memory, as well as size, stride etc.
Currently, I'm loading these images into a WriteableBitmap and then displaying the in the OnRender method of the Canvas. The WriteableBitmap fits this task very nicely.
WriteableBitmap temp = new WriteableBitmap(ImageWidth, ImageHeight, 96, 96, PixelFormat, null);
temp.WritePixels(new Int32Rect(0, 0, ImageWidth, ImageHeight), pointerToImageData, ImageHeight * stride, (int)stride);
Questions:
Does the WritableBitmap first copy the Image Data to another place (in managed memory)?
If so, is there any way to directly render the Image Data on screen (e.g. using DirectX?)
Are there any other tips and tricks which I could use?
Thanks and best whishes
Daniel
Edit: Tried after suggestion by Clemens: using BitmapSource.Create, using this methods heightens the memory consumtion to the point where I start suspecting that the method copies the whole image and Writeable bitmap may not.

WPF's Image control displays ImageSource, you need to convert your image to it, and not just Drawing.Image. Use System.Windows.Interop.Imaging to convert the image.
I'm not sure how your image is saved in the unmanaged memory, but you can use one of two methods. If it's saved as a bitmap you may be able to use CreateBitmapSourceFromHBitmap.
Or you may need to use CreateBitmapSourceFromMemorySection, here's how: http://social.msdn.microsoft.com/Forums/vstudio/en-US/a8d86cd0-10cc-4349-aa9c-e62d7d066508/createbitmapsourcefrommemorysection.
As for the memory allocation, you would always have to copy the image, aside from the fact that you'll have to convert the image to managed memory, the UI thread can't work with anything it didn't create. You must copy the image using the UI thread before you can display it.

Related

What's the best method for rendering a decoded frame?

After decoding and reaching to a frame, I need to know the best method for rendering it. I use Bitmap for this purpose, but every time I have to create a new bitmap, one for each frame. Maybe this is not the best method, since lots of memory is consumed and rebuilding bitmaps may take time. To clarify, I give the code:
ret = FFmpegInvoke.sws_scale(
convertContext,
&frame->data_0,
frame->linesize,
0,
frame->height,
&convertedFrame->data_0,
convertedFrame->linesize);
Debug.Assert(ret >= 0);
var bmp = new Bitmap(dest_width, dest_height,
convertedFrame->linesize[0], PixelFormat.Format32bppPArgb,
new IntPtr(convertedFrame->data_0));
pictureBox1.Image = bmp;
I've added a picture box control to my form. I create a Bitmap from every decoded and converted frame's data and then set the picture box's image to the bitmap.
After finding the best method, and knowing whether I've done correctly, I need to know why is my problem with a background worker I put in the form. Apparently concurrent accessing to the bitmap object causes exception. Nevermind, first I need to make sure about this one.

How to create anti-aliased text on a Bitmap with ExtTextOut?

In my WinForm app I draw into a System.Drawing.Bitmap. I create fonts from a LOGFONT and draw using the GDI function ExtTextOutW. However the output is terrible. It has bad jaggies and looks like the antialiaser was on LSD. Reading around this seems a common issue - is there a resolution?
If I use:
lf.lfQuality = FontQuality.NONANTIALIASED_QUALITY
when I create the font then the horrible jaggies go away but of course there is no antialiasing.
Is there a way to create smooth text in a Bitmap with ExtTextOutW?
It is possible but a bit tricky and it can't have transparent background.
You will need to:
Create in-memory bitmap buffer that is compatible with display device context (IntPtr.Zero handle)
Fill the buffer background with solid color or other background
Render the text into the memory bitmap
Copy from in-memory bitmap to image device context (BitBlt)
See GDI text rendering to image for more details.

Adding whitespace to the top of an image using a given hDC

The Layout:
I'm using third party controls to scan documents. I have an event that occurs when I scan an image (mostly used to add annotations the image). The event provides me with only one property named .PAhDC. This property is a handle to the DC that stores the image before it is written to a file. Thus I can make changes to the image before it gets written to a file.
The Expected Results:
I would like to simply add a 1/4 inch (lets say 100 pixels) of white space line on the very top of the image. If my original image is 200x200 (WxH) then my new final image would need to be 200x300.
Question And Other Thoughts:
How can I alter an existing image with only knowing it's DC handle? I was thinking of doing something like the following...
Create a new DC.
Create a new Bitmap 100 pixels taller than the original image.
Use that new bitmap in the new DC.
Copy the original image to the new bitmap (100 pixels from the top as a start point).
Then use something like SelectObject to replace the old bitmap in the original hDC with the new one and then destroy the old bitmap object.
Note: I would like to do this with MANAGED CODE as much as possible. Using SelectObject() was the only way I could think of but it's of course unmanaged code... :/
You can't without cooperation with the owner of the bitmap and DC.
The DeviceContext is purely a viewport onto an underlying DIB/bitmap and has no concept of size or dimensions (beyond the clipping region) While you can create a new bitmap and select it into the DC, it's highly likely that the application will just ignore what you've done and use the DIB that it has created.
The end result of this will be GDI object leaks and no change to the underlying image.
To do what you you ask, you will need full cooperation with the other code and them adding a method that allows you to replace the underlying data.
Sure, you can do this in managed code. All P/Invoke declarations are readily available from any decent search engine.
When creating a new DC, make sure it's a DC compatible with the original one
When creating a new bitmap, make sure it's compatible with the DC

How To Handle Image as Background to CAD Application

My application is used to design airports for a flight simulator. Users can add one or more images as background. The image(s) can be sized accurately and then used as a template to lay down features such as runways, aprons and so on.
I use a third party graphics library (Piccolo) which has an image class (as far as I can see it is a simple wrapper for System.Drawing.Image).
So far I have done little except allow the user to add an image, size it and so on. It will be no surprise that users sometimes complain of poor performance. We tell them not to load large images (up to 100k seem OK) but don't stop them and 100Mb bitmaps have been used with horrible results.
I need to fix this in a couple of ways. First by converting any image they use to an efficient format (size vs definition) and second by ensuring that the loaded image is suitably sized for the dimensions - at the moment I don't do anything specific to deal with the resizing of say a 2000 x 2000 image to fit a 500 x 500 area of the display.
The default 1:1 display of the application represents 1m per pixel. Once the user has resized the image to fit accurately would I be right in thinking that the best resoultion for the image would be to resample it to that size? I am aware that if the user zooms in way past 1:1 which they will probably do then the clarity of the image will fall.
My ignorance of handling images is complete. I have looked at some image manipulation libraries (ImageMagick and the free version of dotIamge) first for converting the input image to a standard one and second for resizing -resampling. The truth is that they do far more than I need.
Any pointer much appreciated.
Yes, resampling so that the bitmap doesn't constantly have to be rescaled for every paint should make a big difference. The default Graphics.InterpolationMode makes pretty images but is not cheap.
Another biggie is the pixel format of the bitmap. Format32bppPArgb is 10 times faster than any of the others on most video adapters.
private static Bitmap Resample(Image img, Size size) {
var bmp = new Bitmap(size.Width, size.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (var gr = Graphics.FromImage(bmp)) {
gr.DrawImage(img, new Rectangle(Point.Empty, size));
}
return bmp;
}

imageList.Draw doesn't draw with different size?

The ImageList has a method named "Draw":
imageList.Draw(graphics, bounds.X, bounds.Y, bounds.Width, bounds.Height, imgIndex);
I use this method to draw an image on a graphics object of a PrintDocument. When using the original image size (16 x 16 pixels), the image is drawn correct. If however, I change the bounds size, nothing is drawn. Even changing the size to 32 x 32 (double size) has no effect. Nothing is drawn. I need to change the drawn size because of the different dpi ... Where am I gong wrong ?
Edit: The solution seems to be simply to use the g.DrawImage method instead. Why imageList.Draw() doesn't draw is still a mistery to me ...
g.DrawImage(imageList.Images[imgIndex], bounds);
ImageList.Draw() is a bit unusual, it takes advantage of the built-in support that the native image list code inside of Windows has for rendering an image in the list. This is an optimization, it avoids the cost of converting the internal image as stored in the native image list back to a managed Image object.
One side-effect however is that this drawing happens without regard for any of the transforms that were applied to the Graphics object. A 16x16 image in the list is going to be rendered as 16x16 pixels on paper. Which is indeed a bit hard to find back, printers have very high resolution (600 dots per inch is typical), that image turns into a decimal point.
Image lists were really meant to be the source of images for the TreeView and ListView controls, it is not a good general purpose collection object for images. Like a List<Image>. Your workaround is good, the Image property converts the internal bitmap back to a managed Image, Graphics.DrawImage() will then scale it appropriately to get a size on paper that's close to the size on the screen. However with the graininess you get from making an image 6 times larger. Note that you should Dispose() that object.

Categories