2

I want to copy pixels from BMP1 to BMP2 but the copied image is gabbled. Why?

Note: The input image is pf8bit;

TYPE
  TPixArray = array[0..4095] of Byte;
  PPixArray = ^TPixArray;

procedure Tfrm1.CopyImage;
VAR
  BMP1, BMP2: TBitmap;
  y, x: Integer;
  LineI, LineO: PPixArray;
begin
 BMP1:= TBitmap.Create;
 BMP2:= TBitmap.Create;
 TRY
   BMP1.LoadFromFile('test.bmp');

   BMP2.SetSize(BMP1.Width, BMP1.Height);   
   BMP2.PixelFormat:= BMP1.PixelFormat;

   for y:= 0 to BMP1.Height -1 DO
    begin
     LineI := BMP1.ScanLine[y];
     LineO := BMP2.ScanLine[y];

     for x := 0 to BMP1.Width -1 DO
        LineO[x]:= LineI[x];
    end;

  //BMP2.SaveToFile('out.bmp');
  imgOut.Picture.Assign(BMP2); //TImage
 FINALLY
   FreeAndNil(BMP2);
   FreeAndNil(BMP1);
 END;
end;

For the saved image, a graphic editor says "Pixel depth/colors: indexed, 256 color palette".

Gabriel
  • 20,797
  • 27
  • 159
  • 293
  • 2
    What's your actual problem? Why insisting on copying scanlines and not using `BMP2.Assign()`? – AmigoJack Aug 12 '20 at 15:04
  • 1
    @AmigoJack - Easy: because I need to do some processing on the pixels :) So assign won't work :) – Gabriel Aug 12 '20 at 15:22
  • Next time add that hidden detail to your question so nobody unnecessarily answers with something that could have been avoided right away. Also be aware that this way a potential ICC profile in your source bitmap is not copied, too. – AmigoJack Aug 12 '20 at 15:38
  • @AmigoJack - If I clearly mentioned ScanLine in my code and also in headline it means that I need it that way. But this discussion is pointless anyway. Andreas ALREADY provided a solution! – Gabriel Aug 12 '20 at 16:23

1 Answers1

6

It might be worth pointing out that an 8-bit bitmap isn't necessarily greyscale.

Instead, it is a bitmap with a "colour table" consisting of up to 256 entries, and each pixel refers to an entry in this table. So if a pixel's value is 185, this means that it should use the colour at location 185 in the bitmap's "colour table". Hence, an 8-bit bitmap works entirely different compared to a 16-, 24- or 32-bit bitmap, which does not have a colour table, but instead has actual RGB(A) values at each pixel.

The problem in your case is likely that the target pixmap doesn't have the same colour table as the source bitmap.

I have actually never worked with 8-bit bitmaps and palettes before, but I think it is this simple:

var
  s, t: TBitmap;
  y: Integer;
  sp, tp: PByte;
  x: Integer;
begin

  s := TBitmap.Create;
  try
    s.LoadFromFile('C:\Users\Andreas Rejbrand\Desktop\bitmap.bmp');
    Assert(s.PixelFormat = pf8bit);

    t := TBitmap.Create;
    try

      t.PixelFormat := pf8bit;
      t.SetSize(s.Width, s.Height);
      t.Palette := s.Palette;        // <-- Let the new image have the same colour table

      for y := 0 to s.Height - 1 do
      begin
        sp := s.ScanLine[y];
        tp := t.ScanLine[y];
        for x := 0 to s.Width - 1 do
          tp[x] := sp[x];
      end;

      t.SaveToFile('C:\Users\Andreas Rejbrand\Desktop\bitmap2.bmp');

    finally
      t.Free;
    end;

  finally
    s.Free;
  end;
Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384