Showing posts with label tutorial. Show all posts
Showing posts with label tutorial. Show all posts

Sunday, August 25, 2019

Per pixel bitmap animation in HTML5/JavaScript

LICSON showed how to simulate GLSL shader effects on HTML5 Canvas using pure JavaScript:

//The requestAnimFrame fallback for better and smoother animation
window.requestAnimFrame = (function () {
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || 
 window.mozRequestAnimationFrame || window.oRequestAnimationFrame || 
 window.msRequestAnimationFrame || function (callback) {
        window.setTimeout(callback, 1000 / 60);
    };
})();

//Prepare our canvas
var canvas = document.querySelector('#render');
var w = window.innerWidth;
var h = window.innerHeight;
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');

var time = Date.now();//record initial time
var buffer = ctx.createImageData(w, h);//The back buffer we used to paint the result into the canvas

//The main render function
//Calculate a color value from elapsed time and [x,y] coordinates (scaled to [0,1])
function render(time, fragcoord) {
    /* put the GLSL fragment shader's JavaScript equivalent here. */
    //begin of per pixel bitmap manipulation
    var x = fragcoord[0]; var y = fragcoord[1];
    var red = x;
    var green = y;
    var blue = 1/(1+time);
    var alpha = 1;
    //end of per pixel bitmap manipulation
    return [red,green,blue,alpha]; //the final color value (scaled to [0,1])
};

function animate() {
    var delta = (Date.now() - time) / 1000;
    buffer = ctx.createImageData(w, h);
    ctx.clearRect(0, 0, w, h);
    for (var x = 0; x < w; x++) {
        for (var y = 0; y < h; y++) {
            var ret = render(delta, [x/w, y/h]);
            var i = (y * buffer.width + x) * 4;
            buffer.data[i] = ret[0] * 255;//red
            buffer.data[i + 1] = ret[1] * 255;//green
            buffer.data[i + 2] = ret[2] * 255;//blue
            buffer.data[i + 3] = ret[3] * 255;//alpha
        }
    }
    ctx.putImageData(buffer, 0, 0);
    requestAnimFrame(animate);
};

window.onresize = function () {
    w = window.innerWidth;
    h = window.innerHeight;
    canvas.width = w;
    canvas.height = h;
};

animate();

Demo & Full Source Code: http://vvv.flaswf.tk/demo/?url=HTML5Pixels

The difference: In AS3/Haxe, a color value is represented by a single Unsigned Int value (0xAARRGGBB). In HTML5, the color data (BitmapData) array stores a whole color value as four neighboring elements (integer between 0 and 255), representing the red, green, blue and alpha values, respectively. To get/set the color value at coordinate (x,y), you should go to index "i=(y*buffer.width+x)*4", then the red, green, blue and alpha values are respectively "buffer.data[i], buffer.data[i+1], buffer.data[i+2], buffer.data[i+3]".

Links:
https://licson.net/post/glsl-fragment-shaders-in-javascript/
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas
https://bruce-lab.blogspot.com/2019/02/four-ways-for-per-pixel-bitmap.html
Faster Canvas Pixel Manipulation with Typed Arrays
References:
HTML5 Canvas: Native Interactivity and Animation for the Web, see Animation Loop (P.27), Pixel Manipulation (P.170).
Foundation HTML5 Animation with JavaScript, see Animation loops (P.16), Pixel manipulation (P.94).
HTML5 Game Development Insights, see High-Performance Update Loops (P.106), A Bitmap API Example (P.246).
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData
https://www.w3schools.com/js/js_htmldom_animate.asp
https://www.w3schools.com/tags/canvas_putimagedata.asp

Saturday, January 27, 2018

Saturday, November 18, 2017

Deploy ZeroNet to Heroku as a Public or Private ZeroNet Proxy Service

Note: due to formatting issues, some bash commands below have incomplete display. Just copy and paste the bash code block somewhere for the complete commands.
Deploy ZeroNet to Heroku as a Public or Private ZeroNet Proxy Service

Introduction

This is a tutorial on deploying ZeroNet to Heroku as a Python web app. You can host a (1) public or (2) private ZeroNet proxy, or only host (3) your personal zsites (ZeroNet site) statically (with all your zsite contents there) or dynamically (as an open gate to the clearnet and fetch your zsite contents from the zero network) on Heroku. An example zsite hosted on Heroku:
https://dcentral.herokuapp.com

Things are much easier if you have a VPS and wish to host ZeroNet on it, please check the following two video tutorials:
How to Create a Private ZeroNet Proxy (for Phones, only for yourself)
How to Create a ZeroNet Public Proxy for Everyone to Use
However, VPS will cost you some money. On the other hand, you can deploy and use ZeroNet on Heroku for free, although with some limitations.

Limitations

  • No persistent storage with Heroku dynos.
    • If you host a (1) public or (2) private ZeroNet proxy, your user data will not be saved on Heroku. For example, if you visit your proxy and subscribed some new sites, and next time you visit the proxy again, all subscriptions will disappear and it is like a fresh new deployment. The reason is data/zsites downloaded on Heroku are on a temporary space and will be erased whenever a session ends (e.g., when your dyno sleeps and restarts, at most every 24 hours).
    • If you want to host (3) your personal zsites statically, you need to deploy every change of your zsite (e.g., after you published a new post) to Heroku. Otherwise, it is equivalent to host it dynamically because user interactions through the ZeroNet UI will not be saved, and the Heroku copy of your zsite files is always the same as your last deployment, which can be older than the latest copy in the zero network. Although it will update automatically and fetch your latest zsite contents from the zero network whenever you visit your zsite hosted on Heroku, the updates can not be saved for your next visit, which means it has to download even the same updates from the zero network next time — in other words, it is like to host your zsite dynamically.
  • Heroku does not allow opening ports or use multiple ports. By default, ZeroNet use the port 43110 to serve the web UI, and also want the port 15441 for peer communication, although the second port is optional. However, Heroku only allows you to use one port, so we have to use the only one for ZeroNet web UI. Without a port for peer communication means we have to run ZeroNet in slow mode and creating new sites or publishing new contents may not work well (but you can do that locally).
  • The free dyno of Heroku has some quota limitations: https://devcenter.heroku.com/articles/free-dyno-hours

Tips: If you want to host a public ZeroNet proxy without any restriction on users, you can do it in just one-click by using the Heroku deployment button in this repository:
https://github.com/BruceJawn/HeroNet
And if you want to host a private ZeroNet proxy for yourself, try this repository:
https://github.com/BruceJawn/ZeroNet-private-proxy
Otherwise, you should read the following contents.

