Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/thebes/gfxFontMissingGlyphs.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 * This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "gfxFontMissingGlyphs.h"
7
8
#include "gfxUtils.h"
9
#include "mozilla/gfx/2D.h"
10
#include "mozilla/gfx/Helpers.h"
11
#include "mozilla/gfx/PathHelpers.h"
12
#include "mozilla/LinkedList.h"
13
#include "mozilla/RefPtr.h"
14
#include "nsDeviceContext.h"
15
#include "nsLayoutUtils.h"
16
#include "TextDrawTarget.h"
17
18
using namespace mozilla;
19
using namespace mozilla::gfx;
20
21
#define X 255
22
static const uint8_t gMiniFontData[] = {
23
    0,X,0, 0,X,0, X,X,X, X,X,X, X,0,X, X,X,X, X,X,X, X,X,X, X,X,X, X,X,X, X,X,X, X,X,0, 0,X,X, X,X,0, X,X,X, X,X,X,
24
    X,0,X, 0,X,0, 0,0,X, 0,0,X, X,0,X, X,0,0, X,0,0, 0,0,X, X,0,X, X,0,X, X,0,X, X,0,X, X,0,0, X,0,X, X,0,0, X,0,0,
25
    X,0,X, 0,X,0, X,X,X, X,X,X, X,X,X, X,X,X, X,X,X, 0,0,X, X,X,X, X,X,X, X,X,X, X,X,0, X,0,0, X,0,X, X,X,X, X,X,X,
26
    X,0,X, 0,X,0, X,0,0, 0,0,X, 0,0,X, 0,0,X, X,0,X, 0,0,X, X,0,X, 0,0,X, X,0,X, X,0,X, X,0,0, X,0,X, X,0,0, X,0,0,
27
    0,X,0, 0,X,0, X,X,X, X,X,X, 0,0,X, X,X,X, X,X,X, 0,0,X, X,X,X, 0,0,X, X,0,X, X,X,0, 0,X,X, X,X,0, X,X,X, X,0,0,
28
};
29
#undef X
30
31
/* Parameters that control the rendering of hexboxes. They look like this:
32
33
        BMP codepoints           non-BMP codepoints
34
      (U+0000 - U+FFFF)         (U+10000 - U+10FFFF)
35
36
         +---------+              +-------------+
37
         |         |              |             |
38
         | HHH HHH |              | HHH HHH HHH |
39
         | HHH HHH |              | HHH HHH HHH |
40
         | HHH HHH |              | HHH HHH HHH |
41
         | HHH HHH |              | HHH HHH HHH |
42
         | HHH HHH |              | HHH HHH HHH |
43
         |         |              |             |
44
         | HHH HHH |              | HHH HHH HHH |
45
         | HHH HHH |              | HHH HHH HHH |
46
         | HHH HHH |              | HHH HHH HHH |
47
         | HHH HHH |              | HHH HHH HHH |
48
         | HHH HHH |              | HHH HHH HHH |
49
         |         |              |             |
50
         +---------+              +-------------+
51
*/
52
53
/** Width of a minifont glyph (see above) */
54
static const int MINIFONT_WIDTH = 3;
55
/** Height of a minifont glyph (see above) */
56
static const int MINIFONT_HEIGHT = 5;
57
/**
58
 * Gap between minifont glyphs (both horizontal and vertical) and also
59
 * the minimum desired gap between the box border and the glyphs
60
 */
61
static const int HEX_CHAR_GAP = 1;
62
/**
63
 * The amount of space between the vertical edge of the glyphbox and the
64
 * box border. We make this nonzero so that when multiple missing glyphs
65
 * occur consecutively there's a gap between their rendered boxes.
66
 */
67
static const int BOX_HORIZONTAL_INSET = 1;
68
/** The width of the border */
69
static const int BOX_BORDER_WIDTH = 1;
70
/**
71
 * The scaling factor for the border opacity; this is multiplied by the current
72
 * opacity being used to draw the text.
73
 */
74
static const Float BOX_BORDER_OPACITY = 0.5;
75
76
#ifndef MOZ_GFX_OPTIMIZE_MOBILE
77
78
static RefPtr<DrawTarget> gGlyphDrawTarget;
79
static RefPtr<SourceSurface> gGlyphMask;
80
static RefPtr<SourceSurface> gGlyphAtlas;
81
static Color gGlyphColor;
82
83
/**
84
 * Generates a new colored mini-font atlas from the mini-font mask.
85
 */
