Issue 2-23, June 11, 1997

Be Engineering Insights: The new font engine, PART 2: Porting font code from DR8 to the Preview Release

By Pierre Raynaud-Richard

(...The stranger stood up, but they still couldn't see his face. The leader of the security team looked one last time at his boss before taking action. But his gaze was fixed on the stranger, captivated. No instruction. So he barked the order he had been holding back...

"FIR--"

A thin beam of energy shot up from the stranger's belt, widening with distance to a tenth of an inch. It sliced diagonally through the neck of the security leader. A final groan burbbled from his gaping throat, then his body collapsed slowly; the head, neatly detached, rolled on the ground...)

Perhaps the link to "Porting Font Code from DR8 to the Preview Release" is not clear. But at least it's not just another boring introduction. So first, let's go through a quick overview of the main API changes:

Let's give more details about BView and BFont.

What Happened To BView

What happened to BView is quite simple: Almost all the font related functions have been moved (and in some cases modified or enhanced) to the BFont class. Here are the functions that have moved:

BView::GetCharEscapement(...)
BView::GetCharEdges(...)
BView::GetFontInfo(...)
BView::SetFontName(...)
BView::SetFontRotation(...)
BView::SetFontShear(...)
BView::SetSymbolSet(...)

A logical exception, BView->DrawChar(...) is unchanged, and BView->DrawString(...) is just slightly enhanced with the introduction of escapements_delta. Those optional parameters define an additional escapement, different for space characters and non-space characters, allowing easy justification of a full line of text.

We were reluctant to force everyone to use the BFont API for every single font-related operation, so we decided to keep a subset of the BFont API in BView. Specifically, StringWidth() and GetFontHeight() are retained so that you can easily get the dimensions of the rect used by a string. Additionally, SetFontSize() was so commonly used that we decided to keep it in BView as well.

All other font setting changes must be done by modifying the BView's BFont object; you use GetFont() and SetFont() to access and set the object.

BFont Properties

Here are the standard properties of the new BFont API:

Family and Style names: you can get the complete list from global functions:

count_font_families()
get_font_family(index, &font_family)
count_font_styles(font_family)
get_font_style(font_family, index, &font_style).

The list of families is cached for each application after the first use. As the user is allow to add and remove fonts files at any time, this list could change at any time. But, for technical reasons—and because the user really shouldn't be changing the font all that much anyway—this list is not updated automatically in the Preview Release.

You can force a "font update" by calling:

bool update_font_families(bool check_only)

If check_only is TRUE, the function lets you know (through the return value) if the global font list has changed, but it doesn't actually update your app's version of the list. If check_only is FALSE, it will go ahead and update your app, if necessary. Updating can take a long time, and it blocks the calling thread, so the "check only" option gives your app a chance to put up a warning to the user, or spawn a thread to do the updating in the background.

The list of styles can be different (and usually is) for each different family. A lot of different names exist for something representing the same "style." For example, plain is often called "roman" or "regular." Because of this, family and style should always be set at the same time, in one call:

BFont::SetFamilyAndStyle(constfont_family,
                         constfont_style)

Nevertheless, if you just want to change the style of the current family, you pass NULL as the family. Or if you just want to select the "standard" style of a font (the plain equivalent, if available), you pass NULL as the style.

  • Size: In points, any positive floating-point value smaller than 10,000. The global BFont objects have individual default sizes.

  • Rotation: In degrees, with a default value of 0.

  • Shear: In degrees, in the range [45, 135] with a default value of 90.

  • Encoding: This is the character set encoding used to describe characters when talking with the font engine. The BeOS should use only B_UNICODE_UTF8 (the default value) for now. Other encodings may be supported later.

  • Encoding: This is the character set encoding used to describe characters when talking to the font engine. The BeOS should use only B_UNICODE_UTF8 (the default value) for now. A small set of standard 8 bit encodings are also supported for compatibility (ISO-8859-1 to 10, and Macintosh Roman).

  • Spacing_mode: This parameter was described in great detail in the previous newsletter.

  • Faces: This parameter is currently reserved for future extensions, such as underline or strikethrough.

  • Flags: This controls the selection of special options. The only one available for now is B_DISABLE_ANTI_ALIASING. Furthermore, anti-aliasing should ONLY be turned off by the rare application that displays monocolor bitmap fonts in large point sizes.

When using BView::SetFont(), you can set a subset of these parameters by using the setting_mask. This gives you two ways modify a BView's font setting:

A. GetFont() -> modify the parameter -> SetFont()

B. Set the parameter in a dummy BFont object -> SetFont() using a setting flag to modify only this parameter

Typically, A is written in 3 lines of code, B in only 2.

Porting Examples

That's enough general information for now (you can find more in the documentation and code examples). Let's take a look at a few practical cases:

To do:selecting a standard font:
Before:SetFontName("Erich");
Now:SetFont(be_plain_font);

To do:changing the point size:
Before:SetFontSize(12.0);
Now:SetFontSize(12.0);

To do:getting the width of a string:
Before:StringWidth("The string I prefer");
Now:StringWidth("My beloved string");

To do:getting the ascent of the current font:
Before:GetFontInfo(&info); info.ascent;
Now:GetFontHeight(&height); height.ascent;

To do:changing the rotation of the font:
Before:SetFontRotation(110.0);
Now:GetFont(&tmpFont); tmpFont.SetRotation(110.0);
 SetFont(&tmpFont);
Or:fooFont.SetRotation(110.0);
 SetFont(&fooFont, B_FONT_ROTATION);

To do:saving a font setting:
Before:GetFontInfo(&curInfo);
Now:GetFont(&curFont);

To do:restoring a font setting:
Before:SetFontName(oldInfo.name);
 SetFontSize(oldInfo.size);
 SetFontRotation(oldInfo.rotation);
 SetFontShear(oldInfo.shear);
Now:SetFont(&oldFont);

And you will find much more in future sample code.

Drawing Modes

The last thing I want to talk about is a small but very important detail: The first two drawing modes used in the BView (COPY and OVER) were equivalent for text in DR8. That's no longer the case in the Preview Release. And the bad thing is that an incorrect usage of those modes will make the anti-aliasing look REALLY BAD. So please, read the following carefully and update your code if necessary.

BEFORE: Both COPY and OVER were basically doing what the OVER mode was supposed to do. Their performance was similar, and many people used one or the other without seeing any real difference on the screen.

NOW: The mode OVER reads the background before drawing, as the anti-aliasing is done using alpha blending. But, depending on the user configuration, the cost of reading back the frame buffer of the graphic card can be prohibitive.

This is where the COPY mode shows the power of its simplified definition: By computing the alpha-blending between the HighColor of the glyph itself and the LowColor of the BView (which represents the color of the uniform background), COPY is much faster than OVER. However, you can only use COPY on a uniform background, and you ABSOLUTELY NEED TO HAVE THE LOWCOLOR SET PROPERLY (it's the responsibility of the programmer).

I hope you understand now why many quick ports from DR8 to the Preview Release (probably including a few apps of our own) displayed ugly looking anti-aliased fonts here and there...

Here's another good reason to use the fast COPY mode as much as possible: Fonts never erase their background (for the simple reason that there is no consistent way to define the limits of the background of a character). Consequently, the programmer is responsible for erasing the background properly before calling DrawBitmap.

For example, let's say you just want to change the color of >>the font; you may be tempted to just redraw the text in the new color, thinking it will overwrite the previously drawn text (some Be engineers were doing that). This was true in both modes (COPY and OVER) in DR8.

With the Preview Release, it's still true in COPY mode, but doesn't work anymore in OVER mode: The rasterizer will use the already anti-aliased background, increasing the amount of anti-aliasing with each redraw, until it reaches a saturation point. This doesn't look good at all (the poor guinea pigs who got the alpha version of Advanced Access probably remember the ugly text in menubars).

To conclude these two articles on the new font engine, I would say there are still many areas that could be improved in terms of both quality and performance. Be assured that we're thinking about it, but improvements probably won't be published before the next release. In the meantime, I will be happy to answer any other questions at: pierre@be.com.


Sailing to Support Nirvana, PART 2

By Ed Romson

Welcome back to our discussion about sailing towards technical support Nirvana. This week I'll share some thoughts on outsourcing, and on how to maximize the satisfaction of your customers while holding down support costs.

Let's recap the last "Sailing to Support Nirvana" article before we start. I discussed piloting your own day-sailer with a limited support staff, and captaining a larger organization that provided "soup to nuts" support to your customers. We explored a formula that showed the base cost of handling contacts and a method of that black magic art, forecasting contact volume. Let's reproduce here the chart we used, because the result (cost of each support contact) will be important to our discussion of outsourcing:

** Chart 1 **

Personnel cost (per person)...................$100,000
Number of contacts handled/day/person...............30
Number of people.....................................2
Efficiency factor..................................0.8
Number of work days................................260
Calls handled/person/year.........................6240
Cost per contact................................$16.03

I'd like to start off with an exploration of the controversy over whether or not to outsource. Believe me, I have been all over the map on this one. Five years ago, I was a major opponent of outsourcing. I felt that we would lose control over the quality of information provided to our customers. I mean, who understood our products better than the people who had supported them for years? How could an outside company ever gain enough knowledge to effectively support our beloved products? Over the years, I have changed my position almost 100 percent. I've seen outsourcing work in the US, Japan and Europe.

The secret is to realize that outsourcing is not a panacea. Some (less than successful) companies think that they can outsource and forget. They outsource and never have to worry about that work again. After all, isn't that the work which they are contracting for? Well, yes, however you must keep management control over the outsource vendor. A strong vendor manager, someone who knows the support business and is excellent at managing projects, is essential for the success of an outsourcing project. The thing you are really contracting for is the "production line" work of answering the questions.

Another requirement is that there must be someone inside the company to handle the calls escalated from the first-line people. Remember those first two sailors you brought aboard to help sail your original sloop? Those guys and gals are now invaluable at teaching your new vendor the ropes, and they are there to serve as the masters of escalation. This not only makes effective use of your higher paid resources, but also allows for job enrichment for those poor swabs who have been slogging away, answering the first-level questions.

What is your involvement in all of this? You own the fleet. You have contracted with a company to provide the sails and the manpower. You are setting the course and determining the cargo to carry; that is, what is supported, and how it is supported. You keep an eye on the fleet, making sure your customers are satisfied, and your ships don't run aground. You take soundings with a regular customer survey, and you make course corrections based on what your customers are telling you.

You may be asking whether this outsourcing is more expensive than growing your support capacity in-house. I'll share some of the pricing I've seen in the recent past. I must admit that I've changed some of the numbers to protect the guilty, but the numbers in Chart 2 below reflect bids you might receive if you were to ask for quotes from some of the larger outsource vendors:

** Chart 2 **

Sample outsource quote—8 minute call (10 x 5 support)

Contacts/   Team     Price/     Price/
     Day    Size    Minute    Contact

      50       3     $1.70     $13.60
     100       5     $1.40     $11.20
     150       7     $1.30     $10.40
     200       8     $1.15     $ 9.20
     250      10     $1.10     $ 8.80
     300      11     $1.05     $ 8.40
     450      14     $0.99     $ 7.92

This chart needs some explanation. The assumptions are listed at the top, and the chart assumes that the average telephone call will be 8 minutes in length (a good assumption for software contacts with reasonably sophisticated users). It also assumes that you are accepting contacts from 8am to 6pm (10 hours a day), Monday through Friday (five days a week).

The "Price per Minute" and "Price per Contact" are figures that the outsource company gives you to say "this is what it will cost you." You may remember that I came up with a cost per contact of $16.03 when I did the calculation for in-house staff. That makes sense; you are paying those "fully loaded costs" and you have very little economy of scale with just a few support people. You also want to pay more for your internal or "institutional" knowledge keepers. In this scenario, you are going to use your internal people as information resources, and as second level support resources—natural tasks for higher cost people.

Now, what does all this mean? The decision that needs to be made regards the point at which it makes sense to outsource, rather than grow your support resources inside the company. You will need internal support resources for higher level support, the "Masters" or "Chiefs" on your boat. The crew can be pressed from outside and it makes sense to do so when the volume of your contacts (calls and email) grows beyond the capacity of your core of support engineers. This "break point" will be unique to your circumstance.

The only advice I can give is to look at the ability to scale up Customer Support as your product sells and grows in complexity. This lack of ability to scale up without astronomical costs has crippled many companies. In today's business environment, keeping core strengths within the company, and outsourcing everything else is the winning solution.

Outsourcing is never the easy way out. As I've said, I've done it a few times, and I am (obviously) not shy about sharing my thoughts and opinions. If you are considering this route for technical support and need someone off whom to bounce ideas, I'd be happy to help. Here at Be, we are also looking at various scenarios where we can establish a framework for support (and printing, packaging and distribution for that matter) while allowing our developers to join in and enjoy some of the benefits of the economy of scale. If you are interested in discussing that concept, I'd be happy to help with that as well.

In the Be Newsletter #72, I invoked the ghost of Mayor Koch and asked for feedback on our support of your efforts. I am still interested. How are we doing? It's romson@be.com.


News From The Front

By William Adams

Pssstt... Hey buddy. Come here. I've got a secret for you. Have you ever walked into a fine jewelry shop in the dark and tried to find the crown jewels? Well, to continue our barrage of sample and useful documentation, I submit to you a little tool to help you do your best. There are quite a few ways to deal with images in the world. There is the Datatypes library, there are resource files, and there is the Rraster application.

Rraster and Datatypes allow you to at run-time read in any image file of any type the user so happens to have a codec for. The image resource allows you to pack a known image in along with the executable so that it's always there. Packing things into resources is a good way to go because you can use one of the various resource editing tools like InterfaceElements and IconWorld to manipulate them.

Another technique for getting images into your application is the embedded graphic. That is, you do something like:

#define width   64
#define height  64
#define cspace  B_RGB_32_BIT
#define bytesperpixel  3

unsigned char bits[] = {0,0,0,0,...};

BRect aRect(0,0,width-1,height-1);
BBitmap bitmap = new BBitmap(aRect, cspace);
int bytesperrow = bytesperpixel*width;

for (int row=0; row<height; row++)
{
  int bitoffset = row*bytesperpixel*width;
  int bitmapoffset = row*bitmap->BytesPerRow();
  bitmap->SetBits((char *)bits+bitsoffset, bytesperrow,
    bitmapoffset, cspace);
}

This is nifty if you have some sort of tool that allows you to convert any image into an easy format which is simply a "C" structure. Well... we have such a thing:

ftp://ftp.be.com/pub/dr9/samples/mkimghdr.tgz

This little utility utilizes the codecs of the Rraster application to turn just about any graphic into this useful format. The codecs that shipped with the Advanced Access release were for Targa and Gif images. There are a whole slew of new codecs for other formats that will show up in the Preview Release. These include JFIF, TIFF, PCX, PBM, XBM, and PNG. The same technique can be utilized with the Datatypes library. Thus for all those codecs that everyone is hard at work producing for Datatypes, you will be able to create simple "C" structures and include them in your app. Of course, using Datatypes, there is already a "C" structure Datatype, but there you have it.

You know, one thing about life with a 2 year old is that there is never a dull moment. My current schedule gives me the night duty. That means, from about 6:00pm until 9:30pm I'm in charge of entertaining and eventually putting to bed a fiery bundle of joy. Well, how do you entertain a 2 year old? Use the BeOS of course, but the mouse is a horrible little rodent to be avoided at all costs, so do you prefer a graphics tablet? I do. I've been playing with the Wacom tablets and driver... Oops, did I say that? Well, nothing has changed in the system to make it easy for any pointing device to show up as a mouse, but that doesn't mean we can't play anyway:

ftp://ftp.be.com/pub/dr9/samples/wacom.tgz

These tablets are neat things. They tell you positional and button information like a mouse, but then there's things like proximity, pressure and tilt. Somebody slap me with something killer using this stuff please!! Oh, does that look like a device driver sample? Why yes it is.

If you're a real programmer,
code is your documentation

 

It's the beginning of summer. I have been told that VideoMania has been bad to the pocket books of a few developers. That's a good thing. It's this excitement to do neat things that drives the leapfrog development cycle. Keep up the good work and we'll be inspired to pump out the killer code to support you.


Modem Wars

By Jean-Louis Gassée

It looks like we averted a standards war with the new generation of 56 Kbps modems. The two-and-a-half warring factions managed to agree on something without either directly agreeing or exactly knowing what they agreed on. Trust me, I'll explain in a moment. Before doing so, I need to disclose I'm a director of 3Com. The company has announced an agreement to acquire US Robotics, one of the companies mentioned below.

The fight got off to an uglier start than usual. The previous generation of 33.6 Kbps modems had seen a small scale attempt to preempt the V.something standard with a V.Fast offering, but the International Telecommunications Union (ITU) ratified a norm soon enough for everyone to quickly comply and customers to buy in peace.

Not so with the 56K generation. Last year, Rockwell, whose chips powered about 80% of V.34 modems, announced their new K56Plus standard; AT&T announced their own V.flex2 and US Robotics their X2 modulation. Each one with the firm intent of "owning" the new norm in the absence one sanctioned by the ITU.

Rockwell announced first but USR quickly took the lead. Their strategy is retroactively obvious: they figured out buyers needed to connect their modem at the higher speed they were paying for. Before you e-mail me with therapeutic advice for platitudinous thoughts, I beg you to consider what USR did and their opposition didn't, or did much later.

USR preemptively "convinced" a number of ISPs, big and small, to announce support, meaning access for X2 modems. Besides actually providing means to instantiate the function customers were paying for, USR also wanted to create a perception, they wanted a self-fulfilling prophecy. "We are winning, join the winner and, as a result, we'll all win." Somehow, the promoters of the other two standards never got around to such an energetic unfurling of their standard. They agreed to make K56Plus and V.flex2 "interoperable" (that's where the "two-and-a-half" above comes from), thus reducing the confusion; but they were two or three months behind USR in actually delivering modems to the retail channel.

Let's stop for a moment and look at the difference between USR and their opponents. Lucent or Rockwell produce the technology, others adopt it and make modems. A division of labor and energy. USR "makes" the technology (Motorola disagrees, they say some of it comes from their patent portfolio...) as well as the device. No division here, no questioning whose job it is to promote the standard and whose it is to sell the devices.

Still, there were potential losers in this war, not manufacturers, but losers of the worst possible kind: customers, people with our money in their pockets. What if the ITU, when a few million modems have already been sold, decides for the "wrong" standard, meaning the "other one" my modem and my ISP don't use? Worse, the ITU could decide to bless neither K56Plus/V.flex2 nor X2.

Fear, Uncertainty and Doubt, FUD, is usually deployed by the dominant player against a pesky interloper when the latter threatens to take too much business away from the former: don't buy this because it will become obsolete, incompatible, or any such reason. In the case of 56K modems, FUD worked against everyone because a third party, the ITU, holds the keys to the norm.

As a result, the "double blind" agreement mentioned at the beginning magically appeared in order to create peace of mind and more sales. Most 56K modem suppliers have independently or competitively come to offer a guaranteed upgrade to the as yet unknown standard (see Motorola's and USR's Web pages for examples of their guarantees). Hopefully, DSP flexibility and software permitting, this isn't as bold as one might think, but I always wince when the future is presented as "a mere matter of software." And, somehow, I wonder if ISPs aren't, in fact, the primary target of such promises. If ISPs refuse to take the plunge, the 56K business will die once all people like me have bought one and found a paucity of responding access points.

As we depend on Web marketing and delivery of application software, we're happy to see one obstacle to faster downloads on its way to oblivion. While we wait for the XDSL messiah, we'll continue to adore the good old telephone system.

Creative Commons License
Legal Notice
This work is licensed under a Creative Commons Attribution-Non commercial-No Derivative Works 3.0 License.