TinyLine SVG Programming Guide

3 Viewer Example

3.1 Overview

The Viewer Example is a Static SVG Tiny Viewer that:

  • Parses and processes the static language features of SVG Tiny 1.1
  • Supports zooming and panning of SVG Tiny 1.1 documents

In other words, there are no animations and links. You can read, parse and display SVG Tiny document and also zoom and pan it.

viewer1 viewer2

View this example as applet (Java-enabled browsers only)

The Viewer Example includes the following classes:

tinyapp.Viewer
tinyapp.ViewerCanvas
com.tinyline.app.ImageConsumer
com.tinyline.app.MIDPTiny2DProducer

Please note, that we use J2ME MIDP 2.0 source as a reference. But it's easy to see that there is not a big difference in code for J2ME Persona Profile (CDC and J2SE) version as well.

Let's see how the Viewer application draws SVG Tiny documents. Here we describe how SVG Tiny input stream turns into SVGNode tree and then displays onto the Java device screen.

3.2 SVG Pipeline

svgpipeline

During the parsing process SVGParser callbacks build internal OM (SVGNode tree ) from the input SVG stream. The SVGDocument has the SVGNode tree root as its member.

The SVGRaster translates SVGNode objects into graphics primitives and draws them (rasterization process) using Tiny2D onto the TinyBuffer object.

The TinyProducer interface gets a notification that new pixels of the TinyBuffer object are ready to be send onto physical screen.

Before the pipeline can work we need to set up and create its elements

The init process: Here we need to create all objects in the pipeline and define needed hooks:

       // 1. Creates the pixels buffer (width X height)
       TinyBuffer buffer = new TinyBuffer();
       buffer.width = width;
       buffer.height = height;
       buffer.pixels32 = new int[width * height];

       // 2. Creates the SVGRaster graphics
       raster = new SVGRaster(buffer);
       t2d = raster.getTiny2D();

       // 3. Creates the TinyProducer and links it to the Tiny2D
       imageProducer = new MIDPTiny2DProducer(t2d);
       imageProducer.setConsumer(this);
       t2d.setProducer(imageProducer);

       // 4. Sets the ImageLoader implementation needed for
       // loading bitmaps
       SVGImageElem.setImageLoader(this);

The parsing process:

       // 1. Create an empty SVGT document
       SVGDocument doc = raster.createSVGDocument();

       . . .

       // 2. Read and parse the SVGT stream
       TinyBuffer pixbuf = raster.getTiny2D().getTarget();
       // 3. Create the SVGT attributes parser
       SVGAttr attrParser = new SVGAttr(pixbuf.width, pixbuf.height);
       // 4. Create the SVGT stream parser
       SVGParser parser = new SVGParser(attrParser);
       // 5. Parse the input SVGT stream parser into the document
       parser.load(doc,is);

The rasterizing process:

       // 1. Set the SVGT document to be drawn
       raster.setSVGDocument(document);
       // 2. Invalidate the current clip (i.e. set
       // the clip to the pixel buffer bounds)
       raster.setCamera();
       // 3. Update pixels under the current clip.
       //    At this step all SVGNode objects are to be
       //    drawn onto the pixel buffer.
       raster.update();
       // 4. Call TinyProducer interface to notify
       //    consumers that there are new pixels
       t2d.sendPixels();

The SVGRaster class rasterizes the tree of SVGNodes onto the TinyBuffer. This process is called a "rendering process".

3.3 Rendering process

Each SVGNode "paint" operation draws over some area of the pixel buffer. When the area overlaps a previously painted area the new paint partially or completely obscures the old. When the paint is not completely opaque, the result is defined by the simple alpha blending rules. Nodes in the SVG tree have an implicit z drawing order. Subsequent nodes are painted on top of previously painted nodes. When a graphic object is rendered, the geometry, image, and attribute information are combined to calculate which pixel values must be changed

paint() vs. update()

What is the purpose of having separate paint() and update() methods? A call to update() implies that the area defined by the device clip rectangle is "damaged" and must be completely repainted or updated, however a call to paint() does not imply this, which enables to do incremental painting.

3.4 Zoom and Pan

The SVGRaster class has two members 'view' and 'origview', which are viewports. The 'view' defines the rectangular region into which SVG content is rendered. The 'origview' just stores the original viewport of the SVGRaster object.

In order to shift or 'pan' the SVG content onto (x,y) distance we call the following function:

   /**
    * Pans the current SVGT document.
    * @param x The distance on X coordinate.
    * @param y The distance on Y coordinate.
    */
   public void pan(int x , int y)
   {
        // 1. Get the current viewport
        SVGRect view = raster.view;
        // 2. Get the SVGT document
        SVGDocument doc = raster.getSVGDocument();
        // 3. Get the root of the SVGT document
        SVGNode root = doc.root;
        // 4. Get the current scale value
        int scale = ((SVGSVGElem) root).getCurrentScale();
        // 5. Scale pan distances according to the current
        //    scale factor. Change the current viewport.
        view.x += Tiny2D.div(x << Tiny2D.FIX_BITS, scale);
        view.y += Tiny2D.div(y << Tiny2D.FIX_BITS, scale);
        // 6. Recalculate the current 'camera' transform
        raster.setCamera();
        // 7. Update pixels under the current clip.
        raster.update();
        // 8. Notify TinyProducer about new pixels.
        t2d.sendPixels();
   }

The same flow you can find for the zoom operation.

The next example application is more interesting.

© 2008 TinyLine. All rights reserved.