86
static bool
87
MakeGlyphAtlas(const Color& aColor)
88
0
{
89
0
    gGlyphAtlas = nullptr;
90
0
    if (!gGlyphDrawTarget) {
91
0
        gGlyphDrawTarget = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
92
0
            IntSize(MINIFONT_WIDTH * 16, MINIFONT_HEIGHT), SurfaceFormat::B8G8R8A8);
93
0
        if (!gGlyphDrawTarget) {
94
0
            return false;
95
0
        }
96
0
    }
97
0
    if (!gGlyphMask) {
98
0
        gGlyphMask = gGlyphDrawTarget->CreateSourceSurfaceFromData(
99
0
            const_cast<uint8_t*>(gMiniFontData), IntSize(MINIFONT_WIDTH * 16, MINIFONT_HEIGHT),
100
0
            MINIFONT_WIDTH * 16, SurfaceFormat::A8);
101
0
        if (!gGlyphMask) {
102
0
            return false;
103
0
        }
104
0
    }
105
0
    gGlyphDrawTarget->MaskSurface(ColorPattern(aColor), gGlyphMask, Point(0, 0),
106
0
                                  DrawOptions(1.0f, CompositionOp::OP_SOURCE));
107
0
    gGlyphAtlas = gGlyphDrawTarget->Snapshot();
108
0
    if (!gGlyphAtlas) {
109
0
        return false;
110
0
    }
111
0
    gGlyphColor = aColor;
112
0
    return true;
113
0
}
114
115
/**
116
 * Reuse the current mini-font atlas if the color matches, otherwise regenerate it.
117
 */
118
static inline already_AddRefed<SourceSurface>
119
GetGlyphAtlas(const Color& aColor)
120
0
{
121
0
    // Get the opaque color, ignoring any transparency which will be handled later.
122
0
    Color color(aColor.r, aColor.g, aColor.b);
123
0
    if ((gGlyphAtlas && gGlyphColor == color) || MakeGlyphAtlas(color)) {
124
0
        return do_AddRef(gGlyphAtlas);
125
0
    }
126
0
    return nullptr;
127
0
}
128
129
/**
130
 * Clear any cached glyph atlas resources.
131
 */
132
static void
133
PurgeGlyphAtlas()
134
0
{
135
0
    gGlyphAtlas = nullptr;
136
0
    gGlyphDrawTarget = nullptr;
137
0
    gGlyphMask = nullptr;
138
0
}
139
140
// WebRender layer manager user data that will get signaled when the layer
141
// manager is destroyed.
142
class WRUserData : public layers::LayerUserData, public LinkedListElement<WRUserData>
143
{
144
public:
145
    explicit WRUserData(layers::WebRenderLayerManager* aManager);
146
147
    ~WRUserData();
148
149
    static void
150
    Assign(layers::WebRenderLayerManager* aManager)
151
0
    {
152
0
        if (!aManager->HasUserData(&sWRUserDataKey)) {
153
0
            aManager->SetUserData(&sWRUserDataKey, new WRUserData(aManager));
154
0
        }
155
0
    }
156
157
    void
158
    Remove()
159
0
    {
160
0
        mManager->RemoveUserData(&sWRUserDataKey);
161
0
    }
162
163
    layers::WebRenderLayerManager* mManager;
164
165
    static UserDataKey sWRUserDataKey;
166
};
167
168
static RefPtr<SourceSurface> gWRGlyphAtlas[8];
169
static LinkedList<WRUserData> gWRUsers;
170
UserDataKey WRUserData::sWRUserDataKey;
171
172
/**
173
 * Generates a transformed WebRender mini-font atlas for a given orientation.
174
 */
