Home » C# ProgrammingRSS

24 bit bmp to rGB565 file conversion

Hi

I want to convert 24 bit bmp files to RGB565 format to write to a serial TFT colour display.

I have heard this may be quite an easy thing to do using c#, has anyone any experience of doing this?

The size of the 24bit bmp will always be 320x240 pixels as my TFT display is 320x240

Any suggestions gratefully received.

Phil

 

 

17 Answers Found

 

Answer 1

Use the FormatConvertedBitmap class.
 

Answer 2

WOW that was fast!!!

Didn't know that class existed will check out the help file  and see if I figure out how to use it.

Many thanks.

 

Answer 3

Hi again

Found reference to the the above but......

When I start a new visual studio windows forms project and try to add windows.controls, windows.media and windows.media.imaging they aren't listed as options.

All I get for typing using system.windows.    is forms option???

Am I missing something????

Help please.

 

Answer 4

Start a new WPF application or add the WPF references to your WF project.  You need PresentationCore, PresentationFramework and WindowsBase

 

Answer 5

Aha

Just aquestion of knowing what references to add..... many thanks will try it.

 

 

Answer 6

Problem with FormatConvertedBitmap its that it doesn't do rgb565 but bgr565 flipping your red and blue channels, really annoying.

Good thing  the classic System.Drawing class can be alot of help here (you need to have a reference to System.Drawing.dll)

 

staticvoid Main(string[] args)
  {
   //Load up the original bitmap.
   Bitmap source = (Bitmap)Bitmap.FromFile("test24.bmp");
   
   //Creat a 16 bit  copy of it using the graphics class
   Bitmap dest = new Bitmap(source.Width, source.Height, System.Drawing.Imaging.PixelFormat.Format16bppRgb565);
   using (Graphics g = Graphics.FromImage(dest))
   {
    g.DrawImageUnscaled(source, 0, 0);
   }
   
   //Get a copy of the byte array you need to send to your tft
   Rectangle r = new Rectangle(0,0,dest.Width,dest.Height);
   BitmapData bd = dest.LockBits(r, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format16bppRgb565);
   byte[] TftData = newbyte[dest.Width * dest.Height * 2];
   Marshal.Copy(bd.Scan0, TftData, 0, TftData.Length);
   dest.UnlockBits(bd);
   
   //A 16 bit copy of the data you need to send to your tft is now sitting in the TftData array.
  }

 

 

 

Answer 7

Problem with FormatConvertedBitmap its that it doesn't do rgb565 but bgr565 flipping your red and blue channels, really annoying.

Good thing  the classic System.Drawing class can be alot of help here (you need to have a reference to System.Drawing.dll)

 

staticvoid Main(string[] args)

 {

  //Load up the original bitmap.

  Bitmap source = (Bitmap)Bitmap.FromFile("test24.bmp");

  

  //Creat a 16 bit  copy of it using the graphics class

  Bitmap dest = new Bitmap(source.Width, source.Height, System.Drawing.Imaging.PixelFormat.Format16bppRgb565);

  using (Graphics g = Graphics.FromImage(dest))

  {

  g.DrawImageUnscaled(source, 0, 0);

  }

  

  //Get a copy of the byte array you need to send to your tft

  Rectangle r = new Rectangle(0,0,dest.Width,dest.Height);

  BitmapData bd = dest.LockBits(r, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format16bppRgb565);

  byte[] TftData = newbyte[dest.Width * dest.Height * 2];

  Marshal.Copy(bd.Scan0, TftData, 0, TftData.Length);

  dest.UnlockBits(bd);

  

  //A 16 bit copy of the data you need to send to your tft is now sitting in the TftData array.

 }

 

 


Have you tested any of your statements or your code?
 

Answer 8


Have you tested any of your statements or your code?



Whats with the condescending tone? But sure i'll play along.

Proof enough? :)

So yeahh I tested my code before posting, you could have looked up if FormatConvertedBitmap did rgb565 but didn't, whats with the double standard here?

 

Answer 9

Something's wrong somewhere:

