Navigation:  Image Viewer Architecture > Rendering >

How rendering is triggered

Previous pageReturn to chapter overviewNext page

We now have some understanding of the rendering object model and what takes place inside a renderer.  But how is the rendering actually triggered?  How do the different objects in the model interact with each other to invoke the renderer?

 

To help us answer these questions, let’s take a look the sequence diagram above that describes the rendering process.

The rendering process can be separated into two steps: Render and Refresh.  Let’s take a look at the Render step first.

Typically, a Tool will alter some property on the PresentationImage (e.g., the zoom) then call PresentationImage.Draw() to update what is seen on the screen.  As you can see in the sequence diagram, this call is propagated right up to the TileControl. At that point, the TileControl’s IRenderingSurface is retrieved and sent down to the PresentationImage, where it’s passed on to the IRenderer.  It is then up to the implementation of IRenderer to render the PresentationImage’s SceneGraph to the IRenderingSurface.  If the implementation of IRenderer performs double- buffering, the completion of this Render step will not result in anything being drawn to the screen, but rather to the back buffer.  For the image to become visible, the Refresh step has to be performed.

The Refresh step is triggered when Invalidate() is called on the TileControl.  Doing so causes the TileControl to receive a paint message from the operating system.  What happens next is almost the same as in the Render step—the IRenderingSurface is sent down to the IRenderer.  The one difference is that IRenderer is told to Refresh instead of Render.  It is then up to the implementation of IRenderer to “flip” the back buffer to the front buffer.  And with that, the image is displayed and rendering is complete.

There are a few things worth noting:

1.The IRenderingSurface that the TileControl owns is obtained from the IRenderer itself, by way of the GetRenderingSurface() method.
2.Associating IRenderingSurface with the TileControl is memory efficient, since the implementor of IRenderingSurface need only allocate enough back buffer memory for PresentationImages that are actually going to be displayed.  If IRenderingSurface were associated with IRenderer instead, much memory would be wasted, since in effect, an IRenderingSurface would have to be instantiated for every PresentationImage, regardless of whether it was currently visible or not.
3.IRenderer can be completely stateless, since its sole responsibility is to render the PresentationImage’s SceneGraph to the IRenderingSurface.
4.We mentioned that Invalidate() is called explicitly in TileControl to force a repaint.  In fact, TileControl will receive the same paint message automatically whenever any part of it is made visible that was previously obscured (e.g. when another window is dragged over it).  Thus, the Refresh step also functions as an efficient repainting mechanism, since it prevents a PresentationImage from being needlessly re-rendered, which can be expensive.