Tuesday, January 28, 2014

Monkey Write: Brush strokes

Back when I started Monkey Write at the AT&T hackathon, I was already varying the pen stroke width by the pressure of the touch events. But the pressure ranges change a lot on depending on the device. I was not able to get consistent stroke rendering with all those pressure ranges, so I only vary the stroke width by pressure if I know it is from the active stylus reported via an HTC PenEvent.

I was really excited when I saw the beautiful Markers app. Its pressure-sensitive strokes work on many devices, plus it is open sourced with Apache License 2.0, so all I have to do is to integrate that into Monkey Write. Well, all I have to do is to find time to integrate that into Monkey Write, which I finally did!

PressureCooker

When I first looked into the Markers code I was very amused by the PressureCooker class. What a name! It calibrates the pressure coming from a series of touch events, which is what I need. I lifted that class and put it into Monkey Write, but that alone did not make beautiful strokes. This was where I stopped the first time I looked into Markers, for the rest involves a more complicated co-ordination among the Slate, TiledBitmapCanvas and SpotFilter classes.

Markers architecture

I finally set aside some time to understand how Markers work.

  1. Convert each incoming touch event into a Spot.
  2. Add the Spot to a SpotFilter.
  3. The SpotFilter takes a Plotter in its constructor. After filtering it calls the plot() function of the Plotter.
  4. The Plotter renders the Spot on screen. In Markers this is handled by the inner class MarkersPlotter in Slate, which draws on the TiledBitmapCanvas.

Where is the PressureCooker, you ask? It is used inside the plot() function. Instead of using Spot.pressure directly, it is calibrated by the PressureCooker.

Monkey Write modifications

Here is what I did to incorporate Markers stroke rendering into Monkey Write:

  1. Replace my own class with Spot to store touch events.
  2. Add a TiledBitmapCanvas to the character writing custom view (called SketchPad).
  3. Make SketchPad implement Plotter, which takes a Spot and renders to the TiledBitmapCanvas.
  4. Add a SpotFilter to SketchPad. As touch events are captured by onTouch, pass the Spots to the SpotFilter.
  5. In SketchPad.onDraw(), call TiledBitmapCanvas.drawTo() after rendering the base character to show the pen strokes.
  6. After the user writes a stroke, grade it. If it was not a good stroke, call TiledBitmapCanvas.step(-1) to remove that stroke.

Pen styles

After I set up the basic pressure cooking and spot filtering I started to experiment with different pen styles. This essentially means changing the pen tip, or how to render each touch point aka Spot.

Basic style

The basic style renders the pen tip as a solid circle. Fairly straight forward:

c.drawCircle(x, y, r, mPaint);

Brush style

Markers has an airbrush style, which draws a bitmap as the pen tip. I looked at the bitmap and thought, hey, that's just a RadialGradient! I decided to generate that programmatically so I can vary the alpha value on the fly:

private Shader createBrushShader(
    float width, float alphaStart, float alphaEnd) {
  final float center = width / 2;
  final float radius = Math.max(1, width / 2);
  final int red = Color.red(mPaint.getColor());
  final int green = Color.green(mPaint.getColor());
  final int blue = Color.blue(mPaint.getColor());
  return new RadialGradient(
      center, center, radius,
      Color.argb(alphaStart, red, green, blue),
      Color.argb(alphaEnd, red, green, blue),
      Shader.TileMode.CLAMP);
}
mPaint.setShader(mBrushShader);
c.drawCircle(x, y, r, mPaint);
I update mBrushShader with createBrushShader() whenever the user changes the width or alpha from the UI.

Pencil style

Once I started playing with Shaders I could not stop. I decided to mimic a pencil stroke on a rough paper by plotting little dots at the pen tip.

private Shader createPencilShader(float alpha) {
  final int size = 32;
  int color = Color.rgb(
      Color.red(mPaint.getColor()),
      Color.green(mPaint.getColor()),
      Color.blue(mPaint.getColor()));
  float threshold = alpha / 255f;

  int[] colors = new int[size * size];
  for (int i = 0; i < colors.length; ++i) {
    colors[i] = (Math.random() >= threshold) ? 0 : color;
  }

  Bitmap bitmap = Bitmap.createBitmap(
      colors, size, size, Bitmap.Config.ARGB_8888);
  return new BitmapShader(
      bitmap, TileMode.REPEAT, TileMode.REPEAT);
}
mPaint.setShader(mPencilShader);
c.drawCircle(x, y, r, mPaint);

I use a BitmapShader to draw the little dots. It is a tiling bitmap, each pixel is either transparent or the chosen color. The lower the alpha value, the more transparent pixels.

Constant width

Finally I want to provide an option for users who don't want variable width. This is achieved by ignoring the pressure from the touch event and supplying a constant value to the pen tip renderer.

Mix and match

With that you can have a lot of fun making different pen styles.

Here is a very transparent blue stroke. Looks like water, doesn't it?

You can pick different styles for different strokes:

Hopefully these beautiful strokes will make it even more fun to practice writing Chinese. Download Monkey Write and try them out!

Monday, January 27, 2014

Authoring for Pluralsight

When I decided to become a public speaker, I had no idea where it would lead me. I have met many wonderful people at various conferences, and it opens so many doors. So when my friend Corey Latislaw asked me if I'd like to be introduced to Pluralsight to record some video classes, I went, "Sure!"

Audition

