Lazarus

Programming => Graphics => Graphics and Multimedia => BGRABitmap and LazPaint => Topic started by: Okoba on September 14, 2016, 12:42:07 am

Title: Make a transparent form with BGRABitmap
Post by: Okoba on September 14, 2016, 12:42:07 am
Hi,

I want to make a splash screen but I faced some issues that I write here (http://forum.lazarus.freepascal.org/index.php/topic,34034.0.html).

As you can see it can fairly be done with TPicture but not with TBGRAbitmap at least my test goes wrong.

I need to use BGRABitmap because I want to do some painting so can anyone help me with this?
Title: Re: Make a transparent form with BGRABitmap
Post by: RAW on September 14, 2016, 01:14:21 am
Don't know what you are doing wrong... or is it really W10 ??? :D
It's exactly the same with BGRABitmap.
I used TPicture, because BGRABitmap is normally overkill for such a task, but If you want to draw some more, then it's a good choice I think...
There is also opBitmap... but I don't know that one...

Are you sure the problem isn't elsewhere ???

Code: Pascal  [Select][+][-]
  1. Unit Unit1;
  2.  {$mode objfpc}{$H+}
  3.  
  4. Interface
  5.  USES
  6.   Windows, Classes, SysUtils, Forms, Controls, Graphics,
  7.   BGRABitmap;
  8.  
  9.  TYPE
  10.   TForm1 = Class(TForm)
  11.  
  12.     Procedure FormCreate (Sender: TObject);
  13.  
  14.    PRIVATE
  15.     bfBlend: TBlendFunction;
  16.     pPos   : TPoint;
  17.     sSize  : TSize;
  18.     dwStyle: DWORD;
  19.   End;
  20.  
  21.  VAR
  22.   Form1: TForm1;
  23.  
  24.   Function UpdateLayeredWindow (hwnd   : HWND;
  25.                                 hdcDst : HDC;
  26.                                 pptDst : PPoint;
  27.                                 psize  : PSize;
  28.                                 hdcSrc : HDC;
  29.                                 pptSrc : PPoint;
  30.                                 crKey  : TColor;
  31.                                 pblend : PBlendFunction;
  32.                                 dwFlags: DWORD): BOOL; StdCall; External 'user32';
  33.  
  34. Implementation
  35.  {$R *.lfm}
  36.  
  37.  
  38. Procedure TForm1.FormCreate(Sender: TObject);
  39.   Var
  40.    PNG: TBGRABitmap;
  41.  Begin
  42.   FormStyle  := fsStayOnTop;
  43.   Borderstyle:= bsNone;
  44.    Top := 150;
  45.    Left:=  30;
  46.  
  47.   PNG:= TBGRABitmap.Create;
  48.    Try
  49.     PNG.LoadFromFile('I:\LAZ.png'); // must be a premultiplied PNG
  50.  
  51.     dwStyle:= GetWindowLongA(Self.Handle, GWL_EXSTYLE);
  52.      If (dwStyle And WS_EX_LAYERED = 0)
  53.      Then SetWindowLong(Self.Handle, GWL_EXSTYLE, dwStyle Or
  54.                                      WS_EX_LAYERED Or
  55.                                      WS_EX_TOPMOST);
  56.  
  57.      pPos    := Point(0, 0);
  58.      sSize.cx:= PNG.Width;
  59.      sSize.cy:= PNG.Height;
  60.  
  61.      bfBlend.BlendOp            := AC_SRC_OVER;
  62.      bfBlend.BlendFlags         := 0;
  63.      bfBlend.SourceConstantAlpha:= 255;
  64.      bfBlend.AlphaFormat        := AC_SRC_ALPHA;
  65.  
  66.     UpdateLayeredWindow(Self.Handle, 0, Nil, @sSize,
  67.                         PNG.Bitmap.Canvas.Handle,
  68.                         @pPos, 0, @bfBlend, ULW_ALPHA);
  69.    Finally
  70.     PNG.Free;
  71.    End;
  72.  End;
  73.  
  74. End.
  75.  
Title: Re: Make a transparent form with BGRABitmap
Post by: RAW on September 14, 2016, 02:23:33 am
Hmmm.. strange... If I use the code mentioned above and set the IDE to RELEASE, then everything works as expected...
But when I change it to DEBUG I get an Error.

Can anybody confirm that ?
Maybe Circular is interested in this Bug/Error/Exception... whatever that might be...

RUN ERROR(215)
BGRAreadPNG
1132

Title: Re: Make a transparent form with BGRABitmap
Post by: circular on September 14, 2016, 09:57:19 am
That's surprising. Error 215 is arithmetic overflow, and the line 1132 contains the difference of two bytes stored in a signed result.

What if you change the line by adding NativeInt conversion?
Code: [Select]
dp := NativeInt(left) - NativeInt(diag);
Title: Re: Make a transparent form with BGRABitmap
Post by: Okoba on September 14, 2016, 10:36:52 am
Thanks SoE.
So I've got a new problem here and I think its blending problem, take a look at code and screenshot, I will work on it.
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.     Windows, Win32Extra, Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  9.     BGRABitmap,BGRABitmapTypes;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     procedure FormCreate(Sender: TObject);
  17.   private
  18.  
  19.   public
  20.     //bmp: TBGRABitmap;
  21.     bfBlend: TBlendFunction;
  22.     pPos: TPoint;
  23.     sSize: TSize;
  24.     dwStyle: DWORD;
  25.   end;
  26.  
  27. var
  28.   Form1: TForm1;
  29.  
  30.  
  31. implementation
  32.  
  33. {$R *.lfm}
  34.  
  35. { TForm1 }
  36.  
  37. procedure TForm1.FormCreate(Sender: TObject);
  38. var
  39.   PNG: TBGRABitmap;
  40. begin
  41.   FormStyle := fsSystemStayOnTop;
  42.   Borderstyle := bsNone;
  43.   Position := poScreenCenter;
  44.  
  45.   PNG := TBGRABitmap.Create;
  46.   try
  47.     //PNG.LoadFromFile('splash.png'); // must be a premultiplied PNG
  48.     PNG.SetSize(Width,Height);
  49.     PNG.FillEllipseAntialias(100,100,100,100,BGRA(255,255,255,150));
  50.     PNG.FillEllipseAntialias(175,100,100,100,BGRA(255,0,0,150));
  51.     PNG.FillEllipseAntialias(250,100,100,100,BGRA(0,0,0,150));
  52.  
  53.     dwStyle := GetWindowLongA(Self.Handle, GWL_EXSTYLE);
  54.     if (dwStyle and WS_EX_LAYERED = 0) then
  55.       SetWindowLong(Self.Handle, GWL_EXSTYLE, dwStyle or WS_EX_LAYERED or WS_EX_TOPMOST);
  56.  
  57.     pPos := Point(0, 0);
  58.     sSize.cx := PNG.Width;
  59.     sSize.cy := PNG.Height;
  60.  
  61.     bfBlend.BlendOp := AC_SRC_OVER;
  62.     bfBlend.BlendFlags := 0;
  63.     bfBlend.SourceConstantAlpha := 255;
  64.     bfBlend.AlphaFormat := AC_SRC_ALPHA;
  65.  
  66.     UpdateLayeredWindow(Self.Handle, 0, nil, @sSize,
  67.       PNG.Bitmap.Canvas.Handle, @pPos, 0, @bfBlend, ULW_ALPHA);
  68.   finally
  69.     PNG.Free;
  70.   end;
  71. end;
  72.  
  73. end.
  74.  

And I dont have the problem SoE mentioned.
Title: Re: Make a transparent form with BGRABitmap
Post by: Okoba on September 14, 2016, 11:34:06 am
Circular can you take a look at this link?
https://msdn.microsoft.com/en-us/library/windows/desktop/dd183393(v=vs.85).aspx
Maybe you find out why blending has problem.
Title: Re: Make a transparent form with BGRABitmap
Post by: circular on September 14, 2016, 12:06:47 pm
Hi! There is a comment in the code about values to premultiply. You can try to premultiply like this:
Code: [Select]
for y := 0 to PNG.Height-1 do
begin
  p := PNG.Scanline[y];
  for x := PNG.Width-1 downto 0 do
  begin
    p^.red := p^.red*p^.alpha div 255;
    p^.green := p^.green*p^.alpha div 255;
    p^.blue := p^.blue*p^.alpha div 255;
    inc(p);
  end;
end;

Note: to add the variables, move the text cursor to the variable and press Ctrl-Shift-C.
Title: Re: Make a transparent form with BGRABitmap
Post by: Okoba on September 14, 2016, 04:19:09 pm
Very good! Thanks.
Title: Re: Make a transparent form with BGRABitmap
Post by: RAW on September 14, 2016, 04:24:12 pm
Quote
What if you change the line by adding NativeInt conversion?

Thank you very much...
When I change these 2 lines then it's working very fine with DEBUG and RELEASE.
Code: Pascal  [Select][+][-]
  1. dl := pPrev^ - NativeInt(diag);
  2. dp := NativeInt(left) - NativeInt(diag);
  3.  

by the way: I haven't checked my BGRABitmap-Version, maybe I should download the latest version...
Title: Re: Make a transparent form with BGRABitmap
Post by: Okoba on September 14, 2016, 04:27:07 pm
I think you should Get the Git version from Github or if you are very sensetice get version 9.1 from SourceForge.
Title: Re: Make a transparent form with BGRABitmap
Post by: circular on September 14, 2016, 06:33:43 pm
Thanks SoE for the feedback. I will update on Git accordingly.
Title: Re: Make a transparent form with BGRABitmap
Post by: RAW on July 15, 2017, 04:31:55 am
Quote
for y := 0 to PNG.Height-1 do
begin
  p := PNG.Scanline[y];
  for x := PNG.Width-1 downto 0 do
  begin
    p.red := p.red*p.alpha div 255;
    p.green := p.green*p.alpha div 255;
    p.blue := p.blue*p.alpha div 255;
    inc(p);
  end;
end;

I see this right now while playing around with BGRABitmap and it should be like this:
(just in case anybody wants to use it and don't know why this won't compile..)
Code: Pascal  [Select][+][-]
  1. y, x: Integer;
  2. BGRA: TBGRABitmap;
  3. p   : PBGRAPixel;
  4.  
  5. // premultiply PNG
  6. For y:= 0 To BGRA.Height-1
  7. Do
  8.  Begin
  9.   p:= BGRA.Scanline[y];
  10.  
  11.   For x:= BGRA.Width-1 DownTo 0
  12.   Do
  13.    Begin
  14.     p^.red  := p^.red  *p^.alpha Div 255;
  15.     p^.green:= p^.green*p^.alpha Div 255;
  16.     p^.blue := p^.blue *p^.alpha Div 255;
  17.  
  18.     Inc(p);
  19.    End;
  20.  End;

If you use CTRL+SHIFT+C then automagically p would become "pointer" I guess and not PGRAPixel...  just to make it easier...


EDIT:
This is Anders Melander's version ... just a bit changed...
(is working fine, if it's necessary to precalculate ??? I don't know.. the result looks the same to me...)

Code: Pascal  [Select][+][-]
  1. Procedure PreBMP(BMP: TBGRABitmap);
  2.   Var
  3.    Row, Col: Integer;
  4.    p       : PBGRAPixel;
  5.    PreMult : Array[Byte, Byte] Of Byte;
  6.  Begin
  7.   // precalculate all possible values of a*b
  8.   For Row:= 0 To 255
  9.   Do
  10.    For Col:= Row To 255
  11.    Do
  12.     Begin
  13.      PreMult[Row, Col]:= Row*Col Div 255;
  14.  
  15.      If (Row <> Col)
  16.      Then PreMult[Col, Row]:= PreMult[Row, Col]; // a*b = b*a
  17.     End;
  18.  
  19.   For Row:= 0 To BMP.Height-1
  20.   Do
  21.    Begin
  22.     Col:= BMP.Width;
  23.     p  := BMP.ScanLine[Row];
  24.  
  25.      While (Col > 0)
  26.      Do
  27.       Begin
  28.        p^.Blue := PreMult[p^.alpha, p^.Blue];
  29.        p^.Green:= PreMult[p^.alpha, p^.Green];
  30.        p^.Red  := PreMult[p^.alpha, p^.Red];
  31.  
  32.        Inc(p);
  33.        Dec(Col);
  34.       End;
  35.    End;
  36.  End;
Title: Re: Make a transparent form with BGRABitmap
Post by: circular on July 15, 2017, 02:09:13 pm
Oh I see, the "^" were missing.

Ctrl-Shit-C does add a PBGRAPixel on my computer.

Precalculating may be faster if the image is significantly bigger than 65536/3 = 21845 pixels, which is about 148x148 pixels.

However, I would suggest to optimize with SHR instead:
Code: Pascal  [Select][+][-]
  1. for x := PNG.Width-1 downto 0 do
  2. begin
  3.   if p^.alpha = 0 then
  4.     p^ := BGRAPixelTransparent
  5.   else
  6.   begin
  7.     p^.red := p^.red*(p^.alpha+1) shr 8;
  8.     p^.green := p^.green*(p^.alpha+1) shr 8;
  9.     p^.blue := p^.blue*(p^.alpha+1) shr 8;
  10.   end;
  11.   inc(p);
  12. end;
Title: Re: Make a transparent form with BGRABitmap
Post by: RAW on July 15, 2017, 06:39:05 pm
Thank you very much ... nice info...  :)