175
static already_AddRefed<SourceSurface>
176
MakeWRGlyphAtlas(const Matrix* aMat)
177
0
{
178
0
    IntSize size(MINIFONT_WIDTH * 16, MINIFONT_HEIGHT);
179
0
    // If the orientation is transposed, width/height are swapped.
180
0
    if (aMat && aMat->_11 == 0) {
181
0
        std::swap(size.width, size.height);
182
0
    }
183
0
    RefPtr<DrawTarget> ref = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
184
0
    RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateSimilarSoftwareDrawTarget(
185
0
            ref, size, SurfaceFormat::B8G8R8A8);
186
0
    if (!dt) {
187
0
        return nullptr;
188
0
    }
189
0
    if (aMat) {
190
0
        // Select appropriate transform matrix based on whether the
191
0
        // orientation is transposed.
192
0
        dt->SetTransform(aMat->_11 == 0 ?
193
0
            Matrix(0.0f, copysign(1.0f, aMat->_12),
194
0
                   copysign(1.0f, aMat->_21), 0.0f,
195
0
                   aMat->_21 < 0 ? MINIFONT_HEIGHT : 0.0f,
196
0
                   aMat->_12 < 0 ? MINIFONT_WIDTH * 16 : 0.0f) :
197
0
            Matrix(copysign(1.0f, aMat->_11), 0.0f,
198
0
                   0.0f, copysign(1.0f, aMat->_22),
199
0
                   aMat->_11 < 0 ? MINIFONT_WIDTH * 16 : 0.0f,
200
0
                   aMat->_22 < 0 ? MINIFONT_HEIGHT : 0.0f));
201
0
    }
202
0
    RefPtr<SourceSurface> mask = dt->CreateSourceSurfaceFromData(
203
0
        const_cast<uint8_t*>(gMiniFontData), IntSize(MINIFONT_WIDTH * 16, MINIFONT_HEIGHT),
204
0
        MINIFONT_WIDTH * 16, SurfaceFormat::A8);
205
0
    if (!mask) {
206
0
        return nullptr;
207
0
    }
208
0
    dt->MaskSurface(ColorPattern(Color(1.0f, 1.0f, 1.0f)), mask, Point(0, 0));
209
0
    return dt->Snapshot();
210
0
}
211
212
/**
213
 * Clear any cached WebRender glyph atlas resources.
214
 */
215
static void
216
PurgeWRGlyphAtlas()
217
0
{
218
0
    // For each WR layer manager, we need go through each atlas orientation
219
0
    // and see if it has a stashed image key. If it does, remove the image
220
0
    // from the layer manager.
221
0
    for (WRUserData* user : gWRUsers) {
222
0
        auto* manager = user->mManager;
223
0
        for (size_t i = 0; i < 8; i++) {
224
0
            if (gWRGlyphAtlas[i]) {
225
0
                uint32_t handle =
226
0
                    (uint32_t)(uintptr_t)gWRGlyphAtlas[i]->GetUserData(
227
0
                        reinterpret_cast<UserDataKey*>(manager));
228
0
                if (handle) {
229
0
                    manager->AddImageKeyForDiscard(
230
0
                        wr::ImageKey{manager->WrBridge()->GetNamespace(), handle});
231
0
                }
232
0
            }
233
0
        }
234
0
    }
235
0
    // Remove the layer managers' destroy notifications only after processing
236
0
    // so as not to mess up gWRUsers iteration.
237
0
    while (!gWRUsers.isEmpty()) {
238
0
        gWRUsers.popFirst()->Remove();
239
0
    }
240
0
    // Finally, clear out the atlases.
241
0
    for (size_t i = 0; i < 8; i++) {
242
0
        gWRGlyphAtlas[i] = nullptr;
243
0
    }
244
0
}
245
246
WRUserData::WRUserData(layers::WebRenderLayerManager* aManager)
247
    : mManager(aManager)
