When you layer Bitmap images on top of each other in Flash/Flex/AIR, an interesting thing happens. Mouse events are captured by the top-most image and aren’t passed through to anything underneath. Even when hovering over, or clicking on transparent areas, the events just don’t get through. Moses over at MosesSupposes created an InteractivePNG solution that does some interesting mouse handling and hit detection.

In the above example (click the image, right-click to view src), I have two transparent png bitmaps layered on top of each other. On the left side, I’m using the standard BitmapImage tag. When you hover over any part of the images (even transparent areas), you’ll notice that you get the SNOWFLAKE! tooltip. Since the snowflake is on top, it is capturing all mouse activity so nothing gets through to Santa underneath.

On the right, I’m using BitmapSprite. When you have a Sprite that is rendered using graphics drawing, mouse events are only captured in areas where the graphics object has physically been drawn on. BitmapSprite is designed to take an input Bitmap, convert it into 1-pixel tall rectangle bitmap fills that are drawn to the sprite’s graphics object. In this manner, any transparent pixel doesn’t capture mouse events and everything is passed down to objects beneath. When you hover over a snowflake pixel, you get the tooltip SNOWFLAKE!, and when you hover over santa, you get a SANTA! tooltip. With this solution, we’re also caching the result as a bitmap so we only pay the rendering penalty once. No mouse-handling or hit detection kung-fu necessary. Nifty!

Post to Twitter

Posted by Andrew, filed under as3, Flex. Date: December 1, 2010, 12:07 pm | 10 Comments »

The Google 3D Maps API for Flash/Flex is a very impressive tool. It allows you to do 3d perspective right inside your .swf application similar to Google Earth.

As part of this API, you can specify a custom-designed clickable marker icon to be displayed on your map. Usually, this is done by setting the icon property of MarkerOptions.

var bmp:Bitmap = loader.content as Bitmap;

var markerOptions:MarkerOptions = new MarkerOptions(
    {
        icon: bs,
        iconAlignment: MarkerOptions.ALIGN_BOTTOM | MarkerOptions.ALIGN_HORIZONTAL_CENTER,
        hasShadow: true,
        tooltip: member.name + " - " + member.phone
    }
); 
           
var marker:Marker = new Marker(latLng, markerOptions);
           
marker.addEventListener(MapMouseEvent.CLICK, handleMarkerClick);
           
map.addOverlay(marker);

The problem with using a bitmap that includes transparency is that it captures mouse events even when hovering over the transparent areas. In one map I designed, I’m using billboard markers that contain quite a lot of transparent space. Even when it looks like I’m hovering over the icon in the background, all the mouse events are being gobbled up by the billboard. You can see this in the following screenshot by looking at the tooltip.

I noticed that if I used a custom-drawn Sprite using the graphics drawing commands, the mouse only captures events where I’ve actually drawn something.

In order to solve this problem, I created a custom BitmapSprite class that accepts bitmapData, converts it to one pixel tall rectangle drawing commands, and then added a sharpening filter to clean up the result (since vectorizing the bitmap seemed to have a blurring effect). Adding a filter at the end also has the added benefit of turning on cacheAsBitmap which improves map performance.

BitmapSprite.as