Quote
    for x := PNG.Width-1 downto 0 do
    begin
      if p^.alpha = 0 then
        p^ := BGRAPixelTransparent
      else
      begin
        p^.red := p^.red*(p^.alpha+1) shr 8;
        p^.green := p^.green*(p^.alpha+1) shr 8;
        p^.blue := p^.blue*(p^.alpha+1) shr 8;
      end;
      inc(p);
    end;

After the first BEGIN there is one line missing:
Code: Pascal  [Select][+][-]
  1. p:= PNG.Scanline[x];
Now it should compile...
Title: Re: Make a transparent form with BGRABitmap
Post by: circular on July 17, 2017, 08:17:55 pm
You're welcome.

Nope. Scanline is for vertical position, not x.

Here it is the horizontal loop only. So the Scanline assignment would be before "for x".
Title: Re: Make a transparent form with BGRABitmap
Post by: RAW on July 17, 2017, 10:04:24 pm
Thanks circular... obviously I need to learn something about graphics... asap...

It compiled very fine, but at runtime I get a SIGSEGV:
..exactly here:
Code: Pascal  [Select][+][-]
  1. If p^.alpha = 0

So I thought there must be something referred to "p", because if I use this line
Code: Pascal  [Select][+][-]
  1. p:= BGRA.Scanline[x];
