static void InitializeLayout() { OSStatus status = ATSUCreateTextLayout(&sTextLayout); ATSUFontFallbacks fallbacks; status = ATSUCreateFontFallbacks(&fallbacks); status = ATSUSetObjFontFallbacks(fallbacks, 0, 0, kATSULastResortOnlyFallback); ATSUAttributeTag tag = kATSULineFontFallbacksTag; ByteCount valueSize = sizeof(fallbacks); ATSUAttributeValuePtr value = &fallbacks; status = ATSUSetLayoutControls(sTextLayout, 1, &tag, &valueSize, &value); status = ATSUSetTransientFontMatching(sTextLayout, true); }
PlatformFont::CharInfo& MacCarbFont::getCharInfo(const UTF16 ch) const { // We use some static data here to avoid re allocating the same variable in a loop. // this func is primarily called by GFont::loadCharInfo(), Rect imageRect; CGContextRef imageCtx; U32 bitmapDataSize; ATSUTextMeasurement tbefore, tafter, tascent, tdescent; OSStatus err; // 16 bit character buffer for the ATUSI calls. // -- hey... could we cache this at the class level, set style and loc *once*, // then just write to this buffer and clear the layout cache, to speed up drawing? static UniChar chUniChar[1]; chUniChar[0] = ch; // Declare and clear out the CharInfo that will be returned. static PlatformFont::CharInfo c; dMemset(&c, 0, sizeof(c)); // prep values for GFont::addBitmap() c.bitmapIndex = 0; c.xOffset = 0; c.yOffset = 0; // put the text in the layout. // we've hardcoded a string length of 1 here, but this could work for longer strings... (hint hint) // note: ATSUSetTextPointerLocation() also clears the previous cached layout information. ATSUSetTextPointerLocation( mLayout, chUniChar, 0, 1, 1); ATSUSetRunStyle( mLayout, mStyle, 0,1); // get the typographic bounds. this tells us how characters are placed relative to other characters. ATSUGetUnjustifiedBounds( mLayout, 0, 1, &tbefore, &tafter, &tascent, &tdescent); c.xIncrement = FixedToInt(tafter); // find out how big of a bitmap we'll need. // as a bonus, we also get the origin where we should draw, encoded in the Rect. ATSUMeasureTextImage( mLayout, 0, 1, 0, 0, &imageRect); U32 xFudge = 2; U32 yFudge = 1; c.width = imageRect.right - imageRect.left + xFudge; // add 2 because small fonts don't always have enough room c.height = imageRect.bottom - imageRect.top + yFudge; c.xOrigin = imageRect.left; // dist x0 -> center line c.yOrigin = -imageRect.top; // dist y0 -> base line // kick out early if the character is undrawable if( c.width == xFudge || c.height == yFudge) return c; // allocate a greyscale bitmap and clear it. bitmapDataSize = c.width * c.height; c.bitmapData = new U8[bitmapDataSize]; dMemset(c.bitmapData,0x00,bitmapDataSize); // get a graphics context on the bitmap imageCtx = CGBitmapContextCreate( c.bitmapData, c.width, c.height, 8, c.width, mColorSpace, kCGImageAlphaNone); if(!imageCtx) { Con::errorf("Error: failed to create a graphics context on the CharInfo bitmap! Drawing a blank block."); c.xIncrement = c.width; dMemset(c.bitmapData,0x0F,bitmapDataSize); return c; } // Turn off antialiasing for monospaced console fonts. yes, this is cheating. if(mSize < 12 && ( dStrstr(mName,"Monaco")!=NULL || dStrstr(mName,"Courier")!=NULL )) CGContextSetShouldAntialias(imageCtx, false); // Set up drawing options for the context. // Since we're not going straight to the screen, we need to adjust accordingly CGContextSetShouldSmoothFonts(imageCtx, false); CGContextSetRenderingIntent(imageCtx, kCGRenderingIntentAbsoluteColorimetric); CGContextSetInterpolationQuality( imageCtx, kCGInterpolationNone); CGContextSetGrayFillColor( imageCtx, 1.0, 1.0); CGContextSetTextDrawingMode( imageCtx, kCGTextFill); // tell ATSUI to substitute fonts as needed for missing glyphs ATSUSetTransientFontMatching(mLayout, true); // set up three parrallel arrays for setting up attributes. // this is how most options in ATSUI are set, by passing arrays of options. ATSUAttributeTag theTags[] = { kATSUCGContextTag }; ByteCount theSizes[] = { sizeof(CGContextRef) }; ATSUAttributeValuePtr theValues[] = { &imageCtx }; // bind the layout to the context. ATSUSetLayoutControls( mLayout, 1, theTags, theSizes, theValues ); // Draw the character! int yoff = c.height < 3 ? 1 : 0; // kludge for 1 pixel high characters, such as '-' and '_' int xoff = 1; err = ATSUDrawText( mLayout, 0, 1, IntToFixed(-imageRect.left + xoff), IntToFixed(imageRect.bottom + yoff ) ); CGContextRelease(imageCtx); if(err != noErr) { Con::errorf("Error: could not draw the character! Drawing a blank box."); dMemset(c.bitmapData,0x0F,bitmapDataSize); } #if TORQUE_DEBUG // Con::printf("Font Metrics: Rect = %2i %2i %2i %2i Char= %C, 0x%x Size= %i, Baseline= %i, Height= %i",imageRect.top, imageRect.bottom, imageRect.left, imageRect.right,ch,ch, mSize,mBaseline, mHeight); // Con::printf("Font Bounds: left= %2i right= %2i Char= %C, 0x%x Size= %i",FixedToInt(tbefore), FixedToInt(tafter), ch,ch, mSize); #endif return c; }
static bool osx_prepare_text(const void *p_text, uindex_t p_length, const MCGFont &p_font) { OSStatus t_err; t_err = noErr; if (t_err == noErr) if (s_layout == NULL) t_err = ATSUCreateTextLayout(&s_layout); if (t_err == noErr) if (s_style == NULL) t_err = ATSUCreateStyle(&s_style); if (t_err == noErr) if (s_colour_space == NULL) s_colour_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); if (t_err == noErr) if (s_colour == NULL) { // Components are grey and alpha const float t_colour_components[] = {1.0, 1.0}; s_colour = CGColorCreate(s_colour_space, t_colour_components); } ATSUFontID t_font_id; Fixed t_font_size; Boolean t_font_is_italic; ATSUTextMeasurement t_imposed_width; if (t_err == noErr) { t_font_size = p_font . size << 16; // MM-2013-09-16: [[ Bug 11283 ]] It appears that ATSUI doesn't like italic being passed as a style parameter to ATSUFONDtoFontID. // Instead, set italic as an attribute tag. uint8_t t_style; t_style = p_font . style & ~italic; t_font_is_italic = p_font . style & italic; // MW-2013-12-05: [[ Bug 11535 ]] Set the imposed width to the fixed advance width // if non-zero. Otherwise use the glyph advance. if (p_font . fixed_advance != 0) t_imposed_width = p_font . fixed_advance << 16; else t_imposed_width = kATSUseGlyphAdvance; // if the specified font can't be found, just use the default // MM-2013-09-16: [[ Bug 11283 ]] Do the same for font styles - if the font/style paring cannot be found, try font with no style. t_err = ATSUFONDtoFontID((short)(intptr_t)p_font . fid, t_style, &t_font_id); if (t_err != noErr) t_err = ATSUFONDtoFontID((short)(intptr_t)p_font . fid, 0, &t_font_id); if (t_err != noErr) t_err = ATSUFONDtoFontID(0, t_style, &t_font_id); if (t_err != noErr) t_err = ATSUFONDtoFontID(0, 0, &t_font_id); } ATSUAttributeTag t_tags[] = { kATSUFontTag, kATSUSizeTag, kATSUQDItalicTag, kATSUImposeWidthTag, }; ByteCount t_sizes[] = { sizeof(ATSUFontID), sizeof(Fixed), sizeof(Boolean), sizeof(ATSUTextMeasurement), }; ATSUAttributeValuePtr t_attrs[] = { &t_font_id, &t_font_size, &t_font_is_italic, &t_imposed_width, }; if (t_err == noErr) t_err = ATSUSetAttributes(s_style, sizeof(t_tags) / sizeof(ATSUAttributeTag), t_tags, t_sizes, t_attrs); if (t_err == noErr) t_err = ATSUSetTextPointerLocation(s_layout, (const UniChar *) p_text, 0, p_length / 2, p_length / 2); if (t_err == noErr) t_err = ATSUSetRunStyle(s_layout, s_style, 0, p_length / 2); if (t_err == noErr) t_err = ATSUSetTransientFontMatching(s_layout, true); return t_err == noErr; }
static bool osx_draw_text_to_cgcontext_at_location(const void *p_text, uindex_t p_length, MCGPoint p_location, const MCGFont &p_font, CGContextRef p_cgcontext, MCGIntRectangle &r_bounds) { OSStatus t_err; t_err = noErr; ATSUFontID t_font_id; Fixed t_font_size; t_font_size = p_font . size << 16; ATSUAttributeTag t_tags[] = { kATSUFontTag, kATSUSizeTag, }; ByteCount t_sizes[] = { sizeof(ATSUFontID), sizeof(Fixed), }; ATSUAttributeValuePtr t_attrs[] = { &t_font_id, &t_font_size, }; ATSLineLayoutOptions t_layout_options; ATSUAttributeTag t_layout_tags[] = { kATSULineLayoutOptionsTag, kATSUCGContextTag, }; ByteCount t_layout_sizes[] = { sizeof(ATSLineLayoutOptions), sizeof(CGContextRef), }; ATSUAttributeValuePtr t_layout_attrs[] = { &t_layout_options, &p_cgcontext, }; if (t_err == noErr) { // if the specified fon't can't be found, just use the default if (ATSUFONDtoFontID((short)(intptr_t)p_font . fid, p_font . style, &t_font_id) != noErr) t_err = ATSUFONDtoFontID(0, p_font . style, &t_font_id); } ATSUStyle t_style; t_style = NULL; if (t_err == noErr) t_err = ATSUCreateStyle(&t_style); if (t_err == noErr) t_err = ATSUSetAttributes(t_style, sizeof(t_tags) / sizeof(ATSUAttributeTag), t_tags, t_sizes, t_attrs); ATSUTextLayout t_layout; t_layout = NULL; if (t_err == noErr) { UniCharCount t_run; t_run = p_length / 2; t_err = ATSUCreateTextLayoutWithTextPtr((const UniChar *)p_text, 0, p_length / 2, p_length / 2, 1, &t_run, &t_style, &t_layout); } if (t_err == noErr) t_err = ATSUSetTransientFontMatching(t_layout, true); if (t_err == noErr) { t_layout_options = kATSLineUseDeviceMetrics | kATSLineFractDisable; t_err = ATSUSetLayoutControls(t_layout, sizeof(t_layout_tags) / sizeof(ATSUAttributeTag), t_layout_tags, t_layout_sizes, t_layout_attrs); } MCGIntRectangle t_bounds; if (p_cgcontext == NULL) { ATSUTextMeasurement t_before, t_after, t_ascent, t_descent; if (t_err == noErr) t_err = ATSUGetUnjustifiedBounds(t_layout, 0, p_length / 2, &t_before, &t_after, &t_ascent, &t_descent); if (t_err == noErr) { t_ascent = (t_ascent + 0xffff) >> 16; t_descent = (t_descent + 0xffff) >> 16; t_after = (t_after + 0xffff) >> 16; t_bounds . x = p_location . x; t_bounds . y = p_location . y - p_font . ascent; t_bounds . width = t_after; t_bounds . height = p_font . descent + p_font . ascent; r_bounds = t_bounds; }
static OSStatus AddNewTextLayoutToContext( DrawContextStruct *context ) { OSStatus err = noErr; ATSUAttributeTag tag; ByteCount valueSize; ATSUAttributeValuePtr valuePtr; UniCharCount runLength = kATSUToTextEnd; if (context->cgContext == NULL) return err; // create a text layout object err = ATSUCreateTextLayoutWithTextPtr( context->textBuffer, kATSUFromTextBeginning, kATSUToTextEnd, context->characterCount, 1, &runLength, &gGlobalStyle, &context->layoutObject ); require_noerr( err, AddNewTextLayoutToContext_err ); // add the cgContext to the text layout tag = kATSUCGContextTag; valueSize = sizeof( CGContextRef ); valuePtr = &context->cgContext; err = ATSUSetLayoutControls( context->layoutObject, 1, &tag, &valueSize, &valuePtr ); require_noerr( err, AddNewTextLayoutToContext_err ); // set font substitution for the new layout err = ATSUSetTransientFontMatching( context->layoutObject, true ); require_noerr( err, AddNewTextLayoutToContext_err ); // now, check the menu and see if we need to install a callback switch ( gDemoMenuSelection ) { case kDemoMenuItemStretch: err = InstallStrechyGlyphCallback( context->layoutObject ); require_noerr( err, AddNewTextLayoutToContext_err ); break; case kDemoMenuItemShrink: err = InstallShrinkyGlyphCallback( context->layoutObject ); require_noerr( err, AddNewTextLayoutToContext_err ); break; case kDemoMenuItemWhitespaceReplace: err = InstallGlyphReplacementCallback( context->layoutObject ); require_noerr( err, AddNewTextLayoutToContext_err ); break; case kDemoMenuItemSineWave: err = InstallGlyphWaveCallback( context->layoutObject ); require_noerr( err, AddNewTextLayoutToContext_err ); break; case kDemoMenuItemNone: default: break; } AddNewTextLayoutToContext_err: return err; }