[SOLVED] RLE help lazarus / 39-Graphics ---------------------------------------------------------------------- Espectr0 · 2024-04-11 10:41 Hola, In my project I need to load/save a compressed image using RLE. 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 // The Run- length encoding method is defined in the US 7912305 B1 patent.// Here’s a quick and dirty definition to this method://// Code                                 Meaning// CCCCCCCC                             One pixel in color C// 00000000 00LLLLLL                      L pixels in color 0 (L between 1 and 63)// 00000000 01LLLLLL LLLLLLLL           L pixels in color 0 (L between 64 and 16383)// 00000000 10LLLLLL CCCCCCCC             L pixels in color C (L between 3 and 63)// 00000000 11LLLLLL LLLLLLLL CCCCCCCC    L pixels in color C (L between 64 and 16383)// 00000000 00000000                      End of line  To Encode: 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 function EncodeImage(const AImage: TBGRABitmap; out ABuffer: TBytes; out APalette: TFPPalette): Integer;var  bmp : TBGRABitmap;  quant : TBGRAColorQuantizer;  x, y, i, len : Integer;  p, r : PBGRAPixel;  bytes : TBytesStream;  clr : Integer;begin  // Reduce image  bmp := TBGRABitmap.Create(AImage);  quant := TBGRAColorQuantizer.Create(bmp, acFullChannelInPalette, 256); // reduce colors  try    quant.ApplyDitheringInplace(daNearestNeighbor, bmp);    bmp.UsePalette := True;    APalette := TFPPalette.Create(quant.ReducedPalette.Count);    bmp.Palette.Count := APalette.Count;    for i := 0 to quant.ReducedPalette.Count-1 do // copy reduced colors to palette    begin      bmp.Palette[i] := (quant.ReducedPalette.Color[i].ToFPColor);      APalette.Color[i] := bmp.Palette[i];    end;     // RLE compress image    bytes := TBytesStream.Create;    try      for y := 0 to bmp.Height-1 do      begin        p := bmp.Scanline[y];        x := 0;        while x < bmp.Width do        begin          i := quant.ReducedPalette.IndexOfColor(p[x]);          if i >= 0 then          clr := i          else            clr := quant.ReducedPalette.FindNearestColorIndex(p[x]);           r := bmp.Scanline[y];          len := 1;          while (x + len < bmp.Width) and (len < $3FFF) do          begin            if r[x + len] <> p[x] then Break;            Inc(len);          end;           if (len <= 2) and (clr <> 0) then // One pixel in color C          begin            bytes.WriteByte(clr);            if len = 2 then bytes.WriteByte(clr);          end          else          begin          // rle id            bytes.WriteByte(0);             if (clr = 0) and (len < $40) then // L pixels in color 0 (L between 1 and 63)            bytes.WriteByte(len)            else if (clr = 0) then  // L pixels in color 0 (L between 64 and 16383)            begin            bytes.WriteByte($40 or (len shr 8));              bytes.WriteByte(len);            end            else if len < $40 then // L pixels in color C (L between 3 and 63)            begin            bytes.WriteByte($80 or len);              bytes.WriteByte(clr);          end            else // L pixels in color C (L between 64 and 16383)            begin              bytes.WriteByte($C0 or (len shr 8));              bytes.WriteByte(len);              bytes.WriteByte(clr);            end;          end;          Inc(x, len);        end;        // end rle id        bytes.WriteByte(0);      bytes.WriteByte(0);      end;    finally      Result := bytes.Size;      SetLength(ABuffer, Result);      Move(bytes.Bytes[0], ABuffer[0], Result);      bytes.Free;    end;  finally    quant.Free;    bmp.Free;  end;end;  To Decode: 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 function DecodeImage(const ABuffer: TBytes; const APalette: TFPPalette; const AWidth, AHeight: Integer): TBGRABitmap;var  bmp : TBGRABitmap;  x, y, idx, i : Integer;  b, len : Byte;  clr : TBGRAPixel;begin  bmp := TBGRABitmap.Create(AWidth, AHeight);  idx := 0;  for y := 0 to bmp.Height-1 do  begin    x := 0;    while x < bmp.Width do    begin    b := ABuffer[idx] and $FF;      Inc(idx);       if (b = 0) and (idx < Length(ABuffer)) then // rle id      begin        b := ABuffer[idx] and $FF;        Inc(idx);         if b = 0 then // next line        begin          Inc(idx);          Break;        end        else if (b and $C0) = $40 then // L pixels in color 0 (L between 1 and 63)      begin          if (idx < Length(ABuffer)) then          begin          len := ((b - $40) shl 8) or (ABuffer[idx] and $FF);            Inc(idx);            clr := FPColorToBGRA(APalette.Color[0]);          for i := 1 to len do            begin              bmp.Scanline[y][x] := clr;              Inc(x);            end;        end;        end        else if (b and $C0) = $80 then // L pixels in color C (L between 64 and 16383)        begin          if (idx < Length(ABuffer)) then          begin            len := (b - $80);          b := ABuffer[idx] and $FF;            Inc(idx);            clr := FPColorToBGRA(APalette.Color[b]);            for i := 1 to len do          begin              bmp.Scanline[y][x] := clr;              Inc(x);            end;          end;        end        else if (b and $C0) <> 0 then // L pixels in color C (L between 3 and 63)        begin          if (idx < Length(ABuffer)) then          begin          len := ((b - $C0) shl 8) or (ABuffer[idx] and $FF);            Inc(idx);            b := ABuffer[idx] and $FF;            Inc(idx);          clr := FPColorToBGRA(APalette.Color[b]);            for i := 1 to len do            begin              bmp.Scanline[y][x] := clr;            Inc(x);            end;          end;        end        else // L pixels in color 0 (L between 64 and 16383)        begin        clr := FPColorToBGRA(APalette.Color[0]);          for i := 1 to b do          begin            bmp.Scanline[y][x] := clr;            Inc(x);          end;        end;      end      else // One pixel in color C      begin        bmp.Scanline[y][x] := FPColorToBGRA(APalette.Color[b]);        Inc(x);      end;    end;  end;   Result := bmp;end;  But I have problems loading the compressed image, I am attaching the original and the restored one. Can you guide me what is the fault? Gracias!! ---------------------------------------------------------------------- speter · 2024-04-12 02:05 I have a program which reads Utah RLE images. I am attaching a Zipped RLE image (the forum doesn't allow .RLE files) and a PNG version. Is this the same format as you are using? cheers S. ---------------------------------------------------------------------- Leledumbo · 2024-04-12 10:14 Since you're too lazy to write a MRE, I'll be too lazy to properly test, too. From what I can read, since the decoded image looks similar, it's not the (de)compression problem (if it's wrong, it would be garbage instead of looking similar). You intentionally reduce the colors by using a TBGRAColorQuantizer. You have two ways to verify: Instead of using your decoder, use an existing image viewer capable of reading RLE compressed bitmapsRemove the quantization part and solely compress the image ---------------------------------------------------------------------- circular · 2024-04-12 12:21 I don't see any obvious problem with the code. The output seems to have only 2 colors. I suggest to write the details to the console when a non black pixel is written, to see where this may happen. ---------------------------------------------------------------------- Espectr0 · 2024-04-27 13:53 I made some changes and uploaded a demo to decode the image, one of the problems I have is the transparency of the image, the black background should be transparent. Any help is welcome https://github.com/URUWorks/UW_BlurayPGSParser ---------------------------------------------------------------------- TRon · 2024-04-28 05:47 Quote from: Espectr0 on April 27, 2024, 03:53:57 pmI made some changes and uploaded a demo to decode the image, one of the problems I have is the transparency of the image, the black background should be transparent. Assuming that the original image you posted (original.bmp) is the result of your decoding then there does not seem to be an issue (see attached picture). In case there is an issue then your issue does not seem related to the RLE decoding rather how the pixels are currently set in BRGABitmap and/or how you have setup your image. The picture shows a image placed on a panel (which background was set to white) that gets loaded (original.bmp) when pressing the button (using the same bitmap assignment as your code). BTW: it is nice you have setup a demo project but... I do not have any blurays anymore (let alone a reader) so can't test. Do you happen to have a (portion) of a sup file that you can share for testing purpose (perhaps a single decoded image will do as well, if only for comparison to the original bmp in case that bmp was not a result of your decoding) ? ---------------------------------------------------------------------- Thaddy · 2024-04-28 09:10 I once wrote an RLE implementation in TurboBASIC/PowerBASIC. If I can find it, it is probably easy to translate to Pascal. ---------------------------------------------------------------------- Espectr0 · 2024-04-28 11:42 The "original.bmp" file was generated with BGRABitmap, I encoded that file with RLE and then decoded it into the "restored.bmp" file but it doesn't look exactly the same, could it be my mistake with the palette? About the demo project, in the same folder is the "test.sup" (big) and/or "test1ds.sup" (small) file to test it. I couldn't leave the background transparent when decoding :S PS: I attached the png of the "test1ds.sup" resulting from other software, so the encoding is fine? ---------------------------------------------------------------------- TRon · 2024-04-28 15:28 Quote from: Espectr0 on April 28, 2024, 01:42:46 pmThe "original.bmp" file was generated with BGRABitmap, I encoded that file with RLE and then decoded it into the "restored.bmp" file but it doesn't look exactly the same, could it be my mistake with the palette? I have to admit that i do not know how BGRABitmap and palettes work icw transparency. I would have to figure that out (so will take me some time). QuoteAbout the demo project, in the same folder is the "test.sup" (big) and/or "test1ds.sup" (small) file to test it. Thank you for that as that allows me to tinker with your code. QuotePS: I attached the png of the "test1ds.sup" resulting from other software, so the encoding is fine? Yes, the encoding seems more than adequate. It is the 'drawing' that does not seem to go as planned/intended. Though having said that, I am not familiar with PGR so perhaps there is more to it than you already did with your code. My first thought is that it is (still) the 'drawing' that goes wrong. ---------------------------------------------------------------------- circular · 2024-04-28 19:53 Most image readers don't support an alpha channel for BMP files. If you want to keep the transparency, better to use a format that has solid alpha support. ---------------------------------------------------------------------- TRon · 2024-04-29 05:58 Quote from: circular on April 28, 2024, 09:53:40 pmMost image readers don't support an alpha channel for BMP files. If you want to keep the transparency, better to use a format that has solid alpha support. Yes, thank you for the reminder as I am/was aware. The situation is: - palette colors and image data is stored raw in a stream - palette colors are stored in YCbCr format - (indexed) pixel data is stored as RLE encoded The process that TS follows is: - read palette colors, convert from YCbCr to FPColor and store into an array (using custom function YCbCr2FPColor) - 'retrieve' image from raw (RLE encoded) data-stream but in that process the color array gets converted into a TFPPalette first - then read raw RLE encoded data into a temp buffer - RLE decode temp buffer, using the palette and use decoded bytes as index to the palette. - in that process the color of the pixel (TBGRAPixel) is retrieved (using the colorindex to the the palette's index) with TBGRAPixel's function FromFPColor - that 'color' is then used to set the corresponding pixel to that color. That all seems to work as intended. However, the code now uses the following assignment: 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   { dummy for better understanding } bmp := retrieveBGRABitmapFromRawData  image1.Picture.Assign(bmp.Bitmap); Where image1 is a TImage component placed on the form and bmp is a TBGRABitmap. The TImage picture stays (visible) empty. When I replace that with: 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   { dummy for better understanding } bmp := retrieveBGRABitmapFromRawData  bmp.draw (Image1.canvas,0,0, true); The picture becomes visible in the TImage component. afaik that would indicate that something did go wrong with the alpha channel but I haven't been able to figure out yet what exactly. PS: attached is a BGRABitmap saved as BMP that the (original) code produces. ---------------------------------------------------------------------- circular · 2024-04-29 07:33 Ah, the last parameter of Draw here is True, specifying opaque drawing. ---------------------------------------------------------------------- TRon · 2024-04-29 08:42 Thank you @circular. I finally managed to locate the culprit... @Espectr0: At least you got an error inside 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 function YCbCr2FPColor(Y, Cb, Cr, A: Byte): TFPColor;begin  Result := TColorToFPColor(YCbCr2RGB(Y, Cb, Cr));  Result.Alpha := A;end; Do you note that the alpha of the TFPColor result is set to the alpha value that was passed to the function ? The original value is a 8 bit ranged value while TPFColor makes use of 16-bit ranged values so your code would have to compensate for that. When corrected: 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 function YCbCr2FPColor(Y, Cb, Cr, A: Byte): TFPColor;begin  Result := TColorToFPColor(YCbCr2RGB(Y, Cb, Cr));  Result.Alpha := A * $101;  // <-- scale up the byte value to 16-bit word value.end;  Then the image displays correctly (e.g. at least better, as it looks like there is still something wrong with semi-transparent pixels but have no idea how/why/what) . PS: The attached picture has a white background because I changed the panel's color to clWhite (I forgot to change that before taking a picture). ---------------------------------------------------------------------- Espectr0 · 2024-04-29 10:55 Thank you @TRon. It works correctly now, the problem was the alpha value