then there is no error even if this is wrong...  :)


I thought I can use it like this: first precalculate and then premultiply...
Code: Pascal  [Select][+][-]
  1. Var
  2.  p: PBGRAPixel;
  3.  x, y: Integer;  
  4.  
  5. // precalculate bigger bitmap
  6. For x:= BGRA.Width-1 DownTo 0
  7. Do
  8.  Begin
  9.   If p^.alpha = 0
  10.   Then p^:= BGRAPixelTransparent
  11.   Else
  12.    Begin
  13.     p^.red  := p^.red  *(p^.alpha+1) SHR 8;
  14.     p^.green:= p^.green*(p^.alpha+1) SHR 8;
  15.     p^.blue := p^.blue *(p^.alpha+1) SHR 8;
  16.    End;
  17.   Inc(p);
  18.  End;
  19.  
  20. // premultiply bigger bitmap
  21. For y:= 0 To BGRA.Height-1
  22. Do
  23.  Begin
  24.   p:= BGRA.Scanline[y];
  25.  
  26.   For x:= BGRA.Width-1 DownTo 0
  27.   Do
  28.    Begin
  29.     p^.red  := p^.red  *p^.alpha Div 255;
  30.     p^.green:= p^.green*p^.alpha Div 255;
  31.     p^.blue := p^.blue *p^.alpha Div 255;
  32.     Inc(p);
  33.    End;
  34.  End;
