
https://forum.lazarus.freepascal.org/index.php?topic=44486.0

How to export bitmap to 4-bit (16 colors) file?

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:

    var
      Bitmap: TBitmap;
    begin
      Bitmap := TBitmap.Create();
      Bitmap.SetSize(100, 100);
     
      Bitmap.Canvas.Brush.Color := clFuchsia;
      Bitmap.Canvas.FillRect(Bitmap.Canvas.ClipRect);
     
      Bitmap.Canvas.Brush.Color := clBlack;
      Bitmap.Canvas.FillRect(20, 20, 50, 50);
    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.

* standard.bmp (29.35 kB, 100x100 - viewed 1212 times.)
* target.bmp (5.19 kB, 100x100 - viewed 1159 times.)

Bart
Re: How to export bitmap to 4-bit (16 colors) file?

Set PixelFormat to pf4bit, before doing the drawing?

Re: How to export bitmap to 4-bit (16 colors) file?

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.

Re: How to export bitmap to 4-bit (16 colors) file?

I haven't tried, but if you use TFPWriterBMP (in FPWriteBMP unit), it has BitsPerPixel property.

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

Re: How to export bitmap to 4-bit (16 colors) file?

Thanks for the suggestions. I have read this article and checked the sample code in this way:

    var
      BitmapImage: TFPMemoryImage;
      BitmapCanvas: TFPImageCanvas;
      BitmapWriter: TFPWriterBMP;
    begin
      BitmapImage := TFPMemoryImage.Create(100, 100);
      BitmapCanvas := TFPImageCanvas.Create(BitmapImage);
      BitmapWriter := TFPWriterBMP.Create();
     
      BitmapCanvas.Brush.FPColor := FPColor(255, 0, 255);
      BitmapCanvas.FillRect(BitmapCanvas.ClipRect);
     
      BitmapCanvas.Brush.FPColor := FPColor(0, 0, 0);
      BitmapCanvas.FillRect(20, 20, 50, 50);
     
      BitmapImage.SaveToFile('standard.bmp', BitmapWriter);
     
      BitmapCanvas.Free();
      BitmapImage.Free();
      BitmapWriter.Free();
    end;

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

    BitmapWriter.BitsPerPixel := 4;

I get an exception in the following line:

    // FPImageException: Image palette is too big or absent
    BitmapImage.SaveToFile('standard.bmp', BitmapWriter);

Why is the target bitmap whole black?

Re: How to export bitmap to 4-bit (16 colors) file?

Okay, I managed to avoid this exception, set the UsePalette property to True. The whole example below:

    var
      BitmapImage: TFPMemoryImage;
      BitmapCanvas: TFPImageCanvas;
      BitmapWriter: TFPWriterBMP;
    begin
      BitmapImage := TFPMemoryImage.Create(100, 100);
      BitmapImage.UsePalette := True;
     
      BitmapCanvas := TFPImageCanvas.Create(BitmapImage);
     
      BitmapWriter := TFPWriterBMP.Create();
      BitmapWriter.BitsPerPixel := 4;
     
      BitmapCanvas.Brush.FPColor := FPColor(255, 0, 255);
      BitmapCanvas.FillRect(BitmapCanvas.ClipRect);
     
      BitmapCanvas.Brush.FPColor := FPColor(0, 0, 0);
      BitmapCanvas.FillRect(20, 20, 50, 50);
     
      BitmapImage.SaveToFile('standard.bmp', BitmapWriter);
     
      BitmapCanvas.Free();
      BitmapImage.Free();
      BitmapWriter.Free();
    end;


The target bitmap has a smaller size (it is actually 4-bit), but it is still whole black.

Re: How to export bitmap to 4-bit (16 colors) file?

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:

    BitmapImage := TFPMemoryImage.Create(100, 100);
    BitmapImage.UsePalette := True;

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

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

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

    BitmapCanvas.Brush.FPColor := BitmapImage.Palette.Color[0];
    BitmapCanvas.FillRect(BitmapCanvas.ClipRect);
     
    BitmapCanvas.Brush.FPColor := BitmapImage.Palette.Color[1];
    BitmapCanvas.FillRect(20, 20, 50, 50);

The full example below:

    var
      BitmapImage: TFPMemoryImage;
      BitmapCanvas: TFPImageCanvas;
      BitmapWriter: TFPWriterBMP;
    begin
      BitmapImage := TFPMemoryImage.Create(100, 100);
      BitmapImage.UsePalette := True;
     
      BitmapImage.Palette.Color[0] := FPColor($FFFF, $0000, $FFFF);
      BitmapImage.Palette.Color[1] := FPColor($0000, $0000, $0000);
     
      BitmapCanvas := TFPImageCanvas.Create(BitmapImage);
     
      BitmapCanvas.Brush.FPColor := BitmapImage.Palette.Color[0];
      BitmapCanvas.FillRect(BitmapCanvas.ClipRect);
     
      BitmapCanvas.Brush.FPColor := BitmapImage.Palette.Color[1];
      BitmapCanvas.FillRect(20, 20, 50, 50);
     
      BitmapWriter := TFPWriterBMP.Create();
      BitmapWriter.BitsPerPixel := 4;
     
      BitmapImage.SaveToFile('standard.bmp', BitmapWriter);
     
      BitmapCanvas.Free();
      BitmapImage.Free();
      BitmapWriter.Free();
    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):

    BitmapWriter := TFPWriterBMP.Create();
    BitmapWriter.BitsPerPixel := 4;
    BitmapWriter.RLECompress := True;


I add sample files to attachments.

* standard.bmp (29.35 kB, 100x100 - viewed 1152 times.)
* 4-bit.bmp (5.14 kB, 100x100 - viewed 1176 times.)
* 4-bit compressed.bmp (0.57 kB, 100x100 - viewed 1124 times.)
