 |
|
 |
I'm trying to migrate my vb6 navigation software (Odysseus, see www.xs4all.nl/~hdb) to Visual Studio 2010 to keep it alive. VisualBasic cannot draw the bitmap charts nearly as fast as VB6 can with GDI, so I'm very interested in using the EditableBitmap class. As I'm a total newby in VisualStudio I have not succeeded in porting this class to Visual Basic 2010 and use it in my software. I'm interested in all help that I can get.
Hilko de Boer
NL
|
|
|
|
 |
|
 |
Hi,
First off very nice code it works so much faster.
However i'm trying to rotate the visiblearea which i managed, but you get 4 black corners.
using (EditableBitmap section = image.CreateView(visibleBitmapRect))
{
Matrix m = new Matrix();
m.RotateAt(45, new Point(visibleClientRect.Width/2, visibleClientRect.Height/2));
pe.Graphics.Transform = m;
pe.Graphics.DrawImageUnscaled(section.Bitmap, visibleClientRect.Location);
}
And i just can't get it to work
Any help would be great!
Edit:
I tried loading my image, rotating it then making editable bitmap out of it which actually worked allot faster,
But it can't handle the size (6000x4000) and gives a out of memory exception (thus not touching your code)
modified on Monday, February 1, 2010 5:26 PM
|
|
|
|
 |
|
 |
Managed to load the rotated image and calculated rotated coordinates,
So i don't no more dynamic rotation required
|
|
|
|
 |
|
 |
Hey,
Im wondering, why are you using the DrawImageUnscaled(Bitmap, Point) function instead of calling the DrawImage(Bitmap, Point) directly? Now it has to make another extra call:
DrawImageUnscaled(Bitmap, Point) ->
DrawImage(Bitmap, Point) ->
DrawImage(Bitmap, int, int) ->
DrawImage(Bitmap, int, int, int, int) ->
DrawImage(Bitmap, Rectangle) and finaly, the bitmap gets drawn
Im actualy not sure about the last overload, wether I use the 'last - 1', or the last one, both seem to be called eventually, however DrawImage(Bitmap, Rectangle) seems to be the fastest.
|
|
|
|
 |
|
 |
To start I would like to say thank you for your class. I created a dll of it for use in a VB.Net project and now one of my operations that used to cut my FPS in half has practically no effect at all on the FPS. Just shows how powerful the class is.
I was wondering if the type of overhead this is supposed to solve also applies to the Bitmap.Clone method. If it does I was wondering if you could create a method that create a actual clone the image data instead of a reference. This would be useful when you need a copy to get "dirty" while maintaining a clean copy to create views from.
|
|
|
|
 |
|
 |
You're welcome - I'm glad it helped!
For cloning a portion of the image for manipulation, what I would recommend is to create a new EditableBitmap instance with the dimensions you want the cloned section to be, and then use System.Buffer.BlockCopy() to copy the desired data from the original bitmap array to the new one. If you're cloning the whole bitmap, you can do a single BlockCopy() call for the whole array. Otherwise, do a separate BlockCopy() call for each scanline, or perhaps in that case it would be faster to copy the values from the one array to the other by hand. Make sure you start at the right location in the array by using the formula:
bmp.StartOffset+(bmp.Stride*yPos)+(xPos*bmp.PixelFormatSize)
...where bmp is your EditableBitmap, and xPos and yPos are the X and Y of the top left corner of the area you copy. Copy the scanlines based on the width of the bitmap and skip based on the Stride, similarly to how I do with the bitmap view setup.
|
|
|
|
 |
|
 |
Thanks for your answer.I didn't feel like doing the per-scanline meathod at the time so I ended up copying the whole image because the pointer generated from CreateView doesn't seem to work with BlockCopy while the main view one did work. After this I used CreateVeiw on the copy to get the result I wanted. Didn't get any real performance boost I could detect this time. Think I may have maxed out the optimization I can do in that area of my program without conquering performance in other areas I haven't determined yet. More testing may be required before I know for sure, but I think I can be satisfied with getting over 30FPS with GDI++ vs the 10-20 I was getting before I started to use your class.
|
|
|
|
 |
|
 |
Big thanks for your articles on this subject, never knew about that Bitmap constructor before.
I'm playing around with a remote (desktop) support tool where I keep track of visible windows and user input and take screenshots when something happends. I then split the screen into smaller squares, check which have changed and send those over the net. With this technique it becomes so incredibly simple and fast!
It has always bugged me how problematic LockBits() is. When used for animation I often run into Exceptions and problems. All gone now
|
|
|
|
 |
