Wednesday, May 09, 2007

[Software] Translucent Selection Rectangle Color

It took over two years but I finally figured out how Microsoft calculates the color of the selection marquee in Windows.

Windows XP implemented a fancy new feature whereby dragging a selection box around files in Explorer windows will draw a transparent rectangle instead of a gray-dotted box.

This is a feature of the Common Controls 6 and can be added to list controls by setting the preprocessor define _WIN32_WINNT to 0x0501 or higher and setting the LVS_EX_DOUBLEBUFFER style of the list control.

It’s no secret that the color of the outline of the selection rectangle is set to COLOR_HIGHLIGHT which is the color set for selected items. But, what about the rest of the rectangle? Where does the color for the inside of the rectangle come from?

Obviously it’s calculated dynamically from the selected color and the color of the window background since it’s “translucent”. A little testing confirms this. But, what is the equation? What formula is used to blend these two colors?

A few years ago I determined to find out. I began by collecting data. This meant using the Display Properties dialog to set the selected and the window background colors, then drawing a selection rectangle and examining what the resulting color is. After doing this for dozens of pairs of colors, I had a data set and decided to figure out what they have in common. This turned out to be trickier than expected and I put it aside and occasionally looked at it briefly over the next two or three years.

This month when I learned of the LVS_EX_DOUBLEBUFFER style, I became more determined than ever to figure it out. Luckily I could not find the original data set (which would have surely side-tracked me for a while), so I decided to compile a new one with a controlled set of values instead of the random ones I had previously used. I used the following values: 0,64,128,192,255 in (almost) all the permutations of R/G/B and selected/background. I created a macro to automate the process of:

This automation allowed me to complete all 289 (17x17) permutations in just a couple of hours instead of a couple of months, since the only thing I needed to do manually was to click the correct boxes in the color picker.

Now I had a fresh, new set of data points that ranged from the min to the max in all directions. It was time to figure out the equation once and for all.

A good place to start was to plot the data points as a 3D graph where X=selected color, Y=background color, Z=blended color to see if there is any kind of visible relationship. A few minutes later and BAM! The plot was clearly a slanted plane:

Plot forms a slanted plane in 3D space.

Now I needed to figure out what the equation of the plane was. This was also easier said than done.

I decided that the most obvious next step was to use Cramer’s rule to determine the planar coefficients A, B, C, and D, then plug them into the equation of a plane and rearrange for Z. This meant taking three points (I had 289 to choose from) and calculating the planar coefficients. Plugging those into the plane equation would result in this z=-(Ax+By+D)/C where ABCD are replaced by numbers. This leaves an equation with two unknowns (two input variables). That would be the solution.

Unfortunately, this did not work. I was using COLORREFs (DWORDs) which combine all three color components in the following manner: C=R+256*G+256*256*B. The problem with doing it this way is that any math will cause carry errors between the components (eg: C*2 would cause the R component to wrap and the carry would overflow into the G component). The solution was to determine three separate equations, one for each of R, G, and B.

Luckily this did not mean triple the work because they were all the same equation as the data shows. Ignoring the G and B components, I took the 289 data points for the R component and recalculated everything. Again, it didn’t work. Even though the points clearly lay on a plane, the numbers (even the surface normals!) varied depending on which three points I used. I was stumped again (and still am; I don’t know why it works in theory but not in practice).

Then I got a stroke of “genius”. By examining the limiting factors, I could reduce the system to its base. Since f(0,0)=41, the mystery formula obviously adds 41 to whatever the rest of the results are. Also, since f(255,255)=255, I know what the limits are. Finally, I knew the other extents: f(0,255)=227 and f(255,0)=69. That turned out to be all I needed (I donated the other 285 data points to charity). I had the following system of equations:

f(000,000)=41
f(000,255)=227
f(255,000)=69
f(255,255)=255
Solving this gave the nice and simple formula z = 41 + (28*x + 186*y) / 255. A quick test showed that the results were correct (actually a few were a bit off, but almost all of those were fixed by rounding instead of truncating, and the last few off-by-ones are due to using different data sizes and order of calculation).

So there you have it. After two-and-a-half years, the solution presented itself: c=(28s+186w)/255+41 (s=selected, w=window background—or texture).

Here is a function for calculating the marquee color (selection rectangle), printed here with separate component variables for clarity’s sake:

COLORREF BlendMarquee() {
COLORREF selected=GetSysColor(COLOR_HIGHLIGHT);
COLORREF windowbg=GetSysColor(COLOR_WINDOW);

BYTE sr=GetRValue(selected);
BYTE sg=GetGValue(selected);
BYTE sb=GetBValue(selected);

BYTE wr=GetRValue(windowbg);
BYTE wg=GetGValue(windowbg);
BYTE wb=GetBValue(windowbg);

COLORREF c=RGB(
RoundDouble(41+(sr*28+wr*186)/255, 0),
RoundDouble(41+(sg*28+wg*186)/255, 0),
RoundDouble(41+(sb*28+wg*186)/255, 0)
);

return c;
}
Tada!

Friday, May 04, 2007

[Software] Fontographer on Windows XP with 1GB+ Ram

Macromedia Fontographer is one of the still few font editing applications there are. Unfortunately, it has barely been updated since the days of Windows 95; the focus has been on the Mac version, and even that has not been updated much.

Fontographer for Windows is a port of the Mac version and as such, it is less stable than native Windows programs, which is why it has not fared as well with newer hardware and operating systems. Running Fontographer on modern systems does not work well and can simply fail and exit the app or cause a catastrophic crash which can actually bring all of Windows down and require a hard reset.

There are two main problems with Fontographer on modern systems. The minor problem is that when the combined physical+virtual RAM exceeds 1.5GB, Fontographer complains that you do not have enough memory and quits. This is because it uses smaller pointers which overflow and look like there is a minuscule amount of RAM.

The solution to this problem (if practical) is to reduce the amount of virtual RAM so that the total memory is less than 1.5GB. Of course this won't help if you have more than 1.5GB of physical RAM and you would have to actually pull some out for the one program. This problem has been around for a long time but there do not seem to be any plans to fix it. Another option is to add enough RAM to the system so that the wrapped available RAM meets Fontographer’s requirements (in other words, ACTUAL_RAM_SIZE mod RAM_POINTER_LIMIT_SIZE >= FONTOGRAPHER_MINIMUM_RAM); surely it can’t need too much (depending on the version, it needs 6-16 megabytes).

The other problem that can occur with Fontographer on modern systems is a complete crash. When run, Fontographer can completely freeze Windows in it's tracks and require pressing the reset button on the case to bring the system back up. This is probably also caused by it's memory allocation problems but may be caused by something else.

One thing to try with this that has had success is to run Fontographer in "compatibility mode"—this is available in Windows 2000 and up. Select the Fontographer executable (eg: "C:\Program Files\FOG41\FONTOG.EXE") and bring up it's properties dialog. Select the Compatibility tab and set it to run as Windows 95.

Fontographer should now work and allow you to create fonts in Windows.

This page is powered by Blogger. Isn't yours?