Assume you’re with Ubuntu/Linux. If you’re using Mac or Windows, it’s better to use VirtualBox with a pre-installed Ubuntu desktop image http://www.osboxes.org/ubuntu/ to save time.

Step 1. Register a Heroku account.

Register your account here: https://signup.heroku.com/signup/dc

Step 2. Install Python 2. (Optional)

Python version 2.X (instead of Python 3.X) is needed for testing and using ZeroNet locally. If you only want to host a (2) private ZeroNet proxy, you can skip this step. If you want to host a (1) public ZeroNet proxy without a zsites whitelist so any user can visit any zsite through your proxy, you can also skip this step. But if you want to host a (1) public ZeroNet proxy and only allow users to visit some whitelisted zsites (disallow users to add new sites), or host (3) your personal zsites, you must be able to run ZeroNet locally.

Ubuntu should have Python 2 installed by default, you can check it by:

python2 --version

If not, to install Python 2.X, you can refer to http://docs.python-guide.org/en/latest/starting/install/linux/

Step 3. Install Git.

sudo apt-get update
sudo apt-get install git

Step 4. Install Heroku CLI.

# Run this from your terminal.
# The following will add our apt repository and install the CLI:
sudo add-apt-repository "deb https://cli-assets.heroku.com/branches/stable/apt ./"
curl -L https://cli-assets.heroku.com/apt/release.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install heroku

Now login Heroku,

heroku login

then input your registered email and password for Heroku.

Step 5. Prepare the ZeroNet app.

Firstly, clone the ZeroNet source code:

git clone https://github.com/HelloZeroNet/ZeroNet.git
cd ZeroNet

Now, add the Python runtime requirement file for Heroku:

cat > runtime.txt << EOF
python-2.7.14
EOF

Alternatively, manually create a file named “runtime.txt” in the folder “ZeroNet”, with the following content

python-2.7.14

Now go to Step 6 if you want to host a (1) public ZeroNet proxy.
Or go to Step 7 if you want to host a (2) private ZeroNet proxy.

Step 6. This step is for hosing a (1) public ZeroNet proxy.

If you want to disallow users to add new sites to your proxy, go to Step 6.1.
If you want to allow users to access any zsite, go to Step 6.2 directly.

Step 6.1.

Run ZeroNet locally, and visit all the zsites you want to be whitelisted for your users.

sudo apt-get update
sudo apt-get install msgpack-python python-gevent
python2 zeronet.py

Open http://127.0.0.1:43110/ in your browser to visit zsites.

You now can delete the file “GeoLite2-City.mmdb” in the “ZeroNet/data” folder to save space for deployment. The file “users.json” in the “ZeroNet/data” folder contains a “master_seed” and a list of zsites you just visited/subscribed. You can login using the “master_seed” as the administrator later to add and delete subscribed sites through the ZeroNet UI in multi-user mode. Make sure your “ZeroNet/data” folder contains a complete copy of the default homepage site “1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D” in the folder “ZeroNet/data/1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D”.

Now enable git to include files in “ZeroNet/data” folder:

nano .gitignore

find and change the following two lines

