Adonit Jot Touch 4 Support for Brushes iPad App

image2

And now for something completely different…

With the announcement of Adobe’s teaming up with Adonit to help manufacture and ship Mighty & Napolean in the first half of 2014, I thought it might be a good idea to start getting my head wrapped around how all this technology works.

I recently heard a great interview with British artist David Hockney about the incredible art he is creating on the iPad.  Unfortunately, I wasn’t able to make it out to his “A Bigger Exhibition” at the de Young museum in San Francisco, which just ended. One thing that did catch my attention in the interview though, was his mention of an app called Brushes that he uses to create his art:

image2

 

Those are just my scribblings.  Not to be confused with anything closely resembling art.

Upon further investigation, I discovered that the Brushes app is in fact open source and available on GitHub.  Brushes as it currently stands, supports the Pogo Connect pen.

In order to give myself a  good project to learn more about the Adonit Jot SDK, I forked the Brushes app and added support for the Adonit Jot Touch 4 stylus. It turned out to be much more straightforward than expected, once I got my head around the Brushes source code. You can find the results here on GitHub.  It’s probably not quite ready to be merged back into the original Brushes app, but it’s definitely a great start.

If you’re interested in what exactly I did, keep reading…

Installing the Jot Touch SDK

After cloning the Jot Touch SDK from GitHub, I copied the JotTouchSDK.framework folder to the Brushes app folder and then opened Brushes.xcodeproj. Then, I followed the instructions in the Jot Touch SDK Getting Started Guide to import the Jot Touch SDK into the app. Just read the guide for that.  There’s no reason for me to repeat some very good documentation.  The two main steps to cover are:

  • Link your project with the CoreMotion.framework, CoreBluetooth.framework and JotTouchSDK.framework frameworks
  • Add ‘-ObjC’ to the Other Linker Flags

I did have to make two minor tweaks to the Brushes app to get it to compile:

I was getting a “No newline at end of file” for JotStylusManager.h.  Rather than changing the code, I just changed the Build Setting ‘Missing Newline at End of File’ to ‘No':

Screen Shot 2014-01-20 at 2.56.53 AM

As well, there was a minor issue in WDAddPath, line 141:

WDLog(@"Empty path bounds with path of length %d", [path.nodes count]);

had to be changed to:

WDLog(@"Empty path bounds with path of length %lu", (unsigned long)[path.nodes count]);

to prevent a casting error.

Defining a New Type of Stylus

WDStylusManager.h defines an enum ‘WDStylusType’ which originally supported WDNoStylus and WDPogoConnectStylus.  I added WDJotTouchStylus:

// Jot Touch 4
#import <JotTouchSDK/JotStylusManager.h> 

typedef enum {
    WDNoStylus = 0,
    WDPogoConnectStylus,
    WDJotTouchStylus,
    WDMaxStylusTypes
} WDStylusType;

I also defined a new stylus manager specifically for the Jot Touch:

@property (nonatomic) JotStylusManager *jotManager;

Then, in WDStylusManager.m, I needed to synthesize the jotManager:

@synthesize jotManager;

Displaying Pen Status

The Brushes app allows you to see the current stylus connection status and battery level:

image

 

In order to provide that information, the dataForStylusType method needed support for the Jot:

    ...
    } else if (type == WDJotTouchStylus) {
        data.productName = NSLocalizedString(@"Jot Touch", @"Jot Touch");
        data.connected = (jotManager.connectionStatus == JotConnectionStatusConnected);
        data.batteryLevel = @(jotManager.batteryLevel / 100.0f);
    }

The Brushes app expects a battery level between 0.0 and 1.0.  The jotManager’s batteryLevel ranges from 0.0 to 100.0 and has to be scaled appropriately.

Supplying Brushes with Pressure Data

One of the great things that separates a stylus from simply your finger for example, is the ability of the stylus to tell the app exactly how much pressure the user is exerting on the display so the app can react appropriately. In order to supply the Brushes app with that information, I had to add Jot support in the pressureForTouch method.

