Saturday, December 11, 2010

Comicbook.js - Release 0.3

For my 0.3 release of comicbook.js I've attempted to optimize the combination of closepixelate.js and video by writing the image to a canvas in memory then copying the image over to the main canvas when the write was finished. View the demo here.

Using the function renderToCanvas() allows me to buffer the write and return the result

  renderToCanvas: function (self, renderOptions, width, height, renderFunction) {
    var buffer = document.createElement('canvas');
    buffer.width = width;
    buffer.height = height;
    renderFunction(buffer.getContext('2d'), renderOptions, self);
    return buffer;
  }

var cached = this.renderToCanvas(
                self,
                renderOptions,
                this.c1.width,
                this.c1.height,
                function (ctx, renderOptions, self) {               
                    var imgData = self.ctx1.getImageData(0, 0, self.c1.width, self.c1.height);//.data;
                    self.ctx1.clearRect( 0, 0, self.c1.width, self.c1.height);
                    for (var i=0, len = renderOptions.length; i < len; i++) {
                     var opts = renderOptions[i],
                          res = opts.resolution,
                          // option defaults
                          size = opts.size || res,
                          alpha = opts.alpha || 1,
                          offset = opts.offset || 0,
                          offsetX = 0,
                          offsetY = 0,
                          cols = self.c1.width / res + 1,
                          rows = self.c1.height / res + 1,
                          halfSize = size / 2,
                          diamondSize = size / Math.SQRT2,
                          halfDiamondSize = diamondSize / 2;

                    offsetX = offsetY = offset;
                   
                     for ( var row = 0; row < rows; row++ ) {
                        var y = ( row - 0.5 ) * res + offsetY,
                          // normalize y so shapes around edges get color
                          pixelY = Math.max( Math.min( y, self.c1.height-1), 0);

                        for ( var col = 0; col < cols; col++ ) { 
                          var x = ( col - 0.5 ) * res + offsetX,
                                // normalize y so shapes around edges get color
                                pixelX = Math.max( Math.min( x, self.c1.width-1), 0),
                                pixelIndex = ( pixelX + pixelY * self.c1.width ) * 4,
                                red = imgData.data[ pixelIndex + 0 ],
                                green = imgData.data[ pixelIndex + 1 ],
                                blue = imgData.data[ pixelIndex + 2 ],
                                pixelAlpha = alpha * (imgData.data[ pixelIndex + 3 ] / 255);

                            ctx.fillStyle = 'rgba(' + red +','+ green +','+ blue +','+ pixelAlpha + ')';

                          switch ( opts.shape ) {
                             case 'circle' :
                                ctx.beginPath();
                                  ctx.arc ( x, y, halfSize, 0, self.PI2, true );
                                  ctx.fill();
                                ctx.closePath();
                                break;
                             case 'diamond' :
                                ctx.save();
                                  ctx.translate( x, y );
                                  ctx.rotate( self.PI1_4 );
                                  ctx.fillRect( -halfDiamondSize, -halfDiamondSize, diamondSize, diamondSize );
                                ctx.restore();
                                break;
                             default : // square           
                                ctx.fillRect( x - halfSize, y - halfSize, size, size );
                          } // switch       
                        } // col
                     } // row   
                  } // options
                }

this.ctx1.drawImage(cached, 0, 0);

This technique has definitely helped the performance. Another method of optimization that is probably more efficient is to eliminate the canvas draw functions altogether and create custom functions which modify the image data array directly.

I'm still stumped as to how to actually create the Halftone/Duotone effect which is pretty disappointing. Though the course is now over I'll continue working on this project on my own time.

Wednesday, December 8, 2010

Testing Firefox changes

The next step after customizing Firefox to open new tabs adjacent to the current tab, was to create an automated test to validate that everything works in all situations.

The first step is to (re)build with the enable-tests option  by placing "ac_add_options --enable-tests" in .mozconfig

Now we'll need to write our test() function using assertions; ok(), is(), isnot()

ok(val, "val exists!");                        // Tests existence
is(val1, val2, "val1 equals val2");            // Tests equality
isnot(val1, val2, "val1 does not equal val2"); // Tests if not equal

My test script looks like this:

function test() {
 var firstTab = gBrowser.addTab();

 var tabs = gBrowser.tabs;

 is(tabs.length, 2, "2 tabs are open");
 is(gBrowser.selectedTab._tPos, 0, "First tab is selected");

 var newTab = gBrowser.addTab();

 is(gBrowser.selectedTab._tPos, 0, "first tab selected");
 is(tabs[2]._tPos, newTab._tPos, "Was inserted at #3");

 gBrowser.moveTabTo(newTab,0);
 is(newTab._tPos,0, "moved new tab to first position");
 var tempLen = tabs.length;
 gBrowser.removeTab(newTab);
 is(tabs.length, tempLen-1 ,"deleted newTab");

 while (tabs.length > 1)
  gBrowser.removeCurrentTab();
}

So where do we put this file?
src\obj-i686-pc-mingw32\_tests\testing\mochitest\browser\browser\base\content\test\browser_tab_test.js

To run the test script run navigate to the src directory and run:

 TEST_PATH=browser/base/content/test/YOUR_TEST_SCRIPT.js make -C $(OBJDIR) mochitest-browser-chrome

When the test finishes you should see the results as either TEST-PASS or TEST-UNEXPECTED-FAIL.

Sunday, December 5, 2010

Customizing Firefox

Continuing with Firefox our next assignment was to modify the browser slightly by making new tabs appear right after the current tab. Since we already know how to get the source code and build it it's just a matter of finding the code we need to modify.

One way of searching through the code is to use http://mxr.mozilla.org.

We eventually found our way to this snippet of code: http://mxr.mozilla.org/mozilla-central/source/browser/base/content/tabbrowser.xml#1326

1321             // Check if we're opening a tab related to the current tab and
1322             // move it to after the current tab.
1323             // aReferrerURI is null or undefined if the tab is opened from
1324             // an external application or bookmark, i.e. somewhere other
1325             // than the current tab.
1326             if ((aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) &&
1327                 Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
1328               let newTabPos = (this._lastRelatedTab ||
1329                                this.selectedTab)._tPos + 1;
1330               if (this._lastRelatedTab)
1331                 this._lastRelatedTab.owner = null;
1332               else
1333                 t.owner = this.selectedTab;
1334               this.moveTabTo(t, newTabPos);
1335               this._lastRelatedTab = t;
1336             }

Just by reading the commented code you'll see that this is exactly what we're looking for. It checks to see if the current tab is related to the new tab and will open it next to the current if true. So by commenting out the first "if" statement we will essentially make all new tabs open next to the current. Our new changes will look like this:

-            if ((aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) &&
-                Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
+            //if ((aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) &&
+            //    Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
               let newTabPos = (this._lastRelatedTab ||
                                this.selectedTab)._tPos + 1;
               if (this._lastRelatedTab)
                 this._lastRelatedTab.owner = null;
               else
                 t.owner = this.selectedTab;
               this.moveTabTo(t, newTabPos);
               this._lastRelatedTab = t;
-            }
+            //}

Thats it, we didn't even have to write any new code, simply commenting out several lines did what we were looking for. Finally build the source code again.

Building Firefox from source

Building Firefox was a lot easier than I thought it would be. When we were initially given the assignment I felt pretty lost because I had no idea what building the code would entail. After doing the research I found that the steps involved are actually quite simple:

Step 1 - Installing Mercurial:

Download Mercurial here and run the setup. The installation will create c:\mozilla-build by default. When the installation is done navigate to the mozilla-build folder and depending what version of visual studio you have you'll run one of:

  • start-msvc8.bat  (VS 2005)
  • start-msvc9.bat  (VS 2008)
  • start-msvc10.bat (VS 2010) 
Step 2 - Downloading the source code:

Run these commands to download the source code and move to the folder:

hg clone http://hg.mozilla.org/mozilla-central/ src

cd src

Step 3 - Building:

Create the config file and build the code by running these commands:

echo '. $topsrcdir/browser/config/mozconfig' > .mozconfig
echo 'mk_add_options AUTOCONF=autoconf2.13' >> .mozconfig
make -f client.mk build

Step 4 - Running you build

Navigate to ~/src/obj/dist/bin/firefox.exe to run your new build

Comicbook.js - Release 0.2

My initial plans after 0.1 of completing the halftone/duotone effect hasn't gone the way I had hoped. Trying to create it on my own has been extremely difficult, so I went to Dave Humphrey for some advice. He introduced me to the Close Pixelate library which creates various pixelation effects on still images:



These effects are partially what I was trying to accomplish. As a result my 0.2 goal became combining close-pixelate.js with video/canvas. You can view my demo here.

There are a few problems that need to be solved for my next release. First is optimizing the code, when a couple or more effects are used it creates a lot of lag due to several nested for loops doing a huge number of canvas drawing. Second is working on the halftone/duotone effect.

I'll also be working with Kenneth Pangilinan on including these effects in Candy.js.

Sunday, November 14, 2010

Mashing up Google Maps and Flickr

I've combined the Google maps api and the Flickr api to produce a mash-up of how they can work together. A demo can be viewed here.

When using Google maps, you'll have to generate a key to point to a domain or directory. You'll also need to generate a key for Flickr, so they can monitor what you're doing with the pictures.

Key pieces of code:

1.


var map = new GMap2(document.getElementById("map"));
window.map = map;
        
map.setCenter(new GLatLng(43.643125,-79.397635), 13);
map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl());

