Clunkdraw

Clunkdraw is a small, clunky clone of Apple's original monochrome Quickdraw imaging library. (Written from scratch based on the published APIs.)

Clunkdraw Windowing Screenshot    Clunkdraw Clipping Screenshot
Clunkdraw Large Text Screenshot    Clunkdraw CopyBits Screenshot
Currently it is a Free Pascal library targeted at driving Waveshare's small e-Paper displays for Raspberry Pi. The above sample displays (from the included test program) demonstrate that you can utilize multiple overlapping display windows (if you want them), clipping to arbitrary shapes (ditto), multiple font styles, sizes, and rotation, full justification of text, large font sizes, a variety of geometric shapes, and bitmap scaling and orientation.

Clunkdraw tarball: clunkdraw.tgz


Background

Once upon a time there was a Small Company making banking computers, their first products were purchased North Star S-100 systems, but they soon started making their own S-100 cards, and later their own machines, both bus-based and not. All were based on the Z-80 microprocessor, often multiple CPUs were used. They made multiple-user machines, they had communications coprocessors for talking amongst themselves and with Big Iron; video subsystems existed that supported Arabic and Hangul (Korean) displays, in addition to European languages, camera capture, graphics display, etc. The Company was successful, but the necessity of using assembly language for all software (for both performance and space reasons) was a heavy burden, and was fast becoming crippling.

The 1984 introduction of the 68000-based Macintosh electrified the entire microcomputer industry, this company included. Proof that a modestly priced 16-bit system using a high level language (like C) was not only at least as powerful as the Z-80 and supported a much larger flat address space, dramatically easing programming, but was capable of supporting nearly any customer's written language using a graphics-only hardware platform, and was even capable of supporting a paradigm shift to a GUI environment.

