gfx/thebes/gfxFT2FontBase.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "gfxFT2FontBase.h"
michael@0 7 #include "gfxFT2Utils.h"
michael@0 8 #include "harfbuzz/hb.h"
michael@0 9 #include "mozilla/Likely.h"
michael@0 10 #include "gfxFontConstants.h"
michael@0 11 #include "gfxFontUtils.h"
michael@0 12
michael@0 13 using namespace mozilla::gfx;
michael@0 14
michael@0 15 gfxFT2FontBase::gfxFT2FontBase(cairo_scaled_font_t *aScaledFont,
michael@0 16 gfxFontEntry *aFontEntry,
michael@0 17 const gfxFontStyle *aFontStyle)
michael@0 18 : gfxFont(aFontEntry, aFontStyle, kAntialiasDefault, aScaledFont),
michael@0 19 mSpaceGlyph(0),
michael@0 20 mHasMetrics(false)
michael@0 21 {
michael@0 22 cairo_scaled_font_reference(mScaledFont);
michael@0 23 ConstructFontOptions();
michael@0 24 }
michael@0 25
michael@0 26 gfxFT2FontBase::~gfxFT2FontBase()
michael@0 27 {
michael@0 28 cairo_scaled_font_destroy(mScaledFont);
michael@0 29 }
michael@0 30
michael@0 31 uint32_t
michael@0 32 gfxFT2FontBase::GetGlyph(uint32_t aCharCode)
michael@0 33 {
michael@0 34 // FcFreeTypeCharIndex needs to lock the FT_Face and can end up searching
michael@0 35 // through all the postscript glyph names in the font. Therefore use a
michael@0 36 // lightweight cache, which is stored on the cairo_font_face_t.
michael@0 37
michael@0 38 cairo_font_face_t *face =
michael@0 39 cairo_scaled_font_get_font_face(CairoScaledFont());
michael@0 40
michael@0 41 if (cairo_font_face_status(face) != CAIRO_STATUS_SUCCESS)
michael@0 42 return 0;
michael@0 43
michael@0 44 // This cache algorithm and size is based on what is done in
michael@0 45 // cairo_scaled_font_text_to_glyphs and pango_fc_font_real_get_glyph. I
michael@0 46 // think the concept is that adjacent characters probably come mostly from
michael@0 47 // one Unicode block. This assumption is probably not so valid with
michael@0 48 // scripts with large character sets as used for East Asian languages.
michael@0 49
michael@0 50 struct CmapCacheSlot {
michael@0 51 uint32_t mCharCode;
michael@0 52 uint32_t mGlyphIndex;
michael@0 53 };
michael@0 54 const uint32_t kNumSlots = 256;
michael@0 55 static cairo_user_data_key_t sCmapCacheKey;
michael@0 56
michael@0 57 CmapCacheSlot *slots = static_cast<CmapCacheSlot*>
michael@0 58 (cairo_font_face_get_user_data(face, &sCmapCacheKey));
michael@0 59
michael@0 60 if (!slots) {
michael@0 61 // cairo's caches can keep some cairo_font_faces alive past our last
michael@0 62 // destroy, so the destroy function (free) for the cache must be
michael@0 63 // callable from cairo without any assumptions about what other
michael@0 64 // modules have not been shutdown.
michael@0 65 slots = static_cast<CmapCacheSlot*>
michael@0 66 (calloc(kNumSlots, sizeof(CmapCacheSlot)));
michael@0 67 if (!slots)
michael@0 68 return 0;
michael@0 69
michael@0 70 cairo_status_t status =
michael@0 71 cairo_font_face_set_user_data(face, &sCmapCacheKey, slots, free);
michael@0 72 if (status != CAIRO_STATUS_SUCCESS) { // OOM
michael@0 73 free(slots);
michael@0 74 return 0;
michael@0 75 }
michael@0 76
michael@0 77 // Invalidate slot 0 by setting its char code to something that would
michael@0 78 // never end up in slot 0. All other slots are already invalid
michael@0 79 // because they have mCharCode = 0 and a glyph for char code 0 will
michael@0 80 // always be in the slot 0.
michael@0 81 slots[0].mCharCode = 1;
michael@0 82 }
michael@0 83
michael@0 84 CmapCacheSlot *slot = &slots[aCharCode % kNumSlots];
michael@0 85 if (slot->mCharCode != aCharCode) {
michael@0 86 slot->mCharCode = aCharCode;
michael@0 87 slot->mGlyphIndex = gfxFT2LockedFace(this).GetGlyph(aCharCode);
michael@0 88 }
michael@0 89
michael@0 90 return slot->mGlyphIndex;
michael@0 91 }
michael@0 92
michael@0 93 void
michael@0 94 gfxFT2FontBase::GetGlyphExtents(uint32_t aGlyph, cairo_text_extents_t* aExtents)
michael@0 95 {
michael@0 96 NS_PRECONDITION(aExtents != nullptr, "aExtents must not be NULL");
michael@0 97
michael@0 98 cairo_glyph_t glyphs[1];
michael@0 99 glyphs[0].index = aGlyph;
michael@0 100 glyphs[0].x = 0.0;
michael@0 101 glyphs[0].y = 0.0;
michael@0 102 // cairo does some caching for us here but perhaps a small gain could be
michael@0 103 // made by caching more. It is usually only the advance that is needed,
michael@0 104 // so caching only the advance could allow many requests to be cached with
michael@0 105 // little memory use. Ideally this cache would be merged with
michael@0 106 // gfxGlyphExtents.
michael@0 107 cairo_scaled_font_glyph_extents(CairoScaledFont(), glyphs, 1, aExtents);
michael@0 108 }
michael@0 109
michael@0 110 const gfxFont::Metrics&
michael@0 111 gfxFT2FontBase::GetMetrics()
michael@0 112 {
michael@0 113 if (mHasMetrics)
michael@0 114 return mMetrics;
michael@0 115
michael@0 116 if (MOZ_UNLIKELY(GetStyle()->size <= 0.0)) {
michael@0 117 new(&mMetrics) gfxFont::Metrics(); // zero initialize
michael@0 118 mSpaceGlyph = 0;
michael@0 119 } else {
michael@0 120 gfxFT2LockedFace face(this);
michael@0 121 mFUnitsConvFactor = face.XScale();
michael@0 122 face.GetMetrics(&mMetrics, &mSpaceGlyph);
michael@0 123 }
michael@0 124
michael@0 125 SanitizeMetrics(&mMetrics, false);
michael@0 126
michael@0 127 #if 0
michael@0 128 // printf("font name: %s %f\n", NS_ConvertUTF16toUTF8(GetName()).get(), GetStyle()->size);
michael@0 129 // printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
michael@0 130
michael@0 131 fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(GetName()).get());
michael@0 132 fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
michael@0 133 fprintf (stderr, " maxAscent: %f maxDescent: %f\n", mMetrics.maxAscent, mMetrics.maxDescent);
michael@0 134 fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.externalLeading, mMetrics.internalLeading);
michael@0 135 fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight);
michael@0 136 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);
michael@0 137 #endif
michael@0 138
michael@0 139 mHasMetrics = true;
michael@0 140 return mMetrics;
michael@0 141 }
michael@0 142
michael@0 143 // Get the glyphID of a space
michael@0 144 uint32_t
michael@0 145 gfxFT2FontBase::GetSpaceGlyph()
michael@0 146 {
michael@0 147 NS_ASSERTION(GetStyle()->size != 0,
michael@0 148 "forgot to short-circuit a text run with zero-sized font?");
michael@0 149 GetMetrics();
michael@0 150 return mSpaceGlyph;
michael@0 151 }
michael@0 152
michael@0 153 uint32_t
michael@0 154 gfxFT2FontBase::GetGlyph(uint32_t unicode, uint32_t variation_selector)
michael@0 155 {
michael@0 156 if (variation_selector) {
michael@0 157 uint32_t id =
michael@0 158 gfxFT2LockedFace(this).GetUVSGlyph(unicode, variation_selector);
michael@0 159 if (id)
michael@0 160 return id;
michael@0 161 id = gfxFontUtils::GetUVSFallback(unicode, variation_selector);
michael@0 162 if (id) {
michael@0 163 unicode = id;
michael@0 164 }
michael@0 165 }
michael@0 166
michael@0 167 return GetGlyph(unicode);
michael@0 168 }
michael@0 169
michael@0 170 int32_t
michael@0 171 gfxFT2FontBase::GetGlyphWidth(gfxContext *aCtx, uint16_t aGID)
michael@0 172 {
michael@0 173 cairo_text_extents_t extents;
michael@0 174 GetGlyphExtents(aGID, &extents);
michael@0 175 // convert to 16.16 fixed point
michael@0 176 return NS_lround(0x10000 * extents.x_advance);
michael@0 177 }
michael@0 178
michael@0 179 bool
michael@0 180 gfxFT2FontBase::SetupCairoFont(gfxContext *aContext)
michael@0 181 {
michael@0 182 cairo_t *cr = aContext->GetCairo();
michael@0 183
michael@0 184 // The scaled font ctm is not relevant right here because
michael@0 185 // cairo_set_scaled_font does not record the scaled font itself, but
michael@0 186 // merely the font_face, font_matrix, font_options. The scaled_font used
michael@0 187 // for the target can be different from the scaled_font passed to
michael@0 188 // cairo_set_scaled_font. (Unfortunately we have measured only for an
michael@0 189 // identity ctm.)
michael@0 190 cairo_scaled_font_t *cairoFont = CairoScaledFont();
michael@0 191
michael@0 192 if (cairo_scaled_font_status(cairoFont) != CAIRO_STATUS_SUCCESS) {
michael@0 193 // Don't cairo_set_scaled_font as that would propagate the error to
michael@0 194 // the cairo_t, precluding any further drawing.
michael@0 195 return false;
michael@0 196 }
michael@0 197 // Thoughts on which font_options to set on the context:
michael@0 198 //
michael@0 199 // cairoFont has been created for screen rendering.
michael@0 200 //
michael@0 201 // When the context is being used for screen rendering, we should set
michael@0 202 // font_options such that the same scaled_font gets used (when the ctm is
michael@0 203 // the same). The use of explicit font_options recorded in
michael@0 204 // CreateScaledFont ensures that this will happen.
michael@0 205 //
michael@0 206 // XXXkt: For pdf and ps surfaces, I don't know whether it's better to
michael@0 207 // remove surface-specific options, or try to draw with the same
michael@0 208 // scaled_font that was used to measure. As the same font_face is being
michael@0 209 // used, its font_options will often override some values anyway (unless
michael@0 210 // perhaps we remove those from the FcPattern at face creation).
michael@0 211 //
michael@0 212 // I can't see any significant difference in printing, irrespective of
michael@0 213 // what is set here. It's too late to change things here as measuring has
michael@0 214 // already taken place. We should really be measuring with a different
michael@0 215 // font for pdf and ps surfaces (bug 403513).
michael@0 216 cairo_set_scaled_font(cr, cairoFont);
michael@0 217 return true;
michael@0 218 }
michael@0 219
michael@0 220 void
michael@0 221 gfxFT2FontBase::ConstructFontOptions()
michael@0 222 {
michael@0 223 NS_LossyConvertUTF16toASCII name(this->GetName());
michael@0 224 mFontOptions.mName = name.get();
michael@0 225
michael@0 226 const gfxFontStyle* style = this->GetStyle();
michael@0 227 if (style->style == NS_FONT_STYLE_ITALIC) {
michael@0 228 if (style->weight == NS_FONT_WEIGHT_BOLD) {
michael@0 229 mFontOptions.mStyle = FontStyle::BOLD_ITALIC;
michael@0 230 } else {
michael@0 231 mFontOptions.mStyle = FontStyle::ITALIC;
michael@0 232 }
michael@0 233 } else {
michael@0 234 if (style->weight == NS_FONT_WEIGHT_BOLD) {
michael@0 235 mFontOptions.mStyle = FontStyle::BOLD;
michael@0 236 } else {
michael@0 237 mFontOptions.mStyle = FontStyle::NORMAL;
michael@0 238 }
michael@0 239 }
michael@0 240 }

mercurial