Graphics stack

The graphics stack is not huge, but it is obviously vital to the use of the
system. In general, it had not changed significantly since RISC OS 2, and
many of the APIs dated back to the BBC. Changing any of the graphics system
was prone to break components if care was not taken. As each Select version
took on a focus, one of the focuses was the graphics system.

There were a few things that had been requested, and some which were parts
of the road map developments. The Filer needed to have support for
thumbnailing, and we needed greater use of, and support for, deep modes which
had never been completely finished with RISC OS 3.5 and beyond. For this
to work, the rendering of the thumbnails had to be rock solid.

JPEG handling was poor in many cases, and support for other formats -
whilst possible through !ChangeFSI - was nowhere near what it ought to be.
The Internet had opened a whole load of opportunities, and whilst JPEGs were
very common (and our support poor), there were other formats such as GIFs
(more widely used for icons), and PNGs which had been heavily promoted by Acorn
in Browse, not to mention the multitude of older formats which couldn't be
easily manipulated.

FontManager needed updating to support UTF-8 properly, and the interfaces
it used for rendering needed to take account of hardware acceleration.

Hardware abstraction for the video system was a particular requirement, as
most modern hardware had different capabilities than those expected by the
present RISC OS environment. And hardware acceleration needed to be
introduced across the entire system - we should no longer rely on writing
everything in software now that much faster methods existed.

I guess we'll just try to tackle components as they best fit, but there's
a reasonable amount of ground to cover .

Draw

There's a whole bunch of graphics stuff that was changed for
Select 3, mainly to try to get the system up to a useful
standard. Back when I was at Pace, I had a brief tinker with
replacing HLine - the function which draws a single horizontal line,
and which is used for all primitive operations to perform fills
- with a translucent version, and using it
instead of the regular one in Draw. The idea was that you
could get a stroked or filled path which could overlay
other regions. It didn't really work though - the regions
that ended up being filled sometimes overlapped, so you
ended up with the translucent colour applied twice, which
showed up the joins.

Now I think back, though, I wonder how other Draw operations
worked. Draw can use any of the plot types that have been
set (that is, the GCOL actions). Other than the NOP and Set, the
operations cannot be applied multiple times without doing
the wrong thing. In particular, EOR would restore the
original colour. If you did use an EOR plot type
with a Draw path you'd end up with some sections restored
to the original value. And I've a feeling that this does not
happen, which makes me wonder if my tests for the Draw
translucency was actually right or not. Anyhow, it wasn't
as useful as it could be at the time.

But, one thing that it did do was help me to understand how
Draw worked - how it breaks down paths, then processes them
into lines according to the operations that you requested
in your flags. It's really kinda neat. In theory, adding a
Floating Point variant (as had been originally intended) wouldn't be
too hard, either as a pre-process phase (convert to fixed
point) or as a replacement set of arithmetic operations
in all the early processing. And, beyond that, the raster
line generation that we get at the end of the operation
just calls down to HLine.

I added some relatively simple buffering of line segments
to Draw, so that you could get out a HLine silhouette of
the path that had been filled/stroked. And that was then
fed back into Draw to let you clip one path against
another. The feature wasn't very useful at the time, but
it was just one step along the way (which wasn't
progressed).

After I went back to doing RISCOS Ltd things, I reimplemented
the code - I think I managed to get everything right, as
I only had the external API to work to at that time.
There were a few little wrinkles that I'd not known at
Pace (or maybe I'd sidestepped) during the rewrite, but
it worked quite well - and was never used anywhere .

Part of the problem with making this sort of change is
that it has to a) be used and b) be propagated through
the system. The latter case was the more problematic -
mainly for the non-bitmap printer drivers. They would
have to be updated to be aware of the new DrawV
operations (all Draw operations go into the SWI and then
through the vector, to be picked up by Draw again -
that's how the replacement GDraw could do its magic to
add anti-aliased lines to regular Draw operations,
albeit that changes the behaviour). That was never done
(and was put off for many of the operations that were
later added - the complexity is greater and the whole
Printing stack is very painful).

Propagation to the rest of the system aside, the goal
had been to be able to perform clipped plotting on a
more general level. Using a clip silhouette (or
cached path), it should have been possible to apply this
to sprite and other graphics operations. For accelerated
systems this would need some thought, although it might be
significantly easier than the software version in some
places. The software changes necessary would be tricky
though - not only affecting SpriteExtend (which, though
the change would be complex, would be the easiest of
the places to change) but also the widely distributed
operations throughout the Kernel (VDU drivers and changed
box calculations), the abstracted graphics operations,
the FontManager, and of course all the 3rd party
components which did Direct Screen Access.