GEvent.addListener(map,'moveend',onMapMove);
GEvent.addListener(map,'zoomend',onMapZoom);

The above code creates a map centered at the longitude 43.643125 and latitude -79.397635 which is Toronto. It then adds some controls to the interface to zoom and change views. Finally events are added to update the images.

2. Retrieving the json data using jquery

$.getJSON("http://api.flickr.com/services/rest/?jsoncallback=?",  
{   
  method: "flickr.photos.search",
  tags: $("#tag").val(),
  min_upload_date: "2008-01-01",
  bbox: map.getBounds().getSouthWest().lng() + 
    "," + map.getBounds().getSouthWest().lat() + 
    "," + map.getBounds().getNorthEast().lng() + 
    "," + map.getBounds().getNorthEast().lat(),
  extras: "geo",
  per_page: $("#photonum").val(),
  format: "json",  
  api_key: "708eb7457e4afba46ac064c709db0d04",
},
function(data) {
  $.each(data.photos.photo, function(i,item){   
    var url = "http://farm" + item.farm + ".static.flickr.com/" + 
item.server + "/" + item.id + "_" + item.secret + "_m.jpg";
    $("<img/>").attr("src", url).appendTo("#images");
  });  
});
 
The getJSON method takes the URL and the following array to build a query for returning json.
The following anonymous function is a callback which will handle the retrieved data. 
It parses the json and puts the url of the picture in the div container.