public class BitmapSprite extends Sprite
{
    public function BitmapSprite(data:BitmapData)
    {
        super();
           
        var x:uint;
        var y:uint;
        var alpha:uint;
        var color:uint;
        var width:uint;
        var fillDict:Dictionary = new Dictionary();
        var rectVect:Vector.<Object>;
        var pixelIndex:uint = 0;
        var pixelData:Vector.<uint> = data.getVector(data.rect);
           
        //calculate
        for(y=0; y < data.height; y++)
        {
            for (x=0; x < data.width; x++)
            {
                alpha = pixelData[pixelIndex] >> 24 & 0xFF;
                   
                //only draw pixels that are visible
                if(alpha > 0)
                {
                    color = pixelData[pixelIndex] & 0xFFFFFF;
                    width = 1;
                    if(pixelIndex+1 < pixelData.length)
                    {
                        while(pixelData[pixelIndex] == pixelData[pixelIndex+1])
                        {
                            width++;
                            pixelIndex++;
                            if(pixelIndex+1 == pixelData.length)
                                break;
                            if(x + width == data.width)
                                break;
                        }
                    }
                    rectVect = fillDict[pixelData[pixelIndex]];
                    if(rectVect == null)
                    {
                        rectVect = new Vector.<Object>();
                        fillDict[pixelData[pixelIndex]] = rectVect;
                    }
                       
                    rectVect.push({x: x, y: y, width: width, color: color, alpha: alpha / 255.0});
                       
                    x += width-1;
                }
                else if(pixelIndex == 0 || pixelIndex == pixelData.length-1)
                {
                    //this is either the first or last pixel.
                    //draw it anyway even though it's invisible
                    //so our resulting Sprite has the correct dimensions.
                    rectVect = fillDict[pixelData[pixelIndex]];
                    if(rectVect == null)
                    {
                        rectVect = new Vector.<Object>();
                        fillDict[pixelData[pixelIndex]] = rectVect;
                    }
                    rectVect.push({x: x, y: y, width: width, color: color, alpha: 0});
                }
                pixelIndex++;
            }
        }
           
        //do drawing
        graphics.clear();
        for each(rectVect in fillDict)
        {
            var rectData:Object = rectVect[0];
            graphics.beginFill(rectData.color, rectData.alpha);
               
            for each(rectData in rectVect)
            {
                //do a rectangle
                graphics.drawRect(rectData.x, rectData.y,rectData.width,1);
            }
            graphics.endFill();
        }
    }
}

Usage:

var bmp:Bitmap = loader.content as Bitmap;
var bs:BitmapSprite = new BitmapSprite(bmp.bitmapData);
var sharpen:ConvolutionFilter = new ConvolutionFilter(3, 3, [0, -1, 0, -1, 10, -1, 0, -1, 0], 6);
bs.filters = [sharpen];

var markerOptions:MarkerOptions = new MarkerOptions(
    {
        icon: bs,
        iconAlignment: MarkerOptions.ALIGN_BOTTOM | MarkerOptions.ALIGN_HORIZONTAL_CENTER,
        hasShadow: true,
        tooltip: member.name + " - " + member.phone
    }
); 
           
var marker:Marker = new Marker(latLng, markerOptions);
       
marker.addEventListener(MapMouseEvent.CLICK, handleMarkerClick);
           
map.addOverlay(marker);

After making these changes, map performance doesn’t seem to be affected except for a small increase in the initial load time for each marker. The results speak for themselves.

Post to Twitter

Posted by Andrew, filed under Uncategorized. Date: November 17, 2010, 3:00 pm | 1 Comment »

On a project I’m working on, I need to have semi-transparent containers to hold controls over top of screen-filling bright and colorful background. While I’m not 100% satisfied with it, I think it will suffice for the time being.

I was originally going for an effect similar to the window borders in Windows 7 where the background image is blurred when viewed through the glass. I couldn’t find an easy way to do that effect in Flex 4. If you know of a way, please comment below.

Live Demo – Right-Click to View Source

Post to Twitter

Posted by Andrew, filed under as3, Flex. Date: July 31, 2010, 11:03 pm | 2 Comments »

This summer, my two children have expressed an interest in learning about what daddy does. I’ve never taught kids to program before, but I wanted to share what we’ve been working on so far.

The first thing that we did was find good logic computer games to work on. The best I’ve found so far is lightbot. You give a little robot commands and even call functions to tell him how to move and light up tiles. The downside to this game is that it gets challenging very fast. First and second graders can’t really handle nested loops too well when starting out.

http://armorgames.com/play/2205/light-bot

