Custom oscilloscope overlay widget

Discussion related to the Cross Codebot open source framework
sysrpl
Posts: 108
Joined: Thu Feb 05, 2015 6:31 pm

Custom oscilloscope overlay widget

Postby sysrpl » Sun Mar 22, 2015 6:45 pm


This article describes how to create cross platform desktop widgets using overlays. Here is a oscilloscope overlay widget running on Linux.



And the same oscilloscope overlay widget running on Windows:



Surface overlays are type of window whose pixels are defined by drawing operations to an ISurface interface. Each individual window pixel can have a range of transparency between 0 which is totally invisible, to 255 which is completely opaque, or any value in between the two. Access to cross platform surface overlays is provided by the ISplash interface, which is defined as follows:

{ ISplash is a floating window whose shape is defined by a bitmap }

ISplash = interface
['{291570E9-3567-4C10-8899-CDA04979060F}']
function GetBitmap: IBitmap;
function GetOpacity: Byte;
procedure SetOpacity(Value: Byte);
function GetVisible: Boolean;
procedure SetVisible(Value: Boolean);
function GetHandle: THandle;
{ Move the window to x and y }
procedure Move(X, Y: Integer);
{ Update the window when you're done drawing on bitmap }
procedure Update;
{ Bitmap is the image surface which defines the window shape }
property Bitmap: IBitmap read GetBitmap;
{ Opacity controls the overall transparency of the window }
property Opacity: Byte read GetOpacity write SetOpacity;
{ Visible shows or hide the window }
property Visible: Boolean read GetVisible write SetVisible;
{ Handle is the udnerlying operating system window handle }
property Handle: THandle read GetHandle;
end;


Overlays can be used to create widget type windows, unusual notification popups, custom tooltip windows, or even application splash or about screens.

Here is a listing of the oscilloscope example above.

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
{ We use a prendered png image to define the frame }
FFrame := NewBitmap;
FFrame.LoadFromFile('frame.png');
{ And also we prerender some shadow images }
FInner := NewBitmap;
FInner.LoadFromFile('inner.png');
FOuter := NewBitmap;
FOuter.LoadFromFile('outer.png');
{ This is our surface overlay }
FSplash := NewSplash;
{ Make the overlay match the size of the frame }
FSplash.Bitmap.SetSize(FFrame.Width, FFrame.Height);
{ Move the overlay to the top right of the screen }
FSplash.Move(Screen.Width - FSplash.Bitmap.Width, 0);
end;

procedure TForm1.DrawScope;
const
ScaleX = 30;
ScaleY = 90;
ScaleT = 10;
var
Surface: ISurface;
R: TRectI;
G: IGradientBrush;
P: IPen;
C: TColorB;
T: Double;
X, Y: Float;
A, B: TPointF;
I: Integer;
begin
{ Erase the overlay }
FSplash.Bitmap.Clear;
Surface := FSplash.Bitmap.Surface;
R := FSplash.Bitmap.ClientRect;
R.Inflate(-15, -15);
G := NewBrush(R.Left, R.Top, R.Left, R.Bottom);
{ Draw a gradient background using the color teal }
C := clTeal;
{ With a little bit of transparency }
C.Alpha := $E0;
{ Start out dark at the top }
G.AddStop(C.Darken(0.5), 0);
G.AddStop(C, 0.5);
{ Finish with light at the bottom }
G.AddStop(C.Lighten(0.5), 1);
Surface.FillRect(G, R);
C := clTeal;
R := FSplash.Bitmap.ClientRect;
{ Draw both shadows }
FOuter.Surface.CopyTo(R, Surface, R);
FInner.Surface.CopyTo(R, Surface, R, $7F);
R.Inflate(400, 0);
R.Bottom := R.Bottom * 3;
{ Draw white relection along the top }
G := NewBrush(R);
C := clWhite;
G.AddStop(C.Fade(0), 0);
G.AddStop(C.Fade(0), 0.8);
G.AddStop(C.Fade(0.2), 0.81);
G.AddStop(C.Fade(0), 0.9);
R := FSplash.Bitmap.ClientRect;
R.Inflate(-20, -20);
Surface.FillRect(G, R);
{ Draw the oscilloscope sin wave pattern }
X := R.Left;
Y := FSplash.Bitmap.Height div 2;
{ Animate using time }
T := TimeQuery * ScaleT;
Surface.MoveTo(X, Y + Sin(X / ScaleX + T) * ScaleY);
for I := 0 to R.Width div 2 do
begin
X := X + 2;
Surface.LineTo(X, Y + Sin(X / ScaleX + T) * ScaleY);
end;
C := clTeal;
C := C.Lighten(0.75);
P := NewPen(C.Fade(0.05), 14);
{ Stroke the path with a shrinking pen }
for I := 0 to 3 do
begin
Surface.Stroke(P, True);
P.Color := C.Fade(I / 4 + 0.1);
P.Width := P.Width - 4;
end;
Surface.Path.Remove;
{ Connect the sin wave end points }
A.X := 45;
A.Y := Y + Sin(A.X / ScaleX + T) * ScaleY;
B.X := FSplash.Bitmap.Width - 45;
B.Y := Y + Sin(B.X / ScaleX + T) * ScaleY;
Surface.MoveTo(A.X, A.Y);
Surface.LineTo(B.X, B.Y);
P.Width := 6;
P.Color := C.Fade(0.02);
for I := 0 to 1 do
begin
Surface.Stroke(P, True);
P.Color := C.Fade(I / 4 + 0.2);
P.Width := P.Width - 2;
end;
{ Draw the frame around our graph and shadows }
R := FSplash.Bitmap.ClientRect;
FFrame.Surface.CopyTo(R, Surface, R);
R := TRectI.Create(30, 30);
R.Center(Round(A.X), Round(A.Y));
G := NewBrush(R);
G.AddStop(C.Fade(0.4), 0);
G.AddStop(C.Fade(0), 1);
{ Draw a sparkles of the left }
Surface.FillRect(G, R);
R.Center(Round(B.X), Round(B.Y));
G := NewBrush(R);
G.AddStop(C.Fade(0.4), 0);
G.AddStop(C.Fade(0), 1);
{ And also on the right }
Surface.FillRect(G, R);
{ Tell the overlay we are done drawing }
FSplash.Update;
{ And show it if it's not already visible }
FSplash.Visible := True;
end;

procedure TForm1.TimerTimer(Sender: TObject);
begin
{ Animate }
DrawScope;
end;

taazz
Posts: 1
Joined: Mon Mar 23, 2015 8:03 am

Re: Custom oscilloscope overlay widget

Postby taazz » Mon Mar 23, 2015 10:23 am

Impressive as always. A questions just to complete the info on the article.

is the transparency information encoded on the frame.png file?
eg the frame is opaque and the white rect in the center has a transparency of 160 attach to its pixels and that transparency translates as the transparent parts on the window or is there some other kind of pre-rendering going on? Usual multiplying all pixels with a transparent value avoiding costly calculation later on or what windows pre-multiplied window.


Are the shadows something along the line of a brush or a complete image that already has shadow on all the right places?

Thank you.

PS. sometimes I can be very thick so slap me around a bit if you think I missed something obvious.

sysrpl
Posts: 108
Joined: Thu Feb 05, 2015 6:31 pm

Re: Custom oscilloscope overlay widget

Postby sysrpl » Mon Mar 23, 2015 10:56 am

Taazz,

All good questions. Although it's totally possible to draw the frame and shadows using ISurface commands, sometimes it's just more convenient to draw or edit graphical elements in an external paint program and load them as pngs, especially if they are static or don't need animation.

In this case I am using pngs images with an alpha channel. The alpha channel allows pixels to have varying levels of transparency. So to answer your question, yes transparency information is encoded in the png image files. Here is what the inner and frame pngs looks like in image editor:

Image

Image

Anyone familiar with Photoshop (I prefer Paintshop or Gimp personally) should recognize the checkerboard pattern as the standard background transparency pattern. From this close up of one of the frame corners (pictured below) you can see that transparency isn't always on or off, rather it can be various levels in between, and in programs using Cross Codebot this applies as well. That is, when you draw vectors or paint images you can get pixels of varying transparency levels if you so choose.

Image

Now, regarding image pixel pre multiplication, which is something you mentioned, yes some drawing APIs require it. The good news is Cross Cobebot IBitmap (FFrame is an IBitmap, along with FInner and FOuter) will detect this and correctly pre multiply pixels for you, so it's nothing anyone ever needs to know about. This is a one time thing that will sometimes happen when an image is loaded. As such, most people won't even know what pixel pre-multiplication is. Here is my quick definition:

Pixel pre multiplication: To darken image pixels red, green, and blue values based on the alpha. The simple formula is a = pixel.alpha / high(byte); pixel.red = round(pixel.red * a); pixel.green = round(pixel.green * a); pixel.blue = round(pixel.blue * a);

Obviously this formula isn't applied each time an image is loaded or saved, otherwise all pixels with an alpha value of less than high(byte) or 255 would eventually all become black. Rather there is some intelligence going on behind the scenes to determine if the formula should be applied to an image, and the user (programmer) will have no knowledge of this, as it should be in a properly designed API.

I hope this answers your questions.

zerob
Posts: 1
Joined: Wed Mar 25, 2015 7:17 pm

Re: Custom oscilloscope overlay widget

Postby zerob » Wed Mar 25, 2015 7:30 pm

Somehow i can't send you a private message, but do you know when you release this library? It looks simply amazing and i want to try it.

sysrpl
Posts: 108
Joined: Thu Feb 05, 2015 6:31 pm

Re: Custom oscilloscope overlay widget

Postby sysrpl » Wed Mar 25, 2015 8:16 pm

zerob wrote:Somehow i can't send you a private message, but do you know when you release this library? It looks simply amazing and i want to try it.

My apologies zerob, I changed the baord settings to allow newly registered users to send and receive private messages.

I've also sent you a message with the git server information for your account. I look forward to seeing you on these forums.


Return to “Cross Codebot”

Who is online

Users browsing this forum: No registered users and 2 guests

cron