The first step to becoming a Pluralsight author is to submit an audition video. You pick a topic, create the slides and demo, and explain everything in 10 minutes. It was a bit challenging to find a bite-sized topic like that, but I lifted a part from my Bust the Android Fragmentation Myth talk and created a video for it:

Once the audition video is accepted, we need to come up with an outline for my first course. I went through my existing talks to see what makes sense for the Pluralsight audience, and decided to author a course on Android Layout Fundamentals based on my Android Layout 101 talk.

Equipment

For software, I have already downloaded Camtasia for the audition video. Now I need hardware as well. Namely, a microphone. I bought the Rode Podcaster as recommended by Pluralsight. It was quite pricey, but I figured I'd use it to record pronunciations for Monkey Write as well, so it should be a good investment.

Recording

Here comes the meat of the process: recording the videos. Initially I thought it will be like giving live talks, but it turned out to be quite different. First of all, if I stutter or make a mistake at a live talk, I simply move on. There is no "undo" when you are on stage. But when I record, I feel obligated to edit out the bloopers, so that introduced quite a lot of extra work.

The format of my slides are also a bit different. I like using "highlight slides" to focus attention on the point I am giving. So I will have a slide like this:

public void someFunction() {
  doSomething();
  doSomethingImportant();
  doSomeOtherThing();
}

And the next slide will highlight the part I am addressing:

public void someFunction() {
  doSomething();
  doSomethingImportant();
  doSomeOtherThing();
}

With Camtasia I can add highlighting to the video afterwards, and I found that better than slides highlighting because I can sync it up to the audio. I highlight the words exactly when I am describing them with my voice. The syncing makes the focusing even stronger, and it gives a good cadence to the video.

Review

A pluralsight course consists of multiple modules, each of which needs video clips, slides, demo code and assessment questions. I was focusing on the video part and recorded three modules before I realized I should have completed a single module and submit that for review. So I prepared the rest of the module and sent it off to the editorial team. Overall feedback was good, but I had a few issues:

  • My 'p's are popping. I need to adjust the microphone distance.
  • The volume needs to be more consistent
  • I need to introduce each of my modules

I wish I got the feedback before I recorded so many clips already! I should have submitted my very first clip and waited for feedback before proceeding. Now I know!

Demo

I never live code during my talks because so many things can do wrong. But with video I have more control, and I demoed some Android tools in the last module of Android Layout Fundamentals. I did not realize how hard it is to talk and type at the same time! I considered recording the demo first, adding the voice-over later, but figured I'd forgotten what I was trying to explain by the time I go back to do the voice-over. So instead I type-and-talk while I could, and stopped typing to focus on talking while I couldn't.

Tips and Tricks

I learned a few techniques after producing the first course:

  • To ensure consistent volume, do not stop recording if you make a mistake. Just stop to recompose yourself, and say it again. Later you can edit out the mistakes and silences. I found it hard to speak at the exact same loudness if I stopped talking for too long.
  • I don't like to script my talks, but I noticed that I re-record a lot when I tried to define a concept. So now I write down the definitions and edit them to my liking before I record. By reading out the written definitions I can often do it in one take, which is a major time saver.
  • I love my Kinesis keyboard, but it makes a lot of noise when I type, and it's a bit distracting in the video. So I type on the keyboard of my laptop instead.
  • When doing demos, don't worry too much about talking and typing at the same time. I type, and then explain. Later I split the video and audio tracks in Camtasia and splice them back together so I am talking over the demo.
  • When you are talking during the demos, do not jitter the mouse! It makes the video splicing much harder.

It's live!

I started recording in September, and thought I'd be done by November. But of course things always take longer than expected. I finished everything by mid-December though, so the delay wasn't too bad. And it was so exciting to see my course go live!

I have not received my first royalty check yet, so I cannot tell you if this is worth doing financially. But the Pluralsight people have been super friendly and helpful, and I really enjoyed working with them. In fact I am already thinking about my next courses!

If you are sitting on the fence about becoming a Pluralsight author, go for it! It's very satisfying to see your knowledge solidified in the form of a video course. Ping me if you have questions, or would like to be introduced to the editorial team!

(Full disclosure: I earn a referral bonus when I introduce people to Pluralsight who go on to publish a course)

Thursday, January 16, 2014

Talk at CodeChix + ACM

Yesterday I was invited to speak at the Bay Area ACM meeting, co-hosted by CodeChix. I wanted to bring a topic that is interesting to the general audience, so I decided to Bust the Android Fragmentation Myth with them.

I treat my overview talks quite differently from my deep-dive talks. For deep dives, I jump into the nitty-gritty details fairly quickly, packing my slides densely with tips and tricks. But for overview talks, I go over the problem definition first. In this case, how to develop for so many different Android devices?

I then walked through a Hello World example to give some basic Android background. With all that set, I deliver the meat of the talk, the various techniques for responsive and progressive development.

After the talk I was chatting with some of the audience, one of whom had no programming experience but enjoyed my talk nonetheless. I was very happy to hear that. She asked me for advice to prepare herself before coming to tech events, to which I said, "ask questions". Ask if you don't understand something. Ask for more information if something is interesting and you want to learn more. She looked a bit disappointed since I was telling her something she already was doing, but at the end of the day there is always more to learn, so my rule of thumb is get fresh information while you are at the event. To give her something concrete, I pointed her to my Android Layout Fundamentals video course. Hopefully that'll be useful!