using System;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace ConsoleApplication2
{
  
class Program
  {
    
static void Main(string[] args)
    {
      
BitmapImage BmpImg = new BitmapImage(new Uri("C:\\Temp\\Sample Pictures\\Tree.jpg"));
      
FormatConvertedBitmap FmtCnvBmp = new FormatConvertedBitmap(BmpImg, PixelFormats.Bgr565, null, 1.0);
      
BmpBitmapEncoder BmpEnc = new BmpBitmapEncoder();
      BmpEnc.Frames.Add(
BitmapFrame.Create(FmtCnvBmp));
      BmpEnc.Save(
new FileStream("C:\\Temp\\Conv.bmp"FileMode.Create));
    }
  }
}

 

Answer 10

You're right, something *IS* fishy here, I tested your code on the TFT as well, since framebuffers dont' take bmp's i used the following snippet.

      FormatConvertedBitmap FmtCnvBmp = new FormatConvertedBitmap(BmpImg, PixelFormats.Bgr565, null, 1.0);
      byte[] TftData = newbyte[480*272*2];
      FmtCnvBmp.CopyPixels(TftData, 480*2, 0);
      epson.Display.hcl.SetMemoryWriteAddressRect(0, true);
      epson.Write(TftData, 0, TftData.Length);

(display is 480x272)

Funny thing  is that the bitstream that ends up in TftData is actually RGB565 (so looks fine on the TFT) not BGR565 like FmtCnvBmp was created.  So either FormatConvertedBitmap or CopyPixels is swapping Red and Blue without telling us.

 

 

 

 

Answer 11

I've assumed that ARGB in WF was the same as BGRA in WPF.  That's whats reported for normal 32 bit images.  I've not run into a problem before and I intermingle WF and WPF classes.  The two methods of converting give very different (not visually, but numerically) images.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace ConsoleApplication2
{
  
class Program
  {
    
static void Main(string[] args)
    {
      
Bitmap bmp = new Bitmap("C:\\Temp\\Sample Pictures\\Tree.jpg");
      
byte[] b = new byte[bmp.Width * bmp.Height * 2];
      
GCHandle gch = GCHandle.Alloc(b, GCHandleType.Pinned);
      
IntPtr scan0 = gch.AddrOfPinnedObject();
      
Bitmap dest = new Bitmap(bmp.Width, bmp.Height,4 * ((bmp.Width * 16 + 31) / 32), PixelFormat.Format16bppRgb565, scan0);
      
using (Graphics g = Graphics.FromImage(dest))
      {
        g.DrawImage(bmp, 0, 0);
      }
      gch.Free();
      dest.Save(
"C:\\Temp\\WFConv.bmp"ImageFormat.Bmp);
    }
  }
}

 

Answer 12

I ran some more tests on this, by the looks of it its a bug in the framework (a very desireable bug in this case, but still a bug)

   //Setup a render target bitmap 1x1
   RenderTargetBitmap rtb = new RenderTargetBitmap(1, 1, 96, 96, PixelFormats.Pbgra32);
   
   //Fill this bitmap with 0% red, 100% green, 50% blue
   Rect fr = new Rect(0,0,1,1);
   Rectangle r = new Rectangle();
   r.Width = 1;
   r.Height = 1;
   r.Fill = new SolidColorBrush(Color.FromRgb(0,255,128));
   r.Arrange(fr);
   rtb.Render(r);
   
   //Read the pixel value back
   byte[] colors = new byte[4];
   rtb.CopyPixels(colors, 4, 0);

   // Expected results
   // colors[0] = 0x80 //B
   // colors[1] = 0xff //G
   // colors[2] = 0 //R
   // colors[3] = 0xFF //A

   //Actual results match expected results (use debugger to validate, or take my word for it) 

   //Now lets convert  to Bgr565
   FormatConvertedBitmap conv = new FormatConvertedBitmap(rtb, PixelFormats.Bgr565, null, 0.0);
   byte [] colors16 = new byte[2];
   conv.CopyPixels(colors16, 2, 0);

   //Expected values
   //B = 10000 50% blue
   //G = 111111 100% green
   //R = 00000  0% red 
   
   //Turning that into sigle uint16 1000 0111 1110 0000 = 0x87E0
   //We're on a little endian platform so that'll be {0xe0,0x87}

   //Actual result {0xf0,x07} -> 0x07f0 -> 0000 0111 1111 0000
   //B 00000  0% blue
   //G 111111 100% green
   //R 10000  50% red 

   //Red and blue mysteriously have swapped places , this is not a little vs big endian byte order issue because green is still intact.

 Made a report of it on connect, lets see what happens :)

 

Answer 13

