Setting TBitmap.PixelFormat is broken? lazarus / 39-Graphics ---------------------------------------------------------------------- domasz · 2022-11-30 12:05 Very simple code. Reading a 24bit BMP, converting to 32bit and 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 := TBitmap.Create;  Bmp.LoadFromStream(Str);  Bmp.PixelFormat := pf32bit;  Bmp.SaveToFile('hejka.bmp'); Saved image is still 24bpp. ---------------------------------------------------------------------- marcov · 2022-11-30 15:05 Setting the pixel format afaik doesn't convert. It just prepares the buffer for the next (write to the image) action to be 32-bit sized. ---------------------------------------------------------------------- domasz · 2022-11-30 15:08 So this needs to be changed because in Delphi this does convert and is used by a lot of graphic libraries and functions. ---------------------------------------------------------------------- theo · 2022-11-30 15:40 You could try this: 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 FPReadBMP, FPImage, FPWriteBMP; ....var  Image: TFPCustomImage;  Reader: TFPCustomImageReader;  Writer: TFPWriterBMP;begin  Image := TFPMemoryImage.Create(10, 10);  Reader := TFPReaderBMP.Create;  Writer := TFPWriterBMP.Create;  Image.LoadFromFile('test.bmp', Reader);  Writer.BitsPerPixel:=32;  Image.SaveToFile('test32.bmp', Writer);  Image.free;  Reader.free;  Writer.free;end; ---------------------------------------------------------------------- KodeZwerg · 2022-11-30 16:16 Hello theo, thank you for your snippet, I just tried it, partially I can transform BPP from-to but now I run into an error where the created file is 0 byte. Is there also a method to convert/adjust palette for destination BPP? My try was to convert from 32bpp to 8bpp. ---------------------------------------------------------------------- KodeZwerg · 2022-11-30 16:20 Quote from: marcov on November 30, 2022, 04:05:46 pmSetting the pixel format afaik doesn't convert. It just prepares the buffer for the next (write to the image) action to be 32-bit sized. I did try what you was writing, unsuccessful. I loaded 32bpp Bitmap and create a second TBitmap with 16bits PixelFormat, draw the source on and saved = 32bpp ---------------------------------------------------------------------- theo · 2022-11-30 16:30 Quote from: KodeZwerg on November 30, 2022, 05:16:05 pmMy try was to convert from 32bpp to 8bpp. For 8bpp you need a palette (Color Quantization). I don't know how to do this with fpImage. It seems to work with BGRABitmap though. See: https://forum.lazarus.freepascal.org/index.php/topic,61252.0.html ---------------------------------------------------------------------- KodeZwerg · 2022-12-01 09:24 Quote from: theo on November 30, 2022, 05:30:59 pmQuote from: KodeZwerg on November 30, 2022, 05:16:05 pmMy try was to convert from 32bpp to 8bpp. For 8bpp you need a palette (Color Quantization). I don't know how to do this with fpImage. It seems to work with BGRABitmap though. See: https://forum.lazarus.freepascal.org/index.php/topic,61252.0.html Thanks! That was what I needed for my little project to easy reduce/slim very big pictures! (yes, with loss of colors...) ---------------------------------------------------------------------- geraldholdsworth · 2023-02-01 11:36 I have noticed that with TBitmap, it will always report a PixelFormat of either pf24bit or pf32bit, despite what is actually in the bitmap. For example, on the project I am currently working on, it uses entirely 8 bit colour depth, and PixelFormat still tells me it is 24 bit. I have to resort to getting the actual bit depth from the bitmap header itself (which correctly says 8 ). I also have to resort to building bitmaps at low level to ensure I get 8 bit colour depth. ---------------------------------------------------------------------- TRon · 2023-02-01 11:40 Quote from: geraldholdsworth on February 01, 2023, 12:36:12 pmI have noticed that with TBitmap, it will always report a PixelFormat of either pf24bit or pf32bit, despite what is actually in the bitmap. For example, on the project I am currently working on, it uses entirely 8 bit colour depth, and PixelFormat still tells me it is 24 bit. I have to resort to getting the actual bit depth from the bitmap header itself (which correctly says 8 ). I also have to resort to building bitmaps at low level to ensure I get 8 bit colour depth. That is because TBitmap is not an independent bitmap but is related to your (desktop) environment. The underlying hardware determines the actual used pixeldepth for TBitmap. That is why you should never use it for such conversion purposes. ---------------------------------------------------------------------- geraldholdsworth · 2023-02-01 11:52 Quote from: TRon on February 01, 2023, 12:40:21 pmThat is because TBitmap is not an independent bitmap but is related to your (desktop) environment. The underlying hardware determines the actual used pixeldepth for TBitmap. That did cross my mind. Thank you for clarifying it. ---------------------------------------------------------------------- nanobit · 2023-02-01 14:10 TBitmap is supposed to support any standard PixelFormat, regardless of environments. It's just, that format conversion in the PixelFormat setter is not implemented. Another (current) limitation is the enforced use of 24bit for palettebased bitmaps, see comment in DefaultReaderDescription(), but this is also independent of environments. ---------------------------------------------------------------------- TRon · 2023-02-01 14:43 Quote from: nanobit on February 01, 2023, 03:10:55 pmTBitmap is supposed to support any standard PixelFormat, regardless of environments. It's just, that format conversion in the PixelFormat setter is not implemented. A common misconception, depending on what you actually meant by that. The handle of a bitmap is opaque and is so for a reason. The bitmap is internally stored in/as the best possible format of the used interface. Note that TBitmap class <> MS file stored bitmap. It just happens to be able to read/write them. ---------------------------------------------------------------------- nanobit · 2023-02-01 15:54 TBitmap has many uses, GUI is just one (the most common) of them. Pixelformat is a writable property (like in Delphi) and there is also this todo-comment in TCustomBitmap.SetPixelFormat(): "{$note todo copy image into new format }", for data conversion. ---------------------------------------------------------------------- geraldholdsworth · 2023-02-01 16:50 I just wrote my own to scale up/down a bitmap and convert it (if needed) to 8bpp. Works fine for my needs, but might be a bit slow for bigger images - it goes through every pixel in the bitmap (low level access, after copying the bitmap to a TMemoryStream) and finds it in the palette, or the closest colour to it that is in the palette. The palette I'm using is a generic one which covers most colours, rather than generate one of my own. The scaling up/down is just using TBitmap's StretchDraw method. Theo's method (above) should also work OK, although I haven't tried it yet. ---------------------------------------------------------------------- TRon · 2023-02-02 04:20 Quote from: nanobit on February 01, 2023, 04:54:41 pmTBitmap has many uses, GUI is just one (the most common) of them. GUI is /the only/ use for TBitmap class. You can't use the class without a widget back-end. QuotePixelformat is a writable property (like in Delphi) and there is also this todo-comment in TCustomBitmap.SetPixelFormat(): "{$note todo copy image into new format }", for data conversion. That comment has been there for over 2 decades now  (the original comment was written a bit different though, and in another file). Let's assume for a moment that you are right and that TBitmap must/should be able to convert graphical data from 64-bit to 2 bit (just to name an arbitrary example), then how would that be converted and more importantly how is that format internally stored  ? Do keep multi-platform support in mind. If you have found an answer to that then try and see what implementations are already available to do such conversions and have their own internal storage formats that are cross-platform compatible. Hint: Have a look at fcl-image and bgrabitmap. There is absolutely no need whatsoever to abuse the TBitmap class with even more confusing functionality. ---------------------------------------------------------------------- nanobit · 2023-02-02 10:00 Again, the writable TBitmap.PixelFormat is possible, and within the current design. TBitmap uses internally TRawImage (interface-independent) and the subsequent Handles (interface-dependent) support multiple formats too, including a fallback (Default) format if needed, within the range of TPixelFormat. TPixelFormat is Delphi compatible and declares only a small common set of formats. More support is not required by documentation. ---------------------------------------------------------------------- domasz · 2023-02-03 23:20 Quote from: TRon on February 02, 2023, 05:20:35 amLet's assume for a moment that you are right and that TBitmap must/should be able to convert graphical data from 64-bit to 2 bit (just to name an arbitrary example), [..] There is absolutely no need whatsoever to abuse the TBitmap class with even more confusing functionality. TBitmap comes from Delphi and in Delphi it's a super popular thing to do to convert internal bitness of TBitmap. Quite likely most graphic routines written in Delphi won't work correctly until this is fixed. And here's how it is used very often. It's simple and really fast: 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 P: PByteArray;      x,y: Integer;begin  Bmp := TBitmap.Create;  Bmp.LoadFromFile('test.bmp');  Bmp.PixelFormat := pf32bit; //now it doesn't matter if test.bmp was 24bpp, 8bitt, 1bpp or 32bpp. It is now 32bpp. Always, so we can safely do the below:   for y:=0 to Bmp.Height-1 do begin   P := Bmp.Scanline[y];    for x:=0 to Bmp.Width-1 do begin      P[4*x    ] := 255; //B     P[4*x+1] := 255; //G     P[4*x+2] := 255; //R     P[4*x+3] := 255; //A or X   end;  end;end; ---------------------------------------------------------------------- TRon · 2023-02-04 12:44 Quote from: domasz on February 04, 2023, 12:20:37 amTBitmap comes from Delphi Small correction: the class implementation by Delphi is an encapsulation around the MS/Windows bitmap. All functionality of that class originates from Windows. Quote...and in Delphi it's a super popular thing to do to convert internal bitness of TBitmap. Quite likely most graphic routines written in Delphi won't work correctly until this is fixed. And here's how it is used very often. It's simple and really fast: I am aware of how that works /for Delphi/ icw /windows/. But please do feel free to ignore the cross-platform and underlying hw compatibility because ... who cares ? The current Lazarus bitmap implementation is already an abomination because of these restrictions. It took only 20 years to get to this point so who knows ? Perhaps it will be implemented in another 2 weeks ? ---------------------------------------------------------------------- domasz · 2023-02-04 13:05 Quote from: TRon on February 04, 2023, 01:44:30 pmBut please do feel free to ignore the cross-platform and underlying hw compatibility because ... who cares ? The current Lazarus bitmap implementation is already an abomination because of these restrictions. It took only 20 years to get to this point so who knows ? Perhaps it will be implemented in another 2 weeks ?  Here's one way how to solve the problem- make TBitmap work just like in Delphi but with a notice it's platform-specific and if a programmer wants portability then should switch to TBGRABitmap or something else. I have been using Delphi since Delphi 3. I recently switched to Lazarus and this damn PixelFormat is the only thing I am not happy about. This clearly shows Lazarus is amazing. There's more problems I have with Delphi today (like Unicode and it's GUI and how slow it is). ---------------------------------------------------------------------- nanobit · 2023-02-04 13:34 Quote from: TRon on February 04, 2023, 01:44:30 pmIt took only 20 years to get to this point so who knows ? Perhaps it will be implemented in another 2 weeks ?  Many fields in FPC/Lazarus have only a single contributor or less, so decades of non-work don't say much about the difficulty yet. The implementation of TBitmap.PixelFormat is just unfinished. But indeed, there is no quick solution here, but it's possible with many incremental improvements under the umbrella of the current design. In SetPixelFormat(): Before converting to a new PixelFormat, the function needs to query if the destination format is possible. And only then convert (between TRawImage objects). The first enhancement (prerequisite) would be that widgetsets support more formats in: function TWidgetSet.rawImage_queryDescription(): boolean; AFAIK, all widgetsets could be extended to allow (1,4,8,16,24,32) for TRawImage. The first step would implement the most popular formats (nonpalette: 24, 32). Finally, also TRawImageQueryFlag would need to be extended by a few bitness values (default is used if bitness is unrequested or unsupported). As parttime WinApp developer I have no time/need/testsystems for this, but a more complete TBitmap would be useful for many.