# Data dir
data/*
*.db

to

# Data dir
#data/*
#*.db

then ctrl+o, enter key to save and ctrl+x to exit.

Step 6.2.

Enable multi-user mode for ZeroNet,

mv  ./plugins/disabled-Multiuser ./plugins/Multiuser

Alternatively, manually rename the folder “disabled-Multiuser” in “ZeroNet/core/plugins/” to “Multiuser”.

Step 6.3.

Now, add the Procfile for Heroku:
If you want to disallow users to add new sites to your proxy,

cat > Procfile << EOF
web: python zeronet.py --ui_ip "*" --ui_port \$PORT --multiuser_no_new_sites
EOF

If you want to allow users to add new sites to your proxy,

cat > Procfile << EOF
web: python zeronet.py --ui_ip "*" --ui_port \$PORT
EOF

Alternatively, manually create a file named “Procfile” in the “ZeroNet” folder with the following content

web: python zeronet.py --ui_ip "*" --ui_port $PORT --multiuser_no_new_sites

or

web: python zeronet.py --ui_ip "*" --ui_port $PORT

depending on whether you want to disallow users to add new sites or not.

Note, ZeroNet use the default local IP 127.0.0.1 and port 43110 to serve the web UI. However, as you will install ZeroNet on a remote machine, you must set the UI IP as “*“, as described in https://zeronet.readthedocs.io/en/latest/faq/#is-it-possible-to-install-zeronet-to-a-remote-machine. Also, Heroku requires all web app to bind a dynamic port (which can be accessed by the environment var $PORT) it assigns you when your app starts, so you need to change the default port for ZeroNet UI to $PORT.

Now go to Step 9.

Step 7. This step is for hosing a (2) private ZeroNet proxy.

Now enable the UiPassword, so a user can only access the UI with the password.

mv  ./plugins/disabled-UiPassword ./plugins/UiPassword

Alternatively, manually rename the folder “disabled-UiPassword” in “ZeroNet/core/plugins/” to “UiPassword”.

Now add the Procfile for Heroku:

cat > Procfile << EOF
web: python zeronet.py --ui_ip "*" --ui_port \$PORT --ui_password yourpassword
EOF

Alternatively, manually create a file named “Procfile” in the “ZeroNet” folder with the following content

web: python zeronet.py --ui_ip "*" --ui_port $PORT --ui_password yourpassword

Change “yourpassword” above to your desired password to access the ZeroNet UI.

Tips: you can use Heroku’s config variables to set your password later. Just replace “–ui_password yourpassword” above by “–ui_password $mypassword” and you can later add and set the config variable “mypassword” in your Heroku dashboard, https://dashboard.heroku.com/apps/your_heroku_app_name/settings → Config Variables, then restart your dyno,

heroku restart

or you can use Heroku CLI to set the config variable

heroku config:set mypassword=yourpassword

Now go to Step 9.

Step 8. This step is for hosing (3) only your zsite.

Assume you have a zsite with address “1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui”. Check the tutorial: https://zeronet.readthedocs.io/en/latest/using_zeronet/create_new_site/ for creating your zsites.
Run ZeroNet locally, and visit your zsite “1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui”

sudo apt-get update
sudo apt-get install msgpack-python python-gevent
python2 zeronet.py

Open http://127.0.0.1:43110/1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui in your browser to visit your zsite.
If you want to host your zsite, make sure a full updated copy of your latest zsite is downloaded to, e.g., “ZeroNet/Data/1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui”.
Now enable git to include files in “ZeroNet/data” folder:

nano .gitignore

find and change the following two lines

# Data dir
data/*
*.db

to

# Data dir
#data/*
#*.db

then ctrl+o to save and ctrl+x to exit.
Next, enable multi-user mode for ZeroNet,

mv ./plugins/disabled-Multiuser ./plugins/Multiuser

Alternatively, manually rename the folder “disabled-Multiuser” in “ZeroNet/core/plugins/” to “Multiuser”.
Now add the Procfile for Heroku:

cat > Procfile << EOF
web: python zeronet.py --ui_ip "*" --ui_port \$PORT --multiuser_no_new_sites --homepage 1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui
EOF

Alternatively, manually create a file named “Procfile” in the “ZeroNet” folder with the following content

web: python zeronet.py --ui_ip "*" --ui_port $PORT --multiuser_no_new_sites --homepage 1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui

Change “1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui” above to your zsite address.

Note the default homepage for ZeroNet UI web interface is “1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D”, you can change it to your zsite using the flag “–homepage”.

Tips: you can use Heroku’s config variables to set your site address later. Just replace “–homepage 1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui” above by “–homepage $mysiteaddress” and you can later add and set the config variable “mysiteaddress” in your Heroku dashboard, https://dashboard.heroku.com/apps/your_heroku_app_name/settings → Config Variables, then restart your dyno,

heroku restart

or just use Heroku CLI to set the config variable

heroku config:set mysiteaddress=1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui

Also, you can use a custom domain and point its root to https://yourappname.herokuapp.com/1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui.

Step 9. Deploy ZeroNet to Heroku.

#Create an app on Heroku with a random app name
heroku create
#Use the following if you want to specify your app name
# heroku apps:create myappname
#Commit all changes, you need to do the following Steps A, B, C
#every time you modified something before deploy/update your changes to Heroku
#(A) add the modified files to the local git repository:
git add .
#(B) commit the changes to the repository:
git commit -m "notes_on_changes"
#(C) deploy the code:
git push heroku master
#Ensure that at least one instance of the app is running:
heroku ps:scale web=1
#Now visit the app at the URL generated by its app name
heroku open
#View logs
heroku logs --tail

In you browser, you should be able to see the ZeroNet web UI served by Heroku at your Heroku app URL.

Step 10. If you want to update something.

Firstly, make the changes using your local copy of ZeroNet. For example, you can update “ZeroNet/data/users.json” to whitelist some new zsites by visiting them using your local ZeroNet copy. Or if you’re hosting your own zsite statically, you can post some new contents through your local ZeroNet, so your local copy of “ZeroNet/Data/1DCNTRLnCAGxhZh4GEbkLAJu8AVFAM82ui” is newer and has more contents than the Heroku copy.

Then, deploy the changes,

#(A) add the modified files to the local git repository:
git add .
#(B) commit the changes to the repository:
git commit -m "notes_on_changes"
#(C) deploy the code:
git push heroku master

If you only want to host your zsite dynamically, you don’t need to deploy again after you updated your zsite.

References:

https://github.com/HelloZeroNet/ZeroNet/issues/824 (Other modification is if you enable –multiuser_no_new_sites, then normal users will not able to add new sites (users in data/users.json still can))
https://github.com/HelloZeroNet/ZeroNet/issues/1011 (–multiuser_no_new_sites)
https://devcenter.heroku.com/articles/getting-started-with-python
https://stackoverflow.com/questions/21984960/escaping-a-dollar-sign-in-unix-inside-the-cat-command

Tuesday, November 18, 2014

Get URL and Read URL Parameters in JavaScript, HaXe and AS3

Find the URL of the embedding page of a swf is a very basic way for domain locking flash games. Similar things can be done for JavaScript based online games. To get the URL, in JavaScript you can use

var myURL=document.URL;
In HaXe (targeting JavaScript or HTML5), you can use
var myURL:String=js.Browser.window.document;
In AS3, to find the path url of the swf, you can use
root.loaderInfo.loaderURL;
//or
root.loaderInfo.url;
and for finding the path url of the embedding page, you can use
ExternalInterface.call("window.location.href");
Besides, it's common to see url parameters, for example: "http://mysite.com/index.html?param1=1234&param2=somestr&param2=someotherstr" To read the parameters, in the above example, that is "1234", "somestr" and "someotherstr", in JavaScript, you can use the snippet provided by http://stackoverflow.com/a/979995/1100006 or the function given at http://css-tricks.com/snippets/javascript/get-url-variables
In HaXe, you can use the following HaXe function
//translated from http://css-tricks.com/snippets/javascript/get-url-variables/
function getQueryVariable(variable):String
{
       var query:String = js.Browser.window.location.search.substring(1);
       var vars:Array = query.split("&");
       for ( i in 0...vars.length) {
               var pair:Array = vars[i].split("=");
               if(pair[0] == variable){return pair[1];}
       }
       return("null");
}
In AS3, if the parameters are given in the path of the swf' url, for example, "http://mysite.com/myswf.swf?param1=1234&param2=somestr&param2=someotherstr", or if the parameters are declared in flashvars, then you can simply use "root.loaderInfo.parameters" object to access all the parameters, for example,
var myStr:String = root.loaderInfo.parameters.param1;
However, to read parameters of the embedding page' url, you still need the help of JavaScript, see the following pages for an example:
http://snipplr.com/view/44852/how-to-access-query-string-arguments-with-as3/ http://www.abdulqabiz.com/blog/archives/2006/03/06/how-to-get-url-query-string-variables-within-flex-application/

References:
http://stackoverflow.com/questions/979975/how-to-get-the-value-from-url-parameter http://www.javascriptcookbook.com/article/Get-the-current-URL-via-JavaScript
http://stackoverflow.com/questions/2127962/get-current-browser-url-actionscript-3
http://snipplr.com/view/47055/get-url-of-the-page-where-swf-is-embedded/ http://snipplr.com/view/28103/as3-get-url-of-current-flash-movie-swf/

Saturday, November 15, 2014

Create a SWC for Flash & AS3 Projects from OpenFL

If your code is pure haxe, it's easy to generate an AS3 library swc file for your AS3 projects, as documented at http://old.haxe.org/manual/swc:

haxe -swf mylib.swc MyLibClasses --macro include('mypackage')
For example, to generate the "TextParser.swc" from "TextParser.hx", I used
haxe -swf TextParser.swc TextParser.hx
However, if your project is based on the OpenFL library, more efforts may be needed. Anyway, since you can compile your project to a swf, one ultimated way is to load the external swf at run time and get all public classes and methods inside using the "getDefinition" function from the "ApplicationDomain" class, see for example http://flassari.is/2008/07/swf-class-explorer-for-as3/. However, this way is not the most efficient and need you to write more code. For the most cases, it is possible to generate a swc file from your OpenFL based code. Things you need to do are
1. Rename the package name "openfl" to "flash", e.g., "openfl.display.Sprite" to "flash.display.Sprite".
2. Make your code independent of the openfl only classes and methods. For example, don't use "openfl.Assets.getBitmapData" directly in your code, use a external wrapper object as the assets host such as "myAssetsHost" and redirect all function calls to its public method which wraps "openfl.Assets.getBitmapData". Then in your AS3 project, you can reimplement the wrapper object using AS3 methods for handling assets.
An example is the JiuGongGe.swc, I refactored the original source code to make it independent of "openfl.Assets". It also depends on the library "Actuate", so I copied the library folder "motion" from the installed path "D:\HaxeToolkit\haxe\lib\actuate\1,7,5\motion" to the folder contains the source code of JiuGongGe, then use the command
haxe -swf JiuGongGe.swc JGG.hx  --no-traces --macro include('motion.Actuate')
to get the swc file.

Links:
http://old.haxe.org/manual/swc#creating-swc-with-haxe
http://www.openfl.org/archive/community/general-discussion/how-create-swc-openfl/
https://groups.google.com/forum/#!topic/haxelang/sld9ov4D-tA
http://stackoverflow.com/questions/13020201/how-could-i-convert-an-existing-swf-file-to-an-swc-for-using-as-a-library

Tuesday, November 11, 2014

TextParser - A Simple Class for Parsing Text Files

TextParser is a simple utility HaXe class for parsing text files. You can use this class to read String, Int variables from a text file. For example, the JiuGongGe UI uses a text file to configure the UI layout, so it needs to parse the text configuration file to do the set up.

The source code of the class is released with the JiuGongGe UI:
https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/src/TextParser.hx
TextParser is also available in AS3 as a swc:
https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/swc/TextParser.swc
To use the class, add the following line in your main class first:

haxe.initSwc(this);

The following is a simple AS3 example for using the TextParser class to parse a text file line by line and read some strings and integers needed:
var myUILayout:ByteArray = new myUILayoutClass() as ByteArray;
var myTextParser:TextParser = new TextParser(myUILayout.toString());
while (!myTextParser.EndofFile())
{
var Name:String = myTextParser.ReadString();
trace(Name);
if (Name == "#") //comment line
{
    myTextParser.GotoNextLine();
    continue;
}
var CallBackStr:String = myTextParser.ReadString();
trace(CallBackStr);
var IconStr:String = myTextParser.ReadString();
trace(IconStr);
var Color:uint = myTextParser.ReadInteger();
trace(Color);
var Label:String = myTextParser.ReadString();
trace(Label);
var LevelStr:String = myTextParser.ReadString();
trace(LevelStr);
myTextParser.GotoNextLine();
}
Basically, you need to put the parsing process inside a while loop. Where the function EndofFile() is used to check whether it is the end of the text file. The function GotoNextLine() will move the position of the file pointer to the next line. The functions ReadString() and ReadInteger() are used to read string and integer from the text file and update the file pointer's position.

Full source code for the example:
https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/swc/src/Main_TP.as

For a HaXe code example, please check the JiuGongGe UI's source code. 

Monday, November 10, 2014

JiuGongGe.swc - JiuGongGe UI for Flash AS3 Projects

For using the JiuGongGe UI in your Flash/AS3 projects, here is the pre-compiled swc file:
https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/swc/JiuGongGe.swc
An example FlashDevelop project:
https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/swc

To use "JiuGongGe.swc", first add the swc file to your project's library (path). Since the swc is built directly from original HaXe source code, there're two requirements:
1. Your main class must be a sub-class of "MovieClip" instead of "Sprite".
2. Add the following code before using the "JGG" class:

haxe.initSwc(this);

Check the post http://bruce-lab.blogspot.com/2014/11/jiugongge-ui-simple-open-source-ui.html for how to using the UI library - most of the HaXe code in the post also works in Flash. Note that the function "Assets.getText()" and "Assets.getBitmapData()" are provided by OpenFL, so you may need to embed the assets in your AS3 code like the example Flash project.

Wednesday, November 5, 2014

JiuGongGe UI - A Simple Open Source UI Framework in HaXe

JiuGongGe (means 9-cell) UI is a UI framework written in HaXe with OpenFL for all platforms, including Flash, HTML5, Android/iOS and Windows. This UI framework is designed to be lightweight and mobile friendly.

The framework is actually a set of buttons (clickable cells) in a nested hierarchy, with each level containing at most 9 cells. The UI framework is firstly created for the BNote application.

Basic Usage
The framework contains two main classes, "JGGCell" represents a single cell and "JGG" is the whole set of cells. You can customize the look and feel for the cells by modifying and extending the first class. For the most time, you're working with the second class. Firstly, you need to create an instance of the "JGG" class, which is a subclass of "Sprite", so you can add it to the stage and set the positions:

var myJGG:JGG = new JGG();
addChild(myJGG);
myJGG.x = 300;
myJGG.y = 200;
But nothing happens until you initialize the UI:
  
myJGG.init(this,this,Assets.getText("assets/UILayout.txt"));
Where the first parameter is the UI host,usually the parent of the the UI object. All event handler function of the click event triggered by cells should be declared in the UI host as public functions. For example, if you use the stage as the UI host, the following function in your Main class can be used as on click event handler:
 
public function CallBack(event:Event)
 {   
  trace(event.target.name);
 }

The second parameter is the assets host, which will provide the UI framework with icons. The assets host must implement the function "getIcon(ID:String):DisplayObject" as a public method, which will be called by the JGG class to fetch icon resources. For example:
 
public function getIcon(ID:String)
 {   
  return new Bitmap(Assets.getBitmapData("assets/" + ID));
 }
The return value can be a Bitmap, Sprite, or any other display object.

The third parameter is the String of the config file. The cells' hierarchy is declared in the string. Usually, it's better to write the config file in a text file, then you can use "Assets.getText" to get the string and pass it to the JGG's init function, other than directly embed the string content in your code. For each line of the config file, you declare one cell and specify the cell's property. It's not necessary to give all the 9 cells for a level. Just declare used cells only. You should give the following things (with out the "[" and "]") in order and delimited by space:
 
[name_of_cell] [on_click_callback_function_name] [background_color] [icon] [text_label] [index][newline] 
Note strictly one row for one cell, and no spaces within [...] - use "_" or "-" instead. For example,
StartCell JGG_ChangeLevel 0xffffff null Start 0
You can use "#" at the beginning of the line to comment:
# This is a comment line.

Now about some details of each property declaration.
[name_of_cell]: Will be used as the name of the JGGCell class instance. [on_click_callback_function_name]: Use the same name of the public method in the UI host. When the cell is clicked by user, this function will be called. You can use the shortcut string "JGG_ChangeLevel" for folding (for non-center cell)or unfolding (if it is the center cell) levels, without implementing the function in the UI host.
[background_color]: Background color of the cell, e.g., 0xffffff.
[icon]: An ID string used as a parameter of Assets host's public function "getIcon". Use the shortcut string "null" if you want to simply use a text label instead.
[text_label]: This is usually used for debugging or quick prototyping purpose. It's recommended use an icon instead of a text label. You can use "_" for space, "/" for newline in the label string. The label text will shown only if the icon string is "null".
[index]: To give the level information of the cell. For example "0,1,0" - integer sequence separated by ','. It is an array of indices for different levels. "0,1,0" means the cell's index in the root level is "0", in the first level is "1" and in the second level is "0". For each level, there're at most 9 cells, so the indices are all staring from "0" to "8". The cell's position is determined by the index of its last level. Starting from center as "0", go left as "1" then up as "2", then along a clockwise circle to "8".
How to interpret the index: Take "0,2,4,3,5" as an example. Look at the last (5-th) integer "5", it means the cell is in the 4-th level with index "5", so it's position is the center one on the right panel. Now the second last (4-th) integer "3", it means the cell's parent's index (in the 3-rd level) is 3. And the integer "4" means the cells' parent' parent's index is 4... In short, you can think the indices as an index of a multidimensional array, which specifies the position of the the cell in the hierarchy.

For a complete example of the config file, please check: https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/assets/UILayout.txt

Advanced Usage
To change cells' behavior, customize cell's look and feel etc.: You can use public functions foldLevel() and unfoldLevel(TargetCell:JGGCell) to change the levels manually, and getCell(name:String) to get a specific cell. The JGGCell class is a subclass of Sprite, so you can redraw the cell using its Graphics property. See the example's source code for more details: https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/src/Main.hx

Although the UI framework is very simple, I believe it can satisfy the most needs for a simple application with some customization. For example, check the BNote app. However, if you're looking for a more feature complete UI framework in HaXe, try http://haxeui.org/ or http://ui.stablex.ru.

Links:
Flash demo: http://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/bin/flash/bin/JiuGongGe.swf
HTML5 demo: http://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2/bin/html5/bin/index.html
Real Example (BNote): http://bruce-lab.blogspot.com/2014/10/bnote-free-online-taking-handwriting-notes.html
Source Code v0.2 (MIT): https://flaswf.googlecode.com/svn/trunk/JiuGongGeUI-v0.2
SWC for Flash/AS3: http://bruce-lab.blogspot.com/2014/11/jiugonggeswc-jiugongge-ui-for-flash-as3.html

Tuesday, October 28, 2014

BNote - A Free Online Simple Application for Taking Handwriting Notes

BNote is a simple note-taking app based on the the smooth drawing application by Dan Gries at http://www.flashandmath.com. The application is specially designed for touch enabled screen notebooks/tablets to take handwriting notes.


Features:

Smooth Strokes: Thanks to the smooth drawing function by Dan Gries.
Double Input Modes: Pen Input Mode for multi-touch enabled devices and Mouse Input Mode for non-touch screens.
Experimental Software Palm Rejection: Based on input pad, allowing users to rest part of their hands on the screen. Works only in Pen Input Mode and designed for capacitive touchscreen devices without hardware palm detection function and build-in stylus support.

Note the current release (Version 0.1) may contain bugs and deficiencies. Report of bugs, suggestions and feature requests are welcomed, just leave a comment below!

User Guides:

Writing: The app will switch between Pen Input Mode and Mouse Input Mode according to whether the device supports multi-touch mode.
For the Mouse Input Mode, you can use the mouse to write the notes just as any common graffiti board app.
For the Pen Input Mode, you can use a stylus or your finger to write on the board. The input pad (transparent circle) is the area where input/strokes will be accepted while any moves of pen or finger outside the input pad will not produce any stroke on the canvas. This is how the palm rejection works. This allows you to rest part of your hand on the screen, however, too much part of palm on the screen may prevent the app from detecting any moves inside the input pad. So if you find that you can't write anything inside the input pad (while the pen tip is out), try to lift your hand off the screen a bit to let the input pad get focus. It is recommended to use a small point tip stylus pen instead of rubber nib, such as Adonit JOT pro/mini for better writing experience.
How to move the input pad (for Pen Input Mode): During the writing when you want to reposition the input pad (e.g., when starting a new line), tap the pen icon (above the input pad) to stretch back the pen tip. Now you're in protection mode and you can tap any place to move the input pad there. Finally tap the pen icon to stretch out the tip and you can write again.
Settings: Tap the setting icon in the top-right corner to open the setting menu. In the setting menu you can Load/Save notes as a .png picture, switch to full screen, change pen color and thickness, undo last stroke and erase the notes.

Credits:

Smooth drawing application by By Dan Gries (dan@flashandmath.com)
UI Framework: JiuGongGeUI-v0.1 By Bruce Jawn
Icon Artist: Austin Andrews (@templarian)

Links:

http://www.flashandmath.com/advanced/smoothdraw/
http://www.nocircleno.com/graffiti/
http://modernuiicons.com/
Support this Free Sofware!

Wednesday, July 30, 2014

Migrating from Alchemy to CrossBridge/FlasCC - Interop between C/C++ and ActionScript

Part I - Calling C/C++ functions from AS3

For calling C/C++ functions from AS3, one way is use the "as3sig:" annotation to expose some C/C++ functions to AS3. See the CrossBridge Sample 5 SWC and my previous post http://bruce-lab.blogspot.com/2012/12/migrating-from-alchemy-to-flascc.html. Another way is to use "CModule.callI(CModule.getPublicSymbol("youCfunctionname"), args)", see the Sample 4 Animation, where for C++, you will need "extern" declaration before your function definition to prevent the compiler renaming the function.

Part II - Calling AS3 functions from C/C++

To call build-in AS3 functions, you can either use "inline_as3" (see Sample 2 Interop between C/C++ and ActionScript. Actually, asm was also available in Alchemy, although not officially documented) or the API provided by "AS3.h" and "Flash++.h". You can also define local AS3 functions or equivalent C/C++ functions using the two methods in your C/C++ source file. However, if you want to call an AS3 function outside your C/C++ file, e.g., in the "Main.as/Console.as", you may need to pass the reference of your AS3 function or the function host - your main AS3 class to C/C++. There are two different cases depending whether your run the C/C++ code in the background worker.

Case 1 - C/C++ code in UI worker.

If you invoke your C/C++'s main function using "CModule.start()", or "CModule.startAsync()", or just simply call some C/C++ function using the methods in Part I without executing the C/C++ main function, the C/C++ code will run in the UI worker, the same domain as your main AS3 console class. Then things are easy and there are various ways for passing your Main class's reference, see my examples:
https://flaswf.googlecode.com/svn/trunk/flaswfblog/Tutorials/CB_callAS3fromC/nobgcall

Case 2 - C/C++ code in background worker.

If you invoke your C/C++'s main function using "CModule.startBackground()", and want to call some outside AS3 function from your C/C++'s main function, then the thing become a little tricky, because you're not able to share a function reference between workers, i.e., there is no way to pass a function reference from the UI worker - your Main/Console class to the background worker - your C/C++ code, as the class/function reference can't be serialized in AMF3 format. There is one way to circumvent the problem, using MessageChannels:
https://flaswf.googlecode.com/svn/trunk/flaswfblog/Tutorials/CB_callAS3fromC/bgcall

In other situations, you can access your Main UI class's properties from the background worker by the "avm2_ui_thunk" and "CModule.serviceUIRequests" combinations, see http://bruce-lab.blogspot.com/2014/07/crossbridge-quake1-example-simplified.html for details.


Links:
http://forums.adobe.com/message/6002640#6002640

http://www.bytearray.org/?p=4423
http://www.bytearray.org/?p=4423#comment-494548

http://help.adobe.com/en_US/as3/dev/WS2f73111e7a180bd0-5856a8af1390d64d08c-8000.html
http://help.adobe.com/en_US/as3/dev/WS2f73111e7a180bd0-5856a8af1390d64d08c-7fff.html
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/system/Worker.html

http://jacksondunstan.com/articles/tag/workers
http://esdot.ca/site/2012/intro-to-as3-workers-hello-world
http://probertson.com/articles/2012/11/07/as3-concurrency-workers-use-cases-best-practices-links/

Tuesday, July 15, 2014

Crossbridge Quake1 Example Simplified

I made some minor modifications of the official Crossbridge Quake1 example so that the source code is easier to use and mod. The original source code uses a very different workflow from the old Alchemy, that is, compile everything, including the C/C++ code, AS code and data file, into the final swf file directly via a single Makefile. Although this approach is elegant and simple as a sample of the Crossbrige SDK, it will cause several inconveniences for a real project. The drawback is obvious, once you changed something, either the C/C++ code, AS code or the data file, you need to recompile everything.

On the other hand, the old Alchemy workflow, that is, compile all the C/C++ code into an independent swc first, then import the swc in the AS project, is more efficient. Although it is possible to use several separated makefiles for different steps of the whole compiling process, the old Alchemy workflow is more friendly to Flash developers.

So what I did in the simplified version is

1. Changed Makefile to compile the swc instead of swf.
The original Makefile mixes gcc and asc, where gcc is used to compile all the C source files into .o files and the final swf file, and asc is used to compile the "Console.as" file. I just removed the part for compiling the "Console.as" file. (The modified "Console.as" file will be used as the Main class for the AS project, in which it will be linked with the swc file later.) Then, changed the option "-emit-swf" for gcc to "-emit-swc" so the swc file, instead of the swf file, will be created.

2. Simplified the way for supplying the file "pak0.pak" to C/C++, so no "genfs" needed.
Crossbrige introduces genfs for the file system. This can be seen as an advantage over Alchemy, but personally, I feel it is not so easy to use. Crossbrige uses the genfs tool to convert all your files/folders needed in your C/C++ code into plain AS text files so the compiler can compile the converted data files into your swf. However, this is a disaster for modding - every time you change the data files, you need to genfs them into AS files first before they can be used in your Corssbrige projects. Fortunately, there is a simple alternative way (the Alchemy-like way, not officially documented, but actually use the same API as the genfs way) for supplying data to C/C++, see this post http://bruce-lab.blogspot.com/2013/11/migrating-from-alchemy-to.html for details. In this way, you handle all the data using AS3 only, so you can embed files using AS3 code, or load them on the run using a URLLoader.

There is one problem, beyond the quake example. That is when you need to supply many files or folders to C/C++. Manually embedding them in pure AS3 is troublesome. In this scenario, it seems the genfs tool will save your the trouble. However, this can also be solved by pure AS3. My solution is zip everything as one package first, then use some AS3 zip library (such as http://nochump.com/blog/archives/15, http://codeazur.com.br/lab/fzip/) to supply all files programmably. (Actually, the quake data file is a zipped package of many files and folders in a custom format ".pak", and the engine itself takes care of all the unzipping, parsing and loading processes.)

3. Created the FlashDevelop project.
Added the swc file generated previously to lib, modified the "Console.as" file, which imports classes/packages needed from the swc file, and embed the game data file in the Main class "Console.as". Besides, an unimplemented preloader class is added in the AS project. The official way for adding preloader directly for Crossbrige generated swf is also not very handy. With an AS project, it's trivial work to implement the preloader.

You can find the source code:
(SVN, source code only) http://flaswf.googlecode.com/svn/trunk/flaswfblog/Tutorials/CrossBridge_Example_Quake1_Simplified/
(All in One Package, everything you need to compile) http://code.flaswf.tk/2014/07/sdlquake1-for-crossbridge-simplified.html

What I learned from the original quake example

The most important files in the example are "Console.as", "sys_sdl.c", "snd_mix.c". The file "sys_sdl.c" is where you can find the C main function, besides, in the file "sys_sdl.c", function
engineTick() is the main game loop, and
engineTickSound() is the main sound loop.

The sdlquake example almost answers most questions when you want to port an SDL based application or game to Flash with Crossbridge.

Q1. How to start the main game loop?
There are two ways showed in the example to run the main game loop in each frame. The first one is to call the C/C++ main loop function engineTick() from AS3 in an EnterFrame event handler, see the line

CModule.callI(enginetickptr, emptyArgs)
in "Console.as". This is single threaded and everything, including both the main game loop and the screen buffer rendering, runs in the main UI worker only.
The second one is to use a background worker, so you can put the main loop function in an infinite while(true) loop in the C main function. In this case, you need two threads, the background worker (all C code) is running the main game loop and does the blitting job while the main UI worker only renders the screen buffer in the EnterFrame event handler. see this post http://bruce-lab.blogspot.com/2014/05/migrating-from-alchemy-to.html for more.

Q2. How to render the ScreenBuffer?
As I explained in http://bruce-lab.blogspot.com/2012/12/migrating-from-alchemy-to-flascc.html, all you need to do is to get pointer to the Screen Buffer(data/array of colour values) of your C/C++ code, see the line
vbuffer = CModule.getPublicSymbol("__avm2_vgl_argb_buffer")
in "Console.as", then create a BitmapData and use the setPixels method to render the buffer.

Q3. How to get the Keyboard input?
Of course you need to listen the KEY_UP and KEY_DOWN events in AS3. Then you can implement the "read" function in "Console.as", so that key inputs can be read by C using normal C IO.

Q4. How to get the Mouse input?
The example showed how to let C know the mouse position. Firstly, mouse position "mx" and "my" can be captured in AS3.
If you're running the main loop in the UI worker (ST), get the pointer of the C variables for storing mouse position:
vgl_mx = CModule.getPublicSymbol("vgl_cur_mx")
vgl_my = CModule.getPublicSymbol("vgl_cur_my")
Then use domain memory to update the values of the two C variables directly in AS3:
CModule.write32(vgl_mx, mx)
CModule.write32(vgl_my, my)

If you're in MT mode, things are a little tricky. The "handleFrame()" in "sys_sdl.c" is for getting mouse input. The function used inline asm to get the values for the mouse positions.
inline_as3(
"import com.adobe.flascc.CModule;\n"
"%0 = CModule.activeConsole.mx\n"
"%1 = CModule.activeConsole.my\n"
: "=r"(vgl_cur_mx),"=r"(vgl_cur_my) :
);
Since you're running the main loop in the background and mouse position can only be retrieved from the UI worker, you need the
avm2_ui_thunk(handleFrame, NULL);
(in C, the main game loop, where you need to update the mouse position, to queue up uiThunk request for calling handleFrame on the UI Worker)
and
CModule.serviceUIRequests()
(in AS3, the EnterFrame handler, to service the pending uiThunk request) combination.

Q5. How to play the sound data?
Use Sound and SoundChannel class in AS3 to play sound data. To get the sound sample data, use inline asm to writeFloat, and the proxy variable "sndDataBuffer" in AS3, see "snd_mix.c" and "Console.as" for details.

Q6. How to supply the files to C?
See the previous section where I talked about the file system.

Some other notes:

1. If you're using 32-bit system with 32-bit java, you need to pass the option -jvmopt="-Xmx1G" to gcc/g++, otherwise, you may get the error "LLVM ERROR: Error: Unable to launch the Java Virtual Machine. This usually means you have a 32bit JVM installed or have set your Java heap size too large. Try lowering the Java heap size by passing "-jvmopt=-Xmx1G" to gcc/g++."

2. On windows, when compiling the original source code, if the compiler complains that "Console.as" is locked and not accessible, try to use some software to unlock the file "Console.as", then compile again.

Special thanks to Michael Guidry (MadGypsy on quakeone.com) for motivating me to write this post.

Thursday, May 1, 2014

Migrating from Alchemy to FlasCC/CrossBridge - The flyield() Method Alternative

In Alchemy, when you're compiling and reusing C/C++ code in Flash, you may encounter the problem caused by loops. In C/C++, it's very common to use a infinity loop such as

while(1)
{
}
or
for(;;)
{
}
where we will put everything such as main game logic, into the loop and break the loop when certain conditions are satisfied. However, this way doesn't work for Flash. At the time of Alchemy, Flash/Action Script 3 was still 'single' threaded. This infinity loop will block everything - no UI updates, no interaction responses, the Flash program looks like dead - as everything else is waiting for the loop to finish. One way to solve the problem is to break this C/C++ run-loop into frame by frame calls on the AS3 side (See CrossBridge SDK's "Sample 4: Animation" for details). At that time, we have one handy function called "flyield()" in the Alchemy C/C++ API, to simplify the solution. All you need is to stuff such function into those infinity loops, and declare the functions contains those infinity loops as "AS3_FunctionAsync". And when Flash sees this "flyield()" function, it will know that it should freeze the loop for a while, jump out to update the UI and let other things work, then come back to run the loop again.

However, there is no such function "flyield()" any more in FlasCC/CrossBridge. But now, we have two ways to deal with such infinity loops. One is the way provided by the newly introduced concurrency API, the other is the classical "flyield()"-like way.

Thanks to the new concurrency API of Action Script 3, you will never need to worry about the error that "Error #1502: A script has executed for longer than the default timeout period of 15 seconds". With workers, you can do all the computation intensive calculations in the background, and let the UI runs in main thread. So those intensive calculations will not block your whole program any more. And the concurrency API is also available in FlasCC/CrossBridge. To use the concurrency API, you can check the "Sample 9 - Pthreads" in the CrossBridge SDK.

In FlasCC/CrossBridge, you simply can run all the C/C++ code in the background worker by the following line in your "Console.as".
CModule.startBackground(this, new [], new [])
The most common case is in your Main loop, you need to pause the logic and let the UI listen to the user input. In many C/C++ applications, the waiting for user input model is an infinity loop. In this case, you can use "avm2_self_msleep" to pause the loop and add an input event listener, once get the user input, use "avm2_wake" to resume the loop and the main logic.
See the following source code of for more details:
https://flaswf.googlecode.com/svn/trunk/Games/Sanguosha/lib
(especially the file "FlasCCPortLayer.cpp". Update: (2014/7/18) Alternative way for getting input is to use the "avm2_ui_thunk" and "CModule.serviceUIRequests" combination, see the Crossbridge quake1 example for details.)

The above way works for some cases, for example when the C/C++ function consume a lot time to run, but is not so convenient for many C/C++ game logic as the old " flyield()" function in the aforementioned case. You need to reorganize the code structure using workers. To make things easy, actually, there is an almost equivalent function in FlasCC/CrossBridge - "avm2_wait_for_ui_frame".
To use this "flyield()" alternative, you must also run your C/C++ code in the background. In other words, you need the following code in your "Console.as" again:
CModule.startBackground(this, new [], new [])
Then, all you need to do is to insert the following code
avm2_wait_for_ui_frame(0);
somewhere in the infinity loop.

One drawback of this method is that you need to put the C/C++ loop in your main function. This is not very convenient as the old "AS3_FunctionAsync" declaration in Alchemy when you want to pass some parameters into the loop. One way is to initialize parameter as global variables in C/C++ by passing values from AS3 side before call the C/C++ main function(CModule.startBackground), another way is to use the C/C++ "argv" - the second parameter of CModule.startBackground. Well, this may solve the problem to pass parameters, another important issue is that how to return values from C/C++ main to AS3? So you can see this workaround, which only works with the C/C++ main function, is still not so convenient as the old asynchronous functions in Alchemy. In this case, different from the main logic loop, actually you usually want a C/C++ function to do some time consuming calculation and return the result, while not be restricted by the Flash script execution time limit. To resolve all the difficulties and troubles fundamentally, you'd better use the more advanced and complicated Pthreads (See "Sample 9: Pthreads") and Workers (run the FlasCC/CrossBridge wrapper function in the background as what you can do with a time consuming AS3 function, see for example http://esdot.ca/site/2012/intro-to-as3-workers-hello-world) with FlasCC/CrossBridge.

Source code of an example using avm2_wait_for_ui_frame:
https://flaswf.googlecode.com/svn/trunk/flaswfblog/Tutorials/flyieldAlternative

Links:
http://stackoverflow.com/questions/13560133/increase-flash-script-execution-time-flascc
http://forums.adobe.com/message/6227761

Sunday, March 30, 2014

Use Adobe Air to build a mobile APP for your Blog

With Adobe Air, you can easily create a mobile APP for your Blogger powered blog site. What you need is the StageWebView class, which can load and show any html/javascript based web site on the stage within your APP.

But firstly, go to your Blogger dashboard, in the Template tab, enable the Mobile template. (See this post for more details: http://blogger-hints-and-tips.blogspot.com/2012/05/mobile-templates-for-blogger-and-why.html) After this, when visit your Blogger site from mobile device (via mobile browser or APP), your blog will be displayed in a mobile friendly way.

Next, all we need to do is to load your blog url and display it on the stage using the StageWebView class. Here are some code:

var webBrowser:StageWebView = new StageWebView();
webBrowser.stage = this.stage;
webBrowser.viewPort = new Rectangle(0, 0, this.stage.stageWidth, this.stage.stageHeight);
webBrowser.loadURL("http://yourblogurl.blogspot.com");

With these code, the Air APP can load and display your blog by itself. Sometimes, you want to call the device's browser for opening the external links - links not belong to your blog, so the APP behaves more like a blog APP other than a simple browser for viewing any url. To do this, you can use the LOCATION_CHANGING event of the StageWebView class. Whenever a user clicked a link, check if it is an external link, then decide whether to display it within the APP using StageWebView class or use the navigateToURL method to open the link by external web browsers. The code:
webBrowser.addEventListener(LocationChangeEvent.LOCATION_CHANGING, onLocationChanging);
function onLocationChanging(event:LocationChangeEvent):void
         {
                   var newURL:String = event.location;
                   if (newURL.search('blogspot.') == -1)
                   {
                   event.preventDefault();
                   navigateToURL(new URLRequest(newURL));
                   }
          }

Besides, we may also interested in the "historyBack()" and "historyForward()" methods. With these two methods, you can navigate to the previous/next page in the browsing history. Actually, you can even create a fake mobile browser using the API provided by StageWebView class after adding UIs.

Finally, if you want, you can publish your Blogger site with the mobile app to Google Play or the App Store.

The Demo of my Blog (APK for Android):
https://drive.google.com/file/d/0B5V2PrQ8xX_EbzA4TzdBNUwzZXc/edit?usp=sharing

Full FlashDevelop project with source code:
https://flaswf.googlecode.com/svn/trunk/flaswfblog/Tutorials/BloggerApp/

Links:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/StageWebView.html
http://help.adobe.com/en_US/as3/dev/WS901d38e593cd1bac3ef1d28412ac57b094b-8000.html
http://www.adobe.com/inspire-archive/february2010/articles/article5/index.html
http://www.adobe.com/devnet/air/quick_start_as/quickstarts/qs_using_stage_web_view.html
http://www.flashandmath.com/mobile/swv/
http://www.yeahbutisitflash.com/?p=3996
http://thatsthaway.blogspot.com/2012/07/display-and-remove-documents-in-air.html
http://sjespers.com/blog/2011/05/17/displaying-ads-in-your-mobile-air-application/
http://soenkerohde.com/2010/11/air-mobile-stagewebview-uicomponent/
http://www.lucentminds.com/archives/adobe-air-web-browser.html
http://sean.voisen.org/blog/2010/10/making-the-most-of-stagewebview/
http://www.as3gamegears.com/misc/stagewebviewbridge/

Sunday, November 3, 2013

Migrating from Alchemy to FlasCC/CrossBridge - Simplify the File System Using CrossBridge's "addFile" as an Alternative to "supplyFile" in Alchemy

Many times, your C/C++ code may rely on external files to work, such as texture files/model files for a 3D engine. One way to supply those file to the CModule in CrossBrige is to use the memory API, which is fast, but need more wrapper codes. Sometimes, use the standard C/C++ I/O API, such as "FILE" and "ifstream", is more convenient for porting existing C/C++ libraries.

CrossBridge needs to process the external files using "genfs" before you can use them. That's why I tried the URLLoaderVFS, a class by twistedjoe from http://forums.adobe.com/thread/1147910. And for different kinds of methods of loading files - Embed, use http/URLLoader and use local shared objects, you need to genfs the external files differently and link with different .as class, which is detailed documented at http://www.adobe.com/devnet-docs/flascc/docs/Reference.html#section_vfs.
However, these are for compilng a swf directly from C/C++ source files. If you compile C/C++ to a swc first, and then compile the swf from your swc and a main.as file, things can be greatly simplified.

I always thought the FlasCC/CrossBridge's file system is more complicated than Alchemy, before I realized that there is a similar way for supplying external files to the C module.

In Alchemy, we only need to use the function "supplyFile(fileName:String, fileData:ByteArray)", and handle the different methods of loading the data file in AS3, use the "Embed" tag, URLLoader or local shared objects.
In CrossBridge, actually we have an alternative function - CModule.vfs.addFile(fileName:String, fileData:ByteArray).
You only need to call the function to supply the data file needed before the line "CModule.startAsync(this);".
And for the data file itself, you can use AS3 code to load. No matter using which ways, treat them as ByteArrays. Once they are loaded or ready, call the function "addFile".

You can check the source code for details:
https://flaswf.googlecode.com/svn/trunk/flaswfblog/Tutorials/CrossBridgesupplyFile/

Sponsors