I tried turning the actual touch passed into the method into a JotTouch, and then getting the pressure from that, but that didn’t seem to work for some reason, so I ended up just retrieving the current pressure directly as shown below:

- (float) pressureForTouch:(UITouch *)touch realPressue:(BOOL *)isRealPressure
{
    float   pressure = 1.0f;
    BOOL    isReal = NO;

    if ((mode == WDPogoConnectStylus) && [pogoManager oneOrMorePensAreConnected]) {
        isReal = YES;
        pressure = [pogoManager pressureForTouch:touch];
    } else if ((mode == WDJotTouchStylus) && ([jotManager connectionStatus] == JotConnectionStatusConnected)) {
        isReal = YES;
        pressure = [jotManager getPressure] / 2047.0f;
    } else if (mode != WDNoStylus) {
        isReal = YES;
        // since we're in stylus mode, but no styli are active, use 1.0 pressure
    }

    if (isRealPressure) {
        *isRealPressure = isReal;
    }

    return pressure;
}

The Brushes app expects the pressure value to range from 0.0 to 1.0.  The jotManager returns values from 0.0 to 2047.0 so they have to be scaled appropriately.

Listening for Connect/Disconnect Events

Finally, we need to listen for Jot connect/disconnect events and tell the app when these events occur so it can update the display appropriately.

When the Bluetooth state is set in setBlueToothState, if the Bluetooth is on (blueToothState == WDBlueToothLowEnergy), we initialize the jotManager and listen for connection status changes:

- (void) setBlueToothState:(WDBlueToothState)inBlueToothState
{
    if (inBlueToothState == blueToothState) {
        return;
    }

    blueToothState = inBlueToothState;

    if (blueToothState == WDBlueToothLowEnergy) {
        if (!pogoManager) {
            pogoManager = [T1PogoManager pogoManagerWithDelegate:self];
            pogoManager.enablePenInputOverNetworkIfIncompatiblePad = YES;
        }

        if (!jotManager) {
            jotManager = [JotStylusManager sharedInstance];

            [[NSNotificationCenter defaultCenter] 
                addObserver: self
                selector:@selector(jotConnectionChange:)
                name: JotStylusManagerDidChangeConnectionStatus
                object:nil];
            jotManager.rejectMode = NO;
            jotManager.enabled = YES;
        }
    }

    [[NSNotificationCenter defaultCenter] postNotificationName:WDBlueToothStateChangedNotification object:self];
}

This will call jotConnectionChange whenever there is a change to the connection status.  jotConnectionChange is then defined as follows:

- (void)jotConnectionChange:(NSNotification *) note
{
    switch([[JotStylusManager sharedInstance] connectionStatus])
    {
        case JotConnectionStatusOff:
            break;
        case JotConnectionStatusScanning:
            break;
        case JotConnectionStatusPairing:
            break;
        case JotConnectionStatusConnected:
            [self didConnectStylus:NSLocalizedString(@"Jot Touch", @"Jot Touch")];
            break;
        case JotConnectionStatusDisconnected:
            [self didDisconnectStylus:NSLocalizedString(@"Jot Touch", @"Jot Touch")];
            break;
        default:
            break;
    }
}

And that’s really all I had to do.  The only other issue I had was getting the Jot Touch 4 stylus to connect with the iPad the first time.  I kept getting JotConnectionStatusPairing notices, followed by JotConnectionStatusDisconnected notices. I finally realized that the JotConnectionStatusPairing notice was telling me that it was waiting for me to draw on the iPad to confirm that I wanted to pair my device.  Once I did that, it connected just fine, and I never needed to do it again.  The device just connects automatically when I start the app.

Next Steps

The Jot Touch SDK can send you drawing and palm rejection events.  The latter can be used to ignore events generated by your palm resting on the iPad as you draw.

In the meantime, have a play around with it and let me know what you think.

Adobe Create Now World Tour

creativecloud

