* I have experiemented with a different method of countering the colored edges

problem of subpixel-anti-alised font rendering. Personally I find the method
better than filtering the subpixels, since it is a straighter transition
between unfiltered subpixel aa and grayscale aa. There is no added blur
affecting also innocent neighboring pixels. The filter method is better at
hiding jagged diagonal lines though, so Andrej and I agreed to move this to
a general discussion on the mailing list. Screenshots forthcomming...

* Some additional coding style improvements.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26370 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2008-07-10 12:59:20 +00:00
parent 9082b7f4ed
commit d24ece5186

View File

@ -295,7 +295,6 @@ void
decompose_ft_bitmap_mono(const FT_Bitmap& bitmap, int x, int y,
bool flip_y, Scanline& sl, ScanlineStorage& storage)
{
int i;
const uint8* buf = (const uint8*)bitmap.buffer;
int pitch = bitmap.pitch;
sl.reset(x, x + bitmap.width);
@ -305,11 +304,10 @@ decompose_ft_bitmap_mono(const FT_Bitmap& bitmap, int x, int y,
y += bitmap.rows;
pitch = -pitch;
}
for (i = 0; i < bitmap.rows; i++) {
for (int i = 0; i < bitmap.rows; i++) {
sl.reset_spans();
agg::bitset_iterator bits(buf, 0);
int j;
for (j = 0; j < bitmap.width; j++) {
for (int j = 0; j < bitmap.width; j++) {
if (bits.bit())
sl.add_cell(x + j, agg::cover_full);
++bits;
@ -329,7 +327,6 @@ void
decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, int x, int y,
bool flip_y, Scanline& sl, ScanlineStorage& storage)
{
int i, j;
const uint8* buf = (const uint8*)bitmap.buffer;
int pitch = bitmap.pitch;
sl.reset(x, x + bitmap.width);
@ -339,21 +336,20 @@ decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, int x, int y,
y += bitmap.rows;
pitch = -pitch;
}
for (i = 0; i < bitmap.rows; i++) {
for (int i = 0; i < bitmap.rows; i++) {
sl.reset_spans();
if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
// font has built-in mono bitmap
agg::bitset_iterator bits(buf, 0);
int j;
for (j = 0; j < bitmap.width; j++) {
for (int j = 0; j < bitmap.width; j++) {
if (bits.bit())
sl.add_cell(x + j, agg::cover_full);
++bits;
}
} else {
const uint8* p = buf;
for (j = 0; j < bitmap.width; j++) {
for (int j = 0; j < bitmap.width; j++) {
if (*p)
sl.add_cell(x + j, *p);
++p;
@ -369,132 +365,77 @@ decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, int x, int y,
}
#define AVERAGE_BASED_SUBPIXEL_FILTERING
// NOTE stippi: My basic idea is that filtering tries to minimize colored
// edges, but it does so by blurring the coverage values. This will also
// affect neighboring pixels and add blur where there were perfectly sharp
// edges. Andrej's method of filtering adds a special case for perfectly
// sharp edges, but the drawback here is that there will be a visible
// transition between blurred and non-blurred subpixels. I had the idea that
// when simply fading the subpixels towards the plain gray-scale-aa values,
// there must be a sweet spot for when colored edges become non-disturbing
// and there is still a benefit of sharpness compared to straight gray-scale-
// aa. The define above enables this method against the colored edges. My
// method still has a drawback, since jaggies in diagonal lines will be more
// visible again than with the filter method.
// decompose_ft_bitmap_subpix
template<class Scanline, class ScanlineStorage>
void
decompose_ft_bitmap_subpix(const FT_Bitmap& bitmap, int x, int y,
bool flip_y, Scanline& sl, ScanlineStorage& storage)
{
//Filtering weights
const uint8 filter[5] = { 0x10, 0x40, 0x70, 0x40, 0x10 };
#ifdef AVERAGE_BASED_SUBPIXEL_FILTERING
// The weight with which the average of the subpixels is applied to counter
// color fringes (0 = full sharpness ... 255 = grayscale anti-aliasing)
// TODO: This could be a config option, but don't forget to include the
// value in the font cache entry signature generation!
const uint8 averageWeight = 100;
const uint8 subpixelWeight = 255 - averageWeight;
int i, j;
const uint8* buf = (const uint8*)bitmap.buffer;
int pitch = bitmap.pitch;
sl.reset(x-1, x + bitmap.width/3 + 1);
sl.reset(x, x + bitmap.width / 3);
storage.prepare();
if (flip_y) {
buf += bitmap.pitch * (bitmap.rows - 1);
y += bitmap.rows;
pitch = -pitch;
}
for (i = 0; i < bitmap.rows; i++) {
for (int i = 0; i < bitmap.rows; i++) {
sl.reset_spans();
if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
// font has built-in mono bitmap
agg::bitset_iterator bits(buf, 0);
int j;
for (j = 0; j < bitmap.width; j++) {
if (bits.bit())
for (int j = 0; j < bitmap.width; j++) {
if (bits.bit()) {
sl.add_cell(x + j,
agg::cover_full, agg::cover_full, agg::cover_full);
}
++bits;
}
} else {
const uint8* p = buf;
uint32 coverRed;
uint32 coverGreen;
uint32 coverBlue;
int w = bitmap.width/3;
if (w && !(p[0] == p[1] && p[1] == p[2]
&& (w == 1 || (p[3] == p[4] && p[4] == p[5])))){
coverRed = 0;
coverGreen = (p[0] * filter[0]) >> 8;
coverBlue = (p[0] * filter[1] + p[1] * filter[0]) >> 8;
coverGreen = coverGreen | ( -(coverGreen >> 8));
coverBlue = coverBlue | ( -(coverBlue >> 8));
if (coverRed || coverGreen || coverBlue){
sl.add_cell(x - 1, coverRed, coverGreen, coverBlue);
}
}
for (j = 0; j < w; j++) {
if (p[0] == p[1] && p[1] == p[2]
&& (j == 0 || (p[-3] == p[-2] && p[-2] == p[-1]))
&& (j == w-1 || (p[3] == p[4] && p[4] == p[5]))){
coverRed = p[0];
coverGreen = p[0];
coverBlue = p[0];
} else if (p[0] == p[1] && p[1] == p[2]
&& (j < w-1 && p[3] == p[4] && p[4] == p[5])
&& (j == w-2 || (p[6] == p[7] && p[7] == p[8]))){
coverRed = ((j > 0 ? p[-2] * filter[4]
+ p[-1] * filter[3] : 0)
+ p[0] * filter[2] + p[1] * filter[1]
+ p[2] * filter[0])
>> 8;
coverGreen = ((j > 0 ? p[-1] * filter[4] : 0)
+ p[0] * filter[3] + p[1] * filter[2]
+ p[2] * filter[1])
>> 8;
coverBlue = (p[0] * filter[4]
+ p[1] * filter[3] + p[2] * filter[2]) >> 8;
coverRed = coverRed | ( -(coverRed >> 8));
coverGreen = coverGreen | ( -(coverGreen >> 8));
coverBlue = coverBlue | ( -(coverBlue >> 8));
} else if (p[0] == p[1] && p[1] == p[2]
&& (j > 0 && p[-3] == p[-2] && p[-2] == p[-1])
&& (j == 1 || (p[-6] == p[-5] && p[-5] == p[-4]))){
coverRed = (p[0] * filter[2] + p[1] * filter[1]
+ p[2] * filter[0]) >> 8;
coverGreen = (p[0] * filter[3] + p[1] * filter[2]
+ p[2] * filter[1]
+ (j < w-1 ? p[3] * filter[0] : 0))
>> 8;
coverBlue = (p[0] * filter[4] + p[1] * filter[3]
+ p[2] * filter[2]
+ (j < w-1 ? p[3] * filter[1]
+ p[4] * filter[0] : 0))
>> 8;
coverRed = coverRed | ( -(coverRed >> 8));
coverGreen = coverGreen | ( -(coverGreen >> 8));
coverBlue = coverBlue | ( -(coverBlue >> 8));
} else {
coverRed = ((j > 0 ? p[-2] * filter[4]
+ p[-1] * filter[3] : 0)
+ p[0] * filter[2] + p[1] * filter[1]
+ p[2] * filter[0])
>> 8;
coverGreen = ((j > 0 ? p[-1] * filter[4] : 0)
+ p[0] * filter[3] + p[1] * filter[2]
+ p[2] * filter[1]
+ (j < w-1 ? p[3] * filter[0] : 0))
>> 8;
coverBlue = (p[0] * filter[4] + p[1] * filter[3]
+ p[2] * filter[2]
+ (j < w-1 ? p[3] * filter[1]
+ p[4] * filter[0] : 0))
>> 8;
coverRed = coverRed | ( -(coverRed >> 8));
coverGreen = coverGreen | ( -(coverGreen >> 8));
coverBlue = coverBlue | ( -(coverBlue >> 8));
}
if (coverRed || coverGreen || coverBlue) {
sl.add_cell(x + j, coverRed, coverGreen, coverBlue);
}
uint32 coverR;
uint32 coverG;
uint32 coverB;
int w = bitmap.width / 3;
for (int j = 0; j < w; j++) {
uint32 average = (p[0] + p[1] + p[2] + 2) / 3;
coverR = (p[0] * subpixelWeight + average * averageWeight) >> 8;
coverG = (p[1] * subpixelWeight + average * averageWeight) >> 8;
coverB = (p[2] * subpixelWeight + average * averageWeight) >> 8;
if (coverR || coverG || coverB)
sl.add_cell(x + j, coverR, coverG, coverB);
p += 3;
}
if (w && !(p[-3] == p[-2] && p[-2] == p[-1]
&& (w == 1 || (p[-6] == p[-5] && p[-5] == p[-4])))){
coverRed = (p[-2] * filter[4] + p[-1] * filter[3]) >> 8;
coverGreen = (p[-1] * filter[4]) >> 8;
coverBlue = 0;
coverRed = coverRed | ( -(coverRed >> 8));
coverGreen = coverGreen | ( -(coverGreen >> 8));
if (coverRed || coverGreen || coverBlue){
sl.add_cell(x + w, coverRed, coverGreen, coverBlue);
}
}
}
buf += pitch;
@ -503,6 +444,147 @@ decompose_ft_bitmap_subpix(const FT_Bitmap& bitmap, int x, int y,
storage.render(sl);
}
}
#else // filter based anti-colored edges method
// Filtering weights
const uint8 filter[5] = { 0x10, 0x40, 0x70, 0x40, 0x10 };
const uint8* buf = (const uint8*)bitmap.buffer;
int pitch = bitmap.pitch;
sl.reset(x - 1, x + bitmap.width / 3 + 1);
// -1 and +1 to account for additional edge pixels needed by filtering
storage.prepare();
if (flip_y) {
buf += bitmap.pitch * (bitmap.rows - 1);
y += bitmap.rows;
pitch = -pitch;
}
for (int i = 0; i < bitmap.rows; i++) {
sl.reset_spans();
if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
// font has built-in mono bitmap
agg::bitset_iterator bits(buf, 0);
for (int j = 0; j < bitmap.width; j++) {
if (bits.bit()) {
sl.add_cell(x + j,
agg::cover_full, agg::cover_full, agg::cover_full);
}
++bits;
}
} else {
const uint8* p = buf;
uint32 coverR;
uint32 coverG;
uint32 coverB;
int w = bitmap.width / 3;
// handle the left extra edge pixel
if (w && !(p[0] == p[1] && p[1] == p[2]
&& (w == 1 || (p[3] == p[4] && p[4] == p[5])))) {
coverR = 0;
coverG = (p[0] * filter[0]) >> 8;
coverB = (p[0] * filter[1] + p[1] * filter[0]) >> 8;
coverG = coverG | ( -(coverG >> 8));
coverB = coverB | ( -(coverB >> 8));
if (coverR || coverG || coverB)
sl.add_cell(x - 1, coverR, coverG, coverB);
}
for (int j = 0; j < w; j++) {
if (p[0] == p[1] && p[1] == p[2]
&& (j == 0 || (p[-3] == p[-2] && p[-2] == p[-1]))
&& (j == w-1 || (p[3] == p[4] && p[4] == p[5]))) {
coverR = p[0];
coverG = p[0];
coverB = p[0];
} else if (p[0] == p[1] && p[1] == p[2]
&& (j < w-1 && p[3] == p[4] && p[4] == p[5])
&& (j == w-2 || (p[6] == p[7] && p[7] == p[8]))) {
coverR = ((j > 0 ? p[-2] * filter[4]
+ p[-1] * filter[3] : 0)
+ p[0] * filter[2] + p[1] * filter[1]
+ p[2] * filter[0])
>> 8;
coverG = ((j > 0 ? p[-1] * filter[4] : 0)
+ p[0] * filter[3] + p[1] * filter[2]
+ p[2] * filter[1])
>> 8;
coverB = (p[0] * filter[4]
+ p[1] * filter[3] + p[2] * filter[2]) >> 8;
coverR = coverR | ( -(coverR >> 8));
coverG = coverG | ( -(coverG >> 8));
coverB = coverB | ( -(coverB >> 8));
} else if (p[0] == p[1] && p[1] == p[2]
&& (j > 0 && p[-3] == p[-2] && p[-2] == p[-1])
&& (j == 1 || (p[-6] == p[-5] && p[-5] == p[-4]))) {
coverR = (p[0] * filter[2] + p[1] * filter[1]
+ p[2] * filter[0]) >> 8;
coverG = (p[0] * filter[3] + p[1] * filter[2]
+ p[2] * filter[1]
+ (j < w-1 ? p[3] * filter[0] : 0))
>> 8;
coverB = (p[0] * filter[4] + p[1] * filter[3]
+ p[2] * filter[2]
+ (j < w-1 ? p[3] * filter[1]
+ p[4] * filter[0] : 0))
>> 8;
coverR = coverR | ( -(coverR >> 8));
coverG = coverG | ( -(coverG >> 8));
coverB = coverB | ( -(coverB >> 8));
} else {
coverR = ((j > 0 ? p[-2] * filter[4]
+ p[-1] * filter[3] : 0)
+ p[0] * filter[2] + p[1] * filter[1]
+ p[2] * filter[0])
>> 8;
coverG = ((j > 0 ? p[-1] * filter[4] : 0)
+ p[0] * filter[3] + p[1] * filter[2]
+ p[2] * filter[1]
+ (j < w-1 ? p[3] * filter[0] : 0))
>> 8;
coverB = (p[0] * filter[4] + p[1] * filter[3]
+ p[2] * filter[2]
+ (j < w-1 ? p[3] * filter[1]
+ p[4] * filter[0] : 0))
>> 8;
coverR = coverR | ( -(coverR >> 8));
coverG = coverG | ( -(coverG >> 8));
coverB = coverB | ( -(coverB >> 8));
}
if (coverR || coverG || coverB)
sl.add_cell(x + j, coverR, coverG, coverB);
p += 3;
}
// handle the right extra edge pixel
if (w && !(p[-3] == p[-2] && p[-2] == p[-1]
&& (w == 1 || (p[-6] == p[-5] && p[-5] == p[-4])))) {
coverR = (p[-2] * filter[4] + p[-1] * filter[3]) >> 8;
coverG = (p[-1] * filter[4]) >> 8;
coverB = 0;
coverR = coverR | ( -(coverR >> 8));
coverG = coverG | ( -(coverG >> 8));
if (coverR || coverG || coverB)
sl.add_cell(x + w, coverR, coverG, coverB);
}
}
buf += pitch;
if (sl.num_spans()) {
sl.finalize(y - i - 1);
storage.render(sl);
}
}
#endif
}
// #pragma mark -