Recent

Author Topic: [SOLVED] How to export bitmap to 4-bit (16 colors) file?  (Read 845 times)

furious programming

  • Sr. Member
  • ****
  • Posts: 328
  • I click a little.
[SOLVED] How to export bitmap to 4-bit (16 colors) file?
« on: February 28, 2019, 06:56:11 pm »
I have an bitmap (standard TBitmap), the content of which is two-colored — the background is pink (clFuchsia) and black rectangles are painted on it, for example:

Code: Pascal  [Select]
  1. var
  2.   Bitmap: TBitmap;
  3. begin
  4.   Bitmap := TBitmap.Create();
  5.   Bitmap.SetSize(100, 100);
  6.  
  7.   Bitmap.Canvas.Brush.Color := clFuchsia;
  8.   Bitmap.Canvas.FillRect(Bitmap.Canvas.ClipRect);
  9.  
  10.   Bitmap.Canvas.Brush.Color := clBlack;
  11.   Bitmap.Canvas.FillRect(20, 20, 50, 50);
  12. end;

I need to save it to the .bmp file in 4-bit form (16 indexed colors). Does anyone know how to do it?

In the attachments I give both files — standard.bmp is just a saved bitmap using the TBitmap.SaveToFile method, and target.bmp is what I need. Thanks in advance for your help.
« Last Edit: February 28, 2019, 08:47:06 pm by furious programming »
Lazarus 2.0.2 with FPC 3.0.4, Windows XP (all 32-bit)

Bart

  • Hero Member
  • *****
  • Posts: 3436
    • Bart en Mariska's Webstek
Re: How to export bitmap to 4-bit (16 colors) file?
« Reply #1 on: February 28, 2019, 07:06:22 pm »
Set PixelFormat to pf4bit, before doing the drawing?

Bart

furious programming

  • Sr. Member
  • ****
  • Posts: 328
  • I click a little.
Re: How to export bitmap to 4-bit (16 colors) file?
« Reply #2 on: February 28, 2019, 07:09:05 pm »
Changing the PixelFormat property does not change anything — I checked it before I created this thread.

I suspect that such a bitmap should be somehow converted using the TLazIntfImage class, but I have no idea how to do it. I have read the article Developing with Graphics, but this one does not provide an example.
« Last Edit: February 28, 2019, 08:01:12 pm by furious programming »
Lazarus 2.0.2 with FPC 3.0.4, Windows XP (all 32-bit)

Handoko

  • Hero Member
  • *****
  • Posts: 3041
  • My goal: build my own game engine using Lazarus
Re: How to export bitmap to 4-bit (16 colors) file?
« Reply #3 on: February 28, 2019, 07:10:25 pm »
I haven't tried, but if you use TFPWriterBMP (in FPWriteBMP unit), it has BitsPerPixel property.

http://wiki.freepascal.org/fcl-image#Image_formats

furious programming

  • Sr. Member
  • ****
  • Posts: 328
  • I click a little.
Re: How to export bitmap to 4-bit (16 colors) file?
« Reply #4 on: February 28, 2019, 07:49:43 pm »
Thanks for the suggestions. I have read this article and checked the sample code in this way:

Code: Pascal  [Select]
  1. var
  2.   BitmapImage: TFPMemoryImage;
  3.   BitmapCanvas: TFPImageCanvas;
  4.   BitmapWriter: TFPWriterBMP;
  5. begin
  6.   BitmapImage := TFPMemoryImage.Create(100, 100);
  7.   BitmapCanvas := TFPImageCanvas.Create(BitmapImage);
  8.   BitmapWriter := TFPWriterBMP.Create();
  9.  
  10.   BitmapCanvas.Brush.FPColor := FPColor(255, 0, 255);
  11.   BitmapCanvas.FillRect(BitmapCanvas.ClipRect);
  12.  
  13.   BitmapCanvas.Brush.FPColor := FPColor(0, 0, 0);
  14.   BitmapCanvas.FillRect(20, 20, 50, 50);
  15.  
  16.   BitmapImage.SaveToFile('standard.bmp', BitmapWriter);
  17.  
  18.   BitmapCanvas.Free();
  19.   BitmapImage.Free();
  20.   BitmapWriter.Free();
  21. end;

As a result of this code, the whole bitmap is black. If I set the number of bits per pixel in the writer:

Code: Pascal  [Select]
  1. BitmapWriter.BitsPerPixel := 4;

I get an exception in the following line:

Code: Pascal  [Select]
  1. // FPImageException: Image palette is too big or absent
  2. BitmapImage.SaveToFile('standard.bmp', BitmapWriter);

Why is the target bitmap whole black?
« Last Edit: February 28, 2019, 08:05:39 pm by furious programming »
Lazarus 2.0.2 with FPC 3.0.4, Windows XP (all 32-bit)

furious programming

  • Sr. Member
  • ****
  • Posts: 328
  • I click a little.