The Adobe Create Now World Tour is about to kick off and if you want to learn about all the latest and greatest with Adobe® Photoshop® CC, Illustrator® CC, InDesign® CC, Dreamweaver® CC, After Effects® CC, Premiere® Pro CC, and much more, I highly suggest you attend one of these free events.

In truth, if you just plain want to get inspired, I’d suggest going.  The events are free and you’ll get to learn from the pros.

Here are the dates and how to register for an event near you:

http://adobe.ly/CCWorldTour

Oh, and if you’re thinking, “Yeah, I’ll do that later when I have time”, the first event is on September 19th, 2013, so don’t wait too long.

 

Responsive Content Using CSS Regions

Screen Shot 2013-08-16 at 2.11.43 PM

CSS Regions allow content to flow across multiple areas of the display, called regions.  The beauty of CSS Regions is that you can separate the content from the layout.  And with that comes the ability to create some responsive content very easily.

Update August 29, 2013: 

I have a new post describing Cross-Browser Responsive Content Using CSS Regions.  I describe there how you can do exactly what is described here, but also use the CSS Regions polyfill to get CSS Regions support on many other browser and devices.

The flow-into CSS property can be used to prevent some content from displaying normally, but rather have its display redirected to a named flow:

#source {
   -webkit-flow-into: main-thread;
   flow-into: main-thread;
}

Then we can specify our layout, and tell the layout where to get its content from using the flow-from CSS property:

.region {
   -webkit-flow-from: main-thread;
   flow-from: main-thread;
}

In the case above, all of the content in the element with ID source will actually get rendered into the layout specified by all of the elements of class region.

Let’s take a look at an example.  First, make sure you are viewing this with either a Webkit nightly, Chrome Canary, or an up-to-date Chrome Stable.  Then, if you haven’t already done so, you need to enable CSS regions by navigating to chrome://flags and clicking on “Enable” under Enable experimental Webkit features.  You’ll then need to relaunch the browser.

Check out this Pen!

I tried to keep the example as simple as possible.  Basically, we have a DIV whose ID is source.  That DIV contains all of the text to be displayed.  But, since we have the following in the CSS:

#source {
 -webkit-flow-into: main-thread;
 flow-into: main-thread;
}

All of the text in that DIV gets redirected into the named flow called main-thread and doesn’t display in its original DIV.

We also have several DIVs, all of class region.  In the CSS we have:

.region {
 width: 150px;
 height: 150px;
 float: left;
 margin: 10px 5px;
 -webkit-flow-from: main-thread;
 flow-from: main-thread;
}

All of those region DIVs will get their content from the main-thread named flow.  And, since they are all float: left, the DIVs will automatically relayout as you adjust the width of the window.

Finally, I stuck an image in the middle of it just for the fun of it, to show that your regions can be easily interspersed with other content.

Try that now. Make this window narrower and wider, and you should see the text automatically adjust.

Now think how much work would be involved in getting this simple example working without CSS Regions.

You can find out more about CSS Regions here.

Adobe CFF Font Rasterizer Contributed to FreeType

JensonW-640x425

Yesterday, Adobe, in cooperation with Google, announced that the Adobe CFF rasterizer has been contributed FreeType.  If you’re a font geek, this is fantastic news.  If not, you might be thinking to yourself, “CFF is what again? Why is this important?”.

In a nutshell, modern outline fonts use two formats, TrueType and CFF.  A TrueType font has a lot of ‘hinting’ in the font file itself, indicating how best the font should be rendered.  CFF font files contain less hinting.  They are dependent more on the quality of the rasterizer.

However, because there is less hinting within the file, and due to its efficient file format, CFF fonts are on average about 20-50% smaller than TrueType fonts.

FreeType is an open source library used for font rendering on Android, iOS, Chrome OS, GNU/Linux, and other free Unix operating system derivatives such as FreeBSD and NetBSD. That makes for more than a billion devices running FreeType.

Let’s take a look at how the Adobe CFF rasterizer improves the rendering of CFF fonts.  The left column below contains a CFF font rendered with the FreeType hinter.  The middle column is rendered with FreeType’s light-auto hinter.  And finally, the right column is rendered with the CFF rasterizer that Adobe has contributed to FreeType:

