From 6a12d167508ec8fb8747e480a12813dbef421586 Mon Sep 17 00:00:00 2001 From: Matthias Melcher Date: Sun, 24 Jan 2016 20:58:12 +0000 Subject: Extracting OpenGL text calls. This is a minimum implementation for testing. Don;t worry. I have a cunning plan for rendering perfect antialiased text into OpenGL contexts quickly on all platforms. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3-porting@11048 ea41ed52-d2ee-0310-a9c1-e6b18d33e121 --- src/Fl_Gl_Device_Plugin.cxx | 1 + src/cfg_gfx/opengl.H | 7 +- src/cfg_gfx/opengl_font.cxx | 133 ++++++++++ src/cfg_gfx/opengl_rect.cxx | 4 - src/cfg_gfx/quartz.H | 24 +- src/cfg_gfx/quartz_font.cxx | 586 ++++++++++++++++++++++++++++++++++++++++++++ src/fl_font.cxx | 2 +- src/fl_font_mac.cxx | 586 -------------------------------------------- 8 files changed, 739 insertions(+), 604 deletions(-) create mode 100644 src/cfg_gfx/opengl_font.cxx create mode 100644 src/cfg_gfx/quartz_font.cxx delete mode 100644 src/fl_font_mac.cxx (limited to 'src') diff --git a/src/Fl_Gl_Device_Plugin.cxx b/src/Fl_Gl_Device_Plugin.cxx index 12430df07..a21aef2b0 100644 --- a/src/Fl_Gl_Device_Plugin.cxx +++ b/src/Fl_Gl_Device_Plugin.cxx @@ -55,6 +55,7 @@ const char *Fl_OpenGL_Display_Device::class_id = "Fl_OpenGL_Display_Device"; #include "cfg_gfx/opengl_arci.cxx" #include "cfg_gfx/opengl_color.cxx" +#include "cfg_gfx/opengl_font.cxx" #include "cfg_gfx/opengl_line_style.cxx" #include "cfg_gfx/opengl_rect.cxx" #include "cfg_gfx/opengl_vertex.cxx" diff --git a/src/cfg_gfx/opengl.H b/src/cfg_gfx/opengl.H index dc25d32ec..48944fcb8 100644 --- a/src/cfg_gfx/opengl.H +++ b/src/cfg_gfx/opengl.H @@ -35,7 +35,6 @@ class FL_EXPORT Fl_OpenGL_Graphics_Driver : public Fl_Graphics_Driver { public: static const char *class_id; const char *class_name() {return class_id;}; - void draw(const char* str, int n, int x, int y); // --- line and polygon drawing with integer coordinates void point(int x, int y); void rect(int x, int y, int w, int h); @@ -86,6 +85,12 @@ public: void color(Fl_Color c); Fl_Color color() { return color_; } void color(uchar r, uchar g, uchar b); + // --- implementation is in src/fl_font.cxx which includes src/cfg_gfx/xxx_font.cxx + void draw(const char *str, int n, int x, int y); + double width(const char *str, int n); + void text_extents(const char*, int n, int& dx, int& dy, int& w, int& h); + int height(); + int descent(); }; diff --git a/src/cfg_gfx/opengl_font.cxx b/src/cfg_gfx/opengl_font.cxx new file mode 100644 index 000000000..a42b92394 --- /dev/null +++ b/src/cfg_gfx/opengl_font.cxx @@ -0,0 +1,133 @@ +// +// "$Id$" +// +// Standard X11 font selection code for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2016 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// http://www.fltk.org/COPYING.php +// +// Please report all bugs and problems on the following page: +// +// http://www.fltk.org/str.php +// + +/* + This module implements a lowest-common-denominator font for OpenGL. + It will always work, even if the main graphics library does not support + rendering text into a texture buffer. + + The font is limited to a single face and ASCII characters. It is drawn using + lines which makes it arbitrarily scalable. I am trying to keep font data really + compact. + */ + + +#include + +/* + |01234567| + -+--------+ + 0| |____ + 1|++++++++|font + 2|++++++++| + 3|++++++++| + 4|++++++++| + 5|++++++++|____ + 6| |descent + 7| | + -+--------+ + */ + + +static const char *font_data[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, /*T*/"\11\71\100\41\45", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, /*e*/"\55\25\14\13\22\52\63\64\14", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, /*s*/"\62\22\13\64\55\15", /*t*/"\41\44\55\65\100\22\62", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + + +#if 0 + +void Fl_OpenGL_Graphics_Driver::draw(const char* str, int n, int x, int y) { + gl_draw(str, n, x, y); +} + +#else + +double Fl_OpenGL_Graphics_Driver::width(const char *str, int n) { + return size_*n*0.5; +} + +int Fl_OpenGL_Graphics_Driver::descent() { + return (int)(size_ - size_*0.8); +} + +int Fl_OpenGL_Graphics_Driver::height() { + return (int)(size_*0.8); +} + +void Fl_OpenGL_Graphics_Driver::text_extents(const char *str, int n, int& dx, int& dy, int& w, int& h) +{ + dx = 0; + dy = descent(); + w = width(str, n); + h = size_; +} + +void Fl_OpenGL_Graphics_Driver::draw(const char *str, int n, int x, int y) +{ + int i; + for (i=0; i63) { + if (cmd=='\100' && rendering) { + glEnd(); + glBegin(GL_POINTS); glVertex2f(px, py); glEnd(); + rendering = 0; + } + } else { + if (!rendering) { glBegin(GL_LINE_STRIP); rendering = 1; } + int vx = (cmd & '\70')>>3; + int vy = (cmd & '\07'); + px = 0.5+x+vx*size_*0.5/8.0; + py = 0.5+y+vy*size_/8.0-0.8*size_; + glVertex2f(px, py); + } + } + } + x += size_*0.5; + } +} + +#endif + + +// +// End of "$Id$". +// diff --git a/src/cfg_gfx/opengl_rect.cxx b/src/cfg_gfx/opengl_rect.cxx index ff10474a1..b1617fcd3 100644 --- a/src/cfg_gfx/opengl_rect.cxx +++ b/src/cfg_gfx/opengl_rect.cxx @@ -28,10 +28,6 @@ #include #include "opengl.H" -void Fl_OpenGL_Graphics_Driver::draw(const char* str, int n, int x, int y) { - gl_draw(str, n, x, y); -} - // --- line and polygon drawing with integer coordinates void Fl_OpenGL_Graphics_Driver::point(int x, int y) { diff --git a/src/cfg_gfx/quartz.H b/src/cfg_gfx/quartz.H index 6a6c5f337..ae597fe13 100644 --- a/src/cfg_gfx/quartz.H +++ b/src/cfg_gfx/quartz.H @@ -43,13 +43,7 @@ class FL_EXPORT Fl_Quartz_Graphics_Driver : public Fl_Graphics_Driver { public: static const char *class_id; const char *class_name() {return class_id;}; - void draw(const char* str, int n, int x, int y); -#ifdef __APPLE__ - void draw(const char *str, int n, float x, float y); -#endif - void draw(int angle, const char *str, int n, int x, int y); - void rtl_draw(const char* str, int n, int x, int y); - void font(Fl_Font face, Fl_Fontsize size); + void draw(Fl_Pixmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy); void draw(Fl_Bitmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy); void draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy); @@ -58,11 +52,6 @@ public: void draw_image(Fl_Draw_Image_Cb cb, void* data, int X,int Y,int W,int H, int D=3); void draw_image_mono(const uchar* buf, int X,int Y,int W,int H, int D=1, int L=0); void draw_image_mono(Fl_Draw_Image_Cb cb, void* data, int X,int Y,int W,int H, int D=1); - double width(const char *str, int n); - double width(unsigned int c); - void text_extents(const char*, int n, int& dx, int& dy, int& w, int& h); - int height(); - int descent(); #if ! defined(FL_DOXYGEN) static Fl_Offscreen create_offscreen_with_alpha(int w, int h); #endif @@ -113,6 +102,17 @@ protected: void color(Fl_Color c); Fl_Color color() { return color_; } void color(uchar r, uchar g, uchar b); + // --- implementation is in src/fl_font.cxx which includes src/cfg_gfx/xxx_font.cxx + void draw(const char *str, int n, int x, int y); + void draw(const char *str, int n, float x, float y); + void draw(int angle, const char *str, int n, int x, int y); + void rtl_draw(const char *str, int n, int x, int y); + void font(Fl_Font face, Fl_Fontsize fsize); + double width(const char *str, int n); + double width(unsigned int c); + void text_extents(const char*, int n, int& dx, int& dy, int& w, int& h); + int height(); + int descent(); }; diff --git a/src/cfg_gfx/quartz_font.cxx b/src/cfg_gfx/quartz_font.cxx new file mode 100644 index 000000000..84a2a707e --- /dev/null +++ b/src/cfg_gfx/quartz_font.cxx @@ -0,0 +1,586 @@ +// +// "$Id$" +// +// MacOS font selection routines for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2016 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// http://www.fltk.org/COPYING.php +// +// Please report all bugs and problems on the following page: +// +// http://www.fltk.org/str.php +// + +#include +#include + +Fl_Fontdesc* fl_fonts = NULL; + +/* from fl_utf.c */ +extern unsigned fl_utf8toUtf16(const char* src, unsigned srclen, unsigned short* dst, unsigned dstlen); + +static CGAffineTransform font_mx = { 1, 0, 0, -1, 0, 0 }; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +static CFMutableDictionaryRef attributes = NULL; +#endif + +const int Fl_X::CoreText_threshold = 100500; // this represents Mac OS 10.5 +// condition when the ATSU API is available at compile time +#define HAS_ATSU (!__LP64__) && MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11 + +Fl_Font_Descriptor::Fl_Font_Descriptor(const char* name, Fl_Fontsize Size) { + next = 0; +# if HAVE_GL + listbase = 0; +# endif + +// knowWidths = 0; + // OpenGL needs those for its font handling + size = Size; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +if (fl_mac_os_version >= Fl_X::CoreText_threshold) { + CFStringRef str = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); + fontref = CTFontCreateWithName(str, size, NULL); + CGGlyph glyph[2]; + const UniChar A[2]={'W','.'}; + CTFontGetGlyphsForCharacters(fontref, A, glyph, 2); + CGSize advances[2]; + double w; + CTFontGetAdvancesForGlyphs(fontref, kCTFontHorizontalOrientation, glyph, advances, 2); + w = advances[0].width; + if ( fabs(advances[0].width - advances[1].width) < 1E-2 ) {//this is a fixed-width font + // slightly rescale fixed-width fonts so the character width has an integral value + CFRelease(fontref); + CGFloat fsize = size / ( w/floor(w + 0.5) ); + fontref = CTFontCreateWithName(str, fsize, NULL); + w = CTFontGetAdvancesForGlyphs(fontref, kCTFontHorizontalOrientation, glyph, NULL, 1); + } + CFRelease(str); + ascent = (short)(CTFontGetAscent(fontref) + 0.5); + descent = (short)(CTFontGetDescent(fontref) + 0.5); + q_width = w + 0.5; + for (unsigned i = 0; i < sizeof(width)/sizeof(float*); i++) width[i] = NULL; + if (!attributes) { + static CFNumberRef zero_ref; + float zero = 0.; + zero_ref = CFNumberCreate(NULL, kCFNumberFloat32Type, &zero); + // deactivate kerning for all fonts, so that string width = sum of character widths + // which allows fast fl_width() implementation. + attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, + 3, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue (attributes, kCTKernAttributeName, zero_ref); + } + if (ascent == 0) { // this may happen with some third party fonts + CFDictionarySetValue (attributes, kCTFontAttributeName, fontref); + CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, CFSTR("Wj"), attributes); + CTLineRef ctline = CTLineCreateWithAttributedString(mastr); + CFRelease(mastr); + CGFloat fascent, fdescent; + CTLineGetTypographicBounds(ctline, &fascent, &fdescent, NULL); + CFRelease(ctline); + ascent = (short)(fascent + 0.5); + descent = (short)(fdescent + 0.5); + } +} +else { +#endif +#if HAS_ATSU + OSStatus err; + // fill our structure with a few default values + ascent = Size*3/4.; + descent = Size-ascent; + q_width = Size*2/3.; + // now we allocate everything needed to render text in this font later + // get us the default layout and style + err = ATSUCreateTextLayout(&layout); + UniChar mTxt[2] = { 65, 0 }; + err = ATSUSetTextPointerLocation(layout, mTxt, kATSUFromTextBeginning, 1, 1); + err = ATSUCreateStyle(&style); + err = ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd); + // now set the actual font, size and attributes. We also set the font matrix to + // render our font up-side-down, so when rendered through our inverted CGContext, + // text will appear normal again. + Fixed fsize = IntToFixed(Size); + ATSUFontID fontID; + ATSUFindFontFromName(name, strlen(name), kFontFullName, kFontMacintoshPlatform, kFontNoScriptCode, kFontEnglishLanguage, &fontID); + + // draw the font upside-down... Compensate for fltk/OSX origin differences + ATSUAttributeTag sTag[] = { kATSUFontTag, kATSUSizeTag, kATSUFontMatrixTag }; + ByteCount sBytes[] = { sizeof(ATSUFontID), sizeof(Fixed), sizeof(CGAffineTransform) }; + ATSUAttributeValuePtr sAttr[] = { &fontID, &fsize, &font_mx }; + if (fontID != kATSUInvalidFontID) err = ATSUSetAttributes(style, 1, sTag, sBytes, sAttr); // set the font attribute + err = ATSUSetAttributes(style, 2, sTag + 1, sBytes + 1, sAttr + 1); // then the size and matrix attributes + // next, make sure that Quartz will only render at integer coordinates + ATSLineLayoutOptions llo = kATSLineUseDeviceMetrics | kATSLineDisableAllLayoutOperations; + ATSUAttributeTag aTag[] = { kATSULineLayoutOptionsTag }; + ByteCount aBytes[] = { sizeof(ATSLineLayoutOptions) }; + ATSUAttributeValuePtr aAttr[] = { &llo }; + err = ATSUSetLineControls (layout, kATSUFromTextBeginning, 1, aTag, aBytes, aAttr); + // now we are finally ready to measure some letter to get the bounding box + Fixed bBefore, bAfter, bAscent, bDescent; + err = ATSUGetUnjustifiedBounds(layout, kATSUFromTextBeginning, 1, &bBefore, &bAfter, &bAscent, &bDescent); + // Requesting a certain height font on Mac does not guarantee that ascent+descent + // equal the requested height. fl_height will reflect the actual height that we got. + // The font "Apple Chancery" is a pretty extreme example of overlapping letters. + float fa = -FixedToFloat(bAscent), fd = -FixedToFloat(bDescent); + if (fa>0.0f && fd>0.0f) { + //float f = Size/(fa+fd); + ascent = int(fa); //int(fa*f+0.5f); + descent = int(fd); //Size - ascent; + } + int w = FixedToInt(bAfter); + if (w) + q_width = FixedToInt(bAfter); + +# define ENABLE_TRANSIENT_FONTS 1 + +# ifdef ENABLE_TRANSIENT_FONTS + // Now, by way of experiment, try enabling Transient Font Matching, this will + // cause ATSU to find a suitable font to render any chars the current font can't do... + ATSUSetTransientFontMatching (layout, true); +# endif +#endif//HAS_ATSU +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + } +#endif +} + +Fl_Font_Descriptor::~Fl_Font_Descriptor() { +/* +#if HAVE_GL + // ++ todo: remove OpenGL font alocations +// Delete list created by gl_draw(). This is not done by this code +// as it will link in GL unnecessarily. There should be some kind +// of "free" routine pointer, or a subclass? +// if (listbase) { +// int base = font->min_char_or_byte2; +// int size = font->max_char_or_byte2-base+1; +// int base = 0; int size = 256; +// glDeleteLists(listbase+base,size); +// } +#endif + */ + if (this == fl_graphics_driver->font_descriptor()) fl_graphics_driver->font_descriptor(NULL); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if (fl_mac_os_version >= Fl_X::CoreText_threshold) { + CFRelease(fontref); + for (unsigned i = 0; i < sizeof(width)/sizeof(float*); i++) { + if (width[i]) free(width[i]); + } + } +#endif +} + +//////////////////////////////////////////////////////////////// + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +static Fl_Fontdesc built_in_table_PS[] = { // PostScript font names preferred when Mac OS ≥ 10.5 +{"ArialMT"}, +{"Arial-BoldMT"}, +{"Arial-ItalicMT"}, +{"Arial-BoldItalicMT"}, +{"Courier"}, +{"Courier-Bold"}, +{"Courier-Oblique"}, +{"Courier-BoldOblique"}, +{"TimesNewRomanPSMT"}, +{"TimesNewRomanPS-BoldMT"}, +{"TimesNewRomanPS-ItalicMT"}, +{"TimesNewRomanPS-BoldItalicMT"}, +{"Symbol"}, +{"Monaco"}, +{"AndaleMono"}, // there is no bold Monaco font on standard Mac +{"ZapfDingbatsITC"} +}; +#endif + +static Fl_Fontdesc built_in_table_full[] = { // full font names used before 10.5 + {"Arial"}, + {"Arial Bold"}, + {"Arial Italic"}, + {"Arial Bold Italic"}, + {"Courier"}, + {"Courier Bold"}, + {"Courier New Italic"}, + {"Courier New Bold Italic"}, + {"Times New Roman"}, + {"Times New Roman Bold"}, + {"Times New Roman Italic"}, + {"Times New Roman Bold Italic"}, + {"Symbol"}, + {"Monaco"}, + {"Andale Mono"}, // there is no bold Monaco font on standard Mac + {"Webdings"} +}; + +static UniChar *utfWbuf = 0; +static unsigned utfWlen = 0; + +static UniChar *mac_Utf8_to_Utf16(const char *txt, int len, int *new_len) +{ + unsigned wlen = fl_utf8toUtf16(txt, len, (unsigned short*)utfWbuf, utfWlen); + if (wlen >= utfWlen) + { + utfWlen = wlen + 100; + if (utfWbuf) free(utfWbuf); + utfWbuf = (UniChar*)malloc((utfWlen)*sizeof(UniChar)); + wlen = fl_utf8toUtf16(txt, len, (unsigned short*)utfWbuf, utfWlen); + } + *new_len = wlen; + return utfWbuf; +} // mac_Utf8_to_Utf16 + +Fl_Fontdesc* Fl_X::calc_fl_fonts(void) +{ + if (!fl_mac_os_version) fl_mac_os_version = calc_mac_os_version(); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + return (fl_mac_os_version >= Fl_X::CoreText_threshold ? built_in_table_PS : built_in_table_full); +#else + return built_in_table_full; +#endif +} + +static Fl_Font_Descriptor* find(Fl_Font fnum, Fl_Fontsize size) { + if (!fl_fonts) fl_fonts = Fl_X::calc_fl_fonts(); + Fl_Fontdesc* s = fl_fonts+fnum; + if (!s->name) s = fl_fonts; // use 0 if fnum undefined + Fl_Font_Descriptor* f; + for (f = s->first; f; f = f->next) + if (f->size == size) return f; + f = new Fl_Font_Descriptor(s->name, size); + f->next = s->first; + s->first = f; + return f; +} + +//////////////////////////////////////////////////////////////// +// Public interface: + +void Fl_Quartz_Graphics_Driver::font(Fl_Font fnum, Fl_Fontsize size) { + if (fnum==-1) { + Fl_Graphics_Driver::font(0, 0); + return; + } + Fl_Graphics_Driver::font(fnum, size); + this->font_descriptor( find(fnum, size) ); +} + +int Fl_Quartz_Graphics_Driver::height() { + if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); + Fl_Font_Descriptor *fl_fontsize = font_descriptor(); + return fl_fontsize->ascent + fl_fontsize->descent; +} + +int Fl_Quartz_Graphics_Driver::descent() { + if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); + Fl_Font_Descriptor *fl_fontsize = font_descriptor(); + return fl_fontsize->descent+1; +} + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +// returns width of a pair of UniChar's in the surrogate range +static CGFloat surrogate_width(const UniChar *txt, Fl_Font_Descriptor *fl_fontsize) +{ + CTFontRef font2 = fl_fontsize->fontref; + bool must_release = false; + CGGlyph glyphs[2]; + bool b = CTFontGetGlyphsForCharacters(font2, txt, glyphs, 2); + CGSize a; + if(!b) { // the current font doesn't contain this char + CFStringRef str = CFStringCreateWithCharactersNoCopy(NULL, txt, 2, kCFAllocatorNull); + // find a font that contains it + font2 = CTFontCreateForString(font2, str, CFRangeMake(0,2)); + must_release = true; + CFRelease(str); + b = CTFontGetGlyphsForCharacters(font2, txt, glyphs, 2); + } + if (b) CTFontGetAdvancesForGlyphs(font2, kCTFontHorizontalOrientation, glyphs, &a, 1); + else a.width = fl_fontsize->q_width; + if(must_release) CFRelease(font2); + return a.width; +} + +static CGFloat variation_selector_width(CFStringRef str16, Fl_Font_Descriptor *fl_fontsize) +{ + CGFloat retval; + CFDictionarySetValue(attributes, kCTFontAttributeName, fl_fontsize->fontref); + CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str16, attributes); + CTLineRef ctline = CTLineCreateWithAttributedString(mastr); + CFRelease(mastr); + retval = CTLineGetOffsetForStringIndex(ctline, 2, NULL); + CFRelease(ctline); + return retval; +} +#endif + +static double fl_mac_width(const UniChar* txt, int n, Fl_Font_Descriptor *fl_fontsize) { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +if (fl_mac_os_version >= Fl_X::CoreText_threshold) { + double retval = 0; + UniChar uni; + int i; + for (i = 0; i < n; i++) { // loop over txt + uni = txt[i]; + if (uni >= 0xD800 && uni <= 0xDBFF) { // handles the surrogate range + retval += surrogate_width(&txt[i], fl_fontsize); + i++; // because a pair of UniChar's represent a single character + continue; + } + if (i+1 < n && txt[i+1] >= 0xFE00 && txt[i+1] <= 0xFE0F) { // handles variation selectors + CFStringRef substr = CFStringCreateWithCharacters(NULL, txt + i, 2); + retval += variation_selector_width(substr, fl_fontsize); + CFRelease(substr); + i++; + continue; + } + const int block = 0x10000 / (sizeof(fl_fontsize->width)/sizeof(float*)); // block size + // r: index of the character block containing uni + unsigned int r = uni >> 7; // change 7 if sizeof(width) is changed + if (!fl_fontsize->width[r]) { // this character block has not been hit yet + //fprintf(stderr,"r=%d size=%d name=%s\n",r,fl_fontsize->size,fl_fonts[fl_font()].name); + // allocate memory to hold width of each character in the block + fl_fontsize->width[r] = (float*) malloc(sizeof(float) * block); + UniChar ii = r * block; + CGSize advance_size; + CGGlyph glyph; + for (int j = 0; j < block; j++) { // loop over the block + // ii spans all characters of this block + bool b = CTFontGetGlyphsForCharacters(fl_fontsize->fontref, &ii, &glyph, 1); + if (b) + CTFontGetAdvancesForGlyphs(fl_fontsize->fontref, kCTFontHorizontalOrientation, &glyph, &advance_size, 1); + else + advance_size.width = -1e9; // calculate this later + // the width of one character of this block of characters + fl_fontsize->width[r][j] = advance_size.width; + ii++; + } + } + // sum the widths of all characters of txt + double wdt = fl_fontsize->width[r][uni & (block-1)]; + if (wdt == -1e9) { + CGSize advance_size; + CGGlyph glyph; + CTFontRef font2 = fl_fontsize->fontref; + bool must_release = false; + bool b = CTFontGetGlyphsForCharacters(font2, &uni, &glyph, 1); + if (!b) { // the current font doesn't contain this char + CFStringRef str = CFStringCreateWithCharactersNoCopy(NULL, &uni, 1, kCFAllocatorNull); + // find a font that contains it + font2 = CTFontCreateForString(font2, str, CFRangeMake(0,1)); + must_release = true; + CFRelease(str); + b = CTFontGetGlyphsForCharacters(font2, &uni, &glyph, 1); + } + if (b) CTFontGetAdvancesForGlyphs(font2, kCTFontHorizontalOrientation, &glyph, &advance_size, 1); + else advance_size.width = 0.; + // the width of the 'uni' character + wdt = fl_fontsize->width[r][uni & (block-1)] = advance_size.width; + if (must_release) CFRelease(font2); + } + retval += wdt; + } + return retval; +} else { +#endif +#if HAS_ATSU + OSStatus err; + Fixed bBefore, bAfter, bAscent, bDescent; + ATSUTextLayout layout; + ByteCount iSize; + ATSUAttributeTag iTag; + ATSUAttributeValuePtr iValuePtr; + + // Here's my ATSU text measuring attempt... This seems to do the Right Thing + // now collect our ATSU resources and measure our text string + layout = fl_fontsize->layout; + // activate the current GC + iSize = sizeof(CGContextRef); + iTag = kATSUCGContextTag; + iValuePtr = &fl_gc; + ATSUSetLayoutControls(layout, 1, &iTag, &iSize, &iValuePtr); + // now measure the bounding box + err = ATSUSetTextPointerLocation(layout, txt, kATSUFromTextBeginning, n, n); + err = ATSUGetUnjustifiedBounds(layout, kATSUFromTextBeginning, n, &bBefore, &bAfter, &bAscent, &bDescent); + // If err is OK then return length, else return 0. Or something... + int len = FixedToInt(bAfter); + return len; +#endif +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + } +#endif + return 0; +} + +double Fl_Quartz_Graphics_Driver::width(const char* txt, int n) { + int wc_len = n; + UniChar *uniStr = mac_Utf8_to_Utf16(txt, n, &wc_len); + if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); + return fl_mac_width(uniStr, wc_len, font_descriptor()); +} + +double Fl_Quartz_Graphics_Driver::width(unsigned int wc) { + if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); + + UniChar utf16[3]; + int l = 1; + if (wc <= 0xFFFF) { + *utf16 = wc; + } + else { +// char buf[4]; +// l = fl_utf8encode(wc, buf); +// l = (int)fl_utf8toUtf16(buf, l, utf16, 3); + l = (int)fl_ucs_to_Utf16(wc, utf16, 3); + } + return fl_mac_width(utf16, l, font_descriptor()); +} + +// text extent calculation +void Fl_Quartz_Graphics_Driver::text_extents(const char *str8, int n, int &dx, int &dy, int &w, int &h) { + if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); + Fl_Font_Descriptor *fl_fontsize = font_descriptor(); + UniChar *txt = mac_Utf8_to_Utf16(str8, n, &n); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +if (fl_mac_os_version >= Fl_X::CoreText_threshold) { + CFStringRef str16 = CFStringCreateWithCharactersNoCopy(NULL, txt, n, kCFAllocatorNull); + CFDictionarySetValue (attributes, kCTFontAttributeName, fl_fontsize->fontref); + CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str16, attributes); + CFRelease(str16); + CTLineRef ctline = CTLineCreateWithAttributedString(mastr); + CFRelease(mastr); + CGContextSetTextPosition(fl_gc, 0, 0); + CGContextSetShouldAntialias(fl_gc, true); + CGRect rect = CTLineGetImageBounds(ctline, fl_gc); + CGContextSetShouldAntialias(fl_gc, false); + CFRelease(ctline); + dx = floor(rect.origin.x + 0.5); + dy = floor(- rect.origin.y - rect.size.height + 0.5); + w = rect.size.width + 0.5; + h = rect.size.height + 0.5; + } +else { +#endif +#if HAS_ATSU + OSStatus err; + ATSUTextLayout layout; + ByteCount iSize; + ATSUAttributeTag iTag; + ATSUAttributeValuePtr iValuePtr; + +// Here's my ATSU text measuring attempt... This seems to do the Right Thing + // now collect our ATSU resources and measure our text string + layout = fl_fontsize->layout; + // activate the current GC + iSize = sizeof(CGContextRef); + iTag = kATSUCGContextTag; + iValuePtr = &fl_gc; + ATSUSetLayoutControls(layout, 1, &iTag, &iSize, &iValuePtr); + // now measure the bounding box + err = ATSUSetTextPointerLocation(layout, txt, kATSUFromTextBeginning, n, n); + Rect bbox; + err = ATSUMeasureTextImage(layout, kATSUFromTextBeginning, n, 0, 0, &bbox); + w = bbox.right - bbox.left; + h = bbox.bottom - bbox.top; + dx = bbox.left; + dy = -bbox.bottom; +//printf("r: %d l: %d t: %d b: %d w: %d h: %d\n", bbox.right, bbox.left, bbox.top, bbox.bottom, w, h); +#endif +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + } +#endif + return; +} // fl_text_extents + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +static CGColorRef flcolortocgcolor(Fl_Color i) +{ + uchar r, g, b; + Fl::get_color(i, r, g, b); + CGFloat components[4] = {r/255.0f, g/255.0f, b/255.0f, 1.}; + static CGColorSpaceRef cspace = NULL; + if (cspace == NULL) { + cspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + } + return CGColorCreate(cspace, components); +} +#endif + +static void fl_mac_draw(const char *str, int n, float x, float y, Fl_Graphics_Driver *driver) { + // convert to UTF-16 first + UniChar *uniStr = mac_Utf8_to_Utf16(str, n, &n); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if (fl_mac_os_version >= Fl_X::CoreText_threshold) { + CFMutableStringRef str16 = CFStringCreateMutableWithExternalCharactersNoCopy(NULL, uniStr, n, n, kCFAllocatorNull); + if (str16 == NULL) return; // shd not happen + CGColorRef color = flcolortocgcolor(driver->color()); + CFDictionarySetValue (attributes, kCTFontAttributeName, driver->font_descriptor()->fontref); + CFDictionarySetValue (attributes, kCTForegroundColorAttributeName, color); + CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str16, attributes); + CFRelease(str16); + CFRelease(color); + CTLineRef ctline = CTLineCreateWithAttributedString(mastr); + CFRelease(mastr); + CGContextSetTextMatrix(fl_gc, font_mx); + CGContextSetTextPosition(fl_gc, x, y); + CGContextSetShouldAntialias(fl_gc, true); + CTLineDraw(ctline, fl_gc); + CGContextSetShouldAntialias(fl_gc, false); + CFRelease(ctline); + } else { +#endif +#if HAS_ATSU + OSStatus err; + // now collect our ATSU resources + ATSUTextLayout layout = driver->font_descriptor()->layout; + + ByteCount iSize = sizeof(CGContextRef); + ATSUAttributeTag iTag = kATSUCGContextTag; + ATSUAttributeValuePtr iValuePtr=&fl_gc; + ATSUSetLayoutControls(layout, 1, &iTag, &iSize, &iValuePtr); + + err = ATSUSetTextPointerLocation(layout, uniStr, kATSUFromTextBeginning, n, n); + CGContextSetShouldAntialias(fl_gc, true); + err = ATSUDrawText(layout, kATSUFromTextBeginning, n, FloatToFixed(x), FloatToFixed(y)); + CGContextSetShouldAntialias(fl_gc, false); +#endif +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + } +#endif +} + +void Fl_Quartz_Graphics_Driver::draw(const char *str, int n, float x, float y) { + // avoid a crash if no font has been selected by user yet ! + if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); + fl_mac_draw(str, n, x, y, this); +} + +void Fl_Quartz_Graphics_Driver::draw(const char* str, int n, int x, int y) { + // avoid a crash if no font has been selected by user yet ! + if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); + fl_mac_draw(str, n, (float)x-0.0f, (float)y+0.5f, this); +} + +void Fl_Quartz_Graphics_Driver::draw(int angle, const char *str, int n, int x, int y) { + CGContextSaveGState(fl_gc); + CGContextTranslateCTM(fl_gc, x, y); + CGContextRotateCTM(fl_gc, - angle*(M_PI/180) ); + draw(str, n, 0, 0); + CGContextRestoreGState(fl_gc); +} + +void Fl_Quartz_Graphics_Driver::rtl_draw(const char* c, int n, int x, int y) { + int dx, dy, w, h; + text_extents(c, n, dx, dy, w, h); + draw(c, n, x - w - dx, y); +} + +// +// End of "$Id$". +// diff --git a/src/fl_font.cxx b/src/fl_font.cxx index a2e5675cb..ca7e87560 100644 --- a/src/fl_font.cxx +++ b/src/fl_font.cxx @@ -48,7 +48,7 @@ #ifdef WIN32 # include "fl_font_win32.cxx" #elif defined(__APPLE__) -# include "fl_font_mac.cxx" +# include "cfg_gfx/quartz_font.cxx" #elif USE_XFT # include "fl_font_xft.cxx" #elif defined(FL_PORTING) diff --git a/src/fl_font_mac.cxx b/src/fl_font_mac.cxx deleted file mode 100644 index 1ab6f3bf6..000000000 --- a/src/fl_font_mac.cxx +++ /dev/null @@ -1,586 +0,0 @@ -// -// "$Id$" -// -// MacOS font selection routines for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2015 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// http://www.fltk.org/COPYING.php -// -// Please report all bugs and problems on the following page: -// -// http://www.fltk.org/str.php -// - -#include -#include - -Fl_Fontdesc* fl_fonts = NULL; - -/* from fl_utf.c */ -extern unsigned fl_utf8toUtf16(const char* src, unsigned srclen, unsigned short* dst, unsigned dstlen); - -static CGAffineTransform font_mx = { 1, 0, 0, -1, 0, 0 }; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 -static CFMutableDictionaryRef attributes = NULL; -#endif - -const int Fl_X::CoreText_threshold = 100500; // this represents Mac OS 10.5 -// condition when the ATSU API is available at compile time -#define HAS_ATSU (!__LP64__) && MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11 - -Fl_Font_Descriptor::Fl_Font_Descriptor(const char* name, Fl_Fontsize Size) { - next = 0; -# if HAVE_GL - listbase = 0; -# endif - -// knowWidths = 0; - // OpenGL needs those for its font handling - size = Size; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 -if (fl_mac_os_version >= Fl_X::CoreText_threshold) { - CFStringRef str = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); - fontref = CTFontCreateWithName(str, size, NULL); - CGGlyph glyph[2]; - const UniChar A[2]={'W','.'}; - CTFontGetGlyphsForCharacters(fontref, A, glyph, 2); - CGSize advances[2]; - double w; - CTFontGetAdvancesForGlyphs(fontref, kCTFontHorizontalOrientation, glyph, advances, 2); - w = advances[0].width; - if ( fabs(advances[0].width - advances[1].width) < 1E-2 ) {//this is a fixed-width font - // slightly rescale fixed-width fonts so the character width has an integral value - CFRelease(fontref); - CGFloat fsize = size / ( w/floor(w + 0.5) ); - fontref = CTFontCreateWithName(str, fsize, NULL); - w = CTFontGetAdvancesForGlyphs(fontref, kCTFontHorizontalOrientation, glyph, NULL, 1); - } - CFRelease(str); - ascent = (short)(CTFontGetAscent(fontref) + 0.5); - descent = (short)(CTFontGetDescent(fontref) + 0.5); - q_width = w + 0.5; - for (unsigned i = 0; i < sizeof(width)/sizeof(float*); i++) width[i] = NULL; - if (!attributes) { - static CFNumberRef zero_ref; - float zero = 0.; - zero_ref = CFNumberCreate(NULL, kCFNumberFloat32Type, &zero); - // deactivate kerning for all fonts, so that string width = sum of character widths - // which allows fast fl_width() implementation. - attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, - 3, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - CFDictionarySetValue (attributes, kCTKernAttributeName, zero_ref); - } - if (ascent == 0) { // this may happen with some third party fonts - CFDictionarySetValue (attributes, kCTFontAttributeName, fontref); - CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, CFSTR("Wj"), attributes); - CTLineRef ctline = CTLineCreateWithAttributedString(mastr); - CFRelease(mastr); - CGFloat fascent, fdescent; - CTLineGetTypographicBounds(ctline, &fascent, &fdescent, NULL); - CFRelease(ctline); - ascent = (short)(fascent + 0.5); - descent = (short)(fdescent + 0.5); - } -} -else { -#endif -#if HAS_ATSU - OSStatus err; - // fill our structure with a few default values - ascent = Size*3/4.; - descent = Size-ascent; - q_width = Size*2/3.; - // now we allocate everything needed to render text in this font later - // get us the default layout and style - err = ATSUCreateTextLayout(&layout); - UniChar mTxt[2] = { 65, 0 }; - err = ATSUSetTextPointerLocation(layout, mTxt, kATSUFromTextBeginning, 1, 1); - err = ATSUCreateStyle(&style); - err = ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd); - // now set the actual font, size and attributes. We also set the font matrix to - // render our font up-side-down, so when rendered through our inverted CGContext, - // text will appear normal again. - Fixed fsize = IntToFixed(Size); - ATSUFontID fontID; - ATSUFindFontFromName(name, strlen(name), kFontFullName, kFontMacintoshPlatform, kFontNoScriptCode, kFontEnglishLanguage, &fontID); - - // draw the font upside-down... Compensate for fltk/OSX origin differences - ATSUAttributeTag sTag[] = { kATSUFontTag, kATSUSizeTag, kATSUFontMatrixTag }; - ByteCount sBytes[] = { sizeof(ATSUFontID), sizeof(Fixed), sizeof(CGAffineTransform) }; - ATSUAttributeValuePtr sAttr[] = { &fontID, &fsize, &font_mx }; - if (fontID != kATSUInvalidFontID) err = ATSUSetAttributes(style, 1, sTag, sBytes, sAttr); // set the font attribute - err = ATSUSetAttributes(style, 2, sTag + 1, sBytes + 1, sAttr + 1); // then the size and matrix attributes - // next, make sure that Quartz will only render at integer coordinates - ATSLineLayoutOptions llo = kATSLineUseDeviceMetrics | kATSLineDisableAllLayoutOperations; - ATSUAttributeTag aTag[] = { kATSULineLayoutOptionsTag }; - ByteCount aBytes[] = { sizeof(ATSLineLayoutOptions) }; - ATSUAttributeValuePtr aAttr[] = { &llo }; - err = ATSUSetLineControls (layout, kATSUFromTextBeginning, 1, aTag, aBytes, aAttr); - // now we are finally ready to measure some letter to get the bounding box - Fixed bBefore, bAfter, bAscent, bDescent; - err = ATSUGetUnjustifiedBounds(layout, kATSUFromTextBeginning, 1, &bBefore, &bAfter, &bAscent, &bDescent); - // Requesting a certain height font on Mac does not guarantee that ascent+descent - // equal the requested height. fl_height will reflect the actual height that we got. - // The font "Apple Chancery" is a pretty extreme example of overlapping letters. - float fa = -FixedToFloat(bAscent), fd = -FixedToFloat(bDescent); - if (fa>0.0f && fd>0.0f) { - //float f = Size/(fa+fd); - ascent = int(fa); //int(fa*f+0.5f); - descent = int(fd); //Size - ascent; - } - int w = FixedToInt(bAfter); - if (w) - q_width = FixedToInt(bAfter); - -# define ENABLE_TRANSIENT_FONTS 1 - -# ifdef ENABLE_TRANSIENT_FONTS - // Now, by way of experiment, try enabling Transient Font Matching, this will - // cause ATSU to find a suitable font to render any chars the current font can't do... - ATSUSetTransientFontMatching (layout, true); -# endif -#endif//HAS_ATSU -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - } -#endif -} - -Fl_Font_Descriptor::~Fl_Font_Descriptor() { -/* -#if HAVE_GL - // ++ todo: remove OpenGL font alocations -// Delete list created by gl_draw(). This is not done by this code -// as it will link in GL unnecessarily. There should be some kind -// of "free" routine pointer, or a subclass? -// if (listbase) { -// int base = font->min_char_or_byte2; -// int size = font->max_char_or_byte2-base+1; -// int base = 0; int size = 256; -// glDeleteLists(listbase+base,size); -// } -#endif - */ - if (this == fl_graphics_driver->font_descriptor()) fl_graphics_driver->font_descriptor(NULL); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - if (fl_mac_os_version >= Fl_X::CoreText_threshold) { - CFRelease(fontref); - for (unsigned i = 0; i < sizeof(width)/sizeof(float*); i++) { - if (width[i]) free(width[i]); - } - } -#endif -} - -//////////////////////////////////////////////////////////////// - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 -static Fl_Fontdesc built_in_table_PS[] = { // PostScript font names preferred when Mac OS ≥ 10.5 -{"ArialMT"}, -{"Arial-BoldMT"}, -{"Arial-ItalicMT"}, -{"Arial-BoldItalicMT"}, -{"Courier"}, -{"Courier-Bold"}, -{"Courier-Oblique"}, -{"Courier-BoldOblique"}, -{"TimesNewRomanPSMT"}, -{"TimesNewRomanPS-BoldMT"}, -{"TimesNewRomanPS-ItalicMT"}, -{"TimesNewRomanPS-BoldItalicMT"}, -{"Symbol"}, -{"Monaco"}, -{"AndaleMono"}, // there is no bold Monaco font on standard Mac -{"ZapfDingbatsITC"} -}; -#endif - -static Fl_Fontdesc built_in_table_full[] = { // full font names used before 10.5 - {"Arial"}, - {"Arial Bold"}, - {"Arial Italic"}, - {"Arial Bold Italic"}, - {"Courier"}, - {"Courier Bold"}, - {"Courier New Italic"}, - {"Courier New Bold Italic"}, - {"Times New Roman"}, - {"Times New Roman Bold"}, - {"Times New Roman Italic"}, - {"Times New Roman Bold Italic"}, - {"Symbol"}, - {"Monaco"}, - {"Andale Mono"}, // there is no bold Monaco font on standard Mac - {"Webdings"} -}; - -static UniChar *utfWbuf = 0; -static unsigned utfWlen = 0; - -static UniChar *mac_Utf8_to_Utf16(const char *txt, int len, int *new_len) -{ - unsigned wlen = fl_utf8toUtf16(txt, len, (unsigned short*)utfWbuf, utfWlen); - if (wlen >= utfWlen) - { - utfWlen = wlen + 100; - if (utfWbuf) free(utfWbuf); - utfWbuf = (UniChar*)malloc((utfWlen)*sizeof(UniChar)); - wlen = fl_utf8toUtf16(txt, len, (unsigned short*)utfWbuf, utfWlen); - } - *new_len = wlen; - return utfWbuf; -} // mac_Utf8_to_Utf16 - -Fl_Fontdesc* Fl_X::calc_fl_fonts(void) -{ - if (!fl_mac_os_version) fl_mac_os_version = calc_mac_os_version(); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - return (fl_mac_os_version >= Fl_X::CoreText_threshold ? built_in_table_PS : built_in_table_full); -#else - return built_in_table_full; -#endif -} - -static Fl_Font_Descriptor* find(Fl_Font fnum, Fl_Fontsize size) { - if (!fl_fonts) fl_fonts = Fl_X::calc_fl_fonts(); - Fl_Fontdesc* s = fl_fonts+fnum; - if (!s->name) s = fl_fonts; // use 0 if fnum undefined - Fl_Font_Descriptor* f; - for (f = s->first; f; f = f->next) - if (f->size == size) return f; - f = new Fl_Font_Descriptor(s->name, size); - f->next = s->first; - s->first = f; - return f; -} - -//////////////////////////////////////////////////////////////// -// Public interface: - -void Fl_Quartz_Graphics_Driver::font(Fl_Font fnum, Fl_Fontsize size) { - if (fnum==-1) { - Fl_Graphics_Driver::font(0, 0); - return; - } - Fl_Graphics_Driver::font(fnum, size); - this->font_descriptor( find(fnum, size) ); -} - -int Fl_Quartz_Graphics_Driver::height() { - if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); - Fl_Font_Descriptor *fl_fontsize = font_descriptor(); - return fl_fontsize->ascent + fl_fontsize->descent; -} - -int Fl_Quartz_Graphics_Driver::descent() { - if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); - Fl_Font_Descriptor *fl_fontsize = font_descriptor(); - return fl_fontsize->descent+1; -} - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 -// returns width of a pair of UniChar's in the surrogate range -static CGFloat surrogate_width(const UniChar *txt, Fl_Font_Descriptor *fl_fontsize) -{ - CTFontRef font2 = fl_fontsize->fontref; - bool must_release = false; - CGGlyph glyphs[2]; - bool b = CTFontGetGlyphsForCharacters(font2, txt, glyphs, 2); - CGSize a; - if(!b) { // the current font doesn't contain this char - CFStringRef str = CFStringCreateWithCharactersNoCopy(NULL, txt, 2, kCFAllocatorNull); - // find a font that contains it - font2 = CTFontCreateForString(font2, str, CFRangeMake(0,2)); - must_release = true; - CFRelease(str); - b = CTFontGetGlyphsForCharacters(font2, txt, glyphs, 2); - } - if (b) CTFontGetAdvancesForGlyphs(font2, kCTFontHorizontalOrientation, glyphs, &a, 1); - else a.width = fl_fontsize->q_width; - if(must_release) CFRelease(font2); - return a.width; -} - -static CGFloat variation_selector_width(CFStringRef str16, Fl_Font_Descriptor *fl_fontsize) -{ - CGFloat retval; - CFDictionarySetValue(attributes, kCTFontAttributeName, fl_fontsize->fontref); - CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str16, attributes); - CTLineRef ctline = CTLineCreateWithAttributedString(mastr); - CFRelease(mastr); - retval = CTLineGetOffsetForStringIndex(ctline, 2, NULL); - CFRelease(ctline); - return retval; -} -#endif - -static double fl_mac_width(const UniChar* txt, int n, Fl_Font_Descriptor *fl_fontsize) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 -if (fl_mac_os_version >= Fl_X::CoreText_threshold) { - double retval = 0; - UniChar uni; - int i; - for (i = 0; i < n; i++) { // loop over txt - uni = txt[i]; - if (uni >= 0xD800 && uni <= 0xDBFF) { // handles the surrogate range - retval += surrogate_width(&txt[i], fl_fontsize); - i++; // because a pair of UniChar's represent a single character - continue; - } - if (i+1 < n && txt[i+1] >= 0xFE00 && txt[i+1] <= 0xFE0F) { // handles variation selectors - CFStringRef substr = CFStringCreateWithCharacters(NULL, txt + i, 2); - retval += variation_selector_width(substr, fl_fontsize); - CFRelease(substr); - i++; - continue; - } - const int block = 0x10000 / (sizeof(fl_fontsize->width)/sizeof(float*)); // block size - // r: index of the character block containing uni - unsigned int r = uni >> 7; // change 7 if sizeof(width) is changed - if (!fl_fontsize->width[r]) { // this character block has not been hit yet - //fprintf(stderr,"r=%d size=%d name=%s\n",r,fl_fontsize->size,fl_fonts[fl_font()].name); - // allocate memory to hold width of each character in the block - fl_fontsize->width[r] = (float*) malloc(sizeof(float) * block); - UniChar ii = r * block; - CGSize advance_size; - CGGlyph glyph; - for (int j = 0; j < block; j++) { // loop over the block - // ii spans all characters of this block - bool b = CTFontGetGlyphsForCharacters(fl_fontsize->fontref, &ii, &glyph, 1); - if (b) - CTFontGetAdvancesForGlyphs(fl_fontsize->fontref, kCTFontHorizontalOrientation, &glyph, &advance_size, 1); - else - advance_size.width = -1e9; // calculate this later - // the width of one character of this block of characters - fl_fontsize->width[r][j] = advance_size.width; - ii++; - } - } - // sum the widths of all characters of txt - double wdt = fl_fontsize->width[r][uni & (block-1)]; - if (wdt == -1e9) { - CGSize advance_size; - CGGlyph glyph; - CTFontRef font2 = fl_fontsize->fontref; - bool must_release = false; - bool b = CTFontGetGlyphsForCharacters(font2, &uni, &glyph, 1); - if (!b) { // the current font doesn't contain this char - CFStringRef str = CFStringCreateWithCharactersNoCopy(NULL, &uni, 1, kCFAllocatorNull); - // find a font that contains it - font2 = CTFontCreateForString(font2, str, CFRangeMake(0,1)); - must_release = true; - CFRelease(str); - b = CTFontGetGlyphsForCharacters(font2, &uni, &glyph, 1); - } - if (b) CTFontGetAdvancesForGlyphs(font2, kCTFontHorizontalOrientation, &glyph, &advance_size, 1); - else advance_size.width = 0.; - // the width of the 'uni' character - wdt = fl_fontsize->width[r][uni & (block-1)] = advance_size.width; - if (must_release) CFRelease(font2); - } - retval += wdt; - } - return retval; -} else { -#endif -#if HAS_ATSU - OSStatus err; - Fixed bBefore, bAfter, bAscent, bDescent; - ATSUTextLayout layout; - ByteCount iSize; - ATSUAttributeTag iTag; - ATSUAttributeValuePtr iValuePtr; - - // Here's my ATSU text measuring attempt... This seems to do the Right Thing - // now collect our ATSU resources and measure our text string - layout = fl_fontsize->layout; - // activate the current GC - iSize = sizeof(CGContextRef); - iTag = kATSUCGContextTag; - iValuePtr = &fl_gc; - ATSUSetLayoutControls(layout, 1, &iTag, &iSize, &iValuePtr); - // now measure the bounding box - err = ATSUSetTextPointerLocation(layout, txt, kATSUFromTextBeginning, n, n); - err = ATSUGetUnjustifiedBounds(layout, kATSUFromTextBeginning, n, &bBefore, &bAfter, &bAscent, &bDescent); - // If err is OK then return length, else return 0. Or something... - int len = FixedToInt(bAfter); - return len; -#endif -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - } -#endif - return 0; -} - -double Fl_Quartz_Graphics_Driver::width(const char* txt, int n) { - int wc_len = n; - UniChar *uniStr = mac_Utf8_to_Utf16(txt, n, &wc_len); - if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); - return fl_mac_width(uniStr, wc_len, font_descriptor()); -} - -double Fl_Quartz_Graphics_Driver::width(unsigned int wc) { - if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); - - UniChar utf16[3]; - int l = 1; - if (wc <= 0xFFFF) { - *utf16 = wc; - } - else { -// char buf[4]; -// l = fl_utf8encode(wc, buf); -// l = (int)fl_utf8toUtf16(buf, l, utf16, 3); - l = (int)fl_ucs_to_Utf16(wc, utf16, 3); - } - return fl_mac_width(utf16, l, font_descriptor()); -} - -// text extent calculation -void Fl_Quartz_Graphics_Driver::text_extents(const char *str8, int n, int &dx, int &dy, int &w, int &h) { - if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); - Fl_Font_Descriptor *fl_fontsize = font_descriptor(); - UniChar *txt = mac_Utf8_to_Utf16(str8, n, &n); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 -if (fl_mac_os_version >= Fl_X::CoreText_threshold) { - CFStringRef str16 = CFStringCreateWithCharactersNoCopy(NULL, txt, n, kCFAllocatorNull); - CFDictionarySetValue (attributes, kCTFontAttributeName, fl_fontsize->fontref); - CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str16, attributes); - CFRelease(str16); - CTLineRef ctline = CTLineCreateWithAttributedString(mastr); - CFRelease(mastr); - CGContextSetTextPosition(fl_gc, 0, 0); - CGContextSetShouldAntialias(fl_gc, true); - CGRect rect = CTLineGetImageBounds(ctline, fl_gc); - CGContextSetShouldAntialias(fl_gc, false); - CFRelease(ctline); - dx = floor(rect.origin.x + 0.5); - dy = floor(- rect.origin.y - rect.size.height + 0.5); - w = rect.size.width + 0.5; - h = rect.size.height + 0.5; - } -else { -#endif -#if HAS_ATSU - OSStatus err; - ATSUTextLayout layout; - ByteCount iSize; - ATSUAttributeTag iTag; - ATSUAttributeValuePtr iValuePtr; - -// Here's my ATSU text measuring attempt... This seems to do the Right Thing - // now collect our ATSU resources and measure our text string - layout = fl_fontsize->layout; - // activate the current GC - iSize = sizeof(CGContextRef); - iTag = kATSUCGContextTag; - iValuePtr = &fl_gc; - ATSUSetLayoutControls(layout, 1, &iTag, &iSize, &iValuePtr); - // now measure the bounding box - err = ATSUSetTextPointerLocation(layout, txt, kATSUFromTextBeginning, n, n); - Rect bbox; - err = ATSUMeasureTextImage(layout, kATSUFromTextBeginning, n, 0, 0, &bbox); - w = bbox.right - bbox.left; - h = bbox.bottom - bbox.top; - dx = bbox.left; - dy = -bbox.bottom; -//printf("r: %d l: %d t: %d b: %d w: %d h: %d\n", bbox.right, bbox.left, bbox.top, bbox.bottom, w, h); -#endif -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - } -#endif - return; -} // fl_text_extents - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 -static CGColorRef flcolortocgcolor(Fl_Color i) -{ - uchar r, g, b; - Fl::get_color(i, r, g, b); - CGFloat components[4] = {r/255.0f, g/255.0f, b/255.0f, 1.}; - static CGColorSpaceRef cspace = NULL; - if (cspace == NULL) { - cspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - } - return CGColorCreate(cspace, components); -} -#endif - -static void fl_mac_draw(const char *str, int n, float x, float y, Fl_Graphics_Driver *driver) { - // convert to UTF-16 first - UniChar *uniStr = mac_Utf8_to_Utf16(str, n, &n); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - if (fl_mac_os_version >= Fl_X::CoreText_threshold) { - CFMutableStringRef str16 = CFStringCreateMutableWithExternalCharactersNoCopy(NULL, uniStr, n, n, kCFAllocatorNull); - if (str16 == NULL) return; // shd not happen - CGColorRef color = flcolortocgcolor(driver->color()); - CFDictionarySetValue (attributes, kCTFontAttributeName, driver->font_descriptor()->fontref); - CFDictionarySetValue (attributes, kCTForegroundColorAttributeName, color); - CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str16, attributes); - CFRelease(str16); - CFRelease(color); - CTLineRef ctline = CTLineCreateWithAttributedString(mastr); - CFRelease(mastr); - CGContextSetTextMatrix(fl_gc, font_mx); - CGContextSetTextPosition(fl_gc, x, y); - CGContextSetShouldAntialias(fl_gc, true); - CTLineDraw(ctline, fl_gc); - CGContextSetShouldAntialias(fl_gc, false); - CFRelease(ctline); - } else { -#endif -#if HAS_ATSU - OSStatus err; - // now collect our ATSU resources - ATSUTextLayout layout = driver->font_descriptor()->layout; - - ByteCount iSize = sizeof(CGContextRef); - ATSUAttributeTag iTag = kATSUCGContextTag; - ATSUAttributeValuePtr iValuePtr=&fl_gc; - ATSUSetLayoutControls(layout, 1, &iTag, &iSize, &iValuePtr); - - err = ATSUSetTextPointerLocation(layout, uniStr, kATSUFromTextBeginning, n, n); - CGContextSetShouldAntialias(fl_gc, true); - err = ATSUDrawText(layout, kATSUFromTextBeginning, n, FloatToFixed(x), FloatToFixed(y)); - CGContextSetShouldAntialias(fl_gc, false); -#endif -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - } -#endif -} - -void Fl_Quartz_Graphics_Driver::draw(const char *str, int n, float x, float y) { - // avoid a crash if no font has been selected by user yet ! - if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); - fl_mac_draw(str, n, x, y, this); -} - -void Fl_Quartz_Graphics_Driver::draw(const char* str, int n, int x, int y) { - // avoid a crash if no font has been selected by user yet ! - if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); - fl_mac_draw(str, n, (float)x-0.0f, (float)y+0.5f, this); -} - -void Fl_Quartz_Graphics_Driver::draw(int angle, const char *str, int n, int x, int y) { - CGContextSaveGState(fl_gc); - CGContextTranslateCTM(fl_gc, x, y); - CGContextRotateCTM(fl_gc, - angle*(M_PI/180) ); - draw(str, n, 0, 0); - CGContextRestoreGState(fl_gc); -} - -void Fl_Quartz_Graphics_Driver::rtl_draw(const char* c, int n, int x, int y) { - int dx, dy, w, h; - text_extents(c, n, dx, dy, w, h); - draw(c, n, x - w - dx, y); -} - -// -// End of "$Id$". -// -- cgit v1.2.3