After some experience with lightbot, we were able to translate some of those concepts directly into the KTurtle environment on Edubuntu linux. KTurtle is a graphical drawing program that moves a pen (the turtle) around a canvas. You move the turtle by issuing text commands such as “forward 100″ and “turnright 90″. I’ve given the kids some challenges to work toward on our white-board. For anything that required a bit of trig and the Pythagorean theorem, I gave them the numbers.

IMAG0015.jpg

How do you teach kids to program? What suggestions do you have for me?

Post to Twitter

Posted by Andrew, filed under Linux. Date: June 2, 2010, 9:18 am | 1 Comment »

In order to debug on your new HTC Incredible smartphone on Windows 7, you need to install USB drivers from Google. Well, it turns out that the phone is too new for Google to have included support for the Incredible in their driver package. Here is how I got it all working though. It may or may not work for you.

1.) Install the Android SDK and download the USB drivers into a folder inside your SDK as Google tells you to do. My drivers ended up in C:\android-sdk-windows\usb_driver

2.) You next need to hack the file android_winusb.inf to add support for the HTC Incredible.

Find the section labeled [Google.NTx86]. At the end of that section, add the following lines.

;
;HTC Incredible
%SingleAdbInterface%        = USB_Install, USB\VID_0BB4&PID_0C9E
%CompositeAdbInterface%     = USB_Install, USB\VID_0BB4&PID_0C9E&MI_01

Find the section [Google.NTamd64]. At the end of that section, add the following lines.

;
;HTC Incredible
%SingleAdbInterface%        = USB_Install, USB\VID_0BB4&PID_0C9E
%CompositeAdbInterface%     = USB_Install, USB\VID_0BB4&PID_0C9E&MI_01

On your Incredible, go to Settings->Applications->Development and turn on USB debugging. NOW, you can connect your phone to the PC.

On your PC, Go to Start->Right-Click My Computer->Manage

You should see a device with a warning on it called Other->ADB. Right-click it and choose Update Driver Software… Install the drivers manually and point that to your usb_drivers folder. Ignore any warnings about unsigned drivers and everything should install just fine. After installation, I see Android Phone->Android Composite ADB Interface in the Device Manager.

After that, I went to the cmd prompt and typed

&gt;adb devices
* daemon not running. starting it now *
* daemon started successfully *
List of devices attached
HT048HJ00425    device

Post to Twitter

Posted by Andrew, filed under Android. Date: May 1, 2010, 10:04 am | 80 Comments »

I recently had an opportunity to give a talk at 360|Flex along with Randy Troppmann. Slides and code examples are posted below. Video of the session will come sometime in the unforeseen future as they have many hours of other sessions to post-process before getting to mine.

Presentation PDF (warning: large)

MileageBuddy (viewsrc enabled)

HeatmapExample (viewsrc enabled)

Test GPX file

Post to Twitter

Posted by Andrew, filed under 360 Flex, AIR, as3, Data, Degrafa, Flex, GPS. Date: March 11, 2010, 5:04 pm | No Comments »

I had the privilege of speaking to the Indy Flex User Group last night on the topic of Degrafa. While I’m not one of the architects or contributors to the project, I do make extensive use of Degrafa in most of my personal and consulting development work. Below is presentation material and example code I used during the presentation. All the examples have view-source enabled. Just right-click.

Presentation:
Degrafa_IndyFlex_User_Group.pdf

Simple Circle Example:
DegrafaExample1

Button Skinning Example:
DegrafaExample2

Component Development Speedometer Example:
DegrafaExample3

Axiis Example:
Smith Chart Example

ChromaPassword FXG Example:
ChromaPassword

MiniShapeDesigner Example (note, not mine):
MiniShapeDesigner

Post to Twitter

Posted by Andrew, filed under Degrafa, Flex. Date: February 24, 2010, 9:44 am | 3 Comments »

I’m slated to speak at 360|Flex San Jose on March 7-10 on the topic of GPS data and doing neat things with it in Flex/AIR. I’ll be co-presenting along with Randy Troppmann. Randy had been doing work for quite awhile in the GPS space with his work on runningmap.com. Runningmap.com is a website that allows runners/walkers/joggers/movers to track and catalog their accomplishments. They’ve also developed an iPhone application that can record your gps track as you run. It’s quite an honor to be able to present with such an accomplished developer.