248
0
{
249
0
    gWRUsers.insertFront(this);
250
0
}
251
252
WRUserData::~WRUserData()
253
0
{
254
0
    // When the layer manager is destroyed, we need go through each
255
0
    // atlas and remove any assigned image keys.
256
0
    if (isInList()) {
257
0
        for (size_t i = 0; i < 8; i++) {
258
0
            if (gWRGlyphAtlas[i]) {
259
0
                gWRGlyphAtlas[i]->RemoveUserData(reinterpret_cast<UserDataKey*>(mManager));
260
0
            }
261
0
        }
262
0
    }
263
0
}
264
265
static already_AddRefed<SourceSurface>
266
GetWRGlyphAtlas(DrawTarget& aDrawTarget, const Matrix* aMat)
267
0
{
268
0
    uint32_t key = 0;
269
0
    // Encode orientation in the key.
270
0
    if (aMat) {
271
0
        if (aMat->_11 == 0) {
272
0
            key |= 4 | (aMat->_12 < 0 ? 1 : 0) | (aMat->_21 < 0 ? 2 : 0);
273
0
        } else {
274
0
            key |= (aMat->_11 < 0 ? 1 : 0) | (aMat->_22 < 0 ? 2 : 0);
275
0
        }
276
0
    }
277
0
    // Check if an atlas was already created, or create one if necessary.
278
0
    RefPtr<SourceSurface> atlas = gWRGlyphAtlas[key];
279
0
    if (!atlas) {
280
0
        atlas = MakeWRGlyphAtlas(aMat);
281
0
        gWRGlyphAtlas[key] = atlas;
282
0
    }
283
0
    // The atlas may exist, but an image key may not be assigned for it to
284
0
    // the given layer manager.
285
0
    auto* tdt = static_cast<layout::TextDrawTarget*>(&aDrawTarget);
286
0
    auto* manager = tdt->WrLayerManager();
287
0
    if (!atlas->GetUserData(reinterpret_cast<UserDataKey*>(manager))) {
288
0
        // No image key, so we need to map the atlas' data for transfer to WR.
289
0
        RefPtr<DataSourceSurface> dataSurface = atlas->GetDataSurface();
290
0
        if (!dataSurface) {
291
0
            return nullptr;
292
0
        }
293
0
        DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ);
294
0
        if (!map.IsMapped()) {
295
0
            return nullptr;
296
0
        }
297
0
        // Transfer the data and get an image key for it.
298
0
        Maybe<wr::ImageKey> result =
299
0
            tdt->DefineImage(atlas->GetSize(),
300
0
                             map.GetStride(),
301
0
                             atlas->GetFormat(),
302
0
                             map.GetData());
303
0
        if (!result.isSome()) {
304
0
            return nullptr;
305
0
        }
306
0
        // Assign the image key to the atlas.
307
0
        atlas->AddUserData(reinterpret_cast<UserDataKey*>(manager),
308
0
                           (void*)(uintptr_t)result.value().mHandle,
309
0
                           nullptr);
310
0
        // Create a user data notification for when the layer manager is
311
0
        // destroyed so we can clean up any assigned image keys.
312
0
        WRUserData::Assign(manager);
313
0
    }
314
0
    return atlas.forget();
315
0
}
316
317
static void
318
DrawHexChar(uint32_t aDigit, Float aLeft, Float aTop, DrawTarget& aDrawTarget,
319
            SourceSurface* aAtlas, const Color& aColor,
320
            const Matrix* aMat = nullptr)
321
0
{
322
0
    Rect dest(aLeft, aTop, MINIFONT_WIDTH, MINIFONT_HEIGHT);
323
0
    if (aDrawTarget.GetBackendType() == BackendType::WEBRENDER_TEXT) {
324
0
        // For WR, we need to get the image key assigned to the given WR layer manager
325
0
        // for referencing the image.
326
0
        auto* tdt = static_cast<layout::TextDrawTarget*>(&aDrawTarget);
327
0
        auto* manager = tdt->WrLayerManager();
328
0
        wr::ImageKey key = {
329
0
            manager->WrBridge()->GetNamespace(),
330
0
            (uint32_t)(uintptr_t)aAtlas->GetUserData(reinterpret_cast<UserDataKey*>(manager))
331
0
        };
332
0
        // Transform the bounds of the atlas into the given orientation, and then also transform
333
0
        // a small clip rect which will be used to select the given digit from the atlas.
334
0
        Rect bounds(aLeft - aDigit * MINIFONT_WIDTH, aTop, MINIFONT_WIDTH * 16, MINIFONT_HEIGHT);
335
0
        if (aMat) {
336
0
            // Width and height may be negative after the transform, so move the rect
337
0
            // if necessary and fix size.
338
0
            bounds = aMat->TransformRect(bounds);
339
0
            bounds.x += std::min(bounds.width, 0.0f);
340
0
            bounds.y += std::min(bounds.height, 0.0f);
341
0
            bounds.width = fabs(bounds.width);
342
0
            bounds.height = fabs(bounds.height);
343
0
            dest = aMat->TransformRect(dest);
344
0
            dest.x += std::min(dest.width, 0.0f);
345
0
            dest.y += std::min(dest.height, 0.0f);
346
0
            dest.width = fabs(dest.width);
347
0
            dest.height = fabs(dest.height);
348
0
        }
349
0
        // Finally, push the colored image with point filtering.
350
0
        tdt->PushImage(key,
351
0
                       wr::ToLayoutRect(bounds),
352
0
                       wr::ToLayoutRect(dest),
353
0
                       wr::ImageRendering::Pixelated,
354
0
                       wr::ToColorF(aColor));
355
0
    } else {
356
0
        // For the normal case, just draw the given digit from the atlas. Point filtering is used
357
0
        // to ensure the mini-font rectangles stay sharp with any scaling. Handle any transparency
358
0
        // here as well.
359
0
        aDrawTarget.DrawSurface(aAtlas,
360
0
                                dest,
361
0
                                Rect(aDigit * MINIFONT_WIDTH, 0, MINIFONT_WIDTH, MINIFONT_HEIGHT),
362
0
                                DrawSurfaceOptions(SamplingFilter::POINT),
363
0
                                DrawOptions(aColor.a, CompositionOp::OP_OVER, AntialiasMode::NONE));
364
0
    }
365
0
}
366
367
void
368
gfxFontMissingGlyphs::Purge()
369
0
{
370
0
    PurgeGlyphAtlas();
371
0
    PurgeWRGlyphAtlas();
372
0
}
373
374
#else // MOZ_GFX_OPTIMIZE_MOBILE
375
376
void
377
gfxFontMissingGlyphs::Purge()
378
{
379
}
380
381
#endif
382
383
void
384
gfxFontMissingGlyphs::Shutdown()
385
0
{
386
0
    Purge();
387
0
}
388
389
void
390
gfxFontMissingGlyphs::DrawMissingGlyph(uint32_t aChar,
391
                                       const Rect& aRect,
392
                                       DrawTarget& aDrawTarget,
393
                                       const Pattern& aPattern,
394
                                       uint32_t aAppUnitsPerDevPixel,
395
                                       const Matrix* aMat)