Title: Re: Make a transparent form with BGRABitmap
Post by: circular on July 17, 2017, 11:42:33 pm
Oh ok maybe I did not express myself very well. Let's try again. I am not talking about precaculating, but about doing the computation as follows:
Code: Pascal  [Select][+][-]
  1.   For y:= 0 To PNG.Height-1 Do
  2.   Begin
  3.     p:= PNG.Scanline[y];
  4.     for x := PNG.Width-1 downto 0 do
  5.     begin
  6.       if p^.alpha = 0 then
  7.         p^ := BGRAPixelTransparent
  8.       else
  9.       begin
  10.         p^.red := p^.red*(p^.alpha+1) shr 8;
  11.         p^.green := p^.green*(p^.alpha+1) shr 8;
  12.         p^.blue := p^.blue*(p^.alpha+1) shr 8;
  13.       end;
  14.       inc(p);
  15.     end;
  16.   End;
  17.  

But of course, you could also do the precalculation using SHR and then applying it. That would avoid a multiply+SHR and use a memory access instead. Could be useful on a big picture.
Title: Re: Make a transparent form with BGRABitmap
Post by: RAW on July 18, 2017, 12:45:23 am
Ah, ok.. understood... thanks again.
Title: Re: Make a transparent form with BGRABitmap
Post by: circular on July 18, 2017, 09:04:58 pm
You're welcome.
Title: Re: Make a transparent form with BGRABitmap
Post by: RAW on September 12, 2017, 10:21:09 pm
I've played a little bit with premultiply procedures and it looks like precalculation is not necessary and in general I can't see really big differences...