I don't believe the pixels  of images are dependent upon platform byte order.  Most image containers have a field to specify the byte order of the tags, but not the pixels.  But, the image formats have been so bastardized recently, anything is possible.  I been using FormatConvertedBitmap because it converts indexed images.   I haven't run into a problem with formats before.  RGB has always been BGR on Windows.
 

Answer 14

True in WPF they finally realized that is might be a good idea to have the PixelFormats actually match the actual dataformat. so PixelFormats.Bgra32 now actually gives you what you expect (unlike the winforms Format32bppPArgb which actually gave you bgra) , all I can say about that is *Finally*, takes alot of confusion out of the equation. I'm not arguing any issues with the 32 bit  formats of the data ,the issue is that in bgr565 the actual data layout if rgb565. Now in high level manipulation where you just throw bitmaps and files  around none of this matters.  However when you work with low level hardware access such a tft that *demands* its data in a specific format, data formats suddenly matter *alot*

Again this 'bug' is very desirable and I'm for one certainly not looking to have it fixed (I work alot with tft panels and none of the 16 bits panels will accept BGR all of them want RGB), but if they just renamed PixelFormats.Bgr565 to PixelFormats.Rgb565 or make a note in the documentation that this will be guaranteed in RGB format we'd be done, but I'd hate to write  software that relies on this only to have MS fix is in a future service pack breaking all the software that relies on this odd behaviour.

 

 

 

Answer 15

Phew that was quite an exchange between you two!

I'm very grateful though because now I think I'll be able to do the conversion.

Special thanks to Ray M as I need to generate a file  to download over an RS232 link to the TFT display  and not display it on my computer.

Just found these intelligent TFT display panel complete with touch screen that comunicate via RS232 so our instruments can be easily and cheaply upgraded from mono to full colour  displays!

Thanks again guys.

 

Answer 16

Hi John

Been playing around with bitmap creation and modification but noticed that when I use your example below I lose the right hand side of the picture?

So I have 2 questions

1 is there a limit on the size  of the jpg file  in terms of pixels, the one I'm converting is 2560x1920.

2. Can I resize the original jpeg as I convert  it to a bmp, I require a 320x240 bmp. I tried just setting those as the size of the new bitmap but only got a corner of the original jpeg file of course.

Sorry to be a pain but playing around with images is new to me.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace ConsoleApplication2
{
  
class Program
  {
    
static void Main(string[] args)
    {
      
Bitmap bmp = new Bitmap("C:\\Temp\\Sample Pictures\\Tree.jpg");
      
byte[] b = new byte[bmp.Width * bmp.Height * 2];
      
GCHandle gch = GCHandle.Alloc(b, GCHandleType.Pinned);
      
IntPtr scan0 = gch.AddrOfPinnedObject();
      
Bitmap dest = new Bitmap(bmp.Width, bmp.Height,4 * ((bmp.Width * 16 + 31) / 32), PixelFormat.Format16bppRgb565, scan0);
      
using (Graphics g = Graphics.FromImage(dest))
      {
        g.DrawImage(bmp, 0, 0);
      }
      gch.Free();
      dest.Save(
"C:\\Temp\\WFConv.bmp"ImageFormat.Bmp);
    }
  }
}

 

Answer 17

I put the original http://ypmdta.bay.livefilestore.com/y1pl2DMUNzpw0zhN8arBHwttEVHzp-OMWm9xJA5tq6VYMtpcsNPbTabbSU95pGq1zJ0XHsP-4V1W6zBUnHo2AQM_xz_zIJwJy5l/Tree.jpg?psid=1 and the converted http://ypmdta.bay.livefilestore.com/y1p5f-HLymdB8LX7tEudaAGvDuK4h7yfKEsP38GLUUFwEQh8TrW5TBPM-a6Lzes9E_p9mxWNtGL8g9Jcv_iIcbOH2R6CpusuBhE/WFConv.bmp?psid=1 image on skydrive.  They look spatially identical to me.

1.  GDI+ has a practical limit in memory of about 500 MB which converts to a 75% quality JPEG of about 25 MB.  If WPF has a limit, it's at least 10 times the GDI+ limit.

2.  Using the various DrawImage overloads, you can crop and/or resize an image any way you wish.

 
 
 

<< Previous      Next >>


Microsoft   |   Windows   |   Visual Studio   |   Follow us on Twitter