ClearType™ refers to Microsoft's technology that exploits the inherent misregistration in some computer display devices to draw the lines and curves of text and graphics to sub-pixel accuracy.
The Apple II family had used a very similar technique two decades earlier.
Normal software treats computer displays as containing square white pixels.
On the other hand, actual pixels on a color LCD are tall rectangles of red, green, and blue, and the hardware can generally address the individual components of a pixel separately.
A 4x3 pixel section of a display:
+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | |
|R|G|B|R|G|B|R|G|B|R|G|B|
+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | |
|R|G|B|R|G|B|R|G|B|R|G|B|
+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | |
|R|G|B|R|G|B|R|G|B|R|G|B|
+-+-+-+-+-+-+-+-+-+-+-+-+
Thus, if software treats RGB as a single unit, an image of all red pixels will be offset 1/3 pixel to the left of an image of all green pixels, and an image of all blue pixels will be offset 1/3 pixel to the right.
To start, apply a low-pass filter (the 5-tap FIR filter [1 3 4 3 1]/12 works well) to the image to avoid color fringing caused by beats with the color subcarrier. Then sample alternately red, green, and blue components of successive pixels to produce a final image at nearly triple the horizontal resolution of an ordinary image.
Implementation
Patent? So what? Here's a little program I wrote to do ClearType processing on an arbitrary bitmap. Most LCDs are RGB, but some (such as Game Boy Advance and some iBook models) are BGR; you can specify the assumed pixel ordering on the command line. (The choice of RGB or BGR depends largely on endianness differences between the computer and the display hardware.) The software uses the Allegro library (http://alleg.sourceforge.net/) for bitmap file loading and saving.
/*
clearize.c
Applies a 3-to-1 horizontal scaling with color component
misregistration, similar to Microsoft's ClearType
Copyright (c) 2002 Damian Yerrick
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
*/
#include <allegro.h>
#include <stdio.h>
typedef struct DEEP_RGB
{
unsigned short r, g, b, a;
} DEEP_RGB;
/* ct_filter() *************************
Filter a scanline as follows:
1. Apply the low-pass FIR filter [1 3 4 3 1].
2. Combine adjacent red, green, and blue samples to produce
a final sample.
This filter has a gain of 12x image amplitude.
*/
void ct_filter(DEEP_RGB *src, DEEP_RGB *dst, size_t width)
{
int i, i3, c;
for(i = 0, i3 = 0;
i < width;
++i, i3 += 3)
{
/* do red */
if(i == 0)
c = 4 * src[0].r;
else
c = src[i3 - 2].r + 3 * src[i3 - 1].r;
c += 4 * src[i3].r + 3 * src[i3 + 1].r + src[i3 + 2].r;
dst[i].r = c;
/* do green */
if(i == 0)
c = src[0].g;
else
c = src[i3 - 1].g;
c += 3 * src[i3].g + 4 * src[i3 + 1].g + 3 * src[i3 + 2].g;
if(i == width - 1)
c += src[i3 + 2].g;
else
c += src[i3 + 3].g;
dst[i].g = c;
/* do blue */
c = src[i3].b + 3 * src[i3 + 1].b + 4 * src[i3 + 2].b;
if(i == width - 1)
c += 4 * src[i3 + 2].b;
else
c += 3 * src[i3 + 3].b + src[i3 + 4].b;
dst[i].b = c;
}
}
const char help_text[] =
"Performs misregistration anti-aliasing (commonly called ClearType)\n"
"with 3x horizontal reduction on an image file.\n"
"usage: clearize [options] infile outfile\n\n"
"options:\n"
" -b Use BGR instead of RGB\n"
" -? Display this help\n\n"
"infile can be *.bmp, *.pcx, *.lbm, *.tga\n"
"outfile is a 24-bit bitmap and can be *.bmp, *.pcx, *.tga\n";
int main(int argc, const char **argv)
{
int inf_idx = -1, outf_idx = -1;
BITMAP *in_bmp, *out_bmp;
size_t width;
int arg;
char bgr = 0, need_help = 0;
unsigned int y;
DEEP_RGB *rgb_src, *rgb_dst;
if(argc < 3)
{
fputs(help_text, stderr);
return 1;
}
for(arg = 1; arg < argc; arg++)
{
if(argv[arg][0] == '-')
{
if(!strcmp(argv[arg], "-b"))
{
bgr = 1;
}
else if(!strcmp(argv[arg], "--help") ||
!strcmp(argv[arg], "-?"))
{
/* -? puts help text on stdout instead of stderr */
fputs(help_text, stdout);
return 0;
}
else
{
/* couldn't recognize the option so print an error message
followed by usage info */
fputs("Unrecognized option: ", stderr);
fputs(argv[arg], stderr);
fputc('\n', stderr);
need_help = 1;
break;
}
}
else
{
if(inf_idx == -1)
inf_idx = arg;
else if(outf_idx == -1)
outf_idx = arg;
else
{
/* couldn't recognize the extra arguments
so print an error message followed by usage info */
fputs("Too many arguments\n", stderr);
need_help = 1;
break;
}
}
}
if(need_help)
{
fputs(help_text, stderr);
return 1;
}
install_allegro(SYSTEM_NONE, &errno, atexit);
set_color_depth(24);
in_bmp = load_bitmap(argv[inf_idx], NULL);
if(!in_bmp)
{
fputs("Could not load bitmap ", stderr);
perror(argv[inf_idx]);
return 1;
}
width = in_bmp->w / 3;
out_bmp = create_bitmap(width, in_bmp->h);
rgb_src = malloc(width * 3 * sizeof(DEEP_RGB));
rgb_dst = malloc(width * sizeof(DEEP_RGB));
if(!out_bmp || !rgb_src || !rgb_dst)
{
perror("Could not allocate memory for bitmaps");
destroy_bitmap(out_bmp);
destroy_bitmap(in_bmp);
free(rgb_src);
free(rgb_dst);
return 1;
}
for(y = 0; y < in_bmp->h; y++)
{
unsigned int x, x3;
for(x = 0; x < width * 3; x++)
{
int c = getpixel(in_bmp, x, y);
if(bgr)
{
rgb_src[x].b = getr(c);
rgb_src[x].g = getg(c);
rgb_src[x].r = getb(c);
}
else
{
rgb_src[x].r = getr(c);
rgb_src[x].g = getg(c);
rgb_src[x].b = getb(c);
}
}
ct_filter(rgb_src, rgb_dst, width);
for(x = 0; x < width; x++)
{
if(bgr)
putpixel(out_bmp, x, y,
makecol((rgb_dst[x].b + 6) / 12,
(rgb_dst[x].g + 6) / 12,
(rgb_dst[x].r + 6) / 12));
else
putpixel(out_bmp, x, y,
makecol((rgb_dst[x].r + 6) / 12,
(rgb_dst[x].g + 6) / 12,
(rgb_dst[x].b + 6) / 12));
}
}
destroy_bitmap(in_bmp);
free(rgb_src);
free(rgb_dst);
if(save_bitmap(argv[outf_idx], out_bmp, NULL))
{
fputs("Could not save bitmap ", stderr);
perror(argv[outf_idx]);
destroy_bitmap(out_bmp);
return 1;
}
destroy_bitmap(out_bmp);
return 0;
}
Additional Resources
- Sub-Pixel Font Rendering Technology (http://www.grc.com/cleartype.htm): A more thorough explanation of the technique and its history, including a freely downloadable binary demo for Windows computers.
Copyright © 2002 Damian Yerrick.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the writeup entitled "GNU Free Documentation License".