AM+  = Anders Melander's version with precalculation
AM-  = Anders Melander's version without precalculation
CShr = Circular's Shr-version
CDiv = Circular's Div-version

Code: Pascal  [Select][+][-]
  1. JPG 1920 x 1200
  2.    AM+  AM-  CShr  CDiv
  3. 1.   0,  16,   0,    15 ms
  4. 2.   0,   0,  16,     0 ms
  5. 3.   0,   0,  15,     0 ms
  6.      
  7. JPG 2602 x 1734
  8.    AM+  AM-  CShr  CDiv
  9. 1.  16,  15,  16,    15 ms
  10. 2.  15,  16,   0,    16 ms
  11. 3.  16,   0,  16,    15 ms
  12.      
  13. JPG 8310 x 5500
  14.    AM+  AM-  CShr  CDiv
  15. 1. 156, 156, 141,   171 ms
  16. 2. 141, 140, 125,   156 ms
  17. 3. 140, 140, 110,   171 ms

Obviously when it comes to very big images then the CSHR-version is the best, but normally the pictures I use are a lot smaller so it doesn't matter what version, at least on my system and in this little test... funny...

And the winner is... CShr-version
Code: Pascal  [Select][+][-]
  1. Procedure PremultiplyBGRA(BMP: TBGRABitmap);
  2.   Var
  3.    iX, iY: Integer;
  4.    p: PBGRAPixel;
  5.  Begin
  6.   For iY:= 0 To BMP.Height-1
  7.   Do
  8.    Begin
  9.     p:= BMP.Scanline[iY];
  10.  
  11.     For iX:= BMP.Width-1 Downto 0
  12.     Do
  13.      Begin
  14.       If p^.Alpha = 0
  15.       Then p^:= BGRAPixelTransparent
  16.       Else
  17.        Begin
  18.         p^.Red  := p^.Red  *(p^.Alpha+1) SHR 8;
  19.         p^.Green:= p^.Green*(p^.Alpha+1) SHR 8;
  20.         p^.Blue := p^.Blue *(p^.Alpha+1) SHR 8;
  21.        End;
  22.       Inc(p);
  23.      End;
  24.    End;
  25.  End;

CDiv-version
Code: Pascal  [Select][+][-]
  1. Procedure PremultiplyBMP(BMP: TBGRABitmap);
  2.   Var
  3.    iX, iY: Integer;
  4.    p: PBGRAPixel;
  5.  Begin
  6.   For iY:= 0 To BMP.Height-1
  7.   Do
  8.    Begin
  9.     p:= BMP.Scanline[iY];
  10.  
  11.     For iX:= BMP.Width-1 DownTo 0
  12.     Do
  13.      Begin
  14.       p^.Red  := p^.Red  *p^.Alpha Div 255;
  15.       p^.Green:= p^.Green*p^.Alpha Div 255;
  16.       p^.Blue := p^.Blue *p^.Alpha Div 255;
  17.  
  18.       Inc(p);
  19.      End;
  20.    End;
  21.  End;

