Recent

Author Topic: Make a transparent form with BGRABitmap  (Read 12377 times)

Okoba

  • Hero Member
  • *****
  • Posts: 528
Make a transparent form with BGRABitmap
« 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.

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?

RAW

  • Hero Member
  • *****
  • Posts: 868
Re: Make a transparent form with BGRABitmap
« Reply #1 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.  
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

RAW

  • Hero Member
  • *****
  • Posts: 868
Re: Make a transparent form with BGRABitmap
« Reply #2 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

Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

circular

  • Hero Member
  • *****
  • Posts: 4196
    • Personal webpage
Re: Make a transparent form with BGRABitmap
« Reply #3 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);
Conscience is the debugger of the mind

Okoba

  • Hero Member
  • *****
  • Posts: 528
Re: Make a transparent form with BGRABitmap
« Reply #4 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.

Okoba

  • Hero Member
  • *****
  • Posts: 528
Re: Make a transparent form with BGRABitmap
« Reply #5 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.

circular

  • Hero Member
  • *****
  • Posts: 4196
    • Personal webpage
Re: Make a transparent form with BGRABitmap
« Reply #6 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.
« Last Edit: July 15, 2017, 02:00:02 pm by circular »
Conscience is the debugger of the mind

Okoba

  • Hero Member
  • *****
  • Posts: 528
Re: Make a transparent form with BGRABitmap
« Reply #7 on: September 14, 2016, 04:19:09 pm »
Very good! Thanks.

RAW

  • Hero Member
  • *****
  • Posts: 868
Re: Make a transparent form with BGRABitmap
« Reply #8 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...
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

Okoba

  • Hero Member
  • *****
  • Posts: 528
Re: Make a transparent form with BGRABitmap
« Reply #9 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.

circular

  • Hero Member
  • *****
  • Posts: 4196
    • Personal webpage
Re: Make a transparent form with BGRABitmap
« Reply #10 on: September 14, 2016, 06:33:43 pm »
Thanks SoE for the feedback. I will update on Git accordingly.
Conscience is the debugger of the mind

RAW

  • Hero Member
  • *****
  • Posts: 868
Re: Make a transparent form with BGRABitmap
« Reply #11 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;
« Last Edit: July 15, 2017, 04:55:03 am by RAW »
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

circular

  • Hero Member
  • *****
  • Posts: 4196
    • Personal webpage
Re: Make a transparent form with BGRABitmap
« Reply #12 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;
Conscience is the debugger of the mind

RAW

  • Hero Member
  • *****
  • Posts: 868
Re: Make a transparent form with BGRABitmap
« Reply #13 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...
« Last Edit: July 17, 2017, 10:11:16 am by RAW »
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

circular

  • Hero Member
  • *****
  • Posts: 4196
    • Personal webpage
Re: Make a transparent form with BGRABitmap
« Reply #14 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".
Conscience is the debugger of the mind

 

TinyPortal © 2005-2018