396
0
{
397
0
    Rect rect(aRect);
398
0
    // If there is an orientation transform, reorient the bounding rect.
399
0
    if (aMat) {
400
0
        rect.MoveBy(-aRect.BottomLeft());
401
0
        rect = aMat->TransformBounds(rect);
402
0
        rect.MoveBy(aRect.BottomLeft());
403
0
    }
404
0
405
0
    // If we're currently drawing with some kind of pattern, we just draw the
406
0
    // missing-glyph data in black.
407
0
    Color color = aPattern.GetType() == PatternType::COLOR ?
408
0
        static_cast<const ColorPattern&>(aPattern).mColor :
409
0
        ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f));
410
0
411
0
    // Stroke a rectangle so that the stroke's left edge is inset one pixel
412
0
    // from the left edge of the glyph box and the stroke's right edge
413
0
    // is inset one pixel from the right edge of the glyph box.
414
0
    Float halfBorderWidth = BOX_BORDER_WIDTH / 2.0;
415
0
    Float borderLeft = rect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth;
416
0
    Float borderRight = rect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth;
417
0
    Rect borderStrokeRect(borderLeft, rect.Y() + halfBorderWidth,
418
0
                          borderRight - borderLeft,
419
0
                          rect.Height() - 2.0 * halfBorderWidth);
420
0
    if (!borderStrokeRect.IsEmpty()) {
421
0
        ColorPattern adjustedColor(color);
422
0
        adjustedColor.mColor.a *= BOX_BORDER_OPACITY;
423
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
424
        aDrawTarget.FillRect(borderStrokeRect, adjustedColor);
425
#else
426
        StrokeOptions strokeOptions(BOX_BORDER_WIDTH);
427
0
        aDrawTarget.StrokeRect(borderStrokeRect, adjustedColor, strokeOptions);
428
0
#endif
429
0
    }
430
0
431
0
#ifndef MOZ_GFX_OPTIMIZE_MOBILE
432
0
    RefPtr<SourceSurface> atlas =
433
0
        aDrawTarget.GetBackendType() == BackendType::WEBRENDER_TEXT ?
434
0
            GetWRGlyphAtlas(aDrawTarget, aMat) :
435
0
            GetGlyphAtlas(color);
436
0
    if (!atlas) {
437
0
        return;
438
0
    }
439
0
440
0
    Point center = rect.Center();
441
0
    Float halfGap = HEX_CHAR_GAP / 2.f;
442
0
    Float top = -(MINIFONT_HEIGHT + halfGap);
443
0
    // We always want integer scaling, otherwise the "bitmap" glyphs will look
444
0
    // even uglier than usual when zoomed
445
0
    int32_t devPixelsPerCSSPx =
446
0
        std::max<int32_t>(1, AppUnitsPerCSSPixel() /
447
0
                             aAppUnitsPerDevPixel);
