Development of IconSharp21 April 2014
Over the weekend I released my second GTK# based program, which is an icon editor called IconSharp. The spark that led to the writing of IconSharp came while I was finishing off SprigFern, and decided that I ought to create a custom program icon for it. Since SprigFern was developed using MonoDevelop under Linux I looked for a decent Linux-based icon editor, and found out that basically there aren't any. The only one I found was KIconEdit which aside from only exporting PNG files looks like it has suffered a fair bit of bit-rot. While Gimp can create (multiple-icon) ICO files by treating each layer as a separate icon, Gimp felt somewhat too heavyweight and not really geared towards icon-sized image production. Because of their limited size creating icons requires a lot of pixel-by-pixel editing, which although Gimp supports is not really its intended mode of operation.
In any case I felt that making an approximate clone of KIconEdit was a reasonably-sized vehicle for learning about GTK#'s graphics capabilities, and although I had intended to target it at ICO files that contain multiple icons, neither of these features made it into the first release. Nevertheless even getting to this stage revealed to me a lot of new stuff about C# and GTK#, which is the main focus of this article.
Working out image functionsAlthough there is on-line SDK documentation for GTK#, it is not exactly great and some of the links are brittle, so the most frustrating part of development is the up-front experimentation needed to work out what bitmapped image functions are available. GTK# uses confusing terminology referring to images as server-side and client-side, which I suspect is to do with ASP.NET web applications rather than the X-server server/client split, but what you really need to know about is Drawables and Pixbufs:
- A drawable is something that you can get a GC (Graphics Context) from and draw using primitives such as Lines & Points, and the done thing seems to be to get the GC at startrup time rather than getting/releasing at draw time as is typically done under Windows. In practice there are two derived classes of Drawables that are of interest: The on-screen
Gtk.DrawingAreaand the off-screen
Gdk.Pixmap. You can use
DrawDrawable()to blit between them. Only annoying thing is that there does not seem to be any obvious flood-fill functions.
- A pixbuf is a wrapper around an RGB or RGBA bitmapped image, and from experimentation the raw pixel arrays passed to the constructor can be edited directly as well as using the Pixbuf member functions. Using
CompositeColorSimple()you can blit between two Pixbufs, scaling in the process if needed. You can blit to a Drawable using
DrawPixbuf()and get pixel data from a Drawable into a Pixbuf using
Visual designUnlike SprigFern which used an entirely dynamic user interface, IconSharp does not have the same on-the-fly requirements, so it made sense to use the opportunity to learn about Glade. Since I had worked out how Glade resources can be embedded into the binary executable I did not have the aversion to it I had developed in the past, and I was interested how the GUI editor itself compared to Stetic built into MonoDevelop. Coming up with an interface is an iterative process of having a rough layout in mind, seeing what widgets there are, and seeing how good the layout looks in practice. Quite early on I set my mind on the left-hand tool palette layout, although I did look at other variants while I was unsure whether certain widgets could be orientated either horizontally or vertically. GUI development is not exactly a major interest of mine and I have somewhat conservative tastes, so I settled on a layout after two or so hours of playing around with Glade.
GTK & Glade irritationsI used two versions of Glade: v3.8.3 that ships with Slackware 14.1, and v3.12.1 that ships with Ubuntu 12.04. From my experience of them, I have some major whinges about Glade, how it integrates into GTK#, and with GTK# as a whole:
- GTK version
- As far as I can tell, the latest stable GTK# seems to only support GTK v2.12, which among other issues does not properly support
Gtk.Builder, so you have to use the now-deprecated libglade format.
- DrawArc broken
- The circle/segment drawing functions of GTK# simply do not work, and as a result I ditched circle/ellipse drawing. Maybe I used the functions incorrectly, but when I input what multiple sources claim is the correct parameters, assuming this means show-in-the-dark experimentation.
- Compatibility options
- Although Glade allows you to choose compatibility options, these are only in the v3.8.3 of Glade. More annoyingly, if an error is flagged you have to use Save As rather than Save, even if you click past the warning dialog. Which leads onto..
- Spurious compatibility warnings
- I needed to choose at least v2.16 to stop Glade complaining about spurious Use Action Appearance properties, even though as far as I could tell these were not being used.
- No preview
- Although Glade v3.12.1 has a preview option, v3.8.3 does not. Not a blocker, but certainly very annoying.
- Need to embed icons manually
- A side-effect of removing (since it is redundant) the default MainWindow from the MonoDevelop project file is that code implementing the content of the Stock Icons does not get generated. MonoDevelop seems very all-or-nothing when it comes to GUI elements.
- Glade files treated as binary
- For some reason Subversion detects Glade files as XML, but tags them as
application/xml, which is treated as a binary format. Use
svn propset svn:mime-type text/xml $filenameif you want
svn diffto work.
- Cannot catch Glib.GException
- As a test I decided to see what happens if complete garbage is passed to
new Gdk.Pixbuf()rather than a recognised file format. The resulting exception cannot be caught, which I consider a major issue.
Tool iconsMaking icons for an icon editor program is a little chicken-and-egg, but it would be bad show if at least some of them were not made in the program I wrote for the purpose. GTK seems to give a fair amount of latitude between versions and distributions for built-in icons, so in the end most tools had to use custom ones. A big irony of all this is that I don't regard myself as being particularly artistic, so many Of the tool icons are somewhat 1990's in style.
Overall GUI experienceGlade and Pixbuf et al aside, making the GUI for IconSharp was less of a learning process than it was with SprigFern, in large part because it is my second GTK# application, but even with a few hours messing around with the former it was still well within a weekend's effort. This is because there was little new that was conceptual, even though I was using a whole set of new widgets. An example of why difficulty is an inverse function of experience, and how even a little experience can count for a lot.
Views on C#Being my second recent C# program I made use of more C# specific features compared to SprigFern, and there are a few that stand out as worthy of a mention. Firstly is the features I consider interesting:
- In C# properties are get/set functions that are disguised as a variable, which at first seems like syntactic sugar, but in the longer-term feels a more natural way to query information. It is the only C# feature I missed when using C++ to write a sockets wrapper.
- Auto-implemented properties
- Auto-implemented properties are a short-cut way of declaring a variable that is to only be accessed through an implicitly-generated property. The main idea is to allow future conversion of a public variable into a property without breaking binary compatibility, although it also provides a way of making public variables that are also read-only for external access. A good thing given the indiscipline that can result from public variables.
- These are a cross between function pointers and partially evaluated functions. In practice it allows you to pass callback functions that include access to other variables. In functional languages like Haskell this is equivalent to passing partially evaluated functions, but C# delegates is the first time I've seen it done cleanly in a mainstream procedural language.
- Struct vs class
- In C# classes are passed by reference, whereas structures are passed by value. In other words, C# classes are like C++ classes allocated using the new operator, whereas structures are like stack-allocated C++ classes. This is different from C++ where the difference between a structure and class is whether class members by default are public or private.
- Bytes are not integers
- One thing I have always liked about C is that it is pretty shameless in treating a block of bytes as an array of integer values, but C# treats assigning between
inttypes as an error. In practice you end up putting casts everywhere, which gets annoying if you need to do it a lot.
- signed vs. unsigned
- C# does not allow implicit conversion between signed and unsigned integers, which is made all the more annoying as the choices made by built-in classes & functions make it impossible to adopt a single-signedness-policy. In particular, arrays use signed integers for indices. Like above, you end up putting casts everywhere.
- Switch statement fall-thru not permitted
- C# requires any switch cases that contain code to finish with a
breakrather than permitting fall-thru to the next switch case. I understand this is because such things more often than not are bugs, but it still pisses me off because there are circumstances where I wanted to do this.
Time commitmentIconSharp was around six weeks from initial idea to first release, but actual development time based on source control check-ins was 24 hours spread over a little more than a month. Most of a working week's worth of spare time commitment over a month is actually quite fast, and it is a lot better than the 90-100 hours over 7 months that SprigFern took. A major part of this was having less of a stop-start development, although it did help that this time round there was no international travel getting in the way. A little bit every now and then doesn't really work that much with software writing, as you constantly forget design rationale.
Cut featuresRelatively early on it became apparent that my parallel development of a C#-based ICO file format container would be far from trivial, so for the initial release I made a single-icon editor that supports PNG my milestone. I also cut circle/ellipse support for them in GTK# is broken, and if I was to add them then I would be on the road to ditching GDK rendering altogether in favour of first-principles drawing algorithms. In addition, cut & paste support is only internal and does not interact with the system clipboard.
As it turned out thinking about what features I considered cut motivated me to implement a few extra of them, so all things considered curtailment of intended features was not very extensive. I also added a few that I thought of during development, but of course on this count there are wish-list things such as flood-filling of background, that did not make the cut.