Bitmap 16bit R5G6B5 lazarus / 39-Graphics ---------------------------------------------------------------------- JZS · 2024-09-15 10:15 Hi all, How to convert any bitmap image to 16bit R5 G6 B5 please? ---------------------------------------------------------------------- Thaddy · 2024-09-15 12:12 for rgb32Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function RGB32ToRGB565(R, G, B: Byte): Word;begin  Result := ((R and $F8) shl 8) or ((G and $FC) shl 3) or (B shr 3);end;Other conversions are similar. This is a per pixel conversion. ---------------------------------------------------------------------- JZS · 2024-09-15 14:07 Quote from: Thaddy on September 15, 2024, 02:12:00 pmOther conversions are similar. This is a per pixel conversion. Thank you Thaddy. So there is no general rule to apply to any PixelFormat (16, 24, 32) to generate the 16bit 565? Or is it more complex than I thought? For simple example can I apply this to Canvas.Pixels[x,y]? something like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   dst_bitmap.width:= src_bitmap.width;  dst_bitmap.height:= src.bitmap.height;  dst_bitmap.PixelFormat := pf16bit;    for i:= 0 to src_bitmap.Width-1 do     for j:= 0 to src_bitmap.Height-1 do                                 //sure there might be a better way         dst_bitmap.Canvas.Pixels[i, j]:= RGB32ToRGB565(GetRValue(src.Canvas.Pixels[i, j]),GetGValue(src.Canvas.Pixels[i, j]),GetGValue(src.Canvas.Pixels[i, j])); Sorry I haven't got much knowledge about the conversion between the Bitmap types programmatically. Although I have spent many hours today but it seems like there is no documentation or even clear examples to show the method of accomplishing such task. ---------------------------------------------------------------------- Thaddy · 2024-09-15 15:50 Yes, you can. But you maybe misunderstood this bit: The 32 and 24 bit format have full 8 bit depth per color. What I did is adjust and scaled  so that the colors are weigted as 5 bits red, 6 bits green and 5 bits blue for a total of 16 bits instead of 24/32 bit, ignoring the transparancy bit (which can't be stored in a 16 bit color) ---------------------------------------------------------------------- JZS · 2024-09-15 16:25 What am I missing in the above example to produce 16bit 565? ---------------------------------------------------------------------- Thaddy · 2024-09-15 16:32 Maybe how the scaling works, but I guess I was over cautious. ---------------------------------------------------------------------- domasz · 2024-09-15 21:49 Perhaps pf16bit doesn't work in Lazarus. It has problems with some modes. If you just want to save to 16bit BMP you can do something like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var Buffer: array of Word;beginBufSize := src_bitmap.Width*src_bitmap.Height;SetLength(Buffer, BufSize);  k := 0; //here you need to put 54 bytes of BMP header data//http://www.ue.e ti.pg.gda.pl/fpgalab/zadania.spartan3/zad_vga_struktura_pliku_bmp_en.h tml for i:= 0 to src_bitmap.Width-1 do     for j:= 0 to src_bitmap.Height-1 do begin                                  //sure there might be a better way         Buffer[k] := RGB32ToRGB565(GetRValue(src.Canvas.Pixels[i, j]),GetGValue(src.Canvas.Pixels[i, j]),GetGValue(src.Canvas.Pixels[i, j]));         Inc(k);   end; F := TFileStream.Create('res.bmp', fmCreate);F.Write(Buffer[0], BufSize);F.Free; ---------------------------------------------------------------------- circular · 2024-09-16 08:12 Quote from: JZS on September 15, 2024, 06:25:18 pmWhat am I missing in the above example to produce 16bit 565? pf16bit means that the internal storage is supposed to be 16 bit (but in practice that may not be the case). The pixels properties are either TFPColor or an index to the palette. So it is not possible to assign the 16 bit encoding. This could be done though with a TLazIntfImage with proper initialization. Or by having the image writing quantize the RGB channels. With a 32bit image, which is the default with TBitmap, you can simulate 16 bit by reducing the number of colors you're using and avoiding antialiasing. For this, you would quantize the RGB channels. The generic way for this would be as follows: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i maxEncodedRed := 1 shl bitDepth - 1; // with bitDepth = 5, maxEncodedRed = 31encodedRed := (red16bit * maxEncodedRed + 32767) div 65535;quantizedRed := (encodedRed * 65535 + (maxEncodedRed shr 1)) div maxEncodedRed; Which route would you rather take? Regards ---------------------------------------------------------------------- JZS · 2024-09-16 09:47 Thank you Circular. I have no idea how to quantize the RGB channels or educing the number of colors I do not know much about this subject and was hoping to find a working example that can simplify the concept, but now it seems like I have to go back to college Can this be done with BGRABitmap in less complexity? ---------------------------------------------------------------------- Thaddy · 2024-09-16 10:40 Circular's formula is about the same as mine, but uses more expensive operations: divide and multiply. This depends on cpu, though, but usually the boolean operations are faster. ---------------------------------------------------------------------- circular · 2024-09-16 16:27 Quote from: Thaddy on September 16, 2024, 12:40:00 pmCircular's formula is about the same as mine, but uses more expensive operations: divide and multiply. This depends on cpu, though, but usually the boolean operations are faster. Yes, I was giving the general way, that would work as well for another format. Quote from: JZS on September 16, 2024, 11:47:17 amI have no idea how to quantize the RGB channels or educing the number of colors I do not know much about this subject and was hoping to find a working example that can simplify the concept, but now it seems like I have to go back to college Well, it depends a bit what your objectives are. If you just want to write a BMP file, then you can simply initialize the BMP writer to use 16 bits per pixel. That would be the BitsPerPixel property of TFPWriterBMP of FPWriteBMP unit. You don't need BGRABitmap for that. The image writer can be supplied as a parameter to the SaveToFile method. ---------------------------------------------------------------------- jamie · 2024-09-16 21:22 why does this need to be so difficult? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode Delphi}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls; type  T16BitColor = bitPacked Record    R:0..4;    G:5..10;    B:11..15;  end;   { TForm1 }   TForm1 = class(TForm)    Button1: TButton;    procedure Button1Click(Sender: TObject);  private   public   end; var  Form1: TForm1;implementation {$R *.lfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject);Var  A:WORD; //Simulate a 16bit color  C:TCOlor;begin  A := RanDom(65535);  With T16BitColor(A) do  Begin  Caption := Byte(R).ToString+','+   Byte(G).ToString+','+   Byte(B).ToString;   C:= RGBToCOlor(R,g,b); //Do this also while we are in here.  end;end; end.    Use the Bit masking to convert a 16 bit over to a rgb. etc. P.S>  The R and B maybe backwards. Btw, I forgot to shift upwards the R,G,B Values, but you can figure that out. ---------------------------------------------------------------------- wp · 2024-09-16 22:09 Try this, using two TLazIntfImages to explicitely convert pixel format: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses  FPWriteBMP, IntfGraphics, GraphType; procedure SaveBitmapToStream16bpp(ABitmap: TBitmap; AStream: TStream);var  srcImg, destImg: TLazIntfImage;  rawImg: TRawImage;  writer: TFPWriterBMP;  x, y: Integer;begin  // Convert source bitmap to a TLazIntfImage  srcImg := ABitmap.CreateIntfImage;   // Prepare destination image for 16bpp  destImg := TLazIntfImage.Create(0, 0);  try    rawImg.Init;    rawImg.Description.Init_BPP16_R5G6B5(srcImg.Width, srcImg.Height);    rawImg.CreateData(false);    destImg.SetRawImage(rawImg);     // Copy pixels from source to destination image (and convert them to 16bpp automatically)    for y := 0 to destImg.Height-1 do      for x := 0 to destImg.Width-1 do        destImg.Colors[x, y] := srcImg.Colors[x, y];     //Save destination image to stream    writer := TFPWriterBMP.Create;    try      writer.BitsPerPixel := 16;   // important!      destImg.SaveToStream(AStream, writer);    finally    writer.Free;    end;  finally    destImg.Free;    srcImg.Free;  end;end; procedure SaveBitmapToFile16bpp(ABitmap: TBitmap; AFileName: String);var  stream: TFileStream;begin  stream := TFileStream.Create(AFileName, fmCreate);  try    SaveBitmapToStream16bpp(ABitmap, stream);  finally    stream.Free;  end;end; { TForm1, contains a button and two images } procedure TForm1.Button1Click(Sender: TObject);begin  Image1.Picture.LoadFromFile(SRC_FILENAME);  SaveBitmapToFile16bpp(Image1.Picture.Bitmap, DEST_FILENAME);  Image2.Picture.LoadFromFile(DEST_FILENAME);end; Or this, using implicit conversion by setting the destination bitmap's pixelformat and drawing the original bitmap onto its canvas (still a LazIntfImage is needed for saving the 16bpp bitmap): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure SaveBitmapToStream16bpp(ABitmap: TBitmap; AStream: TStream);var  bmp: TBitmap;  img: TLazIntfImage;  writer: TFPWriterBMP;begin  bmp := TBitmap.Create;  try    // Convert source bitmap to 16bpp    bmp.PixelFormat := pf16bit;    bmp.SetSize(ABitmap.Width, ABitmap.Height);    bmp.Canvas.Draw(0, 0, ABitmap);     // Save the destination bitmap to stream, retaining the 16bpp    img := bmp.CreateIntfImage;    try      writer := TFPWriterBMP.Create;      try        writer.BitsPerPixel := 16;  // Important        img.SaveToStream(AStream, writer);      finally        writer.Free;    end;    finally      img.Free;    end;  finally    bmp.Free;  end;end; ---------------------------------------------------------------------- JZS · 2024-09-17 06:38 Quote from: circular on September 16, 2024, 06:27:07 pmQuote from: Thaddy on September 16, 2024, 12:40:00 pmCircular's formula is about the same as mine, but uses more expensive operations: divide and multiply. This depends on cpu, though, but usually the boolean operations are faster. Yes, I was giving the general way, that would work as well for another format. Quote from: JZS on September 16, 2024, 11:47:17 amI have no idea how to quantize the RGB channels or educing the number of colors I do not know much about this subject and was hoping to find a working example that can simplify the concept, but now it seems like I have to go back to college Well, it depends a bit what your objectives are. If you just want to write a BMP file, then you can simply initialize the BMP writer to use 16 bits per pixel. That would be the BitsPerPixel property of TFPWriterBMP of FPWriteBMP unit. You don't need BGRABitmap for that. The image writer can be supplied as a parameter to the SaveToFile method. This is what I tried but I do not get correct result: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure RGB565(const ASrc, ADest: String);var  img: TFPMemoryImage;  reader: TFPCustomImageReader;  writer: TFPWriterBMP;  i, j: Integer;  clr: TFPColor;begin  img := TFPMemoryImage.Create(0, 0);  try    reader := TFPReaderbmp.Create;    try      img.LoadFromFile(ASrc, reader);    finally      reader.Free;    end;     for j := 0 to img.Height - 1 do      for i := 0 to img.Width - 1 do      begin        clr := img.Colors[i, j];        clr:= TColorToFPColor(((clr.Red and $F8) shl 8) or ((clr.Green and $FC) shl 3) or (clr.Blue shr 3));        img.Colors[i, j] := clr;      end;     writer := TFPWriterbmp.Create;    writer.BitsPerPixel:= 16;    try      img.SaveToFile(ADest, writer);    finally      writer.Free;    end;  finally    img.Free;  end;end; I haven't tried Jamie and WP code yet. Will do and come back again. ---------------------------------------------------------------------- circular · 2024-09-17 07:39 Hi JZS, We may have overcomplicated things here. You don't need to quantize yourself the values if you're using the image writer to do that. So in the code your propose, just keep the following: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure RGB565(const ASrc, ADest: String);var  img: TFPMemoryImage;  reader: TFPCustomImageReader;  writer: TFPWriterBMP;begin  img := TFPMemoryImage.Create(0, 0);  try    reader := TFPReaderbmp.Create;  try      img.LoadFromFile(ASrc, reader);    finally      reader.Free;    end;     writer := TFPWriterbmp.Create;    writer.BitsPerPixel:= 16;    try      img.SaveToFile(ADest, writer);    finally      writer.Free;    end;  finally    img.Free;  end;end; The manual quantization is only if you want to do something with the data as 16 bits. Regards ---------------------------------------------------------------------- wp · 2024-09-17 08:11 You're right. I thought I had checked this case. But I think I had used a TBitmap for the reading step: the following code still produces a 24bit bitmap: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button3Click(Sender: TObject);var  bmp: TBitmap;  img: TLazIntfImage;  writer: TFPWriterBMP;begin  bmp := TBitmap.Create;  try    bmp.LoadFromFile(SRC_FILENAME);    img := bmp.CreateIntfImage;    writer := TFPWriterBMP.Create;    try      img.SaveToFile(DEST_FILENAME);    finally      writer.Free;      img.Free;    end;  finally    bmp.Free;  end;end;Using a TFPCustomImage for the reading step, like in your code, fixes the issue. Just: what is happening that TBitmap does not work this way? ---------------------------------------------------------------------- JZS · 2024-09-17 16:42 Thank you Jamie, but I couldn't get any positive result using your code, most likely I did not use it correctly, but at least I tried, and this is what I did: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure RGB565JamieEx(const ASrc, ADest: String);Var  A:WORD; //Simulate a 16bit color   img: TFPMemoryImage;  reader: TFPCustomImageReader;  writer: TFPWriterBMP;  i, j: Integer;  clr: TFPColor;begin  img := TFPMemoryImage.Create(0, 0);  try    reader := TFPReaderbmp.Create;  try      img.LoadFromFile(ASrc, reader);    finally      reader.Free;    end;     for j := 0 to img.Height - 1 do      for i := 0 to img.Width - 1 do      begin        clr := img.Colors[i, j];        A := FPColorToTColor(clr);         With T16BitColor(A) do        Begin       Clr:= TColorToFPColor(RGBToCOlor(R,g,b));        end;        img.Colors[i, j] := clr;      end;     writer := TFPWriterbmp.Create;    //writer.BitsPerPixel:= 16; //makes no difference    try      img.SaveToFile(ADest, writer);    finally      writer.Free;    end;  finally    img.Free;  end;end; ---------------------------------------------------------------------- JZS · 2024-09-17 16:47 Thank you WP, your code gives identical result to Circular example. ---------------------------------------------------------------------- TRon · 2024-09-17 16:55 If i remember correctly you need to set the colorformat of your new customimage, then make sure the colors get scaled correctly, then write the bmp using the required format. I am very curious about circular's single step solution because to my knowledge things do not work that way (but do keep in mind that circular is better versed in these kind of topics). rgb565 is a special one and not your default 16-bit bmp image ---------------------------------------------------------------------- JZS · 2024-09-17 16:58 Quote from: circular on September 17, 2024, 09:39:37 amHi JZS, We may have overcomplicated things here. You don't need to quantize yourself the values if you're using the image writer to do that. So in the code your propose, just keep the following: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure RGB565(const ASrc, ADest: String);var  img: TFPMemoryImage;  reader: TFPCustomImageReader;  writer: TFPWriterBMP;begin  img := TFPMemoryImage.Create(0, 0);  try    reader := TFPReaderbmp.Create;  try      img.LoadFromFile(ASrc, reader);    finally      reader.Free;    end;     writer := TFPWriterbmp.Create;    writer.BitsPerPixel:= 16;    try      img.SaveToFile(ADest, writer);    finally      writer.Free;    end;  finally    img.Free;  end;end; The manual quantization is only if you want to do something with the data as 16 bits. Regards Thank you Circular, your code works but am not sure if the result is the same to 16bit 565 generated image. if I compare the generated 16bit 565 image from GIMP and the image produced with the above code the result is kinda different, if compared as bits and bytes, I might be wrong though, the outcome generally looks identical, it is only the content statistics that is different. Now I have a question about the types of 16bit that can be generated by GIMP are R5G6B5, A1R5G5B5 and X1R5G5B5. How does GIMP do it to manipulate the 16bit result image? ---------------------------------------------------------------------- circular · 2024-09-17 22:11 Hi JZS, Happy to hear it works.  I am not sure what is the "content statistics". Can you give me more details? Quote from: JZS on September 17, 2024, 06:58:02 pmNow I have a question about the types of 16bit that can be generated by GIMP are R5G6B5, A1R5G5B5 and X1R5G5B5. How does GIMP do it to manipulate the 16bit result image? I suppose X1R5G5B5 is 15 bits per pixel, where X1 indicate the padding. I am not sure what you would like to know about what GIMP does. Are you referring to the way the quantization is done? Or what happens when you edit the 16bit image? ---------------------------------------------------------------------- wp · 2024-09-17 23:16 Just uploaded to my github a small tool to analyze and display the headers of bmp files: https://github.com/wp-xyz/bmpExplorer/tree/main ---------------------------------------------------------------------- JZS · 2024-09-18 03:13 Quote from: circular on September 18, 2024, 12:11:53 amHi JZS, Happy to hear it works.  I am not sure what is the "content statistics". Can you give me more details? I did not use benchmark tool to tell the difference so I used dirty and quick bits count/location comparison. And there were few differences. Thanks WP for the rescue, if I use WP app "bmpExplorer" I can name few differences now, and if you like I am attaching the four bmp pictures (Parrots (Original), GIMP_16bit565, wp_16bit, and Circular_16bit) (BTW WP bitmap result image is identical with Circular result). The differences are as follows: GIMP: Horizontal resolution: 72 ppi Vertical resolution: 72 ppi Offset to image data: 138 ($  8A) Size of info header: 124 ($  7C) Horizontal resolution: 2835 px/m, 72 ppi Vertical resolution: 2835 px/m, 72 ppi WP & Circular: Horizontal resolution: 3 ppi Vertical resolution: 3 ppi Offset to image data: 66 ($  42) Size of info header: 40 ($  28) Horizontal resolution: 100 px/m, 3 ppi Vertical resolution: 100 px/m, 3 ppi Quote from: circular on September 18, 2024, 12:11:53 am I am not sure what you would like to know about what GIMP does. Are you referring to the way the quantization is done? Or what happens when you edit the 16bit image? I asked that question since it is only one flag to set (bpp=16) and we have the 16bit 565, so I wanted to know how about the other two variations (A1 555 & x1 555), I was only curious what alteration that makes the difference. But my original goal here is to be able to generate 16bit r5g6b5 bitmaps using Lazarus. I have a hardware device that only accepts 16bit565 bitmap, which I can not have access to test with the new result until next week, meanwhile you are stuck with me looking at the variations that different tools offer ---------------------------------------------------------------------- circular · 2024-09-18 09:29 Hi JZS, Ok, let's take a look at the differences between GIMP-16bit565 and Circular_16bit/wp_16bit565. First, looking at the file size, it is nearly identical, showing that the bits per pixel is roughly the same. So I presume your main objective here is reached. The header size is different, but that probably doesn't matter. There are indeed additional fields but they are mostly set to default. Regarding the resolution, it is an indication for printing in the header of the file. Support for resolution in TFPCustomImage was added in FPC 3.2.3, which at the time of writing this, is not yet published. You can either use trunk or use BGRABitmap for this. With resolution support, you can match GIMP if you like by setting the relevant properties before saving: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i bmp.ResolutionUnit := roPixelsPerInch;bmp.ResolutionWidth := 72;bmp.ResolutionHeight := 72;bmp.SaveToFile(...);  Looking at the pixel data, there are some minor differences. Looking at the first values, it seems GIMP values are sometimes offset by 1 for example $A2 instead of $A1. This is probably dues to rounding in the quantization. Indeed TFPWriteBMP and TLazIntfImage truncate values. If you would like the same quantization as GIMP, you need to quantize manually before saving. You can do a quantization with rounding like GIMP using the formula I've provided at the beginning of this thread: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var   c: TFPColor;  maxEncodedRed, encodedRed: longword;begin  ...  maxEncodedRed := 1 shl bitDepth - 1; // with bitDepth = 5, maxEncodedRed = 31  encodedRed := (c.red * maxEncodedRed + 32767) div 65535;  c.red := (encodedRed * 65535 + (maxEncodedRed shr 1)) div maxEncodedRed;You would need to adapt this for green and blue channels as well of course. ---------------------------------------------------------------------- wp · 2024-09-18 09:50 The TFPWriterBMP used to write bmp files to disk has a nasty annoyance: x and y resolution are initialized with the value 100, and it probably is assumed that these are pixels per inch (ppi). But in reality Microsoft had decided to interpret the X and YResolution as pixels per meter (ppm)! To convert between ppi and ppm you must apply a correction factor 0.0254: ppi = ppm * 0.0254 - the 100 ppm in the file header therefore condense to small 3 ppi. The writer, however, has properties XPelsPerMeter and YPelsPerMeter so that you can override the hard-coded 100. If you want 100 ppi (rather than ppm) then specify the value 100/0.054 = 3937 pixels per meter: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  writer: TFPWriterBMP;...  writer := TFPWriterBMP.Create;  try    writer.XPelsPerMeter := round(ppi / 0.0254);    writer.YPelsPerMeter := round(ppi / 0.0254);    writer.BitsPerPixel := 16;    Bitmap.SaveToFile(filename, writer);  finally    writer.Free;  end; QuoteI wanted to know how about the other two variations (A1 555 & x1 555)Looking at the FPWriterBMP source code it can be found that its BitsPerPixel here have the value 15 (rather than 16). I don't know what condition must be met so that the empty bit in A1 R5G5B5 / X1 R5G5B5 is interpreted as A=Alpha or is ignored (X). ---------------------------------------------------------------------- circular · 2024-09-19 11:24 Yes, wp is right, with current version of FPC 3.2.2 you would set the resolution at the level of the BMP image writer. That raises a question about backward compatibility. I have the impression that the changes in trunk are not compatible as the new writer uses the resolution set at the level of the image. I imagine that we can fix it: - by having an undefined state for the XPelsPerMeter / YPelsPerMeter values so that unless they are assigned, they will be overridden by the values of the image. - by ignoring the ResolutionX / ResolutionY values at the level of the image if they are the default (equal to zero). ---------------------------------------------------------------------- wp · 2024-09-19 14:54 Quote from: circular on September 19, 2024, 01:24:03 pmThat raises a question about backward compatibility. I have the impression that the changes in trunk are not compatible as the new writer uses the resolution set at the level of the image. Sorry I don't understand. The FPWriterBMP only allows to set a value for the image resolution, it was hardcoded before. I don't see an issue with backward compatibility, the default resolution did not change. The problem is that the default resolution value is measured in wrong units (ppi rather than ppm). A compatibility issue would arise if the default resolution value would be changed to the real ppm value. Quote from: circular on September 19, 2024, 01:24:03 pm- by ignoring the ResolutionX / ResolutionY values at the level of the image if they are the default (equal to zero). A bitmap can be used by any application, not just ours. Ignoring the resolution is not an option for software which calculates the image size from the resolution. And when the default resolution is zero they would even multiply the image size down to zero. -> no image displayed. And other software which draws the image into a frame of its own size, or draws a screen pixel for each image pixel, ignores the resolution value anyway. ---------------------------------------------------------------------- circular · 2024-09-20 06:43 Hi wp, I suppose there is a misunderstanding. I am talking about the change between FPC 3.2.2 and FPC 3.2.4, introducing ResolutionX and ResolutionY to TFPCustomImage: https://gitlab.com/freepascal.org/fpc/source/- /commit/987d87569a82b1f42a5fd94b0d4904cf72b2ec67#c166f82bfd4854f379e3a 4627adbde98b87f4718 The BMP writer was changed to use those fields and change XPelsPerMeter / YPelsPerMeter accordingly. Regards