Forum > Graphics
Transparent animation
majolika:
Hello everyone!
It's me again with my newbie's question.
I've almost finished my first Lazarus "mine sweeper" project, but I'm stuck trying to make a transparent animation.
The idea was like this: there is a TBitmap (FieldBitmap in my code), on which I draw the game field and everything that happens on it, and at the right moment on another transparent TBitmap2 (AnimationBitmap in my code) I draw an animation on a timer, display it on top of the game field and erase what was drawn when animation finished.
As always, I reproduced this in a test project and it did not work.
I faced two problems:
1. I found out that TBitmap cannot be transparent. But then why does it have the transparency property?! I don't understand. Ok, instead of TBitmap2, I tried using TImage, but this also didn't work. I think, I'm doing something wrong.
2. If, when the application is first launched, the first click on the game field falls on the middle mouse button (I assigned it to start the animation for testing), then part of the animation is drawn in the upper left corner of TBitmap2 for some reason. When restarting the game field or if the first click was the right or left mouse button, then partial rendering in the upper left corner doesn't occur. I assume that the problem is in the initialization of CellXCenter and CellYCenter variables that I use when drawing the animation.
How can I deal with both of these problems?
I will be grateful for any hints.
Here is the code of the test project, and in the attached file is the entire project.
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, LCLType; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Label1: TLabel; Label2: TLabel; PaintBox1: TPaintBox; Timer1: TTimer; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure PaintBox1Paint(Sender: TObject); procedure Timer1Timer(Sender: TObject); private FieldBitmap, AnimationBitmap: TBitmap; AnimationFrame: Integer; procedure FieldInit(Cols, Rows: Integer); procedure StartCaptureAnimation; procedure StopCaptureAnimation; procedure ClearAnimationBitmap; public end; var Form1: TForm1; BoxSize: Integer = 24; FieldRow: integer = 0; FieldCol: integer = 0; Button1Clicked: Boolean = False; Button2Clicked: Boolean = False; FieldWidth, FieldHeight: Integer; ThereWillBeBlood: Boolean = False; CellXCenter, CellYCenter: Integer; implementation {$R *.lfm} procedure TForm1.Button1Click(Sender: TObject);begin Button1Clicked := True; FieldRow := 5; FieldCol := 5; FieldInit(FieldCol, FieldRow);end; procedure TForm1.Button2Click(Sender: TObject);begin Button2Clicked := True; FieldRow := 20; FieldCol := 20; FieldInit(FieldCol, FieldRow);end; procedure TForm1.FormCreate(Sender: TObject);begin FieldBitmap := TBitmap.Create; AnimationBitmap := TBitmap.Create;end; procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);begin FieldBitmap.Destroy; AnimationBitmap.Destroy;end; procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);var CellX, CellY: Integer;begin Label1.Caption := X.ToString + ', ' + Y.ToString; CellX := X div BoxSize; CellY := Y div BoxSize; Label2.Caption := CellX.ToString + ', ' + CellY.ToString;end; procedure TForm1.PaintBox1Paint(Sender: TObject);begin PaintBox1.Canvas.Draw(0,0, FieldBitmap); if ThereWillBeBlood then PaintBox1.Canvas.Draw(0, 0, AnimationBitmap);end; procedure TForm1.FieldInit(Cols, Rows: Integer);var x, y: Integer; r: TRect;begin FieldWidth := Cols * BoxSize; FieldHeight := Rows * BoxSize; PaintBox1.Width := FieldWidth; PaintBox1.Height := FieldHeight; FieldBitmap.Clear; AnimationBitmap.Clear; FieldBitmap.SetSize(FieldWidth, FieldHeight); FieldBitmap.Canvas.Brush.Color := clNone; FieldBitmap.Canvas.FillRect(0, 0, FieldWidth, FieldHeight); AnimationBitmap.SetSize(FieldWidth, FieldHeight); AnimationBitmap.Transparent := True; AnimationBitmap.Canvas.Brush.Color := clNone; AnimationBitmap.Canvas.FillRect(0, 0, FieldWidth, FieldHeight); for x := 0 to Cols - 1 do begin for y := 0 to Rows - 1 do begin r := rect(x * BoxSize, y * BoxSize, x * BoxSize + BoxSize, y * BoxSize + BoxSize); FieldBitmap.Canvas.Frame3d(r, 2, bvRaised); end; end; PaintBox1.Refresh; end; procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);var CellX, CellY: Integer;begin CellX := (X div BoxSize) * Boxsize; CellY := (Y div BoxSize) * Boxsize; CellXCenter := CellX + (Boxsize div 2); CellYCenter := CellY + (Boxsize div 2); case Button of mbLeft: begin FieldBitmap.Canvas.Brush.Color := RGBToColor(225, 25, 25); FieldBitmap.Canvas.Brush.Style := bsSolid; FieldBitmap.Canvas.Pen.Color := RGBToColor(225, 25, 25); FieldBitmap.Canvas.Pen.Style := psSolid; FieldBitmap.Canvas.Ellipse(CellX + 2, CellY + 2, CellX + BoxSize - 2, CellY + BoxSize - 2); end; mbRight: begin FieldBitmap.Canvas.Brush.Color := RGBToColor(25, 225, 25); FieldBitmap.Canvas.Brush.Style := bsSolid; FieldBitmap.Canvas.Pen.Color := RGBToColor(25, 225, 25); FieldBitmap.Canvas.Pen.Style := psSolid; FieldBitmap.Canvas.Ellipse(CellX + 2, CellY + 2, CellX + BoxSize - 2, CellY + BoxSize - 2); end; mbMiddle: begin ThereWillBeBlood := True; StartCaptureAnimation; end; end; PaintBox1.Repaint; end; procedure TForm1.StartCaptureAnimation;begin AnimationFrame := 0; Timer1.Interval := 50; // slower //Timer1.Interval := 15; // faster Timer1.Enabled := True;end; procedure TForm1.ClearAnimationBitmap;// -----------------------------------// clearing AnimationBitmap.Canvas// -----------------------------------begin // -- method 1 -- works strange //AnimationBitmap.TransparentColor := clFuchsia; //AnimationBitmap.Canvas.Brush.Color := clFuchsia; //AnimationBitmap.Canvas.FillRect(0, 0, FieldWidth, FieldHeight); // -- method 2 - works not perfect //AnimationBitmap.Clear; //AnimationBitmap.SetSize(FieldWidth, FieldHeight); // -- method 3 - works not perfect AnimationBitmap.Canvas.Brush.Color := clNone; //AnimationBitmap.Canvas.Brush.Color := clFuchsia; // clFuchsia instead of clNone - for clarity AnimationBitmap.Canvas.FillRect(0, 0, FieldWidth, FieldHeight); end; procedure TForm1.StopCaptureAnimation;begin Timer1.Enabled := False; ClearAnimationBitmap; PaintBox1.Invalidate; ThereWillBeBlood := False;end; procedure TForm1.Timer1Timer(Sender: TObject);var i, x, y, size: Integer;begin AnimationFrame := AnimationFrame + 1; ClearAnimationBitmap; AnimationBitmap.Canvas.Brush.Color := RGBToColor(150, 0, 0); AnimationBitmap.Canvas.Pen.Style := psClear; AnimationBitmap.Canvas.Brush.Style := bsSolid; AnimationBitmap.Canvas.Ellipse(CellXCenter - AnimationFrame * 4, CellYCenter - AnimationFrame * 4, CellXCenter + AnimationFrame * 4, CellYCenter + AnimationFrame * 4); Randomize; for i := 0 to 10 do begin x := CellXCenter + Random(AnimationFrame * 15) - (AnimationFrame * 7); y := CellYCenter + Random(AnimationFrame * 15) - (AnimationFrame * 7); size := Random(8) + 3; AnimationBitmap.Canvas.Ellipse(x - size, y - size, x + size, y + size); end; PaintBox1.Invalidate; if AnimationFrame > 10 then StopCaptureAnimation;end; end.
TRon:
The logic behind it does not work simply because transparency is not the same as an true alpha channel (it is fake transparency providing the illusion of a pixel being opaque or not). I try come up with an example later but I am a bit busy atm so might take me a while.
If you want to have something to do in the mean-time try have a lok at 32-bit true alpha graphic formats or as an alternative how to "blend" two normal images together into one by means of bitmasking. That is unless someone else is able to come up with a more decent answer on a shorter notice :)
majolika:
--- Quote from: TRon on February 08, 2025, 08:14:31 am ---I try come up with an example later but I am a bit busy atm so might take me a while.
--- End quote ---
It would be nice of you! I will patiently wait and while I wiil be waiting I definitly
--- Quote from: TRon on February 08, 2025, 08:14:31 am ---have a look at 32-bit true alpha graphic formats
--- End quote ---
majolika:
Allright, I simplified my code to just putting a blue circle on a middle mouse click. No animation for now, just trying to get transparency on the AnimationBitmap.
mbLeft — red circle
mbRight — green circle
mbMiddle — blue circle
In FieldInit trying this code:
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} --- //AnimBitmap.PixelFormat := pf24bit; AnimBitmap.PixelFormat := pf32bit; AnimBitmap.SetSize(FieldWidth, FieldHeight); AnimBitmap.TransparentColor := clBlack; AnimBitmap.TransparentMode := tmFixed; //AnimBitmap.TransparentMode := tmAuto; AnimBitmap.Transparent := True; AnimBitmap.Canvas.Brush.Color := clBlack; AnimBitmap.Canvas.FillRect(0, 0, FieldWidth, FieldHeight);
Combination of PixelFormat and TransperentMode gives me two strange behaviors:
pf24bit, tmAuto - black background, multiple blue circles
pf24bit, tmFixed - transparent background, single blue circle in a first click position
pf32bit, tmAuto - black background, multiple blue circles
pf32bit, tmFixed - black background, multiple blue circles
It's so confusing...
P.S.
I was trying to use TBGRABitmap instead of TBitmap. It works fine. Just as I expected (transparent background, multiple blue circles).
I just need to realize how to draw semi-transparent primitives on a fully transparent background.
But what if I don't want to use 3rd party components?
P.P.S.
Reading this article gives me an idea that using TLazIntfImage is a total mess.
You need a TLazIntfImage, TLaxCanvas, TBitmap and Timage (and/or maybe TPaintBox, I don't know) just to get one semi-transparent circle?! Are you kidding me?!
majolika:
I forgot to attach my testing project. Here it is.
Navigation
[0] Message Index
[#] Next page