One of the nice things we could have done would have
been patterned, or specially filled Fonts - grab the
font outline and fill as your clipping region, then
perform a tiled sprite plot using that region. Pretty
font effects.

All-in-all it would have been nice but not particularly
useful, and required a lot of work and maintenance to
keep working.

DrawFile

The DrawFile module was regularly confused by people
with Draw (in a similar way that Filer was confused
with the Filesystems), because it's one of the main
ways that people see the lower level component. It was
pretty easy to get used to people talking about one
and mentally converting what they're saying to what it
actually means in the system. Of course, the same is
also true of the DrawFile format itself.

Anyhow, DrawFile is - essentially - a rework of some
of the RISC_OSLib code that was used to build !Draw.
In particular, though, the text area code was a little
different; see PRM 5a for the differences in how it
operated for text areas. In any case, text areas were never
consistent between implementations - many tools just
tossed the text area blocks completely, so they were
less useful than you might like. I have a feeling that
they were an initial attempt at providing a way to
create posters, but the areas weren't very
friendly. The lack of internal editing in !Draw
itself meant that they never really got as much use.

I remember using them when I was at school to create
some simple posters, before I used !Poster
in anger - a far, far better package for doing that.
It's a little sad that I don't know how I might
generate a poster using multiple A4 sheets these
days, other than by using !Poster. Maybe I never have
a need to do that any more .

Anyhow, DrawFile wasn't a huge area for change
really - there's limited amounts you can do
to make it keep working as it does and yet introduce
new features. Of course, if any new object types were
to be added to the DrawFile format, they would need
to be mirrored from !Draw to the DrawFile
module. I remember
speaking to David Pilling about some ways to provide
clipped regions in Draw, but we never really
progressed beyond discussions, I think (I could be
misremembering).

