gfx/thebes/gfxFT2FontBase.cpp

changeset 0
6474c204b198
     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 +}

mercurial