(Apple's high list prices did not confuse the engineers at the Small Company, they were well aware of the very modest incremental component cost over a Z-80 solution. While Intel's extant 8086 CPU family would also support more memory than a Z-80, the Company was already highly experienced with the memory bank switching and code segmentation necessary to get around the Z80's 64KB limit, and had absolutely zero interest in jumping 'forward' to a 'solution' that still required all the same kind of software machinations in order to avoid the very same 64KB limit. Not Good Enough, and thank you for playing, Intel. Other reasonable candidates were offered by Zilog [Z8000, still with a marked 64KB odor] and National Semiconductor [16032, slow and buggy], but Motorola's offering was the most mature, and support software [OS, C compiler] was readily available, as were second sources of parts through Hitachi. Intel's own 386, their first product to actually compete effectively with the 68000, was just becoming a potential choice at this time, but it was new, expensive, and single-sourced, all of which removed it from consideration.)

The subsequent releases of comparable low-cost Atari and Amiga systems hammered home the proof, and commercial releases of Unix-based systems using the same 68000 processor family showed just how capable these systems could be. All of a sudden the Company had a clear direction in which to go, one that would be a significant improvement over the Z-80 product line, both in customer perception and in ease of development.

However, the first 68010-based next-generation systems the Company developed suffered from a lack of vision on the part of management. While the hardware was very good, as was the DNIX-based system software and the C compiler, the application layer software was very pedestrian, and was based around a VT220 non-graphics 24×80 terminal model that was barely even capable of supporting European languages. (It didn't, but could have easily enough.) No Arabic, no Hangul, and no graphics.

A major problem was that while everybody involved could see the improved capability of the new product line's hardware base, they had essentially all spent that same performance dollar, resulting in severe under-performance in the final result. Crippling underperformance, in fact. They had forgotten that the 68010 'advantage' had already been spent: on using C instead of assembly language. The 68010 had only twice the clock rate of the Z-80, and twice the bus width. Yes, it supported a lot more memory directly and thus eliminated bank-switching considerations, and the CPU's more and larger registers cut down on the data shuffling that generally plagued 8-bit processors, but when using C it wasn't really that much faster than the Z-80 had been at running custom application code. And, the graphics-only video hardware of the new platform was decidedly more sluggish than the more expensive (but less flexible) dedicated-language video hardware of the Z-80 platforms, so even if there had been a performance surplus it was gone already.

(We're getting there, be patient!)

Disaster Strikes

The application team's management had decided to spend 'their' share of the platform upgrade bounty (all of it, naturally, rather than their true share: none of it beyond getting to use C) on moving to a fourth-generation database language product instead of using any form of traditional procedural code, and upon that rock the Company's ship foundered.

The prototypes were slow. Orders (yes, more than one) of magnitude too slow. It was common during development to push some menu keys and then go to lunch. Once you came back it might be ready to look at to see if it had done the right thing, but lunchtime wasn't always long enough. The best engineers in the company were thrown at the problem, and told to 'optimize' it. They tried, and made some headway, but the starting point was so bad... The selected architecture was simply not practical on the platforms of the day.

The language vendor, when contacted, said "Don't do that, that's not what that query language is for, why aren't you accessing the database using the conventional API?" High-end engineering workstations, many times more expensive than any customer would consider purchasing for their bank tellers, underperformed by at least one order of magnitude when running the new code, even when optimized. The Company's own engineers unanimously said "this is bad and can't be fixed, we have to do something else". This all fell upon deaf ears.

Management hired performance consultants to show the Company's engineers the errors of their ways. The consultants tested the new platform, its hardware and system software, and ranked it higher than anything else in its class they had ever seen. Their report basically said: "Listen to your people, they're very good." No matter, management had somehow hitched its wagon to this particular 'solution', and that was that. The deaf ears became hostile ears: The engineers were told that anybody who persisted in nay-saying had better just shut up and fix the problem, or else seek employment elsewhere.

Of course, it was not just that management had chosen the wrong software architecture for the new product, if you don't make an occasional mistake you're not trying hard enough. The sin was that they were completely unwilling to admit that it had been a mistake, and do something else to fix it while it was still possible to do so. The engineers could not solve the architectural problem, and the managers would not budge on the architecture. The Company's entire future was tied to this one decision; the managers were betting the farm, but it wasn't actually their farm to bet. And the months, eventually years, rolled by...

Recovery Plan

Eventually uppermost management, the founder of the Company and chairman of the board, heard about the stalemate via a chance men's-room conversation. It hit the fan, so to speak, because he had heard nothing about the performance problems until then, and had been led to believe that the new product was almost ready to deliver—a complete and utter lie. A tiger team was immediately formed to come up with an alternative, and it knew that it had to hit a home run first time at bat, and quickly. The alternative needed to be a safe bet architecture- and performance-wise, and use the new hardware as-is, but it also needed to be showy, and ready soon. We were late, the Company had had nothing new to show for more than two years, as the entire engineering staff of the company had been engaged in trying to turning the festering pile of shit into a gourmet meal, and the customer base and industry pundits were getting concerned. (They had, naturally enough, heard rumors of upcoming new products, but usually by that time they'd have gotten some sneak peeks at prototypes. This stuff was so bad that nobody would show it to anybody for any reason whatsoever; not even the most gung-ho salesman would risk letting a customer see it in operation.)

To recap: the new hardware and system software was best-in-class, and the system had enough memory and performance to rapidly deploy new procedural applications written in C. But all that was available to the application for user I/O was a fairly sluggish VT-220 subset terminal emulation. This was no better than what the old Z-80 systems could offer, and in fact was worse because it was really quite sluggish in comparison. A Z-80 could drive, using assembly language, a memory-mapped hardware ASCII video subsystem extremely fast. Video responsiveness of all the old systems was unsurpassed in the industry. The new video hardware had met its design targets of supporting the written language of any customer, and supporting facsimile (signatures, checks, etc.) graphics, all at a modest cost, but there was no system or application software to support this, only the terminal emulator, and the innate performance of the display hardware was decidedly marginal as it required system software to do all the character generation rather than using dedicated hardware.

We needed something that looked better, a lot better, to draw attention away from the fact that applications on the new platform felt noticeably slower than on the old. We needed something that could better support languages, and facsimile graphics, than the terminal emulation or the old products. Emphasis on what you've gained compared to the old, not on what you've lost.

The X Window System existed by then, but all known implementations themselves consumed memory in excess of the total available on the target system, leaving none for the application or the operating system, and only had been seen working on much faster hardware than the target system, so it was of no real interest or use.

(And we're here, finally.)

Enter Clunkdraw

Because we knew that a Macintosh, on a similar modest hardware base, was capable of doing the required tasks satisfactorily and very attractively, we knew that Quickdraw, the Mac's Pascal/assembler lean-and-mean imaging engine, was thus capable. So we knew that its imaging model, and API set, was sufficiently capable provided the implementation was well done. The 'safe' choice for us was to follow their API model using a newly written subset library that directly accessed the video hardware, refining the library as necessary until the performance was satisfactory. The first Clunkdraw, written in C, very much lived up to its name, and supported only MoveTo/DrawText using one fixed-pitch font, and EraseRect. But it used the expandable API model of Quickdraw right from the start, which meant that new features could be added as we needed them without having to waste precious time rewriting existing application code. Almost immediately we started dressing up the new application, getting off the 24×80 character grid, using lines and boxes, proportional fonts in multiple sizes and styles... As Clunkdraw and its one client evolved more features were added, and all the low-level drawing routines were written (or rewritten) in assembler for performance. (This process was guided by profiling of the actual code, application and all, rather than by guesswork. The drawing library was always the bottleneck, so that's where the focus remained.) This was very painful and slow to write, as assembler always is, but the performance edge thus gained was sufficient to stay generally below the 'too-slow' perceptual threshold, and the entire rest of the application never needed to use assembly language. The application was rapidly getting much prettier, while not slowing down in operation, and we held to the Quickdraw API model because we knew that it was good enough for the results we wanted. At some point Clunkdraw really wasn't that clunky anymore, but the name stuck.

The alternative product was demonstrable (to customers) within three months, and was a roaring success. (Did we mention that the engineering staff really was quite good?) Some heads did end up rolling regarding the database debacle, as one might expect. Unfortunately the Empress database itself also got caught up in the purge, although it was not at fault and was itself a good performer. We'd paid a million dollars for it and its M-Builder query prototyping language that we'd mis-applied as an application environment, and we ended up throwing it all away. We eventually wrote our own application-specific database, but we'd probably have been better served by using the one we'd already bought. But, feelings were running pretty high at that point...

Eventually Clunkdraw gained a network-independent client/server layer, vaguely similar to what X Windows offers, which dramatically improved what could be done with the system, especially in training, supervisory, and remote debugging roles. But the imaging model remained that of Quickdraw. We also ported Clunkdraw, in C and assembler, to the TI 34010 graphics CPU for use in our later color products. At some point gateway programs to both Windows 3.1 and Macintosh systems were created, allowing them to run our financial applications too. (The translation from Clunkdraw to true Quickdraw was particularly easy, as you can imagine, but the network connectivity was much more difficult, as we did not use TCP/IP but rather an Ethernet form of X.25 networking, and the target Macs didn't even use Ethernet.) Eventually there were many man-years invested in this code base, and it was generally highly satisfactory within its market niche.

Final Result

The Company's old Z-80 products were reasonably priced, extremely performant, and extraordinarily difficult to program as well as having a dated appearance in operation. The new 68010 products were also reasonably priced, sufficiently performant (thanks to Clunkdraw), and easy to program (thanks to sufficient RAM, virtual memory, and C), and had a much more modern (also thanks to Clunkdraw) look. Success! The tiger team received some significant bonuses that year, as well as continued employment. (Sadly, the delay in new product ultimately caused the Company to be sold and merged with a competitor, curtailing the life of the new products.)

Modern (2021) Times

The Clunkdraw products discussed above came and went in their time, and eventually the Company was no more and there were no Clunkdraw-using products left in the field. Clunkdraw itself languished for decades on a disk that I had kept for nostalgia's sake. The bulk of it was 68010 (or 34010) assembly language, there was little chance that I would ever need to even look at it again...

I eventually found myself again working for the same man that had founded the Small Company. I was looking at using a small monochrome e-Paper display, because of its unequalled strengths in the ambient lighting department. Unfortunately the demo library that came with the display kind of sucked, and I found myself waxing nostalgic for the simplicity, capability, and familiarity of Clunkdraw. The e-Paper display did not integrate into the host Raspberry Pi's GUI system, you are intended to drive it as a pure peripheral, using an I/O library of your own.

And then the light went on: Why not resurrect Clunkdraw? At least partially? The environment I needed was Pascal, but Quickdraw itself was clearly very happy in Pascal, since it started out there, and porting from basic C to basic Pascal is a near-trivial mechanical exercise. The RPi is very fast, avoiding assembly language probably isn't a hardship now. And I only needed a couple of calls... all the familiar old arguments. And so the die was thrown. All I needed to do was a mechanical translation of C to Pascal for the few parts I needed, and to write some naive pixel-plotting code to replace the elaborate optimized assembly language of original Clunkdraw, and I'm good. It looked like only a few days would be required for what I needed in the new product, and the results would be infinitely better-looking and more familiar than what I'd get continuing on with the e-Paper demo library...

It was so much fun and worked so well that I got carried away, and ended up porting everything in the monochrome imaging layer over the course of a month. (Not the networking layer nor the color code, which were themselves substantial and not a good fit to the new environment.) The new drawing code itself is very naive, and an extremely poor performer when compared to original Clunkdraw. But, the RPi is so fast and our needs were so modest that it simply doesn't matter here.

No, it's not C, and it's certainly not C++. It's a near-trivial exercise to port it to C, though. It is probably a nearly trivial exercise to port it to any procedural language environment.

Partitioning

Original Clunkdraw was built as a fine-grained .a library (remember this predated the advent of shared object libraries, and even predated Linux) so that a client application needn't contain any code it wasn't using. Pascal does not really support this model. Rather, it prefers larger-grained units. Clunkdraw in Pascal has been split into nine units, mostly just to make it easier to work on.

Also, original Clunkdraw stored all its fonts on disk, in part because the sum of all its fonts occupied a substantial portion of a client's address space, but this definitely introduced an unwelcome external dependency. The RPi supports much larger address spaces, so this implementation puts all but one of the fonts in an optional unit, a large one, but one which can be avoided if you don't need the fonts. Clunkdraw in Pascal relies on nothing outside of itself, and adds no additional dependencies at either compile or run time.

Because it doesn't look like Pascal supports the same concept as C's function pointers, allowing late binding, the number of units is relatively small, and things are packaged together. Unit X refers to Y, which in turn refers to Z, and you end up including a bunch of the units as often as not. This could maybe be done better by an expert, which I am not. But, this is good enough for now.

There are nine units, plus test programs and etc.:

Source
Size
Object
Size
FileContains
105,568 16,001  epaper.pas E-Paper SPI access
155,901 58,776  clunkdraw.pas Lines; Rectangles; Region clipping, drawing; Support
5,342,618 1,087,081  clunkfonts.pas All but one font
30,367 8,724  clunkpoly.pas Polygons
38,358 11,940  clunkregions.pas Region manipulation
33,800 7,994  clunkrounds.pas Ovals; Arcs and Wedges; Rounded Rectangles
6,805 2,305  clunksave.pas MacPaint and PBM file saving (for screen shots)
70,359 25,802  clunktext.pas Text drawing; Default font
91,510 21,396  clunkwinds.pas Overlapping windows
The size of this library is no problem at all for a Raspberry Pi, or something similar, but it's far too big for use on an Arduino. A fine-grained .a C library of Clunkdraw might be useful on an Arduino, if you were not using very many of the Clunkdraw features.

Return to Site Home