AM+ version
Code: Pascal  [Select][+][-]
  1. Procedure Premultiply(BMP: TBGRABitmap);
  2.   Var
  3.    iRow, iCol: Integer;
  4.    p: PBGRAPixel;
  5.    arrByte: Array[Byte, Byte] Of Byte;
  6.  Begin
  7.   For iRow:= 0 To 255
  8.   Do
  9.    For iCol:= iRow To 255
  10.    Do
  11.     Begin
  12.      arrByte[iRow, iCol]:= iRow*iCol Div 255;
  13.  
  14.      If (iRow <> iCol)
  15.      Then arrByte[iCol, iRow]:= arrByte[iRow, iCol];
  16.     End;
  17.  
  18.  
  19.   For iRow:= 0 To BMP.Height-1
  20.   Do
  21.    Begin
  22.     iCol:= BMP.Width;
  23.     p   := BMP.ScanLine[iRow];
  24.  
  25.     While (iCol > 0)
  26.     Do
  27.      Begin
  28.       p^.Blue := arrByte[p^.Alpha, p^.Blue];
  29.       p^.Green:= arrByte[p^.Alpha, p^.Green];
  30.       p^.Red  := arrByte[p^.Alpha, p^.Red];
  31.  
  32.       Inc(p);
  33.       Dec(iCol);
  34.      End;
  35.    End;
  36.  End;

AM original version
Code: Pascal  [Select][+][-]
  1. Procedure PremultiplyBitmap(Bitmap: TBitmap);
  2. type
  3.   pRGBQuad = ^TRGBQuad;
  4.   TRGBQuad = PACKED RECORD
  5.    Blue    : BYTE;
  6.    Green   : BYTE;
  7.    Red     : BYTE;
  8.    Reserved: BYTE
  9.   end;
  10.  
  11. var
  12.   Row, Col: integer;
  13.   p: PRGBQuad;
  14.   PreMult: array[byte, byte] of byte;
  15. begin
  16.   // precalculate all possible values of a*b
  17.   for Row := 0 to 255 do
  18.     for Col := Row to 255 do
  19.     begin
  20.       PreMult[Row, Col] := Row*Col div 255;
  21.       if (Row <> Col) then
  22.         PreMult[Col, Row] := PreMult[Row, Col]; // a*b = b*a
  23.     end;
  24.  
  25.   for Row := 0 to Bitmap.Height-1 do
  26.   begin
  27.     Col := Bitmap.Width;
  28.     p := Bitmap.ScanLine[Row];
  29.     while (Col > 0) do
  30.     begin
  31.       p^.Blue := PreMult[p^.Reserved, p^.Blue];
  32.       p^.Green := PreMult[p^.Reserved, p^.Green];
  33.       p^.Red := PreMult[p^.Reserved, p^.Red];
  34.       inc(p);
  35.       dec(Col);
  36.     end;
  37.   end;
  38. end;

Little test...
Code: Pascal  [Select][+][-]
  1. Procedure TForm1.Button1Click(Sender: TObject);
  2.   Var
  3.    cStart, cStop: Cardinal;
  4.    BGRA: TBGRABitmap;
  5.  Begin
  6.   BGRA:= TBGRABitmap.Create(Image2.Picture.Bitmap, False);
  7.    Try
  8.      cStart:= GetTickCount64;
  9.     PremultiplyCalc(BGRA);
  10.      cStop:= GetTickCount64-cStart;
  11.     Label1.Caption:= IntToStr(cStop);
  12.  
  13.      cStart:= GetTickCount64;
  14.     Premultiply(BGRA);
  15.      cStop:= GetTickCount64-cStart;
  16.     Label2.Caption:= IntToStr(cStop);
  17.  
  18.      cStart:= GetTickCount64;
  19.     PremultiplyBGRA(BGRA);
  20.      cStop:= GetTickCount64-cStart;
  21.     Label3.Caption:= IntToStr(cStop);
  22.  
  23.      cStart:= GetTickCount64;
  24.     PremultiplyBMP(BGRA);
  25.      cStop:= GetTickCount64-cStart;
  26.     Label4.Caption:= IntToStr(cStop);
  27.    Finally
  28.     BGRA.Free;
  29.    End;
  30.  End;
Title: Re: Make a transparent form with BGRABitmap
Post by: circular on September 18, 2017, 08:24:52 am
Interesting. So MUL+SHR is faster than bidimensional array.
TinyPortal © 2005-2018