I’ve been holding off on doing a blog post about 360|Flex until I could give you a sneak peak of the new prototypes of Swift GPS. Swift GPS is a wearable hardware device that will allow you to record GPX files to a USB thumbdrive that tracks your current position and movement. During my part of this session, we’ll go into how to take this GPX data and do neat things with it in Flex like overlay the data onto a 3D map. Other highlights will be doing a bit of heat-mapping using the GPS data.

This conference WILL SELL OUT, so don’t be one of the hundreds of chumps posting regrets to your twitter account after the conference starts and you didn’t get in. Click on my 360|Flex badge on the right-side of the blog to sign up today. If I win the speaker suite for the most signups through my blog badge, I’ll give away a prototype device to one of the people who helped put me over the top. Don’t wait. Sign up today.

Post to Twitter

Posted by Andrew, filed under 360 Flex, Data, Flex, GPS. Date: February 9, 2010, 8:42 pm | No Comments »

If you followed my previous post, you know I dug into some serious networking in order to get myself some static IP addresses. As of today, FlexJunk has officially become self-hosted. Since fixing the IP issues, I’ve built a new server box to house flexjunk.com. I had never built a rack-mount system, so this was my first foray into that arena.

Specs:
Ubuntu Server 9.10 64-bit OS
IStarUSA WO2-15B 15U Open Frame Rack
ARK 4U-500-CA Black Rackmount case
Intel Core i7 2.66GHz
12GB DDR3 1600 Ram
6x Seagate Barracuda 7200rpm 80gb drives (RAID 5)

Post to Twitter

Posted by Andrew, filed under Linux. Date: February 9, 2010, 10:31 am | No Comments »

I recently purchased a block of 8 Static IP addresses for use with my U-Verse plan. U-Verse has always been a very stable and reliable ISP for me the last two years and their price of $15/month was pretty good so I jumped. This gives me 5 usable IPs that I can use to host various websites.

I want to host websites on a machine on my internal LAN in the 192.168.x.x block, but use port forwarding on my firewall so that port 80 can be sent on through. The reason for this is mostly so that I can access the server machine on the internal LAN, host print servers, etc… while still using it as a secured internet webserver.

Normally, ISPs give you a block of IP addresses, you assign them in your router and send them on where you want to go. Not so with U-Verse. Each static IP is actually dynamically assigned via DHCP from the U-Verse gateway router box. Each IP address pulled dynamically off the gateway MUST have a different mac address. Unfortunately, most off the shelf firewall routers don’t support this capability. They have one, maybe two, mac addresses that they use. Normally, the uplink port has a single mac address that it pulls a dhcp ip address from.

I’m using a D-Link DIR-615 rev. C that I had laying around and noticed that OpenWRT listed it as a supported device. OpenWRT is custom firmware flash for routers that runs a small version of the Linux operating system on it. I figured if I could get Linux running on the device, then I’d be able to accomplish what I wanted…namely, pull all 5 usable IP addresses off the U-Verse gateway, and forward certain traffic on a per-IP/port basis to particular internal LAN machines.

