none
WPF BitmapSource DPI change

    Question

  • I'm not used to working with images particularly with WPF, so this is pretty new to me.  I'm reading a 1200x1800 JPG file from disk with a resolution of 180 dpi.  I need to resize it down to, say, 200x300 with a resolution 96 dpi.  I have not discovered a way to manipulate the dpi of the image when saving it.  Can anyone point me in the right direction?

    --- Jim ---
    Monday, March 30, 2009 11:23 PM

Answers

  • Here's some code to help you out:
    In this particular case, SourceImage is a property that returns a BitmapSource object. You can substitute your own BitmapSource object, or your own byte[] of pixel data that you created elsewhere. (This is c#, but you can make the translation easily. I think the main thing to change is the stride)

     double dpi = 96;
     int width = SourceImage.PixelWidth;
     int height = SourceImage.PixelHeight;
    
     int stride = width * 4; // 4 bytes per pixel
     byte[] pixelData = new byte[stride * height];
     SourceImage.CopyPixels(pixelData, stride, 0);
    
     BitmapSource bmpSource = BitmapSource.Create(width, height,    dpi, dpi,  PixelFormats.Bgra32, null, pixelData, stride);
    • Proposed as answer by joeAtBluebeam Thursday, April 09, 2009 4:10 PM
    • Marked as answer by Jimbo Mx Thursday, April 09, 2009 4:54 PM
    Thursday, April 09, 2009 4:09 PM

All replies

  • You can set DPI on an image when you create it. Here's an example of creating it from a memory stream. You can also create it using
    BitmapImage myImage = BitmapImage.Create(...)
    So you probably should create a new bitmapImage object from your existing image and set the dpi properties as desired.
    // Load into memory stream first because WPF caches the data and would prevent the file from being deleted
    
                FileStream fileStream = new
     FileStream(pFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                MemoryStream imageStream = new
     MemoryStream((int
    )fileStream.Length);
                
                int
     readSize = -1;
                byte
    [] data = new
     byte
    [4096];
                do
    
                {
                    readSize = fileStream.Read(data, 0, 4096);
                    imageStream.Write(data, 0, readSize);
                }
                while
     (readSize != 0);
                imageStream.Position = 0;
                fileStream.Close();
    
                BitmapImage bi = new
     BitmapImage();
                
                bi.BeginInit();
                bi.StreamSource = imageStream;
                //bi.DpiX = 
    
                // bi.DpiY =   //set your dpi here
    
                bi.EndInit();
    
    • Proposed as answer by joeAtBluebeam Tuesday, March 31, 2009 11:25 PM
    • Marked as answer by Jimbo Mx Friday, April 03, 2009 11:13 PM
    • Unmarked as answer by Jimbo Mx Wednesday, April 08, 2009 7:06 PM
    • Unproposed as answer by Jimbo Mx Wednesday, April 08, 2009 7:23 PM
    Tuesday, March 31, 2009 11:21 PM
  • The problem with this approach is that DpiX and DpiY properties are ReadOnly and thus cannot be set in code.

    I cannot seem to get the BitmapImage.Create(...) or BitmapSource.Create(...) functions to work, and I'm not finding a lot of documentation.

    I've tried this:


    Dim stride = CByte(CInt(size.Width) \ PixelFormats.Bgr32.BitsPerPixel)
    
    Dim bs = BitmapSource.Create(CInt(size.Width), CInt(size.Height), 96.0#, 96.0#, PixelFormats.Bgr32, Nothing, imageData, stride)
    
    

    In this case, imageData is a byte array (120965 bytes) and size is a Size structure.  size is (567, 553)

    All I get is an ArgumentException indicating "Value does not fall within the expected range".

    Any idea which argument and what I'm doing wrong (or an easier way to do this)?

    --- Jim ---
    Wednesday, April 08, 2009 7:19 PM
  • I think the stride is the width * the number of bytes per pixel.
    Thursday, April 09, 2009 4:02 PM
  • Here's some code to help you out:
    In this particular case, SourceImage is a property that returns a BitmapSource object. You can substitute your own BitmapSource object, or your own byte[] of pixel data that you created elsewhere. (This is c#, but you can make the translation easily. I think the main thing to change is the stride)

     double dpi = 96;
     int width = SourceImage.PixelWidth;
     int height = SourceImage.PixelHeight;
    
     int stride = width * 4; // 4 bytes per pixel
     byte[] pixelData = new byte[stride * height];
     SourceImage.CopyPixels(pixelData, stride, 0);
    
     BitmapSource bmpSource = BitmapSource.Create(width, height,    dpi, dpi,  PixelFormats.Bgra32, null, pixelData, stride);
    • Proposed as answer by joeAtBluebeam Thursday, April 09, 2009 4:10 PM
    • Marked as answer by Jimbo Mx Thursday, April 09, 2009 4:54 PM
    Thursday, April 09, 2009 4:09 PM
  • Thanks Joe...that little nudge did it for me.  I'm a little concerned about hardcoding stride as (width * 4), but PixelFormats.Bgr32 has a property BitsPerPixel that makes things a little cleaner.  It will be nice to stop banging my head against the wall on this one...
    Thursday, April 09, 2009 4:58 PM
  • It's still hardcoded, you're just hardcoding it using an enum instead. If Bgr32 ever represents an image that's not 32 bits, then your enums are changing out from under you, and you probably have bigger problems. However, you could do something like this if you wanted:

    (SourceImage.Format.BitsPerPixel +7)/ 8;

    Which will give you the number of bytes used by the format assigned to the original bitmapSource, rounding up in case it's not a multiple of 8 (like 1 bpp for single color images)
    Thursday, April 09, 2009 6:26 PM
  • The stride is 4 byte aligned. 
    Friday, April 10, 2009 5:00 AM
  • One comment, if the bitmap does not have 4 bytes per pixel e.g. Gray8, consider doing this to avoid "Value does not fall within the expected range" at line of  BitmapImage.Create 

    BitmapSource bmpSource = BitmapSource.Create(width, height, dpi, dpi,SourceImage.Format, null, pixelData, stride);


    Reuters

    Tuesday, August 20, 2013 6:49 PM