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.
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.
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 !Artworks AWRender 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.
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 , 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.
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.
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.