Add experimental subpixel text renderer.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/back/trunk@14343 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Alexander Malmberg 2002-08-27 09:59:22 +00:00
parent e4e04350b0
commit ffb49410b4
4 changed files with 482 additions and 0 deletions

View file

@ -1,3 +1,10 @@
2002-08-27 11:58 Alexander Malmberg <alexander@malmberg.org>
* Source/art/ftfont.m: Add experimental subpixel text renderer.
* Source/art/blit.h, Source/art/blit.m: Add render_blit_subpixel
function, used in subpixel rendering.
2002-08-27 10:23 Alexander Malmberg <alexander@malmberg.org>
* Source/art/ARTContext.m, Source/art/composite.m,

View file

@ -93,6 +93,10 @@ typedef struct draw_info_s
unsigned char r, unsigned char g, unsigned char b,
unsigned char alpha, int num);
void (*render_blit_subpixel)(unsigned char *dst, const unsigned char *src,
unsigned char r, unsigned char g, unsigned char b, unsigned char a,
int num);
void (*composite_sover_aa)(composite_run_t *c, int num);
void (*composite_sover_ao)(composite_run_t *c, int num);

View file

@ -201,6 +201,39 @@ static void MPRE(blit_mono) (unsigned char *adst,
}
static void MPRE(blit_subpixel) (unsigned char *adst, const unsigned char *asrc,
unsigned char r, unsigned char g, unsigned char b, unsigned char a,
int num)
{
const unsigned char *src = asrc;
BLEND_TYPE *dst = (BLEND_TYPE *)adst;
unsigned int nr, ng, nb;
unsigned int ar, ag, ab;
int alpha = a;
if (alpha>127) alpha++;
for (; num; num--)
{
ar = *src++;
ag = *src++;
ab = *src++;
BLEND_READ(dst, nr, ng, nb)
ar *= alpha;
ag *= alpha;
ab *= alpha;
nr = (r * ar + nr * (65280 - ar) + 0xff00) >> 16;
ng = (g * ag + ng * (65280 - ag) + 0xff00) >> 16;
nb = (b * ab + nb * (65280 - ab) + 0xff00) >> 16;
BLEND_WRITE(dst, nr, ng, nb)
BLEND_INC(dst)
}
}
static void MPRE(run_opaque) (render_run_t *ri, int num)
{
#if FORMAT_HOW == DI_16_B5G5R5A1 || FORMAT_HOW == DI_16_B5G6R5
@ -1465,6 +1498,8 @@ static draw_info_t draw_infos[DI_NUM] = {
NPRE(blit_alpha,x), \
NPRE(blit_mono,x), \
\
NPRE(blit_subpixel,x), \
\
NPRE(sover_aa,x), \
NPRE(sover_ao,x), \
NPRE(sin_aa,x), \

View file

@ -27,6 +27,7 @@ copyright 2002 Alexander Malmberg <alexander@malmberg.org>
#include <Foundation/NSValue.h>
#include <Foundation/NSPathUtilities.h>
#include <Foundation/NSFileManager.h>
#include <Foundation/NSUserDefaults.h>
#include <Foundation/NSDebug.h>
#include <AppKit/GSFontInfo.h>
#include <AppKit/NSAffineTransform.h>
@ -55,6 +56,15 @@ copyright 2002 Alexander Malmberg <alexander@malmberg.org>
#include FT_OUTLINE_H
/*
from the back-art-subpixel-text defaults key
0: normal rendering
1: subpixel, rgb
2: subpixel, bgr
*/
static int subpixel_text;
@interface FTFontInfo : GSFontInfo <FTFontInfo>
{
const char *filename;
@ -65,6 +75,10 @@ copyright 2002 Alexander Malmberg <alexander@malmberg.org>
@end
@interface FTFontInfo_subpixel : FTFontInfo
@end
static NSMutableArray *fcfg_allFontNames;
static NSMutableDictionary *fcfg_allFontFamilies;
static NSMutableDictionary *fcfg_all_fonts;
@ -443,8 +457,15 @@ static FT_Error ft_get_face(FTC_FaceID fid, FT_Library lib, FT_Pointer data, FT_
NSArray *rfi;
FTFaceInfo *font_entry;
if (subpixel_text)
{
[self release];
self = [FTFontInfo_subpixel alloc];
}
self = [super init];
NSDebugLLog(@"ftfont", @"[%@ -initWithFontName: %@ matrix: (%g %g %g %g %g %g)]\n",
self, name,
fmatrix[0], fmatrix[1], fmatrix[2],
@ -1287,8 +1308,423 @@ add code to avoid loading bitmaps for glyphs */
NSLog(@"FTC_CMapCache_New failed");
load_font_configuration();
subpixel_text = [[NSUserDefaults standardUserDefaults]
integerForKey: @"back-art-subpixel-text"];
}
@end
@implementation FTFontInfo_subpixel
-(void) drawString: (const char *)s
at: (int)x : (int)y
to: (int)x0 : (int)y0 : (int)x1 : (int)y1 : (unsigned char *)buf : (int)bpl
color:(unsigned char)r : (unsigned char)g : (unsigned char)b : (unsigned char)alpha
transform: (NSAffineTransform *)transform
drawinfo: (draw_info_t *)di
{
const unsigned char *c;
unsigned char ch;
unsigned int uch;
FTC_CMapDescRec cmap;
unsigned int glyph;
int use_sbit;
FTC_SBit sbit;
FTC_ImageDesc cur;
FT_Matrix ftmatrix;
FT_Vector ftdelta;
BOOL subpixel = NO;
if (!alpha)
return;
/* TODO: if we had guaranteed upper bounds on glyph image size we
could do some basic clipping here */
x1 -= x0;
y1 -= y0;
x -= x0;
y -= y0;
/* NSLog(@"[%@ draw using matrix: (%g %g %g %g %g %g)]\n",
self,
matrix[0], matrix[1], matrix[2],
matrix[3], matrix[4], matrix[5]
);*/
cur = imgd;
{
float xx, xy, yx, yy;
xx = matrix[0] * transform->matrix.m11 + matrix[1] * transform->matrix.m21;
yx = matrix[0] * transform->matrix.m12 + matrix[1] * transform->matrix.m22;
xy = matrix[2] * transform->matrix.m11 + matrix[3] * transform->matrix.m21;
yy = matrix[2] * transform->matrix.m12 + matrix[3] * transform->matrix.m22;
/* if we're drawing 'normal' text (unscaled, unrotated, reasonable
size), we can and should use the sbit cache */
if (fabs(xx - ((int)xx)) < 0.01 && fabs(yy - ((int)yy)) < 0.01 &&
fabs(xy) < 0.01 && fabs(yx) < 0.01 &&
xx < 72 && yy < 72 && xx > 0.5 && yy > 0.5)
{
use_sbit = 1;
cur.font.pix_width = xx;
cur.font.pix_height = yy;
/* if (cur.font.pix_width < 16 && cur.font.pix_height < 16 &&
cur.font.pix_width > 6 && cur.font.pix_height > 6)
cur.type = ftc_image_mono;
else*/
cur.type = ftc_image_grays, subpixel = YES, cur.font.pix_width *= 3, x *= 3;
// imgd.type|=|ftc_image_flag_unhinted; /* TODO? when? */
}
else
{
float f;
use_sbit = 0;
f = fabs(xx * yy - xy * yx);
if (f > 1)
f = sqrt(f);
else
f = 1.0;
f = (int)f;
cur.font.pix_width = cur.font.pix_height = f;
ftmatrix.xx = xx / f * 65536.0;
ftmatrix.xy = xy / f * 65536.0;
ftmatrix.yx = yx / f * 65536.0;
ftmatrix.yy = yy / f * 65536.0;
ftdelta.x = ftdelta.y = 0;
}
}
/* NSLog(@"drawString: '%s' at: %i:%i to: %i:%i:%i:%i:%p\n",
s, x, y, x0, y0, x1, y1, buf);*/
cmap.face_id = imgd.font.face_id;
cmap.u.encoding = ft_encoding_unicode;
cmap.type = FTC_CMAP_BY_ENCODING;
for (c = s; *c; c++)
{
/* TODO: do the same thing in outlineString:... */
ch = *c;
if (ch < 0x80)
{
uch = ch;
}
else if (ch < 0xc0)
{
uch = 0xfffd;
}
else if (ch < 0xe0)
{
#define ADD_UTF_BYTE(shift, internal) \
ch = *++c; \
if (ch >= 0x80 && ch < 0xc0) \
{ \
uch |= (ch & 0x3f) << shift; \
internal \
} \
else \
{ \
uch = 0xfffd; \
c--; \
}
uch = (ch & 0x1f) << 6;
ADD_UTF_BYTE(0, )
}
else if (ch < 0xf0)
{
uch = (ch & 0x0f) << 12;
ADD_UTF_BYTE(6, ADD_UTF_BYTE(0, ))
}
else if (ch < 0xf8)
{
uch = (ch & 0x07) << 18;
ADD_UTF_BYTE(12, ADD_UTF_BYTE(6, ADD_UTF_BYTE(0, )))
}
else if (ch < 0xfc)
{
uch = (ch & 0x03) << 24;
ADD_UTF_BYTE(18, ADD_UTF_BYTE(12, ADD_UTF_BYTE(6, ADD_UTF_BYTE(0, ))))
}
else if (ch < 0xfe)
{
uch = (ch & 0x01) << 30;
ADD_UTF_BYTE(24, ADD_UTF_BYTE(18, ADD_UTF_BYTE(12, ADD_UTF_BYTE(6, ADD_UTF_BYTE(0, )))))
}
else
uch = 0xfffd;
#undef ADD_UTF_BYTE
glyph = FTC_CMapCache_Lookup(ftc_cmapcache, &cmap, uch);
cur.font.face_id = imgd.font.face_id;
if (!glyph)
{
cmap.face_id = fallback.font.face_id;
glyph = FTC_CMapCache_Lookup(ftc_cmapcache, &cmap, uch);
if (glyph)
cur.font.face_id = fallback.font.face_id;
cmap.face_id = imgd.font.face_id;
}
if (use_sbit)
{
if (FTC_SBitCache_Lookup(ftc_sbitcache, &cur, glyph, &sbit, NULL))
continue;
if (!sbit->buffer)
{
x += sbit->xadvance;
continue;
}
if (sbit->format == ft_pixel_mode_grays)
{
int gx = x + sbit->left, gy = y - sbit->top;
int px0 = (gx - 2 < 0? gx - 4 : gx - 2) / 3;
int px1 = (gx + sbit->width + 2 < 0? gx + sbit->width + 2: gx + sbit->width + 4) / 3;
int llip = gx - px0 * 3;
int sbpl = sbit->pitch;
int sx = sbit->width, sy = sbit->height;
int psx = px1 - px0;
const unsigned char *src = sbit->buffer;
unsigned char *dst = buf;
unsigned char scratch[psx * 3];
int mode = subpixel_text == 2? 2 : 0;
if (gy < 0)
{
sy += gy;
src -= sbpl * gy;
gy = 0;
}
else if (gy > 0)
{
dst += bpl * gy;
}
sy += gy;
if (sy > y1)
sy = y1;
if (px1 > x1)
px1 = x1;
if (px0 < 0)
{
px0 = -px0;
}
else
{
px1 -= px0;
dst += px0 * DI.bytes_per_pixel;
px0 = 0;
}
if (px1 <= 0)
{
x += sbit->xadvance;
continue;
}
for (; gy < sy; gy++, src += sbpl, dst += bpl)
{
int i, j;
for (i = 0, j = -llip; i < psx * 3; i+=3)
{
scratch[i+mode] =
((j > -1 && j<sx ? src[j ] * 3 : 0)
+ (j > 0 && j<sx + 1? src[j - 1] * 2 : 0)
+ (j > 1 && j<sx + 2? src[j - 2] : 0)
+ (j > -2 && j<sx - 1? src[j + 1] * 2 : 0)
+ (j > -3 && j<sx - 2? src[j + 2] : 0)) / 9;
j++;
scratch[i+1] =
((j > -1 && j<sx ? src[j ] * 3 : 0)
+ (j > 0 && j<sx + 1? src[j - 1] * 2 : 0)
+ (j > 1 && j<sx + 2? src[j - 2] : 0)
+ (j > -2 && j<sx - 1? src[j + 1] * 2 : 0)
+ (j > -3 && j<sx - 2? src[j + 2] : 0)) / 9;
j++;
scratch[i+(mode^2)] =
((j > -1 && j<sx ? src[j ] * 3 : 0)
+ (j > 0 && j<sx + 1? src[j - 1] * 2 : 0)
+ (j > 1 && j<sx + 2? src[j - 2] : 0)
+ (j > -2 && j<sx - 1? src[j + 1] * 2 : 0)
+ (j > -3 && j<sx - 2? src[j + 2] : 0)) / 9;
j++;
}
DI.render_blit_subpixel(dst,
scratch + px0 * 3, r, g, b, alpha,
px1);
}
}
else if (sbit->format == ft_pixel_mode_mono)
{
int gx = x + sbit->left, gy = y - sbit->top;
int sbpl = sbit->pitch;
int sx = sbit->width, sy = sbit->height;
const unsigned char *src = sbit->buffer;
unsigned char *dst = buf;
int src_ofs = 0;
if (gy < 0)
{
sy += gy;
src -= sbpl * gy;
gy = 0;
}
else if (gy > 0)
{
dst += bpl * gy;
}
sy += gy;
if (sy > y1)
sy = y1;
if (gx < 0)
{
sx += gx;
src -= gx / 8;
src_ofs = (-gx) & 7;
gx = 0;
}
else if (gx > 0)
{
dst += DI.bytes_per_pixel * gx;
}
sx += gx;
if (sx > x1)
sx = x1;
sx -= gx;
if (sx > 0)
{
if (alpha >= 255)
for (; gy < sy; gy++, src += sbpl, dst += bpl)
RENDER_BLIT_MONO_OPAQUE(dst, src, src_ofs, r, g, b, sx);
else
for (; gy < sy; gy++, src += sbpl, dst += bpl)
RENDER_BLIT_MONO(dst, src, src_ofs, r, g, b, alpha, sx);
}
}
else
{
NSLog(@"unhandled font bitmap format %i", sbit->format);
}
x += sbit->xadvance;
}
else
{
FT_Face face;
FT_Glyph gl;
FT_BitmapGlyph gb;
if (FTC_Manager_Lookup_Size(ftc_manager, &cur.font, &face, 0))
continue;
/* TODO: for rotations of 90, 180, 270, and integer
scales hinting might still be a good idea. */
if (FT_Load_Glyph(face, glyph, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP))
continue;
if (FT_Get_Glyph(face->glyph, &gl))
continue;
if (FT_Glyph_Transform(gl, &ftmatrix, &ftdelta))
{
NSLog(@"glyph transformation failed!");
continue;
}
if (FT_Glyph_To_Bitmap(&gl, ft_render_mode_normal, 0, 1))
{
FT_Done_Glyph(gl);
continue;
}
gb = (FT_BitmapGlyph)gl;
if (gb->bitmap.pixel_mode == ft_pixel_mode_grays)
{
int gx = x + gb->left, gy = y - gb->top;
int sbpl = gb->bitmap.pitch;
int sx = gb->bitmap.width, sy = gb->bitmap.rows;
const unsigned char *src = gb->bitmap.buffer;
unsigned char *dst = buf;
if (gy < 0)
{
sy += gy;
src -= sbpl * gy;
gy = 0;
}
else if (gy > 0)
{
dst += bpl * gy;
}
sy += gy;
if (sy > y1)
sy = y1;
if (gx < 0)
{
sx += gx;
src -= gx;
gx = 0;
}
else if (gx > 0)
{
dst += DI.bytes_per_pixel * gx;
}
sx += gx;
if (sx > x1)
sx = x1;
sx -= gx;
if (sx > 0)
{
if (alpha >= 255)
for (; gy < sy; gy++, src += sbpl, dst += bpl)
RENDER_BLIT_ALPHA_OPAQUE(dst, src, r, g, b, sx);
else
for (; gy < sy; gy++, src += sbpl, dst += bpl)
RENDER_BLIT_ALPHA(dst, src, r, g, b, alpha, sx);
}
}
/* TODO: will this case ever appear? */
/* else if (gb->bitmap.pixel_mode==ft_pixel_mode_mono)*/
else
{
NSLog(@"unhandled font bitmap format %i", gb->bitmap.pixel_mode);
}
ftdelta.x += gl->advance.x >> 10;
ftdelta.y += gl->advance.y >> 10;
FT_Done_Glyph(gl);
}
}
}
@end