|
 |
After reading the article and seeing the demo, does it mean that I can create "zoomed-in" regions of interest or smaller sub-images based on the original, just using the CreateView method? Does the class merely display a small portion in a client window or say another picute box on a form, or does it resample the original data so that I can massage the bits. Doing the latter allows things like contrast adjustments on regions that are different than the overall image. I would preserve detail this way.
Thanks in advance.
Al
Jer 29:11
|
|
|
|
 |
|
 |
If you read carefully you notice it's the same data array used for both the original bitmap and the view. This means, edit the view and you also edit that part of the original bitmap.
|
|
|
|
 |
|
 |
Then I guess I'll read it again - carefully.
Thanks for replying
Jer 29:11
|
|
|
|
 |
|
 |
I have just uploaded a new codeproject, Generate Silverlight 2 DeepZoom image collection from multi-page tiff. In my project I needed to "cut up" bitmap images in many small tiles, and your EditableBitmap class made a huge difference in the performance of that. Can you please have a look and see if my use of your code is ok?
I have sent you a similar message through LinkedIn.
Thanks,
Berend Engelbrecht
|
|
|
|
 |
|
 |
Are your smaller images a "zoomed-in" version of the original or does the detail get preserved as what would be expected in a new set of images.
Anxiously waiting for feedback from author and other users.
Thanks in advance,
Al
Jer 29:11
|
|
|
|
 |
|
 |
They are square tiles that are "cut out" of the original image. Justin's code has a performance benefit compared to the standard Microsoft API for the case that you do not need to scale the original image to produce the tile.
Actually, for "deepzoom" you do need to provide smaller versions of the source image as well, I just use GDI for scaling there. In the DecosDeepZoom code project page you find a detailed explanation of the structure of a Microsoft Silverlight DeepZoom image.
|
|
|
|
 |
|
 |
Using this in your application, are you concerned with image detail with extreme zooming applied to images? I have a need to inspect 16 bit grayscale images and have to create new images based on a predefined area sections (meaning no drawing boxes with the mouse to define zoomed in area).
-Al
Jer 29:11
|
|
|
|
 |
|
 |
Output resolution in viewport rectangle
Justin's code is fine for that. Output resolution is the same as input resolution, no details are lost. What he made is a "viewport" algorithm that works faster than the standard API in Windows, but otherwise has the same functionality.
Resolution and detail in DeepZoom
Silverlight Deepzoom is nice if your image is large and you want to be able to view an overview of the full image and also quickly zoom in to the maximum detail. You can zoom using the scroll wheel of the mouse - the algorithm for zooming and rendering the visible part of the image is similar to what is used in the satellite images of Google Maps.
However, DeepZoom has some disadvantages that makes it less suitable for your purpose:
1. Although pixel resolution is internally preserved, the DeepZoom viewer uses a similar rendering algorithm to the Windows "Picture and Fax viewer". The viewer introduces some blurring when rendering the image, even if the source image is sharp.
2. The tiles can only be saved as jpeg or png. I think neither of these formats is the best choice for 16 bpp grayscale images. Probably the best for you (depending on the subject of your images) would be jpeg with a very low compression ratio (i.e., close to 100% quality factor), if you would go that route.
Google code, a possible alternative?
If Microsoft Silverlight DeepZoom is not for you, perhaps you can look at what google code has to offer. I am not sure what is the release state of the API/code for this, but recently they published an experiment with high resolution images using the rendering technology of google maps:
Prado museum showcase in google maps
pradomuseum.googlecode.com
I have done some google maps coding and the viewer side of it is not difficult - you only need a few lines of javascript code to embed a google map in your own web page. You can get a free unlock key here to use the google maps API in your web page.
However, Google does not store just any image in Google maps format, the Prado stuff is just an experiment, not production code. I would imagine that it is not really possible to get Google to store your hires images in their datacenters. With Silverlight you do not have that problem, you can use your own webserver to store silverlight code and data without needing an external partner.
Even if the Prado showcase were production code, there could be copyright issues that would make it unwise for you to upload your hires images to Google's cloud. Google's usual disclaimer is not very respectful of other people's copyright ...
|
|
|
|
 |
|
 |
