News: Stay up to date

The Étoilé community is an active group of developers, designers, testers and users. New work is being done every day. Visit often to find out what we've been up to.

News

Opal Summer of Code Report

Posted on 4 September 2010 by Eric Wasylishen

I spent this summer as a participant in the Google Summer of Code program, working on Opal, a graphics library which implements an API compatible with CoreGraphics. Opal was started in 2006 by BALATON Zoltan, and was mostly dormant for the last few years until early 2010 when I started playing with the code and added a few features. Today, it is a reasonably complete implementation with most major features working. I’ll discuss in more detail what I did and some of the neat features of the library.

API notes

One of the challenges with implementing this API was its relationship with the CoreFoundation library. CoreGraphics objects are supposed to be CoreFoundation types, which means they are “toll-free bridged” with NSObject. For instance, if you create a CGImage, the following are all valid ways of retaining it (incrementing its reference count):

CGImageRef anImage = CGImageCreate(...);
CGImageRetain(anImage);
[anImage retain];
CFRetain(anImage);

GNUstep’s history predates Apple’s creation of the CoreFoundation library, and we don’t really have a need for it in GNUstep, other than for porting Mac applications to GNUstep. (There is an library in development called CoreBase, available at svn://svn.gna.org/svn/gnustep/libs/corebase/trunk, which implements the CF functions on top of GNUstep base.) However, to implement Opal, I decided to make it an Objective-C library internally, depending on GNUstep base. For example, CGImage is a regular Objective-C class, inheriting from NSObject. The end result is that Opal is structured quite similar to the existing GNUstep back library, except that the Objective-C API is private to the library, and only a C API is exposed.

If you look at the Opal headers, they appear to use CF types (such as CFArray, CFString, etc.), but these are just typedef'ed in CGBase.h to the respective Obejctive-C types (NSArray *, NSString *, etc.).

Text

Currently, GNUstep’s most featureful backed, which uses the cairo graphics library, uses cairo’s “toy” text API. This API is designed for users of cairo who lack a text layout library to do their own character to glyph mapping and glyph positioning. With the toy API, the user passes cairo a UTF-8 string and lets cairo perform character to glyph mapping and glyph positioning. It’s called the “toy” API because this is a non-trivial problem and cairo only does it correctly in simple cases.

My hope for the long term is to add full support to Opal for doing complex text layout, and support for using advanced typographic features in OpenType fonts. I started on this path by writing a draft implementation of the font descriptor part of the CoreText API.

Right now, Opal implements most of the CGFont functions on both Windows and X11, which allows accessing various font metrics, as well as drawing specific glyphs at specified locations.

As an example, the following code snippet draws the "AE" and "ae" ligatures at (10, 100):

CGGlyph AEligatures[2] = {CGFontGetGlyphWithGlyphName(f, @"AE"),
CGFontGetGlyphWithGlyphName(f2, @"ae")};

CGAffineTransform xform = CGAffineTransformIdentity;
xform = CGAffineTransformTranslate(xform, 10, 100);
CGContextSetTextMatrix(ctx, xform);
CGContextShowGlyphs(ctx, AEligatures, 2);

The full code is in Tests/texttest.m. Here is the output:

Color management

I also implemented the foundations for making Opal handle color management. There are some limitations to what we can do because cairo is not color-management aware, however this mostly affects doing PDF export. Right now, Opal assumes surfaces are in the sRGB color space, and converts colors you draw with to that space. So, if you draw a rectangle with a shade of green in the AdobeRGB color space, that color should be transformed to sRGB before being displayed on screen. This will be easy to support for images as well, I am just missing the code for using the libjpeg/libpng/libtiff libraries to check for colorspace metadata or embedded ICC profiles.

Here's an example (from Tests/colorspace.m:

The top row is 100% green, in AdobeRGB on the left half, and sRGB on the right. These get mapped to the same color in sRGB, so you can't see the difference. The bottom row is 75% green, also in AdobeRGB on the left, and sRGB on the right. Here the left side is mapped to a slightly brighter green in sRGB.

The other bit of work that needs to be done is investigate API’s for asking the windowing system what color space it expects windows to be drawn in. I confess that I have no idea how this works, or if it is supported at all on Windows or X11. Wide-gamut LCD displays whose native color space is AdobeRGB are starting to become more common, and it would be nice to be able to display an AdobeRGB image from a digital camera without an intermediate conversion to sRGB, losing precision.

Images

I wrote the image handling in Opal from scratch, and while it was a bit of work, the result is quite nice. The design of CGImage handles a wide variety of formats (e.g. up to 32 bits per component, integer or floating point samples, arbitrary color spaces). CGImages are immutable, so we can cache a copy of the image converted to a format appropriate for the display device. This enables both high performance for drawing, and the ability to do things like editing an image at its full bit-depth, or doing format conversions without an intermediate conversion to 8-bit ARGB.

Conclusion

You can check out a copy of opal at the URL svn://svn.gna.org/svn/gnustep/libs/opal/trunk. The README file contains installation instructions, and there is a detailed to-do list in the TODO file.