/* Copyright (C) 2002 Free Software Foundation, Inc. Author: Alexander Malmberg This file is part of GNUstep. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, see or write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include "gsc/GSContext.h" #include "gsc/GSGState.h" #include "ftfont.h" #include "FTFontEnumerator.h" #include "FTFaceInfo.h" #include "blit.h" #define DI (*di) /** font handling interface **/ #include FT_CACHE_H #include FT_CACHE_IMAGE_H #include FT_CACHE_SMALL_BITMAPS_H #include FT_CACHE_CHARMAP_H #include FT_OUTLINE_H #if (FREETYPE_MAJOR==2) && ((FREETYPE_MINOR<1) || ((FREETYPE_MINOR==1) && (FREETYPE_PATCH<=2))) #define FT212_STUFF #endif /* TODO: finish screen font handling */ /* from the back-art-subpixel-text defaults key 0: normal rendering 1: subpixel, rgb 2: subpixel, bgr */ static int subpixel_text; #define CACHE_SIZE 257 @interface FTFontInfo : GSFontInfo { @public #ifdef FT212_STUFF FTC_ImageDesc imgd; FTC_ImageDesc advancementImgd; #else FTC_ImageTypeRec imgd; FTC_ImageTypeRec advancementImgd; #endif FTFaceInfo *face_info; BOOL screenFont; /* Profiling (2003-11-14) shows that calls to -advancementForGlyph: accounted for roughly 20% of layout time. This cache reduces it to (currently) insignificant levels. */ unsigned int cachedGlyph[CACHE_SIZE]; NSSize cachedSize[CACHE_SIZE]; CGFloat lineHeight; } @end @interface FTFontInfo_subpixel : FTFontInfo @end static FT_Library ft_library; static FTC_Manager ftc_manager; static FTC_ImageCache ftc_imagecache; static FTC_SBitCache ftc_sbitcache; static FTC_CMapCache ftc_cmapcache; static FT_Error ft_get_face(FTC_FaceID fid, FT_Library lib, FT_Pointer data, FT_Face *pface) { FT_Error err; NSArray *rfi = (NSArray *)fid; int i, c = [rfi count]; // NSLog(@"ft_get_face: %@ '%s'", rfi, [[rfi objectAtIndex: 0] cString]); err = FT_New_Face(lib, [[rfi objectAtIndex: 0] cString], 0, pface); if (err) { NSLog(@"Error when loading '%@' (%08x)", [rfi objectAtIndex: 0], err); return err; } for (i = 1; i < c; i++) { // NSLog(@" do '%s'", [[rfi objectAtIndex: i] cString]); err = FT_Attach_File(*pface, [[rfi objectAtIndex: i] cString]); if (err) { NSLog(@"Error when loading '%@' (%08x)", [rfi objectAtIndex: i], err); /* pretend it's alright */ } } return 0; } @implementation FTFontInfo - initWithFontName: (NSString *)name matrix: (const CGFloat *)fmatrix screenFont: (BOOL)p_screenFont { FT_Face face; FT_Size size; NSArray *rfi; FTFaceInfo *font_entry; FT_Error error; if (subpixel_text) { [self release]; self = [FTFontInfo_subpixel alloc]; } self = [super init]; screenFont = p_screenFont; NSDebugLLog(@"ftfont", @"[%@ -initWithFontName: %@ matrix: (%g %g %g %g %g %g)] %i\n", self, name, fmatrix[0], fmatrix[1], fmatrix[2], fmatrix[3], fmatrix[4], fmatrix[5], p_screenFont); font_entry = [FTFontEnumerator fontWithName: name]; if (!font_entry) { [self release]; return nil; } face_info = font_entry; weight = font_entry->weight; traits = font_entry->traits; fontName = [name copy]; familyName = [face_info->familyName copy]; memcpy(matrix, fmatrix, sizeof(matrix)); /* TODO: somehow make gnustep-gui send unicode our way. utf8? ugly, but it works */ mostCompatibleStringEncoding = NSUTF8StringEncoding; encodingScheme = @"iso10646-1"; if (screenFont) { /* Round up; makes the text more legible. */ matrix[0] = ceil(matrix[0]); if (matrix[3] < 0.0) matrix[3] = floor(matrix[3]); else matrix[3] = ceil(matrix[3]); } imgd.font.pix_width = fabs(matrix[0]); imgd.font.pix_height = fabs(matrix[3]); rfi = font_entry->files; if (screenFont && font_entry->num_sizes && imgd.font.pix_width == imgd.font.pix_height) { int i; for (i = 0; i < font_entry->num_sizes; i++) { if (font_entry->sizes[i].pixel_size == imgd.font.pix_width) { rfi = font_entry->sizes[i].files; break; } } } imgd.font.face_id = (FTC_FaceID)rfi; if ((error=FTC_Manager_Lookup_Size(ftc_manager, &imgd.font, &face, &size))) { NSLog(@"FTC_Manager_Lookup_Size() failed for '%@', error %08x!\n", name, error); return self; } // xHeight = size->metrics.height / 64.0; /* TODO: these are _really_ messed up when fonts are flipped */ /* TODO: need to look acrefully at these and make sure they are correct */ ascender = fabs(((int)size->metrics.ascender) / 64.0); descender = fabs(((int)size->metrics.descender) / 64.0); lineHeight = (int)size->metrics.height / 64.0; xHeight = ascender * 0.5; /* TODO */ maximumAdvancement = NSMakeSize((size->metrics.max_advance / 64.0), ascender + descender); fontBBox = NSMakeRect(0, -descender, maximumAdvancement.width, ascender + descender); descender = -descender; /* printf("(%@) h=%g a=%g d=%g max=(%g %g) (%g %g)+(%g %g)\n",name, xHeight, ascender, descender, maximumAdvancement.width, maximumAdvancement.height, fontBBox.origin.x, fontBBox.origin.y, fontBBox.size.width, fontBBox.size.height);*/ { float xx, yy; #ifdef FT212_STUFF FTC_ImageDesc cur; #else FTC_ImageTypeRec cur; #endif cur = imgd; xx = matrix[0]; yy = matrix[3]; if (xx == yy && xx < 16 && xx >= 8) { int rh = face_info->render_hints_hack; if (rh & 0x10000) { #ifdef FT212_STUFF cur.type = ftc_image_grays; #else cur.flags = FT_LOAD_TARGET_NORMAL; #endif rh = (rh >> 8) & 0xff; } else { #ifdef FT212_STUFF cur.type = ftc_image_mono; #else cur.flags = FT_LOAD_TARGET_MONO; #endif rh = rh & 0xff; } if (rh & 1) #ifdef FT212_STUFF cur.type |= ftc_image_flag_autohinted; #else cur.flags |= FT_LOAD_FORCE_AUTOHINT; #endif if (!(rh & 2)) #ifdef FT212_STUFF cur.type |= ftc_image_flag_unhinted; #else cur.flags |= FT_LOAD_NO_HINTING; #endif } else if (xx < 8) #ifdef FT212_STUFF cur.type = ftc_image_grays | ftc_image_flag_unhinted; #else cur.flags = FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; #endif else #ifdef FT212_STUFF cur.type = ftc_image_grays; #else cur.flags = FT_LOAD_TARGET_NORMAL; #endif advancementImgd = cur; } /* Here, we simply need to make sure that we don't get any false matches the first time a particular cache entry is used. Thus, we only need to initialize the first entry. For all other entries, cachedGlyph[i] will be 0, and that's a glyph that can't possibly hash to any entry except entry #0, so it won't cause any false matches. */ cachedGlyph[0] = 1; return self; } -(void) set { NSLog(@"ignore -set method of font '%@'\n", fontName); } -(CGFloat) defaultLineHeightForFont { return lineHeight; } /* TODO: the current point probably needs updating after drawing is done */ /* draw string at point, clipped, w/given color and alpha, and possible deltas: flags & 0x1: data contains x offsets, use instead of glyph x advance flags & 0x2: data contains y offsets, use instead of glyph y advance flags & 0x4: data contains a single x and y offset, which should be added to font's advancements for each glyph; results are undefined if this option is combined with either x or y offsets (0x1,0x2) flags & 0x8: data contains a single x and y offset, which should be added to font's advancement for glyph identified by 'wch'; if combined with 0x4 deltas contain exactly two offsets for x and y, the first for every character, the second for 'wch'; results are undefined if 0x8 is combined with 0x2 or 0x1 */ -(void) drawString: (const char *)s at: (int)x : (int)y to: (int)x0 : (int)y0 : (int)x1 : (int)y1 : (unsigned char *)buf : (int)bpl : (unsigned char *)abuf : (int)abpl color:(unsigned char)r : (unsigned char)g : (unsigned char)b : (unsigned char)alpha transform: (NSAffineTransform *)transform deltas: (const float *)delta_data : (int)delta_size : (int)delta_flags widthChar: (int) wch drawinfo: (draw_info_t *)di { #if 0 NSLog(@"ignoring drawString"); #else const unsigned char *c; unsigned char ch; unsigned int uch; int d; FTC_CMapDescRec cmap; unsigned int glyph; NSAffineTransformStruct ts; int use_sbit; FTC_SBit sbit; #ifdef FT212_STUFF FTC_ImageDesc cur; #else FTC_ImageTypeRec cur; #endif FT_Matrix ftmatrix; FT_Vector ftdelta; FT_Error error; 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; ts = [transform transformStruct]; /* NSLog(@"[%@ draw using matrix: (%g %g %g %g %g %g)] transform=%@\n", self, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], transform );*/ cur = imgd; { float xx, xy, yx, yy; xx = matrix[0] * ts.m11 + matrix[1] * ts.m21; yx = matrix[0] * ts.m12 + matrix[1] * ts.m22; xy = matrix[2] * ts.m11 + matrix[3] * ts.m21; yy = matrix[2] * ts.m12 + matrix[3] * ts.m22; /* If we're drawing 'normal' text (unscaled, unrotated, reasonable size), we can and should use the sbit cache for screen fonts. */ if (screenFont && 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 (xx == yy && xx < 16 && xx >= 8) { int rh = face_info->render_hints_hack; if (rh & 0x10000) { #ifdef FT212_STUFF cur.type = ftc_image_grays; #else cur.flags = FT_LOAD_TARGET_NORMAL; #endif rh = (rh >> 8) & 0xff; } else { #ifdef FT212_STUFF cur.type = ftc_image_mono; #else cur.flags = FT_LOAD_TARGET_MONO; #endif rh = rh & 0xff; } if (rh & 1) #ifdef FT212_STUFF cur.type |= ftc_image_flag_autohinted; #else cur.flags |= FT_LOAD_FORCE_AUTOHINT; #endif if (!(rh & 2)) #ifdef FT212_STUFF cur.type |= ftc_image_flag_unhinted; #else cur.flags |= FT_LOAD_NO_HINTING; #endif } else if (xx < 8) #ifdef FT212_STUFF cur.type = ftc_image_grays | ftc_image_flag_unhinted; #else cur.flags = FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; #endif else #ifdef FT212_STUFF cur.type = ftc_image_grays; #else cur.flags = FT_LOAD_TARGET_NORMAL; #endif } 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; d=0; 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 (use_sbit) { if ((error=FTC_SBitCache_Lookup(ftc_sbitcache, &cur, glyph, &sbit, NULL))) { NSLog(@"FTC_SBitCache_Lookup() failed with error %08x (%08x, %08x, %ix%i, %08x)\n", error, glyph, cur.font.face_id, cur.font.pix_width, cur.font.pix_height, #ifdef FT212_STUFF cur.type #else cur.flags #endif ); continue; } if (!sbit->buffer) { if (!delta_flags) { x += sbit->xadvance; } else { if (delta_flags & 0x1) x += delta_data[d++]; if (delta_flags & 0x2) y += (ts.m22 < 0) ? delta_data[d++] : -delta_data[d++]; if (delta_flags & 0x4) { x += sbit->xadvance + delta_data[0]; y += /*sbit->yadvance +*/ (ts.m22 < 0) ? delta_data[1] : -delta_data[1]; if ((delta_flags & 0x8) && (uch == wch)) { x += delta_data[2]; y += (ts.m22 < 0) ? delta_data[3] : -delta_data[3]; } } else if (delta_flags & 0x8) { if (uch == wch) { x += sbit->xadvance + delta_data[0]; y += /*sbit->yadvance +*/ (ts.m22 < 0) ? delta_data[1] : -delta_data[1]; } else { x += sbit->xadvance; /*y += sbit->yadvance;*/ } } } continue; } if (sbit->format == ft_pixel_mode_grays) { 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; 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); } } 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); } if (!delta_flags) { x += sbit->xadvance; } else { if (delta_flags & 0x1) x += delta_data[d++]; if (delta_flags & 0x2) y += (ts.m22 < 0) ? delta_data[d++] : -delta_data[d++]; if (delta_flags & 0x4) { x += sbit->xadvance + delta_data[0]; y += /*sbit->yadvance +*/ (ts.m22 < 0) ? delta_data[1] : -delta_data[1]; if ((delta_flags & 0x8) && (uch == wch)) { x += delta_data[2]; y += (ts.m22 < 0) ? delta_data[3] : -delta_data[3]; } } else if (delta_flags & 0x8) { if (uch == wch) { x += sbit->xadvance + delta_data[0]; y += /*sbit->yadvance +*/ (ts.m22 < 0) ? delta_data[1] : -delta_data[1]; } else { x += sbit->xadvance; /*y += sbit->yadvance;*/ } } } } else { FT_Face face; FT_Glyph gl; FT_BitmapGlyph gb; if ((error=FTC_Manager_Lookup_Size(ftc_manager, &cur.font, &face, 0))) { NSLog(@"FTC_Manager_Lookup_Size() failed with error %08x\n",error); continue; } /* TODO: for rotations of 90, 180, 270, and integer scales hinting might still be a good idea. */ if ((error=FT_Load_Glyph(face, glyph, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP))) { NSLog(@"FT_Load_Glyph() failed with error %08x\n",error); continue; } if ((error=FT_Get_Glyph(face->glyph, &gl))) { NSLog(@"FT_Get_Glyph() failed with error %08x\n",error); continue; } if ((error=FT_Glyph_Transform(gl, &ftmatrix, &ftdelta))) { NSLog(@"FT_Glyph_Transform() failed with error %08x\n",error); continue; } if ((error=FT_Glyph_To_Bitmap(&gl, ft_render_mode_normal, 0, 1))) { NSLog(@"FT_Glyph_To_Bitmap() failed with error %08x\n",error); 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; unsigned char *dsta = abuf; if (gy < 0) { sy += gy; src -= sbpl * gy; gy = 0; } else if (gy > 0) { dst += bpl * gy; if (dsta) dsta += abpl * 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; if (dsta) dsta += gx; } sx += gx; if (sx > x1) sx = x1; sx -= gx; if (sx > 0) { if (dsta) for (; gy < sy; gy++, src += sbpl, dst += bpl, dsta += abpl) RENDER_BLIT_ALPHA_A(dst, dsta, src, r, g, b, alpha, sx); else 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); } if (!delta_flags) { ftdelta.x += gl->advance.x >> 10; ftdelta.y += gl->advance.y >> 10; } else { if (delta_flags & 0x1) ftdelta.x += delta_data[d++] * 64.0; if (delta_flags & 0x2) ftdelta.y += delta_data[d++] * 64.0; if (delta_flags & 0x4) { ftdelta.x += (gl->advance.x >> 10) + delta_data[0] * 64.0; ftdelta.y += (gl->advance.y >> 10) + delta_data[1] * 64.0; if ((delta_flags & 0x8) && (uch == wch)) { ftdelta.x += delta_data[2] * 64.0; ftdelta.y += delta_data[3] * 64.0; } } else if (delta_flags & 0x8) { if (uch == wch) { ftdelta.x += (gl->advance.x>>10) + delta_data[0] * 64.0; ftdelta.y += (gl->advance.y>>10) + delta_data[1] * 64.0; } else { ftdelta.x += gl->advance.x >> 10; ftdelta.y += gl->advance.y >> 10; } } } FT_Done_Glyph(gl); } } #endif } -(void) drawGlyphs: (const NSGlyph *)glyphs : (int)length 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: (struct draw_info_s *)di { unsigned int glyph; int use_sbit; FTC_SBit sbit; #ifdef FT212_STUFF FTC_ImageDesc cur; #else FTC_ImageTypeRec cur; #endif FT_Matrix ftmatrix; FT_Vector ftdelta; FT_Error error; NSAffineTransformStruct ts; 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; ts = [transform transformStruct]; /* NSLog(@"[%@ draw using matrix: (%g %g %g %g %g %g)] transform=%@\n", self, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], transform );*/ cur = imgd; { float xx, xy, yx, yy; xx = matrix[0] * ts.m11 + matrix[1] * ts.m21; yx = matrix[0] * ts.m12 + matrix[1] * ts.m22; xy = matrix[2] * ts.m11 + matrix[3] * ts.m21; yy = matrix[2] * ts.m12 + matrix[3] * ts.m22; /* If we're drawing 'normal' text (unscaled, unrotated, reasonable size), we can and should use the sbit cache for screen fonts. */ if (screenFont && 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 (xx == yy && xx < 16 && xx >= 8) { int rh = face_info->render_hints_hack; if (rh & 0x10000) { #ifdef FT212_STUFF cur.type = ftc_image_grays; #else cur.flags = FT_LOAD_TARGET_NORMAL; #endif rh = (rh >> 8) & 0xff; } else { #ifdef FT212_STUFF cur.type = ftc_image_mono; #else cur.flags = FT_LOAD_TARGET_MONO; #endif rh = rh & 0xff; } if (rh & 1) #ifdef FT212_STUFF cur.type |= ftc_image_flag_autohinted; #else cur.flags |= FT_LOAD_FORCE_AUTOHINT; #endif if (!(rh & 2)) #ifdef FT212_STUFF cur.type |= ftc_image_flag_unhinted; #else cur.flags |= FT_LOAD_NO_HINTING; #endif } else if (xx < 8) #ifdef FT212_STUFF cur.type = ftc_image_grays | ftc_image_flag_unhinted; #else cur.flags = FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; #endif else #ifdef FT212_STUFF cur.type = ftc_image_grays; #else cur.flags = FT_LOAD_TARGET_NORMAL; #endif } 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);*/ for (; length; length--, glyphs++) { glyph = *glyphs - 1; if (use_sbit) { if ((error = FTC_SBitCache_Lookup(ftc_sbitcache, &cur, glyph, &sbit, NULL))) { NSLog(@"FTC_SBitCache_Lookup() failed with error %08x (%08x, %08x, %ix%i, %08x)\n", error, glyph, cur.font.face_id, cur.font.pix_width, cur.font.pix_height, #ifdef FT212_STUFF cur.type #else cur.flags #endif ); 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 sbpl = sbit->pitch; int sx = sbit->width, sy = sbit->height; const unsigned char *src = sbit->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); } } 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 ((error=FTC_Manager_Lookup_Size(ftc_manager, &cur.font, &face, 0))) { NSLog(@"FTC_Manager_Lookup_Size() failed with error %08x\n",error); continue; } /* TODO: for rotations of 90, 180, 270, and integer scales hinting might still be a good idea. */ if ((error=FT_Load_Glyph(face, glyph, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP))) { NSLog(@"FT_Load_Glyph() failed with error %08x\n",error); continue; } if ((error=FT_Get_Glyph(face->glyph, &gl))) { NSLog(@"FT_Get_Glyph() failed with error %08x\n",error); continue; } if ((error=FT_Glyph_Transform(gl, &ftmatrix, &ftdelta))) { NSLog(@"FT_Glyph_Transform() failed with error %08x\n",error); continue; } if ((error=FT_Glyph_To_Bitmap(&gl, ft_render_mode_normal, 0, 1))) { NSLog(@"FT_Glyph_To_Bitmap() failed with error %08x\n",error); 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); } } } -(void) drawGlyphs: (const NSGlyph *)glyphs : (int)length at: (int)x : (int)y to: (int)x0 : (int)y0 : (int)x1 : (int)y1 : (unsigned char *)buf : (int)bpl alpha: (unsigned char *)abuf : (int)abpl color: (unsigned char)r : (unsigned char)g : (unsigned char)b : (unsigned char)alpha transform: (NSAffineTransform *)transform drawinfo: (struct draw_info_s *)di { unsigned int glyph; int use_sbit; FTC_SBit sbit; #ifdef FT212_STUFF FTC_ImageDesc cur; #else FTC_ImageTypeRec cur; #endif FT_Matrix ftmatrix; FT_Vector ftdelta; FT_Error error; NSAffineTransformStruct ts; 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; ts = [transform transformStruct]; /* NSLog(@"[%@ draw using matrix: (%g %g %g %g %g %g)] transform=%@\n", self, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], transform );*/ cur = imgd; { float xx, xy, yx, yy; xx = matrix[0] * ts.m11 + matrix[1] * ts.m21; yx = matrix[0] * ts.m12 + matrix[1] * ts.m22; xy = matrix[2] * ts.m11 + matrix[3] * ts.m21; yy = matrix[2] * ts.m12 + matrix[3] * ts.m22; /* If we're drawing 'normal' text (unscaled, unrotated, reasonable size), we can and should use the sbit cache for screen fonts. */ if (screenFont && 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 (xx == yy && xx < 16 && xx >= 8) { int rh = face_info->render_hints_hack; if (rh & 0x10000) { #ifdef FT212_STUFF cur.type = ftc_image_grays; #else cur.flags = FT_LOAD_TARGET_NORMAL; #endif rh = (rh >> 8) & 0xff; } else { #ifdef FT212_STUFF cur.type = ftc_image_mono; #else cur.flags = FT_LOAD_TARGET_MONO; #endif rh = rh & 0xff; } if (rh & 1) #ifdef FT212_STUFF cur.type |= ftc_image_flag_autohinted; #else cur.flags |= FT_LOAD_FORCE_AUTOHINT; #endif if (!(rh & 2)) #ifdef FT212_STUFF cur.type |= ftc_image_flag_unhinted; #else cur.flags |= FT_LOAD_NO_HINTING; #endif } else if (xx < 8) #ifdef FT212_STUFF cur.type = ftc_image_grays | ftc_image_flag_unhinted; #else cur.flags = FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; #endif else #ifdef FT212_STUFF cur.type = ftc_image_grays; #else cur.flags = FT_LOAD_TARGET_NORMAL; #endif } 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);*/ for (; length; length--, glyphs++) { glyph = *glyphs - 1; if (use_sbit) { if ((error = FTC_SBitCache_Lookup(ftc_sbitcache, &cur, glyph, &sbit, NULL))) { if (glyph != 0xffffffff) NSLog(@"FTC_SBitCache_Lookup() failed with error %08x (%08x, %08x, %ix%i, %08x)\n", error, glyph, cur.font.face_id, cur.font.pix_width, cur.font.pix_height, #ifdef FT212_STUFF cur.type #else cur.flags #endif ); 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 sbpl = sbit->pitch; int sx = sbit->width, sy = sbit->height; const unsigned char *src = sbit->buffer; unsigned char *dst = buf; unsigned char *adst = abuf; if (gy < 0) { sy += gy; src -= sbpl * gy; gy = 0; } else if (gy > 0) { dst += bpl * gy; adst += abpl * 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; adst += gx; } sx += gx; if (sx > x1) sx = x1; sx -= gx; if (sx > 0) { for (; gy < sy; gy++, src += sbpl, dst += bpl, adst += abpl) RENDER_BLIT_ALPHA_A(dst, adst, src, r, g, b, alpha, sx); } } 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; unsigned char *adst = abuf; int src_ofs = 0; if (gy < 0) { sy += gy; src -= sbpl * gy; gy = 0; } else if (gy > 0) { dst += bpl * gy; adst += abpl * 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; adst += gx; } sx += gx; if (sx > x1) sx = x1; sx -= gx; if (sx > 0) { for (; gy < sy; gy++, src += sbpl, dst += bpl, adst += bpl) RENDER_BLIT_MONO_A(dst, adst, 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 ((error=FTC_Manager_Lookup_Size(ftc_manager, &cur.font, &face, 0))) { NSLog(@"FTC_Manager_Lookup_Size() failed with error %08x\n",error); continue; } /* TODO: for rotations of 90, 180, 270, and integer scales hinting might still be a good idea. */ if ((error=FT_Load_Glyph(face, glyph, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP))) { NSLog(@"FT_Load_Glyph() failed with error %08x\n",error); continue; } if ((error=FT_Get_Glyph(face->glyph, &gl))) { NSLog(@"FT_Get_Glyph() failed with error %08x\n",error); continue; } if ((error=FT_Glyph_Transform(gl, &ftmatrix, &ftdelta))) { NSLog(@"FT_Glyph_Transform() failed with error %08x\n",error); continue; } if ((error=FT_Glyph_To_Bitmap(&gl, ft_render_mode_normal, 0, 1))) { NSLog(@"FT_Glyph_To_Bitmap() failed with error %08x\n",error); 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; unsigned char *adst = abuf; if (gy < 0) { sy += gy; src -= sbpl * gy; gy = 0; } else if (gy > 0) { dst += bpl * gy; adst += abpl * 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; adst += gx; } sx += gx; if (sx > x1) sx = x1; sx -= gx; if (sx > 0) { for (; gy < sy; gy++, src += sbpl, dst += bpl, adst += bpl) RENDER_BLIT_ALPHA_A(dst, adst, 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); } } } -(BOOL) glyphIsEncoded: (NSGlyph)glyph { FT_Face face; FT_Error error; glyph--; if ((error=FTC_Manager_Lookup_Size(ftc_manager, &imgd.font, &face, 0))) { NSLog(@"FTC_Manager_Lookup_Size() failed with error %08x",error); return NO; } if ((error=FT_Load_Glyph(face, glyph, 0))) { NSLog(@"FT_Load_Glyph() failed with error %08x",error); return NO; } return YES; } - (NSSize) advancementForGlyph: (NSGlyph)glyph { FT_Error error; if (glyph == NSControlGlyph || glyph == GSAttachmentGlyph) return NSZeroSize; if (glyph != NSNullGlyph) glyph--; if (screenFont) { int entry = glyph % CACHE_SIZE; FTC_SBit sbit; if (cachedGlyph[entry] == glyph) return cachedSize[entry]; if ((error=FTC_SBitCache_Lookup(ftc_sbitcache, &advancementImgd, glyph, &sbit, NULL))) { NSLog(@"FTC_SBitCache_Lookup() failed with error %08x (%08x, %08x, %ix%i, %08x)\n", error, glyph, advancementImgd.font.face_id, advancementImgd.font.pix_width, advancementImgd.font.pix_height, #ifdef FT212_STUFF advancementImgd.type #else advancementImgd.flags #endif ); return NSZeroSize; } cachedGlyph[entry] = glyph; cachedSize[entry] = NSMakeSize(sbit->xadvance, sbit->yadvance); return cachedSize[entry]; } else { FT_Face face; FT_Glyph gl; FT_Matrix ftmatrix; FT_Vector ftdelta; float f; NSSize s; f = fabs(matrix[0] * matrix[3] - matrix[1] * matrix[2]); if (f > 1) f = sqrt(f); else f = 1.0; f = (int)f; ftmatrix.xx = matrix[0] / f * 65536.0; ftmatrix.xy = matrix[1] / f * 65536.0; ftmatrix.yx = matrix[2] / f * 65536.0; ftmatrix.yy = matrix[3] / f * 65536.0; ftdelta.x = ftdelta.y = 0; if (FTC_Manager_Lookup_Size(ftc_manager, &imgd.font, &face, 0)) return NSZeroSize; if (FT_Load_Glyph(face, glyph, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) return NSZeroSize; if (FT_Get_Glyph(face->glyph, &gl)) return NSZeroSize; if (FT_Glyph_Transform(gl, &ftmatrix, &ftdelta)) return NSZeroSize; s = NSMakeSize(gl->advance.x / 65536.0, gl->advance.y / 65536.0); FT_Done_Glyph(gl); return s; } } - (NSRect) boundingRectForGlyph: (NSGlyph)glyph { #ifdef FT212_STUFF FTC_ImageDesc *cur; #else FTC_ImageTypeRec *cur; #endif FT_BBox bbox; FT_Glyph g; FT_Error error; glyph--; /* TODO: this is ugly */ cur = &imgd; if ((error=FTC_ImageCache_Lookup(ftc_imagecache, cur, glyph, &g, NULL))) { NSLog(@"FTC_ImageCache_Lookup() failed with error %08x",error); // NSLog(@"boundingRectForGlyph: %04x -> %i\n", aGlyph, glyph); return fontBBox; } FT_Glyph_Get_CBox(g, ft_glyph_bbox_gridfit, &bbox); /* printf("got cbox for %04x: %i, %i - %i, %i\n", aGlyph, bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax);*/ return NSMakeRect(bbox.xMin / 64.0, bbox.yMin / 64.0, (bbox.xMax - bbox.xMin) / 64.0, (bbox.yMax - bbox.yMin) / 64.0); } -(NSPoint) positionOfGlyph: (NSGlyph)g precededByGlyph: (NSGlyph)prev isNominal: (BOOL *)nominal { NSPoint a; FT_Face face; FT_Vector vec; FT_GlyphSlot glyph; if (nominal) *nominal = YES; if (g == NSControlGlyph || prev == NSControlGlyph) return NSZeroPoint; g--; prev--; if (FTC_Manager_Lookup_Size(ftc_manager, &imgd.font, &face, 0)) return NSZeroPoint; if (FT_Load_Glyph(face, prev, FT_LOAD_DEFAULT)) return NSZeroPoint; glyph = face->glyph; a = NSMakePoint(glyph->advance.x / 64.0, glyph->advance.y / 64.0); if (FT_Get_Kerning(face, prev, g, ft_kerning_default, &vec)) return a; if (vec.x == 0 && vec.y == 0) return a; if (nominal) *nominal = NO; a.x += vec.x / 64.0; a.y += vec.y / 64.0; return a; } - (CGFloat) widthOfString: (NSString*)string { unichar ch; int i, c = [string length]; int total; FTC_CMapDescRec cmap; unsigned int glyph; FTC_SBit sbit; #ifdef FT212_STUFF FTC_ImageDesc *cur; #else FTC_ImageTypeRec *cur; #endif cmap.face_id = imgd.font.face_id; cmap.u.encoding = ft_encoding_unicode; cmap.type = FTC_CMAP_BY_ENCODING; total = 0; for (i = 0; i < c; i++) { ch = [string characterAtIndex: i]; cur = &imgd; glyph = FTC_CMapCache_Lookup(ftc_cmapcache, &cmap, ch); /* TODO: shouldn't use sbit cache for this */ if (1) { if (FTC_SBitCache_Lookup(ftc_sbitcache, cur, glyph, &sbit, NULL)) continue; total += sbit->xadvance; } else { NSLog(@"non-sbit code not implemented"); } } return total; } -(NSGlyph) glyphWithName: (NSString *)glyphName { FT_Face face; NSGlyph g; if (FTC_Manager_Lookup_Size(ftc_manager, &imgd.font, &face, 0)) return NSNullGlyph; g = FT_Get_Name_Index(face, (FT_String *)[glyphName lossyCString]); if (g) return g + 1; return NSNullGlyph; } /* conic: (a,b,c) p=(1-t)^2*a + 2*(1-t)*t*b + t^2*c cubic: (a,b,c,d) p=(1-t)^3*a + 3*(1-t)^2*t*b + 3*(1-t)*t^2*c + t^3*d p(t)=(1-t)^3*a + 3*(1-t)^2*t*b + 3*(1-t)*t^2*c + t^3*d t=m+ns= n=l-m q(s)=p(m+ns)= (d-3c+3b-a)*n^3 * s^3 + ((3d-9c+9b-3a)*m+3c-6b+3a)*n^2 * s^2 + ((3d-9c+9b-3a)*m^2+(6c-12b+6a)*m+3b-3a)*n * s + (d-3c+3b-a)*m^3+(3c-6b+3a)*m^2+(3b-3a)m+a q(t)=(1-t)^3*aa + 3*(1-t)^2*t*bb + 3*(1-t)*t^2*cc + t^3*dd = (dd-3cc+3bb-aa)*t^3 + (3cc-6bb+3aa)*t^2 + (3bb-3aa)*t + aa aa = (d-3*c+3*b-a)*m^3+(3*c-6*b+3*a)*m^2+(3*b-3*a)*m+a 3*bb-3*aa = ((3*d-9*c+9*b-3*a)*m^2+(6*c-12*b+6*a)*m+3*b-3*a)*n 3*cc-6*bb+3*aa = ((3*d-9*c+9*b-3*a)*m+3*c-6*b+3*a)*n^2 dd-3*cc+3*bb-aa = (d-3*c+3*b-a)*n^3 aa= (d - 3c + 3b - a) m^3 + (3c - 6b + 3a) m^2 + (3b - 3a) m + a bb= ((d - 3c + 3b - a) m^2 + (2c - 4b + 2a) m + b - a) n + aa cc= ((d - 3c + 3b - a) m + c - 2b + a) n^2 + 2*bb + aa dd= (d - 3c + 3b - a) n^3 + 3*cc + 3*bb + aa p(t) = (1-t)^2*e + 2*(1-t)*t*f + t^2*g ~= q(t) = (1-t)^3*a + 3*(1-t)^2*t*b + 3*(1-t)*t^2*c + t^3*d p(0)=q(0) && p(1)=q(1) -> a=e d=g p(0.5) = 1/8*(2a + 4f + 2d) q(0.5) = 1/8*(a + 3*b + 3*c + d) b+c=1/3*(a+4f+d) p(1/4) = 1/64* p(3/4) = 1/64*(4e+24f+36g) q(1/4) = 1/64* q(3/4) = 1/64*(a + 9b + 27c + 27d) 3b+c=1/3*(3a+8f+d) 3b+c=1/3*(3a+8f+d) b+c=1/3*(a+4f+d) b=1/3*(e+2f) c=1/3*(2f+g) q(t) = (1-t)^3*e + (1-t)^2*t*(e+2f) + (1-t)*t^2*(2f+g) + t^3*g = ((1-t)^3+(1-t)^2*t)*e + (1-t)^2*t*2f + (1-t)*t^2*2f + (t^3+(1-t)*t^2)*g = ((1-t)^3+(1-t)^2*t)*e + 2f*(t*(1-t)*((1-t)+t)) + (t^3+(1-t)*t^2)*g = ((1-t)^3+(1-t)^2*t)*e + 2*(1-t)*t*f + (t^3+(1-t)*t^2)*g = (1-t)^2*e + 2*(1-t)*t*f + t^2*g p(t)=q(t) */ /* TODO: try to combine charpath and NSBezierPath handling? */ static int charpath_move_to(FT_Vector *to, void *user) { GSGState *self = (GSGState *)user; NSPoint d; d.x = to->x / 65536.0; d.y = to->y / 65536.0; [self DPSclosepath]; /* TODO: this isn't completely correct */ [self DPSmoveto: d.x:d.y]; return 0; } static int charpath_line_to(FT_Vector *to, void *user) { GSGState *self = (GSGState *)user; NSPoint d; d.x = to->x / 65536.0; d.y = to->y / 65536.0; [self DPSlineto: d.x:d.y]; return 0; } static int charpath_conic_to(FT_Vector *c1, FT_Vector *to, void *user) { GSGState *self = (GSGState *)user; NSPoint a, b, c, d; [self DPScurrentpoint: &a.x:&a.y]; d.x = to->x / 65536.0; d.y = to->y / 65536.0; b.x = c1->x / 65536.0; b.y = c1->y / 65536.0; c.x = (b.x * 2 + d.x) / 3.0; c.y = (b.y * 2 + d.y) / 3.0; b.x = (b.x * 2 + a.x) / 3.0; b.y = (b.y * 2 + a.y) / 3.0; [self DPScurveto: b.x:b.y : c.x:c.y : d.x:d.y]; return 0; } static int charpath_cubic_to(FT_Vector *c1, FT_Vector *c2, FT_Vector *to, void *user) { GSGState *self = (GSGState *)user; NSPoint b, c, d; b.x = c1->x / 65536.0; b.y = c1->y / 65536.0; c.x = c2->x / 65536.0; c.y = c2->y / 65536.0; d.x = to->x / 65536.0; d.y = to->y / 65536.0; [self DPScurveto: b.x:b.y : c.x:c.y : d.x:d.y]; return 0; } static FT_Outline_Funcs charpath_funcs = { move_to:charpath_move_to, line_to:charpath_line_to, conic_to:charpath_conic_to, cubic_to:charpath_cubic_to, shift:10, delta:0, }; static int bezierpath_move_to(FT_Vector *to, void *user) { NSBezierPath *path = (NSBezierPath *)user; NSPoint d; d.x = to->x / 65536.0; d.y = to->y / 65536.0; [path closePath]; /* TODO: this isn't completely correct */ [path moveToPoint: d]; return 0; } static int bezierpath_line_to(FT_Vector *to, void *user) { NSBezierPath *path = (NSBezierPath *)user; NSPoint d; d.x = to->x / 65536.0; d.y = to->y / 65536.0; [path lineToPoint: d]; return 0; } static int bezierpath_conic_to(FT_Vector *c1, FT_Vector *to, void *user) { NSBezierPath *path = (NSBezierPath *)user; NSPoint a, b, c, d; a = [path currentPoint]; d.x = to->x / 65536.0; d.y = to->y / 65536.0; b.x = c1->x / 65536.0; b.y = c1->y / 65536.0; c.x = (b.x * 2 + d.x) / 3.0; c.y = (b.y * 2 + d.y) / 3.0; b.x = (b.x * 2 + a.x) / 3.0; b.y = (b.y * 2 + a.y) / 3.0; [path curveToPoint: d controlPoint1: b controlPoint2: c]; return 0; } static int bezierpath_cubic_to(FT_Vector *c1, FT_Vector *c2, FT_Vector *to, void *user) { NSBezierPath *path = (NSBezierPath *)user; NSPoint b, c, d; b.x = c1->x / 65536.0; b.y = c1->y / 65536.0; c.x = c2->x / 65536.0; c.y = c2->y / 65536.0; d.x = to->x / 65536.0; d.y = to->y / 65536.0; [path curveToPoint: d controlPoint1: b controlPoint2: c]; return 0; } static FT_Outline_Funcs bezierpath_funcs = { move_to:bezierpath_move_to, line_to:bezierpath_line_to, conic_to:bezierpath_conic_to, cubic_to:bezierpath_cubic_to, shift:10, delta:0, }; /* TODO: sometimes gets 'glyph transformation failed', probably need to add code to avoid loading bitmaps for glyphs */ -(void) outlineString: (const char *)s at: (float)x : (float)y gstate: (void *)func_param { unichar *c; int i; FTC_CMapDescRec cmap; unsigned int glyph; unichar *uch; int ulen; #ifdef FT212_STUFF FTC_ImageDesc cur; #else FTC_ImageTypeRec cur; #endif FT_Matrix ftmatrix; FT_Vector ftdelta; ftmatrix.xx = 65536; ftmatrix.xy = 0; ftmatrix.yx = 0; ftmatrix.yy = 65536; ftdelta.x = x * 64.0; ftdelta.y = y * 64.0; uch = NULL; ulen = 0; GSToUnicode(&uch, &ulen, s, strlen(s), NSUTF8StringEncoding, NSDefaultMallocZone(), 0); cur = imgd; cmap.face_id = imgd.font.face_id; cmap.u.encoding = ft_encoding_unicode; cmap.type = FTC_CMAP_BY_ENCODING; for (c = uch, i = 0; i < ulen; i++, c++) { FT_Face face; FT_Glyph gl; FT_OutlineGlyph og; glyph = FTC_CMapCache_Lookup(ftc_cmapcache, &cmap, *c); cur.font.face_id = imgd.font.face_id; if (FTC_Manager_Lookup_Size(ftc_manager, &cur.font, &face, 0)) continue; if (FT_Load_Glyph(face, glyph, FT_LOAD_DEFAULT)) continue; if (FT_Get_Glyph(face->glyph, &gl)) continue; if (FT_Glyph_Transform(gl, &ftmatrix, &ftdelta)) { NSLog(@"glyph transformation failed!"); continue; } og = (FT_OutlineGlyph)gl; ftdelta.x += gl->advance.x >> 10; ftdelta.y += gl->advance.y >> 10; FT_Outline_Decompose(&og->outline, &charpath_funcs, func_param); FT_Done_Glyph(gl); } if (ulen) { [(GSGState *)func_param DPSmoveto: ftdelta.x / 64.0 : ftdelta.y / 64.0]; } free(uch); } -(void) appendBezierPathWithGlyphs: (NSGlyph *)glyphs count: (int)count toBezierPath: (NSBezierPath *)path { int i; NSGlyph glyph; FT_Matrix ftmatrix; FT_Vector ftdelta; NSPoint p = [path currentPoint]; ftmatrix.xx = 65536; ftmatrix.xy = 0; ftmatrix.yx = 0; ftmatrix.yy = 65536; ftdelta.x = p.x * 64.0; ftdelta.y = p.y * 64.0; for (i = 0; i < count; i++, glyphs++) { FT_Face face; FT_Glyph gl; FT_OutlineGlyph og; glyph = *glyphs - 1; if (FTC_Manager_Lookup_Size(ftc_manager, &imgd.font, &face, 0)) continue; if (FT_Load_Glyph(face, glyph, FT_LOAD_DEFAULT)) continue; if (FT_Get_Glyph(face->glyph, &gl)) continue; if (FT_Glyph_Transform(gl, &ftmatrix, &ftdelta)) { NSLog(@"glyph transformation failed!"); continue; } og = (FT_OutlineGlyph)gl; ftdelta.x += gl->advance.x >> 10; ftdelta.y += gl->advance.y >> 10; FT_Outline_Decompose(&og->outline, &bezierpath_funcs, path); FT_Done_Glyph(gl); } if (count) { [path moveToPoint: NSMakePoint(ftdelta.x / 64.0, ftdelta.y / 64.0)]; } } static int filters[3][7]= { { 0*65536/9, 1*65536/9, 2*65536/9, 3*65536/9, 2*65536/9, 1*65536/9, 0*65536/9}, { 0*65536/9, 1*65536/9, 2*65536/9, 3*65536/9, 2*65536/9, 1*65536/9, 0*65536/9}, { 0*65536/9, 1*65536/9, 2*65536/9, 3*65536/9, 2*65536/9, 1*65536/9, 0*65536/9} }; +(void) initializeBackend { [GSFontEnumerator setDefaultClass: [FTFontEnumerator class]]; [GSFontInfo setDefaultClass: [FTFontInfo class]]; if (FT_Init_FreeType(&ft_library)) NSLog(@"FT_Init_FreeType failed"); if (FTC_Manager_New(ft_library, 0, 0, 4096 * 24, ft_get_face, 0, &ftc_manager)) NSLog(@"FTC_Manager_New failed"); if (FTC_SBitCache_New(ftc_manager, &ftc_sbitcache)) NSLog(@"FTC_SBitCache_New failed"); if (FTC_ImageCache_New(ftc_manager, &ftc_imagecache)) NSLog(@"FTC_ImageCache_New failed"); if (FTC_CMapCache_New(ftc_manager, &ftc_cmapcache)) NSLog(@"FTC_CMapCache_New failed"); { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; NSString *s; NSArray *a; int i; subpixel_text = [ud integerForKey: @"back-art-subpixel-text"]; /* To make it easier to find an optimal (or at least good) filter, the filters are configurable (for now). */ for (i = 0; i < 3; i++) { s = [ud stringForKey: [NSString stringWithFormat: @"back-art-subpixel-filter-%i",i]]; if (s) { int j, c, sum, v; a = [s componentsSeparatedByString: @" "]; c = [a count]; if (!c) continue; if (!(c & 1) || c > 7) { NSLog(@"invalid number of components in filter (must be odd number, 1<=n<=7)"); continue; } memset(filters[i], 0, sizeof(filters[0])); sum = 0; for (j = 0; j < c; j++) { v = [[a objectAtIndex: j] intValue]; sum += v; filters[i][j + (7 - c) / 2] = v * 65536; } if (sum) { for (j = 0; j < 7; j++) { filters[i][j] /= sum; } } NSLog(@"filter %i: %04x %04x %04x %04x %04x %04x %04x", i, filters[i][0],filters[i][1],filters[i][2],filters[i][3], filters[i][4],filters[i][5],filters[i][6]); } } } } -(NSGlyph) glyphForCharacter: (unichar)ch { NSGlyph g; FTC_CMapDescRec cmap; cmap.face_id = imgd.font.face_id; cmap.u.encoding = ft_encoding_unicode; cmap.type = FTC_CMAP_BY_ENCODING; g = FTC_CMapCache_Lookup(ftc_cmapcache, &cmap, ch); if (g) return g + 1; else return NSNullGlyph; } - (NSMultibyteGlyphPacking) glyphPacking { return NSFourByteGlyphPacking; } @end /* TODO: this whole thing needs cleaning up */ @implementation FTFontInfo_subpixel -(void) drawGlyphs: (const NSGlyph *)glyphs : (int)length 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: (struct draw_info_s *)di { FTC_CMapDescRec cmap; unsigned int glyph; int use_sbit; FTC_SBit sbit; #ifdef FT212_STUFF FTC_ImageDesc cur; #else FTC_ImageTypeRec cur; #endif FT_Matrix ftmatrix; FT_Vector ftdelta; NSAffineTransformStruct ts; 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; ts = [transform transformStruct]; /* 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] * ts.m11 + matrix[1] * ts.m21; yx = matrix[0] * ts.m12 + matrix[1] * ts.m22; xy = matrix[2] * ts.m11 + matrix[3] * ts.m21; yy = matrix[2] * ts.m12 + matrix[3] * ts.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*/ #ifdef FT212_STUFF cur.type = ftc_image_grays, subpixel = YES, cur.font.pix_width *= 3, x *= 3; #else cur.flags = FT_LOAD_TARGET_LCD, subpixel = YES; #endif // 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 (; length; length--, glyphs++) { glyph = *glyphs - 1; if (use_sbit) { if (FTC_SBitCache_Lookup(ftc_sbitcache, &cur, glyph, &sbit, NULL)) continue; if (!sbit->buffer) { x += sbit->xadvance; continue; } #ifdef FT212_STUFF if (sbit->format == ft_pixel_mode_grays) #else if (sbit->format == FT_PIXEL_MODE_LCD) #endif { #ifdef FT212_STUFF int gx = x + sbit->left, gy = y - sbit->top; #else int gx = 3 * x + sbit->left, gy = y - sbit->top; #endif 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; int v0, v1, v2; for (i = 0, j = -llip; i < psx * 3; i+=3) { v0 = (0 + + (j > 2 && j 1 && j 0 && j -1 && j -2 && j -3 && j -4 && j 2 && j 1 && j 0 && j -1 && j -2 && j -3 && j -4 && j 2 && j 1 && j 0 && j -1 && j -2 && j -3 && j -4 && j0?v0:0; scratch[i + 1] = v1>0?v1:0; scratch[i + (mode ^ 2)] = v2>0?v2:0; } 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 @interface FTFontInfo (experimental_glyph_printing_extension) -(const char *) nameOfGlyph: (NSGlyph)g; @end @implementation FTFontInfo (experimental_glyph_printing_extension) -(const char *) nameOfGlyph: (NSGlyph)g { static char buf[1024]; /* !!TODO!! */ FT_Face face; g--; if (FTC_Manager_Lookup_Size(ftc_manager, &imgd.font, &face, 0)) return ".notdef"; if (FT_Get_Glyph_Name(face, g, buf, sizeof(buf))) return ".notdef"; return buf; } @end