1

Here is a code snippet where I attempt to put a blue dot icon (Bgr32; 169x169) on a graphic (Bgra32 3985x3443) in a WPF Image control.

string s = new FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName + "\\lg_dot.jpg";
BitmapImage icon = new BitmapImage(new Uri(s));
var baseLayer = img_BackgroundLayer.Source as BitmapImage;
WriteableBitmap composite = new WriteableBitmap(baseLayer);

int bytesPerPixel = (int)(icon.Format.BitsPerPixel + 7) / 8;
int stride = (int)(bytesPerPixel * icon.Width);
int size = stride * (int)icon.Height;
byte[] pixels = new byte[size];                
icon.CopyPixels(pixels, stride, 0);

composite.WritePixels(new Int32Rect(0, 0, (int)icon.Width, (int)icon.Height), pixels, stride, 1000, 1000);

Notice that I am placing the dot (icon) at (1000, 1000) on the image. When viewed however, the upper left of the dot is not at (1000, 1000) it is at about (760,760). If I try to place the icon at (2000,2000) it appears at about (1520,1520). The place the icon appears is always approx (0.76 * X, 0.76 * Y) where X and Y are the target coordinates.

I think I know what is happening here... something to do with putting a Bgr32 image onto a Bgra32 background... but I am not clever enough to see how to solve it.

davecove
  • 1,001
  • 4
  • 16
  • 36

1 Answers1

0

Bytes per pixel formula :

(bits per pixel + 7) / 8

Stride formula :

bytes per pixel * image width

Notice bytes and bits and use integers for calculation !

So for a 1024 pixels wide image :

Bgr32

  • bytes per pixel : (24 + 7) / 8 = 3
  • stride : 3 * 1024 = 3072

Bgra32

  • bytes per pixel : (32 + 7) / 8 = 4
  • stride : 4 * 1024 = 4096

By looking at your code :

  • the stride should be 3, not 4 :D

  • you are needlessly using stride for an int[], remember sizeof(int) == 4

byte offset of the pixel in your image array of byte[] : stride * y + x * bytes per pixel

int offset of the pixel in your image array of int[] : y * width + x

By the way, did you check that your Image.Stretch is set to None ?

Finally, beware of the components order :

BGRA is represented as ARGB (you might see nothing if A is 0 and you think you've set B)

Last tip : https://writeablebitmapex.codeplex.com/ might be easier for you in the end.


EDIT

This is really bad, in my case it is null as BitmapFrameDecode cannot be casted to BitmapImage, it's better to keep a reference to the source instead :

var baseLayer = Image1.Source as BitmapImage;

The formulas I gave you are correct, I only had to change destX and destY as my background was not as large as yours.

var icon = new BitmapImage(new Uri("..\\..\\avatar92.jpg", UriKind.Relative));
var background = new BitmapImage(new Uri("..\\..\\canary_1024_600_a_0.jpg", UriKind.Relative));
var writeableBitmap = new WriteableBitmap(background);

int bytesPerPixel = (icon.Format.BitsPerPixel + 7) / 8;
int stride = bytesPerPixel * icon.PixelWidth;
int size = stride * icon.PixelHeight;
var pixels = new byte[size];
icon.CopyPixels(pixels, stride, 0);
writeableBitmap.WritePixels(new Int32Rect(0, 0, icon.PixelWidth, icon.PixelHeight), pixels, stride, 500, 500);

Image1.Source = writeableBitmap;

enter image description here

Again,

  • Is Image.Stretch == None ?
  • Are the image dimensions correct ? they should be I guess :D
  • Do bitmaps have the same format ? Not that it won't fail if they are different but you'll get all sort of funny surprises, try using a .GIF and a .JPEG file for instance.
  • I really suggest you to use WriteableBitmapEx instead, you can draw pixels, lines, rectangles, shapes etc ...

Here's the same result with WriteableBitmapEx without having to deal with all the details such as stride, array, bpp, etc ...

var icon = new BitmapImage(new Uri("..\\..\\avatar92.jpg", UriKind.Relative));
var background = new BitmapImage(new Uri("..\\..\\canary_1024_600_a_0.jpg", UriKind.Relative));
var img1 = BitmapFactory.ConvertToPbgra32Format(icon);
var img2 = BitmapFactory.ConvertToPbgra32Format(background);
var img1Size = new Size(img1.PixelWidth, img1.PixelHeight);
img2.Blit(new Rect(new Point(500, 500), img1Size), img1, new Rect(img1Size));
Image1.Source = img2;

If you prefer the hard way then make sure your bitmaps have the same format : FormatConvertedBitmap

If you still cannot get it working, post a Short, Self Contained, Correct (Compilable), Example and links to the two images so people can really help you. I found nothing on your code so I guess there's something not right but as you haven't pasted all the code we can't really tell.

Community
  • 1
  • 1
aybe
  • 15,516
  • 9
  • 57
  • 105
  • I edited my original post to use what I think I understood from your answer. No change in behavior tho, the icon is still showing up in the wrong place. – davecove Feb 21 '14 at 02:37
  • Thank you for getting me to use the WriteableBitmapEx package. That is easier and I am going to need all of those Extensions functions. I am still having the same issue, but I am about 98.7% sure I know what is wrong now... the dpi of the two images are different. In your example img1 is 96dpi and img2 is 125dpi. 96/125 = .763 which is why my icon shows up at pixel 763,763 when I aim for 1000,1000. Does WriteableBitmapEx have a method to change dpi? I didn't see one in the Object Browser. – davecove Feb 21 '14 at 14:08
  • Do not forget to mark the answer as accepted ! About DPI I've totally forgot about that, to overcome this issue you should create another bitmap with same dimensions but set DPI to 96 then copy the pixels to it. Here are 2 links, explanations and code http://stackoverflow.com/a/15581258/361899 and http://social.msdn.microsoft.com/Forums/vstudio/en-US/35db45e3-ebd6-4981-be57-2efd623ea439/wpf-bitmapsource-dpi-change?forum=wpf. You should be up and running then, if not come back here. – aybe Feb 21 '14 at 14:27
  • Followup question if I might... now that part of img2 has been overwritten with img1 (and let's assume icon images img3 & img4 have been blit'd in different places on img2), is there a way to remove just img1 without disturbing the others? My instinct is to make another copy of the background (imgA) and Blit an img1-sized piece of imgA onto img2. Dunno if that is the right/best way to do it tho... Thx – davecove Mar 04 '14 at 04:29
  • You can probably use blend mode mask on one of the overload. – aybe Mar 04 '14 at 06:07