How I Built the Paint 4 Kids Windows Store App Using Only HTML5 and SVG

Pietro Brambati | February 21, 2013

Paint 4 Kids is a Windows Store app specifically designed for kids. It’s a simple app for coloring and drawing. You can read about its features directly on the Windows Store site, where you can also see some screen shots. For this article, you can simply think of an app that has several drawings that you can interact with.

From a technical perspective, Paint 4 Kids is built entirely using standard Web technologies, like HTML, CSS, JavaScript and SVG. Consider an important aspect here: with Windows 8, we write an app using our Web skills and we develop for the Web platform using the underlying HTML5 support of the Internet Explorer 10 rendering engine. So, one immediate benefit is that we do not have to test and support all the different browser versions or use any polyfills to emulate the new API features in old browsers. We can use the best of the Web platform and also some specific Windows 8 APIs directly in JavaScript.

This article will discuss the use of Scalable Vector Graphics (SVG) in Paint 4 Kids, starting with some requirements of the project. I’ll address these and some pitfalls of using SVG, and I hope that some of these considerations will apply to your apps as well.

Requirements and Why SVG

Windows Store apps give you a great opportunity in terms of the markets where you can distribute and sell your apps and in terms of devices your apps can run on. Your apps can be used on devices with different screen sizes, different screen resolutions and different pixel densities. This factor must be considered. Read this article to get a good technical understanding on how to scale your apps to different screen sizes and how to test your app using the Windows Simulator.

One of the requirements for Paint 4 Kids is that we want a single drawing to be displayed well at different resolutions. We decided to use SVG, which is a vector image format for two-dimensional graphics. Our approach is to create a drawing at a specific resolution (2560 x 1440, see later for details), scaling down to the current user’s resolution. Another advantage we gain is that it is very simple and fast to fill a path with a color in SVG, and that is one of the main features of the app. When a user taps the screen, it is simple to intercept the corresponding portion of the drawing and fill the path with a specific color.

Another requirement is that we want to reuse some drawings created in XAML and have good tooling support so that we can create and add new drawings to the app in a simple and fast way. Inkscape works very well for this purpose. You can import a XAML drawing and export it to an SVG file, and because the two vector-based formats are very similar, the export is always successful.

One disadvantage of using SVG is that DOM manipulation can become slower with the increase in the number of objects added to the DOM, so some performance testing and optimizations are often necessary. You can read a good table outlining the pros and cons of using SVG, Canvas or both in the same app on this blog post.

Another Paint 4 Kids requirement is that a user be able to save a drawing. Unfortunately, it is not possible, at the time of writing, to create an image from an SVG file, so we end up converting the SVG file to a Canvas object.

An alternative approach would be to use Canvas instead of SVG. With Canvas, you have to create different raw images for different resolutions—at least if you want your drawing to look good at different resolutions (otherwise, the lines look pixelated). Another consideration is how to fill a shape in a drawing. While you are using Canvas with touch points, you are dealing with a raw image, made only of pixels; you cannot deal with shapes like a rectangle, a circle and a path in an easy way. If you want to fill a shape bounded by a black line, you have to implement a flood fill (or seed fill) algorithm to accomplish that purpose, and if the area to fill is big (relative to screen size), you will see the area being filled, whereas with SVG, the fill effect is instantaneous.

Adapting the Drawing to Different Form Factors: Viewport and viewBox

As outlined earlier, we start our SVG drawing with a specific resolution of 2560 x 1440 pixels, and every drawing is loaded dynamically into the DOM from a file inside the Windows Store app. We chose this specific value for convenience; you can start from other resolutions as well. In Paint 4 Kids we want to use a single drawing and make it scale at different resolutions. Look at Figure 1 and Figure 2, which show images of the same drawing running in the Windows Simulator at different resolutions. Figure 1 is taken simulating a 10-inch monitor at the resolution of 1024 x 768 pixels. Figure 2 is on a 27-inch monitor at the resolution 2560 x 1440.

Drawing on 10-inch ScreenFigure 1. Drawing on 10-inch Screen