Friday, November 5, 2010

Learning JavaScript

My initial impression on JavaScript was that it was a basic language that wasn't really useful especially when you're writing a web page in ASP.NET or Java. After watching the lectures by Douglas Crockford I realized that JavaScript is a very powerful language that is simply structured differently, ex. functions are used to do almost everything.

I found it odd that there were many instances where something was clearly not working in the language and were never fixed or adjusted, ex. the typeof function will return object on null and array.

Tips and tricks:

1. Comparison operators (===) and (!==) will compare both type and value which makes it faster than == and != and more reliable.

2. Don't use the Integer, Boolean objects, etc. They have no advantage.

3. If a variable is used but not declared it's recognized as a global variable.

4. parseInt("08") === 0
    parseInt("08",10) === 8


Here are some useful links:

Coding Conventions: http://javascript.crockford.com/code.html

JSLint - Provides code validation: http://www.jslint.com/

Monday, September 20, 2010

First Lab

Our first lab in OSD600 was to run the experiments at chromeexperiments on the nightly builds of Chrome (Chromium) and Firefox (Minefield) and determine which browser was faster and why. Each student was delegated 10 experiments to test, I was assigned 61-70.

The results show pretty conclusively that Chrome ran the experiments much better, 9/10 of them ran faster on Chrome and the last test ran equally on both browsers. The results of my test can be found here.



Brian

Indroduction

Hi,

My name is Brian Law and I'm in the 6th and last semester in the CPA program. I've just completed an 8 month co-op work term so almost everyone I've met this semester is new. I'm really excited about the classes I'm taking (GAM666, OSD600, DBS501, PRJ666) and hoping to learn a lot.

Here's a link to my wiki:
http://zenit.senecac.on.ca/wiki/index.php/User:Blaw1



Brian

Thoughts on some articles

The article called The Cathedral and the Bazaar by Eric Raymond talks about his experience applying "Linus' Law". It was a very interesting read particularly because he gives some very good suggestions for starting ones own open source project, such as being charismatic and releasing builds early and often. I'm sure during the OSD600 course we will also be applying "Linus' Law", and I'm excited to see how our projects will turn out.


The second article was For Mozilla and Google, Group Hugs Get Tricky is about the competition that Mozilla faces in Google, Microsoft and Apple. A large portion of Mozillas funds come from Google and since Google has come out with Chrome this articles brings up the question of what will Mozilla do if Google decides to pull the plug on the deal making Google the default homepage of Firefox. If it were to happen I believe Mozilla would be able to reach a deal with another search engine.