0

It seems like variations of this question have been asked before, but not this specifically. Also, it seems that BitmapImages are different from straight Bitmaps. So here we go:

I've got a BitmapImage x:Name="HeightMapImage" that is pointing to an Image x:Name="image" that is inside ContentPresenter x:Name="contentPresenter" that is in Viewbox x:Name="viewBox". I want to draw both semi-transparently and non-transparently at specific X,Y coordinates on the HeightMapImage.

The reason for this set-up is that the BitmapImage is being scrolled and zoomed. When I draw on the BitmapImage at X,Y I want that to automatically scroll and zoom, too.

I'm a very old geek having written for many machines in many different GDIs over the years. This seems like a 'get the handle' to some graphic device context problem and once I've got it I can merrily draw away.

Your help is greatly appreciated.

Somebody wanted to see the code. Here's the XAML:

<Viewbox x:Name="viewBox" Margin="0,0,0,0">
    <ContentPresenter x:Name="contentPresenter" Width="350" Height="350" >
        <ContentPresenter.Content>
            <Image x:Name="image" Width="350" Height="350">
                <Image.Source>
                    <BitmapImage x:Name="HeightMapImage" UriSource="DinoIslandLogo.bmp" />
                </Image.Source>
            </Image>
        </ContentPresenter.Content>
    </ContentPresenter>
</Viewbox>

And here's a screen capture that somebody wanted:

enter image description here

And here's the code that gets the user's selected bitmap and loads and displays it:

string selectedFileName = dlg.FileName;
BitmapImage bitmap = new BitmapImage();

bitmap.BeginInit();
bitmap.UriSource = new Uri(selectedFileName);
bitmap.EndInit();

image.Source = bitmap;

Does this need to be rewritten for a Writeable Bitmap?

Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
zetar
  • 1,225
  • 2
  • 20
  • 45
  • Dude, post some sample code and XAML and probably a screenshot. Otherwise it's difficult to try to help you. – Federico Berasategui May 26 '13 at 14:50
  • Also, Zooming a bitmap is going to result in a crappy UX. Why do you want that? WPF has built in support for vector graphics. – Federico Berasategui May 26 '13 at 14:52
  • The zooming and scrolling is very smooth and is exactly what I want. – zetar May 26 '13 at 15:06
  • 1
    What you need is a WritableBitmap. I still insist that you're taking the wrong approach. WPF is not bitmap based. – Federico Berasategui May 26 '13 at 15:09
  • The bitmap is 2000 x 2000 x 256 and is loaded by the user at runtime. It's a heightmap and I need to draw colors on top of it. – zetar May 26 '13 at 15:24
  • Okay, didn't understand I was supposed to click 'to accept answers'. Thanks. So, I understand that WPF is vector based; but I don't have a lot of options here. I need to load bitmaps of heighttables and draw on them. Is the solution using a WriteableBitmap? – zetar May 26 '13 at 16:18

1 Answers1

4

You may use a WriteableBitmap instead of (or actually in addition to) a BitmapImage. First create your BitmapImage as usual (but with less code):

var selectedFileName = dlg.FileName;
var bitmap = new BitmapImage(new Uri(selectedFileName));

Then create a WritableBitmap from the BitmapImage and assign that to the Image control:

var writeableBitmap = new WriteableBitmap(bitmap);
image.Source = writeableBitmap;

Now you may modify the WriteableBitmap in order to draw your overlay data. The following code snippet shows how to get and modify a pixel in the bitmap:

if (writeableBitmap.Format.BitsPerPixel == 32)
{
    var x = 10;
    var y = 20;
    var pixelRect = new Int32Rect(x, y, 1, 1);
    var pixelBuffer = new byte[4];
    writeableBitmap.CopyPixels(pixelRect, pixelBuffer, 4, 0);
    // modify pixelBuffer and write it back
    writeableBitmap.WritePixels(pixelRect, pixelBuffer, 4, 0);
}

EDIT: A suggestion for a SetPixel method that takes the overlay color alpha value into account. Please note that this method assumes that the bitmap's pixel format is Bgr32.

public void SetPixel(WriteableBitmap wb, int x, int y, Color color)
{
    var pixelRect = new Int32Rect(x, y, 1, 1);
    var pixelBuffer = new byte[4];
    wb.CopyPixels(pixelRect, pixelBuffer, 4, 0);
    pixelBuffer[0] = (byte)(pixelBuffer[0] * (1F - color.ScA) + color.B * color.ScA);
    pixelBuffer[1] = (byte)(pixelBuffer[1] * (1F - color.ScA) + color.G * color.ScA);
    pixelBuffer[2] = (byte)(pixelBuffer[2] * (1F - color.ScA) + color.R * color.ScA);
    wb.WritePixels(pixelRect, pixelBuffer, 4, 0);
}

Please note also that it is a lot more efficient to set a larger number of pixels in one go. Ideally you would set all overlay pixels at once. You would copy all pixel values into one large array, calculate their new RGB values as shown above, and then write them all back at once.

Clemens
  • 123,504
  • 12
  • 155
  • 268
  • Okay! I've been floundering around just trying to figure out how to create a writeable bitmap. Can you point me to an example of making a transparent 'pixel write' call to this new writeableBitmap? – zetar May 26 '13 at 18:05
  • 1
    See my edit. For a "transparent" overlay you have to calculate the resulting RGB value from the original pixel and the overlay. – Clemens May 26 '13 at 18:38
  • 1
    You may also look into [WriteableBitmapEx](http://writeablebitmapex.codeplex.com/) for a lot of additional functionality. – Clemens May 26 '13 at 18:40
  • Not quite following on the 'transparent' overlay... So let's say I want to write transparently with the following RGB values: 3,87,l89. How would I modify the pixelBuffer? – zetar May 26 '13 at 18:48
  • 1
    Well, calculate the resulting color values. – Clemens May 26 '13 at 18:49
  • This is what I'm using to calculate the transparent color, but I'm just getting black. Do you see anything obvious? pixelBuffer[3] = (byte)(NewColor[3] * Alpha / 255 + pixelBuffer[3] * (255 - Alpha) / 255); pixelBuffer[2] = (byte)(NewColor[2] * Alpha / 255 + pixelBuffer[2] * (255 - Alpha) / 255); pixelBuffer[1] = (byte)(NewColor[1] * Alpha / 255 + pixelBuffer[1] * (255 - Alpha) / 255); pixelBuffer[0] = Alpha; – zetar May 26 '13 at 19:31
  • 1
    The default pixel format of a BitmapImage is Bgr32, so the alpha value is ignored. Hence it doesn't matter what you set in `pixelBuffer[3]`. See my edit for a SetPixel method. – Clemens May 26 '13 at 19:52