!Vector (if I'm remembering right) had its own tagged objects in the
DrawFile format, and !Poster files were actually just
DrawFiles that had extra objects in it, with a
different filetype. But nothing had really caught
on. I remember something about !Vantage doing other
things, but my recollection was that Nemo was
very cagey about such things. Looking back on my
mail, it looks like the clipping discussions with
David were based on discussions with Nemo, too,
and were reasonably well structured. At the time of
those discussions (2003), there really wasn't
enough time to develop things any further - other
issues were the focus then.

Back to DrawFile, though (and then we'll quickly
digress again!)... The graphics system
was being given a little bit of a face lift with
in the form of ColourMapping, and the DrawFile module
was one of the later ones to be updated. Because
of this, many of the wrinkles had already been ironed
out in the earlier changes.

ColourMapping was the ability to specify a
function to change the colours in the rendered image,
just before plotting. This can be used (and is mostly
used) for creating faded versions of the original
image. It can, however, be used to create a few other
effects, such as greyscale, brightness and contrast
control, gamma levels and hue correction (make your
own versions of Warhol's Monroe?). The SpriteExtend
handling of sprite and JPEG plotting had already been updated to
support this, and the !ArtworksAWRender module had long had
the same ability (upon which the definition was
based). Updating DrawFile to use the ColourMapping
was relatively easy - every colour call for the
Draw or Font operations could call the mapping function,
and any Sprite or JPEG call would use the extended
colour mapping variants.

Et voila, we have a DrawFile being plotted with
different colours! Maybe that doesn't seem so
interesting, but being more widely used meant that
the ImageFileRender system could just plot things
with the colour mapping and it would work on any
of the bitmaps from ImageFileConvert, any Artworks
file (because it already supported that, through
ConvertArtworks), and DrawFiles as well.

Why is it even worth putting the effort into that?
Well, it was a step along the way to a distant goal.
Anywhere that you plot a sprite in the desktop, you
have to be able to plot it with a possible colour
translation. Sprite plotting could use a colour
translation table supplied by ColourTrans - either
a palette or a 32K lookup table, but this was only
useful for getting the colour that was intended to
be plotted, and it didn't work in deep modes if you
wanted to make changes to the colours. So, for example, there was
no way to grey out a sprite when you were in a
deep mode - the Wimp just couldn't do it. If you intend that
the 16 bit and 32 bit modes be used commonly by users, being
able to use sprites that were deep colours was an absolute
requirement.

I'll talk about the faded icons in the Wimp elsewhere,
but this meant that we needed a way to change the
colour of deep sprites. A colour mapping
function was the most appropriate (and used in the
Wimp already as part of its palette translation
for shallow modes).

This gave one part of the functionality, but in
the longer term, resolutions would improve and
interfaces would need to change. There had already
been much discussion (and work) in other systems
for vector-based windowing systems and the like,
and we'd already got a good font rendering which
could work equally well in high or low resolution.
We would be expecting to use EX0 EY0 modes (where
the OS unit to pixel resolution is 1:1, and where
!Sprites11 would be the norm), and it was
reasonable to expect that we would use high
resolution sprites later. The system wasn't really
ready yet for that full time - there were still a
few corner cases to work out and the like, but
all the preparation needed to be done.

In general, most of the sprites that we were
creating for icons used in by applications and within
the desktop were no longer hand drawn sprites.
Originals were drawn in !Draw, constrained to a suitable grid
and with common design elements, and then converted
to the Sprites22 (and Sprites24) variants that
were built into the core !Sprites files used by
the OS or applications. Tweaks were often made to
the results where things looked odd, but in general
the original DrawFile was updated to avoid the
problems.

The goal was to remove the conversion step - providing icons
which were entirely vectors and could therefore be
used either at the standard resolution, or scaled
up to meet differing needs (consider a very large
icon mode for Filer, or other indications of the
filetypes on documents in the browsers). It would
mean that little things like the radio icons (to
take a random example) could be improved with
desktop scale if we so desired it. Of course, the
old style bitmaps would still be usable, and
for speed we would probably cache the icons. The
IconCache module would do this, and could generate
sprites from source images at arbitrary
resolutions, but could also directly supply the
original for direct rendering (which might be
faster for some hardware).

The goal might not be to everyone's taste, but that was part
of a general plan I had. The goal wasn't to 'stop using
sprites', but to improve the visual appearance of
the system. We had a lot of facilities available
in the graphics system which hadn't been exploited,
and the aim was to keep the style but present it
better. And maybe we could find newer, more
exciting things as well.

Obviously there was some fun work needed to decide
how to supply objects that were not sprites with named entities for
'sprite pools'. Various solutions were
proposed for this but nothing was satisfactory.
The most sane was a DrawFile with groups, using the
name field to indicate the object type (the group
name being little used in general).

Anyhow, all this meant that across the board we
should be able to perform the operations that would
be performed on sprites on other rendering types,
including DrawFiles.

As with many of the longer term goals, it wasn't
completely relevant whether the ends were to
everyone's satisfaction. The stages that it brought
along the way were all of benefit, and each spawned
its own little 'ooh, look what I can do now!'
moments.

There was another 'small' change in DrawFile, in
order to make other things more useful. Originally,
I believe, the DrawFile to SVG conversion was
performed in !Draw itself. This meant that !Draw
could save DrawFiles (yay) but nobody else could.
You had to import into !Draw and then export them
as SVG by hand. The DrawFile to SVG conversion
was reimplemented in the DrawFile module, providing
an ImageFileConvert conversion. This meant that
any ImageFileConvert compatible application would be able to
make an SVG, given a DrawFile.

I believe the implementation in DrawFile uses the
FontMap module to provide a conversion from the
RISC OS style font names to more common names used
in the font style information in SVG.

There was no need for the DrawFile module to provide
a DrawFile to Sprite image file conversion, because
the ConvertSprite would proxy the ImageFileRender
operations together with sprite redirection to make
a sprite.

ColourTrans

The ColourTrans module provided most colour translation operations which
the rendering system used. A few other support modules provided specialised
tables for performance or modularity reasons, but in general almost everything relied
on ColourTrans. Because of this, the module has been talked about in quite
a few other places - its behaviour has a great impact on the desktop.

The module itself had one of the worst uses of the MessageTrans
module. ColourTrans wanted to decompress its resources, the colour translation
tables. In
order to do so they called MessageTrans to open them as a Messages file,
to get the resources in an area of memory they could access, with the
data being automatically decompressed.

As MessageTrans performed pre-processing on the files, to speed up lookups,
this tended to go a big funny when given a file of binary data - plus it
was obviously semantically wrong to use a message file translation tool
as a decompression interface. Replacing the calls with some proper
decompression calls - directly calling Squash - wasn't too hard,
but gave me a small chuckle at how bad
things could become when you're trying to be clever .

ColourTrans had a number of transient tables that it used to speed up the
conversion operations. Because these were in ResourceFS and the tables
could be changed arbitrarily, the code would discard any references to
the data if ResourceFS changed. Unfortunately this meant that every time
a disc appeared of disappeared through ShareFS, ColourTrans would discard
its tables. Worse, anyone who was using them still might be left pointing
at rubbish.

This was a relatively simple fix, although I'm not sure how
comprehensive it is, now that I'm thinking about it. I'm going to assume
that it was correct as I was pretty good about that kind of thing then and
my memory is not as good as I would like.

Mode strings

When RISC OS 3.5 was introduced, the WindowManager
supported the use of mode strings in the *WimpMode
command. In particular, BASIC could use mode strings to select its modes,
in addition to mode numbers. Sadly, BASIC just used a call to
SWI Wimp_SetMode, which meant that the Wimp mode changed when you
ran any BASIC program that used the basic MODE command. This was a constant frustration for me,
as it is neither a good practice for such things to have side effects, and
it also meant that you might be dumped into a mode you didn't really want
to be in after running applications.. Plus it meant that anything that needed to
handle mode strings had to do so itself.

The simplest thing to do would have been to lift the code from the
WindowManager and put it into
BASIC. As well as duplicating functionality and
therefore making things harder to maintain, it wouldn't solve the problem
of other components having to do the same thing with the mode strings themselves
if they wanted to use them.

To address this, I moved the mode specifiers into the
Kernel, alongside the other screen mode controlling
calls in SWI OS_ScreenMode. This might seem counter to the goal
of moving things out of the Kernel, but there were limitations on this -
in this case the video system (at the level that the Kernel provided it)
was not yet ready to be moved out. Creating a separate component just
to handle mode strings wasn't really suitable. The code was isolated, and
disentangled from some of the other VDU code, so that it could be handled
independently when the time came to move it.

The implementation was relatively simple - a few functions in C to parse
the string from the definition in the PRM and the implementation in the
WindowManager. As usual, this was easier to write
and test before making it into a live part of the Kernel. Whilst implementing
the calls, I found that there was an oddity in the PRMs compared to my own
understanding of the strings. I had assumed that they were always
space-separated, but the PRMs gave an example of a comma-separated string
and the WindowManager supported it. It is also case-insensitive, which was
a little surprising, but simple to support. Both were simple compile-time
options in the C code so that if I found that they weren't useful I could
turn them off again.

Once implemented, I needed to be able to test the code. The usual way to
test such things is to have a set of test cases for positive and negative
cases which you expect for parameter combinations. However, because this
is a conversion, it would be useful to provide a function that performs
the inverse. To complement the parser that decoded the mode string, I
created a similar function that encoded the mode string from a mode specifier.
This would actually be necessary anyhow if we needed to display the mode
in a manner other than a number - as we do in the DisplayManager.
It made the testing faster, as any test
could be performed bidirectionally and ensure that a round trip didn't
lose information (or corrupt any).

These functions were inserted into the SWI OS_ScreenMode, and a
single use function added for selecting a screen mode using the mode
string - just to make
it easier, as this is the common use.

The WindowManager was updated to use the new call,
but also retained the functions for setting the palette when a greyscale
mode was selected. The Wimp caches this information so that it can perform
translations more quickly.

Selecting a mode with the 'G' specifier from the
SWI OS_ScreenMode call will also set up the palette to be greyscale
as part of the mode selection - a flag indicating that the mode is
greyscale was introduced so that this could be passed from the mode
string down to the mode specifier (and vice-versa). When the mode is set
to greyscale in the specifier, the palette set by the Kernel
will then default to that.

Knowing that the mode is greyscale in the mode flags makes the operations that
ColourTrans performs faster. Its palette lookups were
optimised for greyscale so that such modes could be handled better. Because
of the introduction of the alpha-channel masks previously, ColourTrans had
special code to understand that the mask that had been redirected to was
an alpha-channel mask. This became simpler because the flags indicating
that the palette was greyscale would be set when redirected to an alpha
channel mask.

Font Manager

The FontManager was always going to be a source of criticism as it had not
been upgraded to support UTF-8. Now I think about it, this annoys me more
now than it did at the time. The FontManager, being a single component, had
never been updated to support this, but other components had been revamped
and significant other areas of development had been done across the rest of
the system - yet it was rare to hear much praise for those changes. There
are a few ways to look at that, I guess. The most obvious of which is that
if there was such criticism of the lack of work on the FontManager,
it should have been addressed as a
priority. Another is that people just criticise things, whatever you do.
Yet another is that the other areas were in need of improvement, but you
don't get praise for stuff that works.

Whatever might be the case, the reason for it not being upgraded was always
going to be that it was held back by the possibility of merging the sources
at some later date. Early on, there was a reasonable chance that the sources
might have been merged. As time went on, this faded, and with the problems
with Castle it became even less likely - they had very little to offer to
RISCOS Ltd in the way of features and certainly nothing of comparable value
to the significant work that had gone in to other parts of the system. Not
to mention the other political issues.

And with those reasons in mind, the FontManager was never updated to add
the UTF-8 features. Doing so would have required a reasonable investment
in time to add the support, and carried with it a risk that the
existing operation of the module would break. It was planned that such work
would be done some time after the 32 bit upgrades, as by then there was
no expectation that there would be any merging in the future.

FontManager did have some work done on it, though - it wasn't completely
left out cold. The font path handling had always been a source of concern,
and (if configured incorrectly) could crash the machine quite badly. The
code was entirely written in assembler, and because its handling of
long paths was poor, it could easily cause buffer overruns, corrupting the
RMA - with serious consequences.

The manipulation of font paths had become more prominent with RISC OS 4,
as there was now a dedicated tool for installing new fonts, which meant
that paths would be manipulated more. Obviously other font tools existed,
many of which had worked around the font path problems in a variety of
ways, but making the use of the path manipulation more common would mean
it would be more likely to become a problem for people.

Robin Watts had previously written a module called PathUtils whose purpose
was to manipulate paths and enumerate their terminal components. It had been
initially (I believe) written to support !Printers, but performed exactly
the job needed in FontManager. Replacing all the path enumeration code in
FontManager with code that used PathUtils was pretty simple, and because
PathUtils had been tested separately (and was written in C, so was a world
easier to follow) it was known to work properly. The only difficulty was
in ensuring that the integration was done right.

There were still some fun problems with it - especially when invalid paths
were added to the path, which caused the code to cycle forever trying to
resolve it. But these were minor, and since that point there were no further
reports of font path related issues.

One problem with FontManager caused Vantage to fail badly (and probably
others, but Nemo was very quick to point it out, fortunately), and although
the core issue was FontManager it wasn't due to FontManager that the problem
existed. SWI Font_ScanString would report an error if asked to scan
a string of 0 length, where the pointer to the string was 0 - because it
tried to dereference the byte at address 0 (if you'd passed in invalid
values it would also die) in order to get the kerning details for it, and would
find that the table for the character there was invalid.

In the past the value at address 0 had always been valid, so it would have
worked. Due to some changes in the Kernel, the values at address 0 had
changed - the workspace that the instructions at those addresses pointed at
had moved, causing the byte to no longer be 0. The fix was to stop
FontManager dereferencing the pointer if the string length was 0. However,
it is useful to have a 0 byte at address 0, to prevent applications which
also assumed this from failing. The Kernel workspace was updated so that
the byte at 0 was 0.

As an aside, I had often played with making zero page inaccessible, or at
least the first 4K of memory. The problem is that so many things assume
that they can access it, even if they don't actively use it, so it's not
a great idea. It was made read only in user mode for RISC OS 4, I believe.

In the same way as had been done with other areas, the FontManager gained
control of its own dynamic area, releasing the Kernel from that task. This
meant that the memory used by the Font Manager was released between
invocations, in particular (not that it was that useful to leave the
FontManager unloaded, because so many things relied on it). Because it was
always going to be useful to use the FontManager on earlier systems, the
code was set up so that if the dynamic area already exists, it will take
over the use of it. This allows the module to be used on Kernels which
haven't yet relinquished control of the area, eg if you loaded it on RISC OS
3.7.

The InverseTable module had been updated to support other modes, in
order to work with the BlendTable module. The FontManager uses InverseTable
to perform font blending for the anti-aliasing. Because it only supported
256 colour modes previously, FontManager had only been able to perform font
blending in 256 colour modes and above. By adding support for 2, 4 and 16
colour modes in InverseTable, the FontManager could then perform blending in
those modes as well.

There were other minor things like the *LoadFontCache
being disabled, as it relied on the structure of the file being valid for
any new version of the module. The content was versioned, but even loading
the cache when the version was the same could cause problems, so it was
easier just to disable the feature.

This page is maintained by
Justin Fletcher
(gerph@gerph.org).
This site is copyright
Justin
Fletcher. The accuracy of anything on this site is entirely limited
by his belief system and memory at the time of publication - neither of
which should be relied on. The opinions are entirely his, except where he's
changed his mind.
Quotations are copyright their respective authors and whereever possible
attributions have been included.