Re: How to export bitmap to 4-bit (16 colors) file?
« Reply #5 on: February 28, 2019, 07:57:12 pm »
Okay, I managed to avoid this exception, set the UsePalette property to True. The whole example below:

Code: Pascal  [Select]
  1. var
  2.   BitmapImage: TFPMemoryImage;
  3.   BitmapCanvas: TFPImageCanvas;
  4.   BitmapWriter: TFPWriterBMP;
  5. begin
  6.   BitmapImage := TFPMemoryImage.Create(100, 100);
  7.   BitmapImage.UsePalette := True;
  8.  
  9.   BitmapCanvas := TFPImageCanvas.Create(BitmapImage);
  10.  
  11.   BitmapWriter := TFPWriterBMP.Create();
  12.   BitmapWriter.BitsPerPixel := 4;
  13.  
  14.   BitmapCanvas.Brush.FPColor := FPColor(255, 0, 255);
  15.   BitmapCanvas.FillRect(BitmapCanvas.ClipRect);
  16.  
  17.   BitmapCanvas.Brush.FPColor := FPColor(0, 0, 0);
  18.   BitmapCanvas.FillRect(20, 20, 50, 50);
  19.  
  20.   BitmapImage.SaveToFile('standard.bmp', BitmapWriter);
  21.  
  22.   BitmapCanvas.Free();
  23.   BitmapImage.Free();
  24.   BitmapWriter.Free();
  25. end;

The target bitmap has a smaller size (it is actually 4-bit), but it is still whole black.
« Last Edit: February 28, 2019, 08:13:16 pm by furious programming »
Lazarus 2.0.2 with FPC 3.0.4, Windows XP (all 32-bit)

furious programming

  • Sr. Member
  • ****
  • Posts: 328
  • I click a little.
Re: How to export bitmap to 4-bit (16 colors) file?
« Reply #6 on: February 28, 2019, 08:43:49 pm »
Okay, I did it — huge thanks to @Handoko for guiding me.


I checked the image class code carefully. First, set UsePalette to True so that the internal palette object is created in the image class:

Code: Pascal  [Select]
  1. BitmapImage := TFPMemoryImage.Create(100, 100);
  2. BitmapImage.UsePalette := True;

Then fill the palette with used colors (and remember that the color components are 16-bit wide!):

Code: Pascal  [Select]
  1. BitmapImage.Palette.Color[0] := FPColor($FFFF, $0000, $FFFF); // fuchsia
  2. BitmapImage.Palette.Color[1] := FPColor($0000, $0000, $0000); // black

While painting, we can pick colors directly from the palette:

Code: Pascal  [Select]
  1. BitmapCanvas.Brush.FPColor := BitmapImage.Palette.Color[0];
  2. BitmapCanvas.FillRect(BitmapCanvas.ClipRect);
  3.  
  4. BitmapCanvas.Brush.FPColor := BitmapImage.Palette.Color[1];
  5. BitmapCanvas.FillRect(20, 20, 50, 50);

The full example below:

Code: Pascal  [Select]
  1. var
  2.   BitmapImage: TFPMemoryImage;
  3.   BitmapCanvas: TFPImageCanvas;
  4.   BitmapWriter: TFPWriterBMP;
  5. begin
  6.   BitmapImage := TFPMemoryImage.Create(100, 100);
  7.   BitmapImage.UsePalette := True;
  8.  
  9.   BitmapImage.Palette.Color[0] := FPColor($FFFF, $0000, $FFFF);
  10.   BitmapImage.Palette.Color[1] := FPColor($0000, $0000, $0000);
  11.  
  12.   BitmapCanvas := TFPImageCanvas.Create(BitmapImage);
  13.  
  14.   BitmapCanvas.Brush.FPColor := BitmapImage.Palette.Color[0];
  15.   BitmapCanvas.FillRect(BitmapCanvas.ClipRect);
  16.  
  17.   BitmapCanvas.Brush.FPColor := BitmapImage.Palette.Color[1];
  18.   BitmapCanvas.FillRect(20, 20, 50, 50);
  19.  
  20.   BitmapWriter := TFPWriterBMP.Create();
  21.   BitmapWriter.BitsPerPixel := 4;
  22.  
  23.   BitmapImage.SaveToFile('standard.bmp', BitmapWriter);
  24.  
  25.   BitmapCanvas.Free();
  26.   BitmapImage.Free();
  27.   BitmapWriter.Free();
  28. end;

In addition, if the bitmap has large, one-color areas (as in my case), it is worth using RLE compression (the file size will be much smaller):

Code: Pascal  [Select]
  1. BitmapWriter := TFPWriterBMP.Create();
  2. BitmapWriter.BitsPerPixel := 4;
  3. BitmapWriter.RLECompress := True;

I add sample files to attachments.
« Last Edit: February 28, 2019, 11:07:00 pm by furious programming »
Lazarus 2.0.2 with FPC 3.0.4, Windows XP (all 32-bit)