Question

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?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top