1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxFT2FontBase.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,240 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "gfxFT2FontBase.h" 1.10 +#include "gfxFT2Utils.h" 1.11 +#include "harfbuzz/hb.h" 1.12 +#include "mozilla/Likely.h" 1.13 +#include "gfxFontConstants.h" 1.14 +#include "gfxFontUtils.h" 1.15 + 1.16 +using namespace mozilla::gfx; 1.17 + 1.18 +gfxFT2FontBase::gfxFT2FontBase(cairo_scaled_font_t *aScaledFont, 1.19 + gfxFontEntry *aFontEntry, 1.20 + const gfxFontStyle *aFontStyle) 1.21 + : gfxFont(aFontEntry, aFontStyle, kAntialiasDefault, aScaledFont), 1.22 + mSpaceGlyph(0), 1.23 + mHasMetrics(false) 1.24 +{ 1.25 + cairo_scaled_font_reference(mScaledFont); 1.26 + ConstructFontOptions(); 1.27 +} 1.28 + 1.29 +gfxFT2FontBase::~gfxFT2FontBase() 1.30 +{ 1.31 + cairo_scaled_font_destroy(mScaledFont); 1.32 +} 1.33 + 1.34 +uint32_t 1.35 +gfxFT2FontBase::GetGlyph(uint32_t aCharCode) 1.36 +{ 1.37 + // FcFreeTypeCharIndex needs to lock the FT_Face and can end up searching 1.38 + // through all the postscript glyph names in the font. Therefore use a 1.39 + // lightweight cache, which is stored on the cairo_font_face_t. 1.40 + 1.41 + cairo_font_face_t *face = 1.42 + cairo_scaled_font_get_font_face(CairoScaledFont()); 1.43 + 1.44 + if (cairo_font_face_status(face) != CAIRO_STATUS_SUCCESS) 1.45 + return 0; 1.46 + 1.47 + // This cache algorithm and size is based on what is done in 1.48 + // cairo_scaled_font_text_to_glyphs and pango_fc_font_real_get_glyph. I 1.49 + // think the concept is that adjacent characters probably come mostly from 1.50 + // one Unicode block. This assumption is probably not so valid with 1.51 + // scripts with large character sets as used for East Asian languages. 1.52 + 1.53 + struct CmapCacheSlot { 1.54 + uint32_t mCharCode; 1.55 + uint32_t mGlyphIndex; 1.56 + }; 1.57 + const uint32_t kNumSlots = 256; 1.58 + static cairo_user_data_key_t sCmapCacheKey; 1.59 + 1.60 + CmapCacheSlot *slots = static_cast<CmapCacheSlot*> 1.61 + (cairo_font_face_get_user_data(face, &sCmapCacheKey)); 1.62 + 1.63 + if (!slots) { 1.64 + // cairo's caches can keep some cairo_font_faces alive past our last 1.65 + // destroy, so the destroy function (free) for the cache must be 1.66 + // callable from cairo without any assumptions about what other 1.67 + // modules have not been shutdown. 1.68 + slots = static_cast<CmapCacheSlot*> 1.69 + (calloc(kNumSlots, sizeof(CmapCacheSlot))); 1.70 + if (!slots) 1.71 + return 0; 1.72 + 1.73 + cairo_status_t status = 1.74 + cairo_font_face_set_user_data(face, &sCmapCacheKey, slots, free); 1.75 + if (status != CAIRO_STATUS_SUCCESS) { // OOM 1.76 + free(slots); 1.77 + return 0; 1.78 + } 1.79 + 1.80 + // Invalidate slot 0 by setting its char code to something that would 1.81 + // never end up in slot 0. All other slots are already invalid 1.82 + // because they have mCharCode = 0 and a glyph for char code 0 will 1.83 + // always be in the slot 0. 1.84 + slots[0].mCharCode = 1; 1.85 + } 1.86 + 1.87 + CmapCacheSlot *slot = &slots[aCharCode % kNumSlots]; 1.88 + if (slot->mCharCode != aCharCode) { 1.89 + slot->mCharCode = aCharCode; 1.90 + slot->mGlyphIndex = gfxFT2LockedFace(this).GetGlyph(aCharCode); 1.91 + } 1.92 + 1.93 + return slot->mGlyphIndex; 1.94 +} 1.95 + 1.96 +void 1.97 +gfxFT2FontBase::GetGlyphExtents(uint32_t aGlyph, cairo_text_extents_t* aExtents) 1.98 +{ 1.99 + NS_PRECONDITION(aExtents != nullptr, "aExtents must not be NULL"); 1.100 + 1.101 + cairo_glyph_t glyphs[1]; 1.102 + glyphs[0].index = aGlyph; 1.103 + glyphs[0].x = 0.0; 1.104 + glyphs[0].y = 0.0; 1.105 + // cairo does some caching for us here but perhaps a small gain could be 1.106 + // made by caching more. It is usually only the advance that is needed, 1.107 + // so caching only the advance could allow many requests to be cached with 1.108 + // little memory use. Ideally this cache would be merged with 1.109 + // gfxGlyphExtents. 1.110 + cairo_scaled_font_glyph_extents(CairoScaledFont(), glyphs, 1, aExtents); 1.111 +} 1.112 + 1.113 +const gfxFont::Metrics& 1.114 +gfxFT2FontBase::GetMetrics() 1.115 +{ 1.116 + if (mHasMetrics) 1.117 + return mMetrics; 1.118 + 1.119 + if (MOZ_UNLIKELY(GetStyle()->size <= 0.0)) { 1.120 + new(&mMetrics) gfxFont::Metrics(); // zero initialize 1.121 + mSpaceGlyph = 0; 1.122 + } else { 1.123 + gfxFT2LockedFace face(this); 1.124 + mFUnitsConvFactor = face.XScale(); 1.125 + face.GetMetrics(&mMetrics, &mSpaceGlyph); 1.126 + } 1.127 + 1.128 + SanitizeMetrics(&mMetrics, false); 1.129 + 1.130 +#if 0 1.131 + // printf("font name: %s %f\n", NS_ConvertUTF16toUTF8(GetName()).get(), GetStyle()->size); 1.132 + // printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font))); 1.133 + 1.134 + fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(GetName()).get()); 1.135 + fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent); 1.136 + fprintf (stderr, " maxAscent: %f maxDescent: %f\n", mMetrics.maxAscent, mMetrics.maxDescent); 1.137 + fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.externalLeading, mMetrics.internalLeading); 1.138 + fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight); 1.139 + fprintf (stderr, " uOff: %f uSize: %f stOff: %f stSize: %f suOff: %f suSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize, mMetrics.superscriptOffset, mMetrics.subscriptOffset); 1.140 +#endif 1.141 + 1.142 + mHasMetrics = true; 1.143 + return mMetrics; 1.144 +} 1.145 + 1.146 +// Get the glyphID of a space 1.147 +uint32_t 1.148 +gfxFT2FontBase::GetSpaceGlyph() 1.149 +{ 1.150 + NS_ASSERTION(GetStyle()->size != 0, 1.151 + "forgot to short-circuit a text run with zero-sized font?"); 1.152 + GetMetrics(); 1.153 + return mSpaceGlyph; 1.154 +} 1.155 + 1.156 +uint32_t 1.157 +gfxFT2FontBase::GetGlyph(uint32_t unicode, uint32_t variation_selector) 1.158 +{ 1.159 + if (variation_selector) { 1.160 + uint32_t id = 1.161 + gfxFT2LockedFace(this).GetUVSGlyph(unicode, variation_selector); 1.162 + if (id) 1.163 + return id; 1.164 + id = gfxFontUtils::GetUVSFallback(unicode, variation_selector); 1.165 + if (id) { 1.166 + unicode = id; 1.167 + } 1.168 + } 1.169 + 1.170 + return GetGlyph(unicode); 1.171 +} 1.172 + 1.173 +int32_t 1.174 +gfxFT2FontBase::GetGlyphWidth(gfxContext *aCtx, uint16_t aGID) 1.175 +{ 1.176 + cairo_text_extents_t extents; 1.177 + GetGlyphExtents(aGID, &extents); 1.178 + // convert to 16.16 fixed point 1.179 + return NS_lround(0x10000 * extents.x_advance); 1.180 +} 1.181 + 1.182 +bool 1.183 +gfxFT2FontBase::SetupCairoFont(gfxContext *aContext) 1.184 +{ 1.185 + cairo_t *cr = aContext->GetCairo(); 1.186 + 1.187 + // The scaled font ctm is not relevant right here because 1.188 + // cairo_set_scaled_font does not record the scaled font itself, but 1.189 + // merely the font_face, font_matrix, font_options. The scaled_font used 1.190 + // for the target can be different from the scaled_font passed to 1.191 + // cairo_set_scaled_font. (Unfortunately we have measured only for an 1.192 + // identity ctm.) 1.193 + cairo_scaled_font_t *cairoFont = CairoScaledFont(); 1.194 + 1.195 + if (cairo_scaled_font_status(cairoFont) != CAIRO_STATUS_SUCCESS) { 1.196 + // Don't cairo_set_scaled_font as that would propagate the error to 1.197 + // the cairo_t, precluding any further drawing. 1.198 + return false; 1.199 + } 1.200 + // Thoughts on which font_options to set on the context: 1.201 + // 1.202 + // cairoFont has been created for screen rendering. 1.203 + // 1.204 + // When the context is being used for screen rendering, we should set 1.205 + // font_options such that the same scaled_font gets used (when the ctm is 1.206 + // the same). The use of explicit font_options recorded in 1.207 + // CreateScaledFont ensures that this will happen. 1.208 + // 1.209 + // XXXkt: For pdf and ps surfaces, I don't know whether it's better to 1.210 + // remove surface-specific options, or try to draw with the same 1.211 + // scaled_font that was used to measure. As the same font_face is being 1.212 + // used, its font_options will often override some values anyway (unless 1.213 + // perhaps we remove those from the FcPattern at face creation). 1.214 + // 1.215 + // I can't see any significant difference in printing, irrespective of 1.216 + // what is set here. It's too late to change things here as measuring has 1.217 + // already taken place. We should really be measuring with a different 1.218 + // font for pdf and ps surfaces (bug 403513). 1.219 + cairo_set_scaled_font(cr, cairoFont); 1.220 + return true; 1.221 +} 1.222 + 1.223 +void 1.224 +gfxFT2FontBase::ConstructFontOptions() 1.225 +{ 1.226 + NS_LossyConvertUTF16toASCII name(this->GetName()); 1.227 + mFontOptions.mName = name.get(); 1.228 + 1.229 + const gfxFontStyle* style = this->GetStyle(); 1.230 + if (style->style == NS_FONT_STYLE_ITALIC) { 1.231 + if (style->weight == NS_FONT_WEIGHT_BOLD) { 1.232 + mFontOptions.mStyle = FontStyle::BOLD_ITALIC; 1.233 + } else { 1.234 + mFontOptions.mStyle = FontStyle::ITALIC; 1.235 + } 1.236 + } else { 1.237 + if (style->weight == NS_FONT_WEIGHT_BOLD) { 1.238 + mFontOptions.mStyle = FontStyle::BOLD; 1.239 + } else { 1.240 + mFontOptions.mStyle = FontStyle::NORMAL; 1.241 + } 1.242 + } 1.243 +}