Drawing on 27-inch Screen
Figure 2. Drawing on 27-inch Screen

This fixed resolution gives us a sort of virtual space coordinate system. In SVG we can set this coordinate system using the viewBox attribute. If you use a tool to draw using this coordinate system, all the graphical elements are relative to this coordinate system. Now, if you want to scale down (or up) from this resolution to another specific one—suppose, for example, that your tablet has a resolution of 1366 x 768—you have only to set the width and height attributes on the SVG element that contains your drawing. These last two attributes define the viewport of the SVG, which is the actual resolution of our device in this scenario.

The value of the viewBox consists of four numbers that represent the minimum x-coordinate, minimum y-coordinate, width and height of the coordinate system that you want to map and scale on the viewport. Combining the viewBox, width and height attributes gives you the expected result.

The following is the root SVG element for every drawing, a simple XML file. The coordinate system starts at the top-left corner of the screen, as you might expect if you have already worked with SVG.

<svg viewbox="0 0 2560 1440">...

When the drawing is loaded into the DOM (which occurs when the user selects a drawing to color), we can set the viewport attributes.

To find the root of an unnamed SVG element, we use the document.querySelector API, using a pseudo-class selector to find the first SVG element. Because this API is called only once, when a user selects an element to draw, we can overlook any performance lag.

var svgd;
svgd = document.querySelector("svg:first-of-type");
svgd.setAttribute("width", window.innerWidth);
svgd.setAttribute("height", window.innerHeight);

This code also uses the window object and its inner* properties to get the actual resolution in pixels at run time.

Another consideration when dealing with the viewport and viewBox is aspect ratio. If the two coordinate systems have a different ratio of width to height, sometimes you want the resulting image to fit nonuniformly. In other scenarios, you want to preserve the aspect ratio and scale the image uniformly. SVG uses preserveAspectRatio to decide whether the image has to be scaled uniformly or not. (We will discuss this again later when we describe “stamps.”) For our drawings, the default behavior that scales the viewBox uniformly to fit the viewport works as we want.

How to Fill a Path with Colors and Images

Filling an SVG shape, like a path, a rectangle or others, is a very easy step, and it is quite instantaneous. It is like setting a style property in CSS—you don’t need to write the code to find every single pixel surrounded by a line. Take a look at the following code, which is the call back function when a tap event is fired inside the SVG area:

var el = document.elementFromPoint(e.x, e.y);
var selectedColor = "255,0,0,1";
el.setAttribute("style", "fill:rgba(" + 
  selectedColor + "); stroke-width:3;);

In this code, e is an object of type MSPointerEvent. This object is very important: you can get this object if you subscribe to some of the MSPointer* events (like MSPointerDown). With a single line of code you can subscribe to an event that originates with the mouse, touch or even a pen. Also inside MSPointerEvent, you can, if needed, read the pointerType property, which gives you detailed information about which kind of device generated the event. If you are interested in this topic, read this blog post on the APIs for the touch input on Internet Explorer 10 and Windows Store apps.

Back to the code. The e object here is used only to get the x,y point coordinates from the input device, using the elementFromPoint API. Next, el is the specific shape we want to fill with a color, and it is of type SVGPathElement. The rest of the code is straightforward, setting the fill color and the line stroke width of the SVGPathElement, regardless of the actual shape that it really is.

Instead of using the setAttirbute API, you could also directly set the fill and strokeWidth properties on the SVGPathElement. Avoiding the string parsing can give you more performance, even though it will not be perceived by the user in this scenario.

In the sample, the color is a standard solid RGBA color that the user can choose from a color palette, but you can also fill the shape with a path coming from an image or by using gradients. Paint 4 Kids defines not only a common set of colors but also some images, like stones, grass and so on. To do this in SVG, you can define some patterns, as in the following code:

<pattern id="imgPatterns_1" patternUnits="userSpaceOnUse" width="128" height="128">
  <image xlink:href="../images/BRICK.PNG" width="128" height="128" />
</pattern>

There are two things to note in the code: first, we are defining an SVGPatternElement that it based on the imaged contained. Second, you can define the patternUnits property that defines how to fill a shape with the pattern itself. The userSpaceOnuse simply repeats the image several times without any padding between the instances. Once the pattern is defined, we can use it in the fill property shown earlier by using the following syntax:

var selectedColor = "url(#imgPatterns_1)";
el.setAttribute("style", "fill:" + selectedColor + "; stroke-width:3;);

Looking at the code, notice that the fill property is now a url that uses the ID of the pattern element defined above, preceded by the # symbol (for example, #imgPattern_1). Figure 3 shows some pattern effects in use.

Pattern Effects in Use
Figure 3. Pattern Effects in Use

Reusing SVG Elements as Stamps

Paint 4 Kids also gives you the capability to insert some shapes into a drawing as stamps that you can attach to the painting. For users, a stamp is a shape like a pumpkin or a ball that they can put into the drawing wherever they want. They can also decide the size of the shape, and they can insert the same shape multiple times.

From an SVG perspective, every shape can be as complex as you want, so inserting the same shape several times can eventually make your DOM larger and larger, which at the end gives your app some performance lags on slow devices. It is crucial here to optimize how you manage this scenario.

To implement this feature we used a combination of a symbol and a use element in SVG. The symbol element provides a way to group elements, but when a symbol element is added to the DOM of the page it isn’t displayed, so it is something that you can reuse. Therefore, you can insert a complex SVG shape one time and reuse it again and again by using the use keyword. For example, look at the following SVG code:

<symbol id="bottiglia" viewBox="0 0 40 40" preserveAspectRatio="xMinYMin meet" >
  <path …>
</symbol>
<use id="onlyforimg" xlink:href="#bottiglia" height ="80" width="80"></use>

A path (omitted for brevity) that contains the actual shape is contained in a symbol element that also defines a viewBox to set the size of the shape when it was originally drawn. The viewBox here is necessary because we can resize the shape in the use element, where we set the height and width, doubling the original size. (This was another goal of the app mentioned earlier.) The use element has a reference to the id attribute of the symbol element, as you can notice in the code. Furthermore, the use element is very small, and thanks to this we can reuse it a number of times, eventually changing the size of the drawing and reducing the size of the DOM.

In the real app, the use element is created dynamically in JavaScript, setting not only the width and height but also the x and y coordinates to position the shape in a way that the center of the shape is where the user taps the screen. To do this, some calculation (which is not explained here) is necessary to set these two coordinates. To make all this stuff work, we need to use preserveAspectRatio using the meetattribute to obtain a uniform scaling—so that the viewBox is fully contained and not sliced when mapped on the viewport—and also set the alignment. xMin aligns the minimum x of viewBox with the left corner of the viewport, and yMin aligns the minimum y of viewBox with the top edge of the viewport. You can get the full reference for these values from the SVG specs here.

Loading SVG Files

The preceding section of this article discussed standard SVG that could eventually run directly in a modern browser. Now we will mix some Windows Store APIs that can be used for building a Windows Store app. These APIs are specific to the Windows 8 platform and could eventually be called from other supported languages.

Because Paint 4 Kids uses many drawings, a single drawing is loaded asynchronously when it’s needed from the appx file and then put into the DOM of the page. The asynchronous loading is done using the Windows 8 load async API, as shown in the following code. A Windows Store app prevents you from loading dynamic code into the DOM because, in general, this code can come from an external call and eventually create some security issues in your app if the code is malignant. However, the code we’re loading is trusted because it is already in the appx file and provided by us. You can safely call the WinJS.Utilities.setInnerHTMLUnsafe function that allows you to load the drawing into the DOM when needed.

svgfolder.getFileAsync("drawing.svg").then(function (file) {
  file.openAsync(Windows.Storage.FileAccessMode.read).then(function (stream) {
    inputStream = stream.getInputStreamAt(0);
    reader = new Windows.Storage.Streams.DataReader(inputStream);
    size = stream.size;
    reader.loadAsync(size).then(function () {
      svg = reader.readString(size);
      WinJS.Utilities.setInnerHTMLUnsafe(document.getElementById("s1"), svg);
    });
  });
});

The svgfolder is an object of type StorageFolder. You can call an async method for searching a StorageFile object (here error handling is omitted) on which you can open a stream and a dataReader object for reading the content. At the end, the svg variable contains a string that represents the entire SVG drawing. As explained earlier, we can call setInnerHTMLUnsafe to add the drawing to the s1 DOM element.

A nice touch here is that the SVG can be inspected in the DOM Explorer in Visual Studio 2012, as shown in Figure 4. You can select, for example, a specific shape from the app at run time—it also works from the Simulator—and the DOM Explorer will show you the associated SVG at run time, the one that could be the result of the dynamic loading and subsequent transformation applied to it. Very cool stuff!

Using the DOM ExplorerFigure 4. Using the DOM Explorer

Converting the SVG Drawing into Canvas and Saving to an Image

Sometimes you’ll need a JPEG of a drawing. For example, you might want to save the drawing to the file system, or you might want to use the Windows 8 Share charm, or use the drawing in other parts of the app. To do this we’ve used the canvg library that converts an SVG file to a Canvas object. The conversion can take some time depending on the size of the SVG file and your hardware platform. To notify the user that something is going on, we use a flyout with a progress ring that shows up when the conversion starts.

Once you have the Canvas object, you can get a reference to the entire byte array, and then you can use the Windows encoding API to get an image of the format and size you want. Here is a sample function:

function doSaveDrawingToFileEnd (fil) {
  var Imaging = Windows.Graphics.Imaging;
  var stream, encoderId, cc, offset, outputPixelData;
  fil.openAsync(Windows.Storage.FileAccessMode.readWrite).then(function (_stream){
    stream = _stream;
    return Imaging.BitmapEncoder.createAsync(jpegEncoderId, stream);
  }).then(function (encoder) {
    cc = document.getElementById('canvas');
      //ignore the realBRectHeight variable below
      //it is not important to understanding the logic
    offset = (height - realBRectHeight) / 2;
    outputPixelData =
      cc.getContext("2d").getImageData(0, offset, cc.width,cc.height - offset);
    encoder.setPixelData(Imaging.BitmapPixelFormat.rgba8, 
      Imaging.BitmapAlphaMode.straight, width,
      realBRectHeight, 90, 90, outputPixelData.data);
    return encoder.flushAsync();
  }).then(null, function (error) {
    console.log(error.message);
  }).then(function () {
    if (stream) stream.close();
  })
}

First we get a reference to a StorageFile object (not outlined in the code) and the fil variable, which will be our final JPEG image. Then we open a stream for writing and create a BitmapEncoder object, setting the encoding type to jpegEncoderId. This is used to encode the byte array into the image you want. The cc variable is a reference to the canvas object that contains the converted SVG file. Using the standard getImageData method, we get the bytes that we want to convert. (In the code, you can see an offset value used to clip a specific part of the drawing, but you can ignore this value here because it is not particularly useful to understanding the logic.) Now, using the setPixelData API with the encoder, we can set some values for the image to be generated. The image is actually generated when flushAsync is called.

Wrapping Up

Throughout this article, you’ve seen some technical considerations about SVG and how it has been used in Paint 4 Kids. SVG is a fun and powerful Web technology that you can leverage to build amazing Windows Store apps with JavaScript. You can also play with SVG directly in the browser, searching some funny samples on the IE Test drive site. I hope this helps if you have a similar scenario. 

If you want to learn more about how to build Windows Store app, check out Generation App.

About the Author

Pietro Brambati  is a passionate developer ninja. He likes working with different kinds of languages and frameworks, working on different size applications and scaling from mobile device apps to large, enterprise-ready applications. He works as a technical evangelist with Microsoft, where he has the opportunity to work with the developer and academic communities. You can reach him at the main developer events and hackathon all around Italy or on his blog or Twitter.