JensonW-640x425

 

The difference is pretty self-evident.

I don’t think it’s a big leap to understand why it is important to have smaller font files on mobile devices and to have the rendering of those fonts improve dramatically.

More good news for the Web.

Speaking @ Adobe MAX

MAX11_125x125_SPEAKER_grey

MAX11_125x125_SPEAKER_greyI’m going to be speaking again at Adobe MAX this year.  The conference is being held in Los Angeles from May 4-8.  It should be a total hoot.  The Black Keys are even playing at the big bash!

And, in case you haven’t heard, all attendees get a free year membership to Creative Cloud.

Tuesday, May 7th:

11AM – 12PM – SVG Reboot
This will be a beginner’s introduction to SVG and will give those of you that know about SVG an insight into how it’s moving forward and where it’s going.

Wednesday, May 8th:

11AM – 12PM – Go Beyond the Canvas Box to Create Your Own Cinematic Effects
This will be a talk on CSS Filters and Shaders.

1:30PM – 3PM – Creating Special Effects for Your Web Content with CSS Filters and Shaders
This one will also be on CSS Filters and Shaders, but in this case you don’t sit back and listen.  Instead, bring your own laptop and be prepared to get down and dirty creating your own amazing Web effects as I guide you through it all.  I’m pretty psyched to see what people come up with here.

So, if you haven’t already, register for MAX now and come join this amazing conference.

Use the registration code MXSM13 for a $300 discount off the registration price.

Oh, and if you register off my blog, I’ll buy you a beer.

MAX2013-signature-550x75

Hello Tokyo! Testing 1…2…3…

Screen Shot 2013-04-26 at 1.19.52 PM

Screen Shot 2013-04-26 at 1.19.52 PM

I’ve talked about Test the Web Forward before and why everyone should get involved.  The next Test the Web Forward event is going to be at the Google office in Tokyo. So come on out and meet members of the Japanese Web community and experts from all over the world for a weekend of learning, coding, food, drinks and fun.

Help make a more interoperable & better Web!

Here’s the event and here’s how to register.

Test the Web Forward

Test-the-Web-Forward

The Web is constantly evolving, new W3C specs are being hashed out all the time.  Partial and full implementations of these new specs are showing up in different browsers all the time.  But, if we as a Web community want the Web to evolve, and want people to really trust their businesses and technology on the Web as a platform, there needs to be tests in place to ensure that new specs are implemented consistently, correctly, and meet our expectations.

Test the Web Forward arose from that need.  Representatives from the W3C, Google, Mozilla, Microsoft, Opera and Adobe put together hackathons around the world to teach people about new Web features and to get people to help write tests against these features.

I highly encourage you all to at least get on the mailing list and follow @testthewebfwd and #testtwf.

If you’re going to be in the Seattle area on April 12-13, 2013, you should definitely check out the Test the Web Forward – Seattle event, being hosted by Microsoft.

 

Offline Viewing of YouTube Videos

Lately we’ve been starting to create an Adobe TechLive YouTube channel so people can view the recordings for the various TechLive sessions. You can find that channel here:

http://www.youtube.com/user/AdobeTechLive.

Sometimes though, you want to watch a YouTube video offline, perhaps while you’re sitting on a train with spotty Wifi coverage.  There’s a service I came across that lets you download files from all kinds of services, including YouTube:

http://savefrom.net (http://sfrom.net also works)

What’s nice is if you simply append the link to the YouTube video after http://sfrom.net, it will take you to a page that will let you download that specific video.  For example, Christophe Coenraets recently did a TechLive session on architecting a real-world PhoneGap application. You can find the recording here:

http://www.youtube.com/watch?v=eJINt-g9vBg.

 If you want to download that recording for offline viewing though, you simply need to go here:

http://sfrom.net/http://www.youtube.com/watch?v=eJINt-g9vBg

Pretty slick.