In this little tutorial we are going to take a trip back in time, back to the golden age of personal computing. A time when the ultimate computer was the Commodore Amiga and the latest kid on the block was wire-frame 3d graphics and polygon art. I am of course talking about the 1990’s and the glowing polygon effect.
Continue reading
Author Archives: Jon Lennart
Working with javascript structures
When interacting with ordinary javascript libraries or a remote webservice you natually have to go native with your datastructures. Under Smart this can be handled in a variety of ways but the two most common approaches are to either use a record or a variant.
Using records
Records in Smart work just like they do in other object pascal environments. And since Smart compiles to javascript rather than machine opcodes, a record will always compile to an ordinary javascript structure. I should note that javascript really doesnt distinguish between what we would call a record and objects. In javascript everything is an object.
So the following object pascal code:
[sourcecode language=”delphi”]
type TMyInfo = Record
miName: String;
miAge: Integer;
miGender: integer;
End;
[/sourcecode]
Results in the following predictable javascript code:
[sourcecode language=”javascript”]
{
miName: "",
miAge: 0,
miGender: 0
}
[/sourcecode]
This means that when you want to interact with native javascript libraries that expects a structure of values, you can use records to match it directly.
Variants
Using variants to achieve the same thing is perhaps easier, especially since you dont have to predefine the structure by hand. As of writing however it does require you to initialize the variant manually. We will probably add some RTL functions to simplify this very soon, but for now you can use this “one liner” ASM snippet:
[sourcecode language=”delphi”]
Procedure testvariant;
var
mData: variant;
Begin
asm @mData = {}; end; // Set variant to "empty JS object"
mData.miName:=’john doe’;
mData.miAge:=39;
mData.miGender:=1;
end;
[/sourcecode]
The resulting javascript structure will be exactly the same as that produced by the record. One difference to keep in mind though, is that the compiler will output the names “as is”. There is no way for Smart to map the content of a native object so make sure you get the names right.
Another article about Smart
Pascal Game Developer has a nice article about Smart Mobile Studio. Visit their website and have a look: http://www.pascalgamedevelopment.com/
Thinking ahead
I have to come up with some names for the different technologies soon. Smart now have two terms for compiler and also two levels of RTL: the low level RTL that is a part of Eric’s compiler, and a high level RTL which is our “VCL” clone. Im thinking of naming our component library the VJL (visual javascript library), and instead of refering to the compiler infrastructure as “the compiler”, it is more fitting to call it “the build tool”.
One vs. many
As the test group knows, the build-tool takes your source-code, invoked the compiler and finally glues everything together into a final, solid HTML5 document. Since our system was originally designed for iOS development only, and since Smart components (including forms, which is our version of pages in a way) are purely javascript based – a fully operational “app” really only needs one HTML file.
In Alpha 2 i removed the injection of code directly into the document.body section and introduced “Application.Display.View” to the VJL. The reason for this was make the application regard “the screen” abstractly. So by altering what “display.view” points to – you can make your apps live inside any DIV you wish.
A couple of testers have reported back that it could be handy to separate the RTL from the program code. There are a number of scenarios where you might, for example, want to run multiple Smart apps on the same document. If you run a website and want to get rid of Flash for instance, it would be nice to code the new banner in Smart and also host a game or a cool menu written in Smart as well.
Having talked it over with Eric it became clear that this should be reasonable to achieve, but it will come at a price: namely that it defeats the latest compiler switch named “smart linking”. When you smart link your apps, only the code you have used is included in the final binary (in our case “binary” is javascript). It’s the same technique Delphi and C++ uses to make programs as small as possible.
So if we include the feature then it will disable the smart linking option. I am experimenting with this now, by dumping the entire RTL into a separate file. If it doesn’t produce any negative side effects (except a small speed penalty and some bloat) then I’ll include it as an option. Eric might slap me over the head for playing around with this – but it would (in my view) complete the parallel of “Delphi” for javascript.
Reusable libraries
One of my long term goals is to add a new project type to Smart for writing libraries. Which would be the javascript incarnation of DLL’s. Since we have our own IDE we could generate a class-map for the library – which you could then import later so the Smart IDE can keep track of what the libraries export. A bit like a Windows TLB file when working with COM objects.
We should be able to poll this off by isolating the compiled code in it’s own name-space. It would then have it’s own RTL and library – but due to the nature of Javascript, it should actually work 🙂 It would mean a little tweak to the RTL but we are constantly tweaking anyhow.
Why are libraries important? Components, that’s why. By pre-compiling and obfuscating your code – you could write and sell your own components. The other benefits are self evident I think.
Tired of handcrafting your Javascript apps?
Before we started developing Smart Mobile Studio we did a survey of the alternatives for developing mobile apps. That’s actually where the initial idea and motivation came from. During this survey we must have tested at least 14 different “app” machines, languages and “rapid” development environments. We tested the serious players like C++, object pascal, C# and Java – to the outright scams which usually include the phrase “create your app in a day” (but the app you get is just a template with no real value what so ever).
The result of our survey was disappointing to say the least. At the time, using Free-pascal to work with iOS was a bit of a black art so while that was our initial choice – any substantial information regarding cross compilation was virtually non existent, so we decided to go for C# in developing our iOS apps. This involved learning a new language, a new platform and to some extent a whole new way of thinking.
But one thing we did notice was that Javascript had come a long way. While all the native systems were struggling to get aboard the iOS and Android bandwagon – javascript was all over the place. Doing things it’s not supposed to do in classical terms. Everything from silky smooth animations to action packed platform games.
To make a long story short we continued working in C# and object pascal, but as the idea behind cross compilation and using javascript as a medium began to grow, we found ourselves seeking out like-minded people, putting our ideas to the test. Unlike the “app machines” however, we did it properly from the ground up.
So the question is not if javascript works. The question is how to use that power properly.
Working with ordinary web technologies
When we took the time to look at javascript with new eyes, three things became apparent. First, browsers are not just page readers anymore but rather fully fledged multimedia environments. Secondly, Javascript has grown and can make use of the browser in a whole new way. And last but not least: javascript’s lack of structure is both it’s strength and it’s downfall.
Take something simple like a visual component. When you dig into the complexities of this simple task it turns out it’s not that simple after all. As of writing there exist no single standard between the different javascript frameworks on the marked. It is also highly unlikely that it ever will – because it’s not financially beneficial for these companies to work together. They would lose their uniqueness if they did, not to mention the technical difficulties getting two javascript frameworks talking to each other. When you use a framework you stick to it, mixing two different systems could in theory work – but in real life it quickly becomes a mess.
Encapsulation and portability of source is also an issue, not in terms of browser compatibility – but in terms of programmers sharing solutions. There are several ways a javascript developer can create the same thing – and the lack of a common approach (which classical languages solve by syntax, classes and a common foundation like the VCL) makes it extremely time consuming.
After we created a small demo/game in Javascript, testing out ideas and playing around with the multimedia features, it became crystal clear that it simply takes to much time to hand-craft large projects in javascript. You can use one of the many ready-made frameworks out there, but the amount of source code you need to write for a large game or application is ridicules.
So what is the alternative?
The alternative is not to ignore javascript, but rather to use javascript as a medium. What we did with Smart Mobile Studio was to transform a disadvantage into an asset. Javascript is the proverbial “wax that takes many forms”. We projected the rigid and time proven structure of object pascal onto the “everything goes” landscape of javascript. The end result is that you can now use the power of object pascal, it’s encapsulation, it’s class hierarchy, it’s true object inheritance and (thanks to our super clever compiler guru Eric Grange) even fancy concepts like interfaces, closures and var parameters. So not only do you get the power of javascript – you also gain the wisdom and experience of more than 18 years of pascal evolution (not to mention the object pascal community which is a living genius factory).
With a few hundred lines of pascal code you can achieve what it would require thousands – if not tens of thousands lines of javascript to match. But the central strength of pascal is that of scope and size. A large object pascal code-base can be maintained due to the architecture of the language. The same project in “native” javascript terms would be nearly impossible to keep track of. The time and cost involved in developing and maintaining a 200.000 lines long javascript project cannot be justified when you can achieve the same with 20.000 lines of pascal. Object pascal was designed to handle large projects. Javascript was not.
So. Are you tired of spending days hand-crafting javascript that could be delivered in hours?
Taking advantage of classical programming
Let’s face it, Javascript is both a powerful and flexible language, but developing and maintaining large scale projects in a dialect not really designed for it is frustrating. The reason javascript libraries like jQuery, jQTouch and Sencha have remained more or less unrivaled for years now – is first and foremost because of the monumental effort involved.
The key factor itself have to do with the actual language. Javascript is a fantastic language, but sadly it lacks many of the traditional programming concepts native to languages like C++ and object pascal. Concepts like classes, true object inheritance, interfaces and type casting – techniques and technology created exactly for the purpose of making large frameworks manageable and robust.
Changing the game
With Smart Mobile Studio however you can take full advantage of these classical concepts. This is because our product allows you to write ordinary, classical object pascal (made famous by Delphi, Free-pascal or other incarnations of the language) and it’s instantly converted into super charged javascript.
The compiler at the heart of Smart generates javascript in a way that implements all the aforementioned technologies. So even if javascript itself doesn’t support concepts like interfaces, it is crafted by the compiler in javascript itself. And if you are worrying about speed – you will find that even with all these powerful and classical technologies – Smart Mobile Studio can run with the best of them. To make it plain: Smart does for the browser what Delphi did for the Windows platform.
You also get an IDE (integrated development environment) specifically designed for Smart programming
Easy to use, easy to learn and very rewarding
If you haven’t used pascal before then don’t worry. It was designed to be taught in school to students of object oriented software development. Chances are you have already used it yourself or have heard about Delphi or Free-pascal from a co-worker. Object pascal is responsible for hundreds of thousands of applications in the Windows world – and it’s still knocking out killer apps today.
Easy to use doesn’t make it a lightweight in terms of features however. Object pascal is fast, dynamic, robust and can do pretty much everything C++ can do.
With Smart you can now take your knowledge of classical programming one step further – into the domain of super fast HTML5 web applications. You can also use third party products like Phonegap and compile your generated web app into a real, binary executable program – ready for app-store. In fact, only hours after Smart was released to it’s testers, demo projects made it’s way onto the Android marketplace (!)
So what would you rather maintain? 10.000 lines of javascript code or 1000 lines of pascal code to achieve the same thing? Object pascal is a proven, tested by time and established language designed to handle large scale projects. Javascript is not. The benefits of using a compiler that generates javascript are just to great to ignore.
All new Smart Mobile Studio
For the past week and a half things have been quite calm on our website, this is not due to lack of work but rather focusing on some of the more tricky aspects of the IDE. So here they are in no particular order:
Alternative rendering
As an alternative to Chromium I attempted to wrap the Opera DLL’s last weekend, but the differences between Safari and Opera (both style and javascript wise) were to large – so I decided to drop it. Chrome is the browser most compatible with Safari (they both use the same webkit foundation) and as such we will stick with TChromium. We also had to backtrack to find the most stable version of TChromium since the latest revisions simply don’t work as expected.
Keyword proposal
This is an issue that has been driving me nuts for a while, but finally I tracked down the bug (wrong cache pointer .. sigh) – and Smart Mobile Studio now have keyword proposal in the box. We will naturally aim to expand this into the realm of code-completion and other advanced features in the future.
XML file format
In the first versions of the Alpha release we used a custom binary file format. This file format is now replaced by a proper XML format, making it easier to expand the system with new options and data without breaking backwards compatibility. You will need to copy and paste out your units to make the transition though, but it’s a small price to pay for a more versatile system. The new file format also supports external unit files (much like Delphi has by default) and we added a recursive search method to locate implicitly referenced units. Also, a new folder as been added to the mix: “libraries”. If you have made some utility units place those files under IDE/Libraries and the IDE will automatically find them.
What’s next
Once we have completed some of the tickets on fogbugz the final battle begins, namely offscreen rendering of components and the visual form designer. Then we brush up on the look & feel of the IDE and give the standard components more features and bling — then it’s release time.
Crack open the Redbull, im ready to rock 🙂
Variant is your friend
Using variants as mediators is now possible under Smart Mobile Studio. This removes some of the burden when interacting with native javascript objects or external libraries:
[sourcecode language=”delphi”]
procedure TMyObject.InvokeByVariant;
var
mSrc: variant;
Begin
//get the browsers window object
asm
@mSrc = window;
end;
//invoke our method
handleCallback(mSrc);
end;
procedure TMyObject.HandleCallback(a:variant);
Begin
// treat "a" as an object
a.alert(‘this is a test’);
end;
[/sourcecode]
Note: Variants are treated as “unknown” by the codegen. It has no compiler operators for it, meaning you cant write “if a<>1 then” or “a:=12”. Variants are primarily used to transport native values and objects between methods dealing with javascript objects or browser objects.
In the above example we simply calls a procedure passing the native browser object “window” as a parameter. In the HandleCallBack method we invoke the alert method of that object. Once again, Smart deals with variants as unknown entities – so there is no checking that “alert” actually exists.
Files, folders and the dreaded javascript filesystem
One of the trickiest and most frustrating aspects of javascript coding has to be the async file-system. Where opening a file and looping through a read sequence sounds easy enough (and is in most languages) – being async means that everything is handled through events. Why you say? Because javascript is by nature single threaded, so the cpu time has to be divided between all the action that takes place inside the browser. And that includes accessing files and folders.
In short, when you open the filesystem – the actual object you get back is delivered through an event. When you want to read X number of bytes from a file, you invoke it in one place and handle the actual reading someplace else. So it’s not exactly linear and object pascal friendly to say the least.
Using the force, Sith style
To solve this I have come up with the concept of disk actions. A disk action in this case is basically an object that handles everything associated with the action you want to execute (loadfile, savefile, copy etc.) – this gives us the benefit of isolating the code that invokes the filesystem and it’s event handler(s) in one place. And those that want to access the filesystem through Smart Mobile Studio wont have to deal with the ridicules visions of the World Wide Web Consortium (WC3).
My wrapper aims at handling things like this:
[sourcecode language=”delphi”]
FFileSystem:=TW3FileSystem.Create;
try
FFileSystem.open(5 * CNT_MEGABYTE,fstPersistent);
try
FFileSystem.Execute(TW3OpenFileAction.Create
(‘prefs.ini’,IniFileRead);
finally
FFileSystem.close;
end;
finally
FFileSystem.free;
end;
Procedure IniFileRead(sender:TObject;aData:TW3Buffer;
aFilename:String);
Begin
//File is read into a buffer (actually a "blob"
//Use the methods of TW3Buffer and TW3View(ers) to
//access the data in "reader" style
w3_showmessage(aData.toString);
end;
[/sourcecode]
But due to the nature of the javascript callback system, we probably have to move several aspects of the above into events. But at least I will isolate the events in one object, making it a hell of a lot easier to work with than native javascript.
For example, “FFileSystem.Execute” wont be a legal call in the above example, because it will be executed before the event which delivers the file-system native object has called us back. To solve this I have some tricks up my sleeve – but I think it’s wise to play it safe when it comes to building a new RTL from scratch. So expect some event management.
The answere is 42
I also built in a fail-safe for error codes. As I was testing the same code in both Chrome, Safari and FireFox – i noticed that the error codes where different between two browsers (which shall remain anonymous but involves a fox and some iron). The error names were the same (e.g: FileError.QUOTA_EXCEEDED_ERR) but the integer values could differ.
So instead of just hard-coding the numbers I used the newly added TW3Dictionary object(s) to map the error codes in the browser to it’s Object Pascal type enumeration. So even if the browser alters the actual error code our code will work just fine.
[sourcecode language=”delphi”]
//Register known errors in the constructor
FErrors:=TW3IntDictionary.Create;
registerError(fseQuotaExceeded);
registerError(fseNotFound);
registerError(fseSecurity);
registerError(fseInvalidModification);
registerError(fseInvalidState);
registerError(fseUnknown);
//Do evil things with butter
Procedure TW3FileSystem.RegisterError(aKind:TW3FileSystemError);
var
mKey: String;
Begin
if supported then
Begin
try
mKey:=IntToStr(ErrorToNative(aKind));
FErrors.Values[mKey]:=ord(aKind);
except
on e: exception do
Raise EW3Exception.CreateFmt(CNT_ERR_METHOD,
[‘RegisterError’,ClassName,e.message]);
end;
end;
end;
[/sourcecode]
Thank god I only have the audio and video objects left to wrap, because I’m getting a bit tired of low level javascript. But the foundation is getting tweaked and polished (Primoz Gabrijelcic have really given it a run for it’s money, testing every avenue and tidbit in the framework. I dont think I have ever seen a person consume and adapt to information that fast. Also the wizard of code which is Eric Grange is ever watchfull and actually knocked out a full game in under a day after release), so when the foundation is done for version 1.0 – I can fully dedicate myself to high-level component writing.
Having said that — there is plenty of room for you guys to create your own units and start creating components for Smart Mobile Studio!
So far people have created games, advanced mandelbrot explorers, particle systems, hacker group demos and even classes that connect directly to RemObjects services (!) – and we aren’t even out of the alpha phase. Imagine what version 2.0 will look like. Buckle up dorothy because we’re just getting started (!)
Oh almost forgot.. Whenever I have the spare time, I will be implementing a converter for .mod music files (sound-tracker, pro-tracker: those old sample based music files). Im not sure the JS timer system is up for the challenge, perhaps we have to use web-workers (threads) to handle the dispatching – but I really want to add the frosting on the cake later on. No one has ever done anything like this in Javascript – so why not send a spartan to do it 😉
Fair play
And please. Try to avoid copying software. We are a two man company and have dedicated a lot of resources to polling this off. If you use it in some way – please support our work and buy a license when it’s released. If you want your friend to try it out then just ask us and he can register with the alpha team. We hear code written in Smart is already being used in production scenarios, and it’s sort of self-defeating for the community if what we hear is true.
Our prices will be very reasonable. Somewhere between $200 – $400 for the first version which is very humble concidering 6+ months of work has gone into research, figuring out how to do things, building a model and bridging technologies.
Smart Mobile Studio is becoming a weapon of mass construction – ok so let’s show those javascript punters what were made of 🙂
How do i move graphics from the canvas to an image?
In alpha release 3 several missing bits and pieces from the HTML5 standard was added to Smart Mobile Studio. One of the key methods that were missing from our canvas implementation was toDataUrl – a function which has it’s opposite in TW3ControlBackground and TW3Image fromURL and LoadFromUrl methods.
So in effect, it is now very simple to transport off-screen graphics (graphics you have created on a canvas) and use it as either a background or as the content of a normal image.
Here is an quick example of how to create a picture dynamically and display it in a TW3Image control. Also notice that you can actually assign the image to the background of any TW3CustomControl descendant.
To run the example create a new visual project and replace the code in Form1 with the following:
[sourcecode language=”delphi”]
unit Form1;
interface
uses w3system, w3ctrls, w3forms, w3graphics,w3application;
type
TForm1=class(TW3form)
private
{ Private methods }
FImage: TW3Image;
FGraph: TW3GraphicContext;
FButton: TW3Button;
procedure HandleButtonClicked(Sender:TObject);
protected
{ Protected methods }
Procedure InitializeObject;override;
Procedure FinalizeObject;override;
Procedure StyleTagObject;override;
procedure resize;Override;
end;
Implementation
//############################################################################
// TForm1
//############################################################################
Procedure TForm1.InitializeObject;
Begin
inherited;
FImage:=TW3Image.Create(self);
FImage.Background.fromColor(clWhite);
FButton:=TW3Button.Create(self);
FButton.OnClick:=HandleButtonClicked;
FButton.Caption:=’Click to generate’;
FGraph:=TW3GraphicContext.Create(NIL);
End;
Procedure TForm1.FinalizeObject;
Begin
FImage.free;
FButton.free;
FGraph.free;
inherited;
End;
procedure TForm1.HandleButtonClicked(Sender:TObject);
var
FCanvas: TW3Canvas;
aFrom: String;
aTo: String;
Begin
FGraph.Allocate(FImage.Width,FImage.Height);
FCanvas:=TW3Canvas.Create(FGraph);
try
// Clear background
FCanvas.fillstyle:=’rgb(0,0,99)’;
FCanvas.fillrectF(0,0,FGraph.Width,FGraph.Height);
// Draw some text on the canvas
FCanvas.font:=’10pt verdana’;
FCanvas.FillStyle:=’rgb(255,255,255)’;
FCanvas.FillTextF(‘This was generated on a canvas!’,10,20,MAXINT);
//You can load it in as a picture
FImage.LoadFromUrl(FCanvas.toDataUrl(”));
//.. or load it as a background to a picture (or control)
//FImage.background.fromUrl(FCanvas.toDataUrl(”));
finally
FCanvas.free
end;
FGraph.release;
end;
procedure TForm1.resize;
var
hd: Integer;
dy: Integer;
Begin
FImage.setBounds(5,5,width-10,(height div 2) + (height div 3) );
hd:=height – (FImage.top + FImage.Height + 5);
dy:=(hd div 2) – (FButton.height div 2);
FButton.setBounds(5,dy + FImage.Top + FImage.height,width-10,FButton.height);
end;
Procedure TForm1.StyleTagObject;
Begin
inherited;
StyleClass:=’TW3CustomForm’;
End;
end.
[/sourcecode]
Articles about Smart
The well known programming guru and author of Omni Thread Library, Primoz Gabrijelcic, have given the alpha version of Smart Mobile Studio a run for it’s money. He has produced two articles (in a planned series of articles) about how to implement a fully operational fractal explorer under Smart.
We consider it an honour to have Primoz giving our system a solid test-drive. Primoz Gabrijelcic is well known in the Delphi community for both his skill and insight into all things technical.
The first article can be found here:
http://www.thedelphigeek.com/2012/01/first-steps-with-smart-mobile-studio.html
And the latest installment can be read here:
http://www.thedelphigeek.com/2012/01/my-first-smart-program.html
A simple sprite-sheet
A sprite sheet is basically just an image. But it’s an image that is organized in a special way, usually with a 32×32 or 64×64 pixel grid where each cell contains either a character animation frame – or a background tile. Back in the old Amiga days of 8 bit graphics and hand crafted sprites this was the only way to go.
Since the world of mobile javascript game development is more or less en-par with the old Amiga (in terms of cpu power and restrictions. Which is amazing since the Amiga had a 1hz motorola mc68000 CPU) an example of a spritesheet class might come in handy.
A simple spritesheet class
TW3SpriteSheet = class(TObject) private FImage: TW3Image; FReady: Boolean; FColumns: Integer; FRows: Integer; FTotal: Integer; FWidth: Integer; FHeight: Integer; FOnReady: TNotifyEvent; procedure HandleImageReady(sender:TObject); procedure SetWidth(aValue:Integer); procedure Setheight(aValue:Integer); Procedure ReCalcInfo; public Property OnReady:TNotifyEvent read FOnReady write FOnReady; Property SpriteWidth:Integer read FWidth write setWidth; Property SpriteHeight:Integer read FHeight write setHeight; Property Ready:Boolean read FReady; Procedure LoadImage(aUrl:String); Procedure Draw(Canvas:TW3Canvas;dx,dy:Integer;FrameIndex:Integer); Constructor Create;virtual; destructor Destroy;Override; end; constructor TW3SpriteSheet.Create; begin inherited Create; FWidth := 32; FHeight := 32; FColumns := 0; FRows := 0; FTotal := 0; FImage := TW3Image.Create(NIL); FImage.OnLoad := HandleImageReady; end; destructor TW3SpriteSheet.Destroy; begin FImage.onLoad:=NIL; FImage.free; inherited; end; procedure TW3SpriteSheet.setWidth(aValue:Integer); begin FWidth:=w3_ensureRange(aValue,4,MAXINT); ReCalcInfo; end; procedure TW3SpriteSheet.setheight(aValue:Integer); begin FHeight:=w3_ensureRange(aValue,4,MAXINT); ReCalcInfo; end; procedure TW3SpriteSheet.ReCalcInfo; begin if FReady then begin FColumns:=FImage.Width div FWidth; FRows:=FImage.Height div FHeight; FTotal:=FColumns * FRows; end else begin FColumns:=0; FRows:=0; FTotal:=0; end; end; procedure TW3SpriteSheet.HandleImageReady(sender:TObject); begin FReady:=True; ReCalcInfo; if assigned(FOnReady) then FOnReady(self); end; procedure TW3SpriteSheet.LoadImage(aUrl:String); begin FReady:=False; ReCalcInfo; FImage.LoadFromUrl(aUrl); end; procedure TW3SpriteSheet.Draw(Canvas:TW3Canvas; dx,dy:Integer; FrameIndex:Integer); var sx,sy: Integer; begin if FReady and assigned(canvas) and (FrameIndex>=0) and (FrameIndex<FTotal) then Begin sx:=FrameIndex mod FColumns; sy:=FrameIndex div FRows; sx:=sx * FWidth; sy:=sy * FHeight; canvas.DrawImageEx2F(FImage.tagRef,sx,sy,FWidth, FHeight,dx,dy,FWidth,FHeight); end; end;
Using the class
Using the class is simple. Initialize it in TApplication.ApplicationStarting method and load in a picture, like this:
FSheet := TW3SpriteSheet.Create; FSheet.LoadImage('res/sprites.png');
When the actual sprite-sheet image is loaded, the “ready” property will be set to true. So in your PaintView method always check the ready flag before accessing it:
if FSheet.Ready then begin FSheet.Draw(Canvas,100,100,10); // draw tile #10 FSheet.Draw(Canvas,133,100,11); // draw tile #11 FSheet.Draw(Canvas,166,100,12); // draw tile #12 end;
Enjoy!
How do i plot pixels on a offscreen bitmap?
Just like ordinary Delphi, Javascript has it’s own canvas object. As you can imagine the canvas of the browser is quite different from the good old Delphi TCanvas, but not so alien that it doesn’t have it’s similarities. But you will need to brush up on the documentation from Apple in order to use it.
But HTML5 also have something else up it’s sleeve, namely that you can create off-screen bitmaps (wrapped in the TW3ImageData class from w3graphics.pas). The difference between a Delphi bitmap and a HTML5 bitmap is 3 fold:
- The bitmap is obtained from the canvas, not the other way around (Delphi’s TBimap has a canvas property)
- Off-screen bitmaps do not have a canvas, they are in reality DIBS (device independent bitmaps)
- Off-screen bitmaps are 32bit exclusively (RGBA, 8888 format).
This is actually good news because it means we can create games and multimedia dealing with pixel data rather than the (excuse me) somewhat cumbersome canvas object. Most javascript developers actually avoid this part because they are used to “high level” coding only – but coders coming from the Delphi community have been knocking out low level graphics libraries for ages. So if you have ever played around with DIBS (device independent bitmaps) in Delphi – you should feel right at home.
Here is a quick example of how to plot some pixels off-screen, and copy the results onto the screen (note: this is a game project):
[sourcecode language=”delphi”]
unit Project8;
interface
uses w3system, w3components, w3application,
w3game, w3gameapp, w3graphics, w3components;
type
TApplication=class(TW3CustomGameApplication)
private
{ Private methods }
FBuffer: TW3ImageData;
protected
{ protected methods }
procedure ApplicationStarting;override;
procedure ApplicationClosing;override;
Procedure PaintView(Canvas:TW3Canvas);override;
end;
Implementation
//############################################################################
// TApplication
//############################################################################
procedure TApplication.ApplicationStarting;
Begin
inherited;
//Initialize refresh interval, set this to 1 for optimal speed
GameView.Delay:=20;
(* Create a our "offscreen bitmap" *)
FBuffer:=GameView.Canvas.CreateImageData(32,32);
//Start the redraw-cycle with framecounter active
//Note: the framecounter impacts rendering speed. Disable
//the framerate for maximum speed (false)
GameView.StartSession(true);
End;
procedure TApplication.ApplicationClosing;
Begin
GameView.EndSession;
inherited;
End;
// Note: In a real live game you would try to cache as much
// info as you can. Typical tricks are:
// 1: Only get the width/height when resized
// 2: Pre-calculate strings, especially RGB/RGBA values
// 3: Only redraw what has changed, avoid a full repaint
// The code below is just to get you started
procedure TApplication.PaintView(Canvas:TW3Canvas);
Begin
// Clear background
canvas.fillstyle:=’rgb(0,0,99)’;
canvas.fillrectF(0,0,gameview.width,gameview.height);
// Draw our framerate on the screen
canvas.font:=’10pt verdana’;
canvas.FillStyle:=’rgb(255,255,255)’;
canvas.FillTextF(‘FPS:’ + IntToStr(GameView.FrameRate),10,20,MAXINT);
// plot some pixels
if FBuffer<>NIL then
Begin
FBuffer.setPixelC(0,2,clGreen);
FBuffer.setPixelC(1,2,clGreen);
FBuffer.setPixelC(2,2,clGreen);
FBuffer.setPixelC(0,4,clRed);
FBuffer.setPixelC(1,4,clRed);
FBuffer.setPixelC(2,4,clRed);
// paste graphics to display
Canvas.putImageData(FBuffer,20,20);
end;
End;
end.
[/sourcecode]
The result may not look so impressive right now, but it’s the foundation for what will become a very impressive browser based graphics system:
In the future we are going to add a lot more functionality to the TW3ImageData class. Things like line, ellipse, fillrect, circle and flood-fill is already prototyped. But if you feel like playing around with pixels – this is a good start.
Open the file w3graphics.pas and have a look at the methods and properties of TW3ImageData for more information. Also check out this excellent canvas tutorial by Mark Pilgrim.
How do I rotate a control by X degrees?
All descendants of TW3Customcontrol, which is the primary object most visual controls derive from, has a property called Angle. This is a floating point property which will rotate your control in whatever angle you define. Please bear in mind that this property will override any webkit rotation in the current style. If you want the stylesheet to retain it’s position, mark the CSS tag with !important;
For instance:
[sourcecode language=”delphi”]
unit Form1;
interface
uses w3system, w3ctrls, w3forms, w3application;
type
TForm1=class(TW3form)
private
{ Private methods }
FButton: TW3Button;
Procedure HandleClick(Sender:TObject);
protected
{ Protected methods }
Procedure InitializeObject;override;
Procedure FinalizeObject;override;
Procedure StyleTagObject;override;
end;
Implementation
//############################################################################
// TForm1
//############################################################################
Procedure TForm1.InitializeObject;
Begin
inherited;
FButton:=TW3Button.Create(self);
FButton.caption:=’Click me’;
FButton.setBounds(100,50,100,28);
FButton.onClick:=HandleClick;
End;
Procedure TForm1.HandleClick(Sender:TObject);
Begin
if FButton.Angle=60 then
FButton.Angle:=0 else
FButton.Angle:=60;
end;
Procedure TForm1.FinalizeObject;
Begin
inherited;
End;
Procedure TForm1.StyleTagObject;
Begin
inherited;
StyleClass:=’TW3CustomForm’;
End;
end.
[/sourcecode]
When you click the button it will toggle between normal (0 deg) and tilted (60 deg), like this:
How do i create and use a timer?
Under Delphi and free-pascal there is a component called TTimer. Under Smart Mobile Studio however all types of timer objects have a slightly different architecture – and also different names (we will probably provide a TTimer alias before release). The class you are looking for is called “TW3EventRepeater”.
To use a timer start by adding the unit “w3time” to your unit’s uses section. Unlike Delphi, TW3EventRepeater derives from TObject rather than TComponent. There is no point in creating a component (which adds quite a bit of overhead) when a normal object will do.
Using the event repeater from a form is a snap:
[sourcecode language=”delphi”]
unit Form1;
interface
uses w3system, w3ctrls, w3forms, w3application, w3time;
type
TForm1=class(TW3form)
private
{ Private methods }
FTimer: TW3EventRepeater;
FCount: Integer;
function HandleTimer(sender:TObject):Boolean;
protected
{ Protected methods }
Procedure FormActivated;override;
end;
Implementation
//############################################################################
// TForm1
//############################################################################
Procedure TForm1.FormActivated;
Begin
FTimer:=TW3EventRepeater.Create(HandleTimer,1000);
end;
function TForm1.HandleTimer(sender:TObject):Boolean;
Begin
result:=False;
inc(FCount);
if FCount>9 then
Begin
TW3EventRepeater(sender).free;
result:=true;
w3_showmessage(‘time is up!’);
end;
end;
end.
[/sourcecode]
As expected the timer kicks in after 10 seconds informing us that the time has expired
You may have noticed that the callback was a function and not a procedure? If you return “true” the timer will stop of it’s own accord. Since this is javascript and not winapi you can actually dispose of the timer while the timer is called. Because the timer object does not go out of scope until the function returns.