448
0
449
0
    Matrix tempMat;
450
0
    if (aMat) {
451
0
        // If there is an orientation transform, since draw target transforms may
452
0
        // not be supported, scale and translate it so that it can be directly used
453
0
        // for rendering the mini font without changing the draw target transform.
454
0
        tempMat = Matrix(*aMat).PostScale(devPixelsPerCSSPx, devPixelsPerCSSPx)
455
0
                               .PostTranslate(center);
456
0
        aMat = &tempMat;
457
0
    } else {
458
0
        // Otherwise, scale and translate the draw target transform assuming it
459
0
        // supports that.
460
0
        tempMat = aDrawTarget.GetTransform();
461
0
        aDrawTarget.SetTransform(
462
0
            Matrix(tempMat).PreTranslate(center)
463
0
                           .PreScale(devPixelsPerCSSPx, devPixelsPerCSSPx));
464
0
    }
465
0
466
0
    if (aChar < 0x10000) {
467
0
        if (rect.Width() >= 2 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
468
0
            rect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
469
0
            // Draw 4 digits for BMP
470
0
            Float left = -(MINIFONT_WIDTH + halfGap);
471
0
            DrawHexChar((aChar >> 12) & 0xF, left, top, aDrawTarget, atlas, color, aMat);
472
0
            DrawHexChar((aChar >> 8) & 0xF, halfGap, top, aDrawTarget, atlas, color, aMat);
473
0
            DrawHexChar((aChar >> 4) & 0xF, left, halfGap, aDrawTarget, atlas, color, aMat);
474
0
            DrawHexChar(aChar & 0xF, halfGap, halfGap, aDrawTarget, atlas, color, aMat);
475
0
        }
476
0
    } else {
477
0
        if (rect.Width() >= 3 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
478
0
            rect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
479
0
            // Draw 6 digits for non-BMP
480
0
            Float first = -(MINIFONT_WIDTH * 1.5 + HEX_CHAR_GAP);
481
0
            Float second = -(MINIFONT_WIDTH / 2.0);
482
0
            Float third = (MINIFONT_WIDTH / 2.0 + HEX_CHAR_GAP);
483
0
            DrawHexChar((aChar >> 20) & 0xF, first, top, aDrawTarget, atlas, color, aMat);
484
0
            DrawHexChar((aChar >> 16) & 0xF, second, top, aDrawTarget, atlas, color, aMat);
485
0
            DrawHexChar((aChar >> 12) & 0xF, third, top, aDrawTarget, atlas, color, aMat);
486
0
            DrawHexChar((aChar >> 8) & 0xF, first, halfGap, aDrawTarget, atlas, color, aMat);
487
0
            DrawHexChar((aChar >> 4) & 0xF, second, halfGap, aDrawTarget, atlas, color, aMat);
488
0
            DrawHexChar(aChar & 0xF, third, halfGap, aDrawTarget, atlas, color, aMat);
489
0
        }
490
0
    }
491
0
492
0
    if (!aMat) {
493
0
        // The draw target transform was changed, so it must be restored to
494
0
        // the original value.
495
0
        aDrawTarget.SetTransform(tempMat);
496
0
    }
497
0
#endif
498
0
}
499
500
Float
501
gfxFontMissingGlyphs::GetDesiredMinWidth(uint32_t aChar,
502
                                         uint32_t aAppUnitsPerDevPixel)
503
0
{
504
0
/**
505
0
 * The minimum desired width for a missing-glyph glyph box. I've laid it out
506
0
 * like this so you can see what goes where.
507
0
 */
508
0
    Float width = BOX_HORIZONTAL_INSET + BOX_BORDER_WIDTH + HEX_CHAR_GAP +
509
0
        MINIFONT_WIDTH + HEX_CHAR_GAP + MINIFONT_WIDTH +
510
0
         ((aChar < 0x10000) ? 0 : HEX_CHAR_GAP + MINIFONT_WIDTH) +
511
0
        HEX_CHAR_GAP + BOX_BORDER_WIDTH + BOX_HORIZONTAL_INSET;
512
0
    // Note that this will give us floating-point division, so the width will
513
0
    // -not- be snapped to integer multiples of its basic pixel value
514
0
    width *= Float(AppUnitsPerCSSPixel()) / aAppUnitsPerDevPixel;
515
0
    return width;
516
0
}