I first downloaded an OpenWRT package (http://downloads.openwrt.org/snapshots/trunk/ar71xx/openwrt-ar71xx-dir-615-c1-squashfs.uni) for my router. I then held in the reset switch for 30 seconds while plugging in the power. This puts the router into recovery mode. I manually set my computer’s ip address to 192.168.0.2 and navigated to http://192.168.0.1 which brings up the recovery flash page of router where I could apply the .uni flash file for OpenWRT.

I reset my computer’s ip to dynamic and restarted the router. After all the lights stopped blinking, made sure I had a valid 192.168.1.x IP address and did

telnet 192.168.1.1

This brought me up to the linux shell of the router. I ran the passwd command to set my root password for the device and then exited the shell. Once the password for root is set, telnet becomes disabled, but SSH is enabled. I then used putty to connect to 192.168.1.1 again.

Next, I installed the packages ip and kmod-macvlan. These packages allow you to create virtual ethX adapters with separate mac addresses.

opkg update
opkg install ip
opkg install kmod-macvlan
opkg install hostapd-mini
opkg install luci-admin-full  
opkg install luci-fastindex
opkg install luci-app-firewall

I modified /etc/rc.local to create the virtual adapters.

# Put your custom commands here that should be executed once
# the system init finished. By default this file does nothing.

#AMW edits
ip link add link eth1 eth2 type macvlan
ifconfig eth2 hw ether 00:24:01:f5:1b:84

ip link add link eth1 eth3 type macvlan
ifconfig eth3 hw ether 00:24:01:f5:1b:85

ip link add link eth1 eth4 type macvlan
ifconfig eth4 hw ether 00:24:01:f5:1b:86

ip link add link eth1 eth5 type macvlan
ifconfig eth5 hw ether 00:24:01:f5:1b:87

ip link add link eth1 eth6 type macvlan
ifconfig eth6 hw ether 00:24:01:f5:1b:88

ifup -a

route add default gw 75.30.80.1 dev eth1

ntpclient -c 1 -s -h ntp1.dlink.com

exit 0

eth0 is my local LAN interface. eth1 is my main outgoing public internet interface. eth2-6 are virtual macvlan interfaces that will each hold one of the static ip addresses. I also modified the file /etc/config/network so that the new virtual adapters would be included in dhcp setup.

config 'interface' 'loopback'
        option 'ifname' 'lo'
        option 'proto' 'static'
        option 'ipaddr' '127.0.0.1'
        option 'netmask' '255.0.0.0'

config 'interface' 'lan'
        option 'ifname' 'eth0'
        option 'type' 'bridge'
        option 'proto' 'static'
        option 'ipaddr' '192.168.1.1'
        option 'netmask' '255.255.255.0'

config 'interface' 'wan'
        option 'ifname' 'eth1'
        option 'proto' 'dhcp'

#First static IP
config 'interface' 'wan1'
        option 'ifname' 'eth2'
        option 'proto' 'dhcp'
        option 'defaultroute' '0'
        option 'peerdns' '0'
        option 'gateway' '0.0.0.0'

#second static IP
config 'interface' 'wan2'
        option 'ifname' 'eth3'
        option 'proto' 'dhcp'
        option 'defaultroute' '0'
        option 'peerdns' '0'
        option 'gateway' '0.0.0.0'

#third static IP
config 'interface' 'wan3'
        option 'ifname' 'eth4'
        option 'proto' 'dhcp'
        option 'defaultroute' '0'
        option 'peerdns' '0'
        option 'gateway' '0.0.0.0'

#fourth static IP
config 'interface' 'wan4'
        option 'ifname' 'eth5'
        option 'proto' 'dhcp'
        option 'defaultroute' '0'
        option 'peerdns' '0'
        option 'gateway' '0.0.0.0'

#fifth static IP
config 'interface' 'wan5'
        option 'ifname' 'eth6'
        option 'proto' 'dhcp'
        option 'defaultroute' '0'
        option 'peerdns' '0'
        option 'gateway' '0.0.0.0'

I made sure to set the gateway option on all of the virtual adapters to 0.0.0.0 so that the regular default gateway of eth1 would be used instead of the static IP default gateways.

After re-booting the router, I navigated to the U-Verse gateway and configured each of my new mac addresses so they mapped to the static IP addresses instead of internal 10.0.0.x ones.

After rebooting the router one last time, I now had all of my static IPs assigned on the router and could install and utilize the LuCI interface for configuring routing and port forwarding.



Post to Twitter

Posted by Andrew, filed under Linux, security. Date: January 6, 2010, 11:41 am | 17 Comments »

« Previous Entries Next Entries »