[Scummvm-devel] New GUI thoughts in the Wiki

Marcus Comstedt marcus at mc.pp.se
Sat Nov 19 06:51:01 CET 2005


Max Horn <max at quendi.de> writes:

[...]
> For briefness I'll just talk about DLE's in the following :-).
>
> in OSystem:
>     DLE_ID appendElementToDisplayList(DLE e);
                                        ^^^^^
                                        Should probably be DLE& e  :)

>     void removeElementFromDisplayList(DLE_ID id);
>     void clearDisplayList();
>
>     // Could be used to switch between pressed/normal button bitmaps.
>     // Probably should ENFORCE that the new and old DLE have
>     // identical type & rect (so that you can only change the color
>     // of a line, a label, the content of a bitmap etc.)
>     // Not sure if we need this...
>     void updateElementInDisplayList(DLE_ID id, DLE e);
>
>     // Insert an element into the list before another element.
>     // Not sure if we need this...
>     void insertElementIntoDisplayListBefore(DLE's e, DLE_ID id);
>
>
>
> However, I am not yet sure how efficient redrawing would be
> implemented... Maybe like this:
> 1) The z-order of elements in the DL is equal to their order in the DL

This would allow the painters algorithm to be used without any
Z-buffering, so it's probably perfarable, especially to the software
renderer.  Also, it simplifies alpha blending.

> 2) Each DLE has a "dirty" flag marking it as in need of a redraw
> 3) Appending a new element to the end of the DL marks just that
> element as dirty
> 4) Removing an element marks all elements which very previously
> (partially) covered by it as dirty. This in turn has to mark all
> objects that are covering those object as dirty
> 5) Inserting an element makes it dirty, and also everything which
> covers it (partially).

For this dirty-flag to be meaningful, you'd have to be able to combine
multiple DL modifications without an incremental redraw in between.
Otherwise you can just replace "mark as dirty" with "redraw".  But
I suppose we'd have some kind of transaction system similar to what
we have today (everything done between two copyRectToOverlay() goes to
the screen as one unit).

> 6) If the GUI is designed carefully as to reduce overlap, this means
> we get a relatively low redraw count
> 7) Making it possible to modify a DLE in-place could be used to speed
> up in-place modifications. Typical example: A button which switches
> between active, clicked, and disabled. By using
> updateElementInDisplayList, ideally only the button has to be redrawn
> (assuming nothing else covers it); if we handle text via DLEs, too,
> then the button and the text have to be redraw, which is still quite
> moderate.

Yes, if you avoid exposing the background, you'll save a lot with the
algorithm above.  However, while the updateElementInDisplayList would
be ok for button, maybe we need to find a more general solution.
Consider a slider for example.

    +------------------------------
    | A
    |   +-----------------+
    |   | B          |C|  |
    |   +-----------------+

A is the GUI background, which is large and covered by lots of GUI
elements.  If it becomes dirty, we'll have to redraw everything.
B is the slider background, which is much smaller, and only covered by
C, the slider knob.

If we drag the knob, we expose a portion of B which was not previously
visible, so it needs to be redrawn anyway (updateElementInDisplayList
will not help us here).  However, if the check described in 4) above
only checks the bounding boxes, it will determine that A was also
covered by C, and flag it as dirty.  Unless B is semi-transparent,
that's not really necessary.  So we can tweak step 4) a bit: If, when
scanning the DL for DLEs that were covered by the removed DLE, we find
one which actually has the _entire_ bounding box of the removed DLE
inside its own bounding box (and which is not semi-transparent), then
we can terminate the scan (after marking that DLE, and those covering
it, as dirty) and ignore the bounding boxes of any "deeper" DLEs.
Since C is completely contained in B, only B and C would be redrawn
when the knob moves.

And with this improved algorithm, we can also get away with changing a
button without using updateElementInDisplayList(), if we just do it
right:

First, add the new button under the old button, using
insertElementInDisplayListBefore().  This will mark both the old and
the new button as dirty, according to 5).  Then remove the old
button.  This will again mark the new button dirty, accoring to 4),
but then we stop since the new button entirely contains the bounding
box of the old button (since they are the same), and only the new
button will be redrawn.

While it may seem a bit pointless that we can get rid of
updateElementInDisplayList() at the expense of needing 
insertElementInDisplayListBefore(), the former has some arbitrary
limitations on its use which the latter doesn't have, so it's the
better choice.


> Of course we must provide a good default implementation for this, and
> one which is easy to make use of in the existing backends (i.e. it
> must be easy  for all porters to upgrade to the new system, while
> still making it possible for e.g. the DC port to provide a custom
> implementation...).

Yes, ideally a port should be able to simply keep its current
overlay interface, and have the default renderer use that.


  // Marcus






More information about the Scummvm-devel mailing list