Thanks Berend.
I think that DeepZoom's filtering would cause me to lose detail. I am almost sorry I attempted to do this project in .NET in the first place. Plain Win32 GDI, while being a bit more tedious is proving to be the better solution to what I need.
Jer 29:11
|
|
|
|
 |
|
 |
Hi!
Since I was impressed by the speed of drawing, I took your snippet as one of my early c#-exercises today (usually I'm VB-Maniac).
In order to simplify your code I tried to exchange your technic by using Bitmap.LockBits().
In my first tests I get quite similar speed-results, I don't know, how it works with different PixelFormats:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
namespace PictureBoxScroll {
class BitmapFactory : IDisposable {
Bitmap bitmap;
BitmapData bmpData;
public BitmapFactory(Bitmap source) {
bitmap = new Bitmap(source);
bmpData = bitmap.LockBits(
new Rectangle(Point.Empty, source.Size),
ImageLockMode.ReadOnly,
source.PixelFormat);
}
public Bitmap GetBitmapSection(Rectangle rct) {
rct.Intersect(new Rectangle(Point.Empty, bitmap.Size));
if (rct.Width == 0 || rct.Height == 0) return null;
int pixelFormatSize = Image.GetPixelFormatSize(bmpData.PixelFormat) / 8;
int scanOffset = (bmpData.Stride * rct.Y) + (rct.X * pixelFormatSize);
IntPtr scan0 = (IntPtr)(((int)bmpData.Scan0) + scanOffset);
return new Bitmap(rct.Width, rct.Height, bmpData.Stride, bmpData.PixelFormat, scan0);
}
#region IDisposable Stuff
private bool isDisposed;
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
protected void Dispose(bool disposing) {
if (isDisposed) return;
isDisposed = true;
bitmap.UnlockBits(bmpData);
if (disposing) bitmap.Dispose();
}
~BitmapFactory() { Dispose(false); }
#endregion }
}
and in PicturePanel the main-thing goes like:
protected override void OnPaintBackground(PaintEventArgs e) {
e.Graphics.Clear(this.BackColor);
}
protected override void OnPaint(PaintEventArgs pe) {
if (bitmapFactory == null) {
OnPaintBackground(pe);
return;
}
pe.Graphics.InterpolationMode = InterpolationMode.Low;
pe.Graphics.CompositingQuality = CompositingQuality.HighSpeed;
pe.Graphics.SmoothingMode = SmoothingMode.HighSpeed;
Rectangle selectionRect = pe.ClipRectangle;
selectionRect.Location -= new Size(AutoScrollPosition);
using (Bitmap bmp = bitmapFactory.GetBitmapSection(selectionRect)) {
if (bmp == null) {
OnPaintBackground(pe);
return;
}
if (!bmp.Size.Equals(pe.ClipRectangle.Size)) {
OnPaintBackground(pe);
}
pe.Graphics.DrawImageUnscaled(bmp, pe.ClipRectangle.Location);
}
}
I think, its to keep in mind, that it can be considered as "aggressive optimalisation", to hold in memory unmanaged ressources, often bigger than 4 MB. Either as pinned array or as locked Bitmapmap, however.
|
|
|
|
 |
|
 |
It won't work for PixelFormats that take up less than 1 byte (i.e. monochrome, or indexed images). To get it to work, store your PixelFormatSize at the pixel format size in bits and divide by 8 (or shift by 3 bits for speed) wherever you use it in multiplications:
e.g. in the EditableBitmap(int, int, PixelFormat) constructor, change the line
stride = width * pixelFormatSize;
to
stride = width * pixelFormatSize / 8;
Or, I suppose you could just store your pixelFormatSize as a double.
You will also need to replace the EditableBitmap(Bitmap, PixelFormat) constructor with the following to get the code to worked with indexed colour images, since a Graphics object may not be created from a Bitmap using indexed colours:
public EditableBitmap(Bitmap source, PixelFormat format)
: this(source.Width, source.Height, format)
{
BitmapData bitmapData = null;
try
{
bitmapData = source.LockBits(
new Rectangle(0, 0, source.Width, source.Height),
ImageLockMode.ReadOnly,
format);
Marshal.Copy(bitmapData.Scan0, this.Bits, 0, this.Bits.Length);
}
finally
{
if (bitmapData != null)
source.UnlockBits(bitmapData);
}
}
-adam
|
|
|
|
 |