Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/webrender_bindings/Moz2DImageRenderer.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "gfxPrefs.h"
8
#include "gfxUtils.h"
9
#include "mozilla/Mutex.h"
10
#include "mozilla/Range.h"
11
#include "mozilla/gfx/2D.h"
12
#include "mozilla/gfx/RectAbsolute.h"
13
#include "mozilla/gfx/Logging.h"
14
#include "mozilla/gfx/RecordedEvent.h"
15
#include "mozilla/layers/WebRenderDrawEventRecorder.h"
16
#include "WebRenderTypes.h"
17
#include "webrender_ffi.h"
18
19
#include <unordered_map>
20
21
#ifdef XP_MACOSX
22
#include "mozilla/gfx/UnscaledFontMac.h"
23
#elif defined(XP_WIN)
24
#include "mozilla/gfx/UnscaledFontDWrite.h"
25
#else
26
#include "mozilla/gfx/UnscaledFontFreeType.h"
27
#endif
28
29
namespace std {
30
  template <> struct hash<mozilla::wr::FontKey> {
31
0
    size_t operator()(const mozilla::wr::FontKey& key) const {
32
0
      return hash<size_t>()(mozilla::wr::AsUint64(key));
33
0
    }
34
  };
35
36
  template <> struct hash<mozilla::wr::FontInstanceKey> {
37
0
    size_t operator()(const mozilla::wr::FontInstanceKey& key) const {
38
0
      return hash<size_t>()(mozilla::wr::AsUint64(key));
39
0
    }
40
  };
41
};
42
43
namespace mozilla {
44
45
using namespace gfx;
46
47
namespace wr {
48
49
struct FontTemplate {
50
  const uint8_t* mData;
51
  size_t mSize;
52
  uint32_t mIndex;
53
  const VecU8* mVec;
54
  RefPtr<UnscaledFont> mUnscaledFont;
55
56
  FontTemplate()
57
    : mData(nullptr)
58
    , mSize(0)
59
    , mIndex(0)
60
    , mVec(nullptr)
61
0
  {}
62
63
0
  ~FontTemplate() {
64
0
    if (mVec) {
65
0
      wr_dec_ref_arc(mVec);
66
0
    }
67
0
  }
68
};
69
70
struct FontInstanceData {
71
  WrFontKey mFontKey;
72
  float mSize;
73
  Maybe<FontInstanceOptions> mOptions;
74
  Maybe<FontInstancePlatformOptions> mPlatformOptions;
75
  UniquePtr<gfx::FontVariation[]> mVariations;
76
  size_t mNumVariations;
77
  RefPtr<ScaledFont> mScaledFont;
78
79
  FontInstanceData()
80
    : mSize(0)
81
    , mNumVariations(0)
82
0
  {}
83
};
84
85
StaticMutex sFontDataTableLock;
86
std::unordered_map<WrFontKey, FontTemplate> sFontDataTable;
87
std::unordered_map<WrFontInstanceKey, FontInstanceData> sBlobFontTable;
88
89
// Fixed-size ring buffer logging font deletion events to aid debugging.
90
static struct FontDeleteLog {
91
  static const size_t MAX_ENTRIES = 256;
92
93
  uint64_t mEntries[MAX_ENTRIES] = { 0 };
94
  size_t mNextEntry = 0;
95
96
0
  void AddEntry(uint64_t aEntry) {
97
0
    mEntries[mNextEntry] = aEntry;
98
0
    mNextEntry = (mNextEntry + 1) % MAX_ENTRIES;
99
0
  }
100
101
0
  void Add(WrFontKey aKey) {
102
0
    AddEntry(AsUint64(aKey));
103
0
  }
104
105
  // Store namespace clears as font id 0, since this will never be allocated.
106
0
  void Add(WrIdNamespace aNamespace) {
107
0
    AddEntry(AsUint64(WrFontKey { aNamespace, 0 }));
108
0
  }
109
110
0
  void AddAll() {
111
0
    AddEntry(~0);
112
0
  }
113
114
  // Find a matching entry in the log, searching backwards starting at the newest
115
  // entry and finishing with the oldest entry. Returns a brief description of why
116
  // the font was deleted, if known.
117
0
  const char* Find(WrFontKey aKey) {
118
0
    uint64_t keyEntry = AsUint64(aKey);
119
0
    uint64_t namespaceEntry = AsUint64(WrFontKey { aKey.mNamespace, 0 });
120
0
    size_t offset = mNextEntry;
121
0
    do {
122
0
      offset = (offset + MAX_ENTRIES - 1) % MAX_ENTRIES;
123
0
      if (mEntries[offset] == keyEntry) {
124
0
        return "deleted font";
125
0
      } else if (mEntries[offset] == namespaceEntry) {
126
0
        return "cleared namespace";
127
0
      } else if (mEntries[offset] == (uint64_t)~0) {
128
0
        return "cleared all";
129
0
      }
130
0
    } while (offset != mNextEntry);
131
0
    return "unknown font";
132
0
  }
133
} sFontDeleteLog;
134
135
void
136
0
ClearAllBlobImageResources() {
137
0
  StaticMutexAutoLock lock(sFontDataTableLock);
138
0
  sFontDeleteLog.AddAll();
139
0
  sBlobFontTable.clear();
140
0
  sFontDataTable.clear();
141
0
}
142
143
extern "C" {
144
void
145
0
ClearBlobImageResources(WrIdNamespace aNamespace) {
146
0
  StaticMutexAutoLock lock(sFontDataTableLock);
147
0
  sFontDeleteLog.Add(aNamespace);
148
0
  for (auto i = sBlobFontTable.begin(); i != sBlobFontTable.end();) {
149
0
    if (i->first.mNamespace == aNamespace) {
150
0
      i = sBlobFontTable.erase(i);
151
0
    } else {
152
0
      i++;
153
0
    }
154
0
  }
155
0
  for (auto i = sFontDataTable.begin(); i != sFontDataTable.end();) {
156
0
    if (i->first.mNamespace == aNamespace) {
157
0
      i = sFontDataTable.erase(i);
158
0
    } else {
159
0
      i++;
160
0
    }
161
0
  }
162
0
}
163
164
void
165
0
AddFontData(WrFontKey aKey, const uint8_t *aData, size_t aSize, uint32_t aIndex, const ArcVecU8 *aVec) {
166
0
  StaticMutexAutoLock lock(sFontDataTableLock);
167
0
  auto i = sFontDataTable.find(aKey);
168
0
  if (i == sFontDataTable.end()) {
169
0
    FontTemplate& font = sFontDataTable[aKey];
170
0
    font.mData = aData;
171
0
    font.mSize = aSize;
172
0
    font.mIndex = aIndex;
173
0
    font.mVec = wr_add_ref_arc(aVec);
174
0
  }
175
0
}
176
177
void
178
0
AddNativeFontHandle(WrFontKey aKey, void* aHandle, uint32_t aIndex) {
179
0
  StaticMutexAutoLock lock(sFontDataTableLock);
180
0
  auto i = sFontDataTable.find(aKey);
181
0
  if (i == sFontDataTable.end()) {
182
0
    FontTemplate& font = sFontDataTable[aKey];
183
#ifdef XP_MACOSX
184
    font.mUnscaledFont = new UnscaledFontMac(reinterpret_cast<CGFontRef>(aHandle), false);
185
#elif defined(XP_WIN)
186
    font.mUnscaledFont = new UnscaledFontDWrite(reinterpret_cast<IDWriteFontFace*>(aHandle), nullptr);
187
#elif defined(ANDROID)
188
    font.mUnscaledFont = new UnscaledFontFreeType(reinterpret_cast<const char*>(aHandle), aIndex);
189
#else
190
    font.mUnscaledFont = new UnscaledFontFontconfig(reinterpret_cast<const char*>(aHandle), aIndex);
191
0
#endif
192
0
  }
193
0
}
194
195
void
196
0
DeleteFontData(WrFontKey aKey) {
197
0
  StaticMutexAutoLock lock(sFontDataTableLock);
198
0
  sFontDeleteLog.Add(aKey);
199
0
  auto i = sFontDataTable.find(aKey);
200
0
  if (i != sFontDataTable.end()) {
201
0
    sFontDataTable.erase(i);
202
0
  }
203
0
}
204
205
void
206
AddBlobFont(WrFontInstanceKey aInstanceKey,
207
            WrFontKey aFontKey,
208
            float aSize,
209
            const FontInstanceOptions* aOptions,
210
            const FontInstancePlatformOptions* aPlatformOptions,
211
            const FontVariation* aVariations,
212
            size_t aNumVariations)
213
0
{
214
0
  StaticMutexAutoLock lock(sFontDataTableLock);
215
0
  auto i = sBlobFontTable.find(aInstanceKey);
216
0
  if (i == sBlobFontTable.end()) {
217
0
    FontInstanceData& font = sBlobFontTable[aInstanceKey];
218
0
    font.mFontKey = aFontKey;
219
0
    font.mSize = aSize;
220
0
    if (aOptions) {
221
0
      font.mOptions = Some(*aOptions);
222
0
    }
223
0
    if (aPlatformOptions) {
224
0
      font.mPlatformOptions = Some(*aPlatformOptions);
225
0
    }
226
0
    if (aNumVariations) {
227
0
      font.mVariations.reset(new gfx::FontVariation[aNumVariations]);
228
0
      PodCopy(font.mVariations.get(), reinterpret_cast<const gfx::FontVariation*>(aVariations), aNumVariations);
229
0
    }
230
0
  }
231
0
}
232
233
void
234
DeleteBlobFont(WrFontInstanceKey aKey)
235
0
{
236
0
  StaticMutexAutoLock lock(sFontDataTableLock);
237
0
  auto i = sBlobFontTable.find(aKey);
238
0
  if (i != sBlobFontTable.end()) {
239
0
    sBlobFontTable.erase(i);
240
0
  }
241
0
}
242
243
} // extern
244
245
static RefPtr<UnscaledFont>
246
GetUnscaledFont(Translator* aTranslator, WrFontKey aKey)
247
0
{
248
0
  auto i = sFontDataTable.find(aKey);
249
0
  if (i == sFontDataTable.end()) {
250
0
    gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to get UnscaledFont entry for FontKey " << aKey.mHandle
251
0
                                                 << " because " << sFontDeleteLog.Find(aKey);
252
0
    return nullptr;
253
0
  }
254
0
  FontTemplate &data = i->second;
255
0
  if (data.mUnscaledFont) {
256
0
    return data.mUnscaledFont;
257
0
  }
258
0
  MOZ_ASSERT(data.mData);
259
0
  FontType type =
260
#ifdef XP_MACOSX
261
    FontType::MAC;
262
#elif defined(XP_WIN)
263
    FontType::DWRITE;
264
#elif defined(ANDROID)
265
    FontType::FREETYPE;
266
#else
267
    FontType::FONTCONFIG;
268
0
#endif
269
0
  // makes a copy of the data
270
0
  RefPtr<NativeFontResource> fontResource = Factory::CreateNativeFontResource((uint8_t*)data.mData, data.mSize,
271
0
                                                                              aTranslator->GetReferenceDrawTarget()->GetBackendType(),
272
0
                                                                              type,
273
0
                                                                              aTranslator->GetFontContext());
274
0
  RefPtr<UnscaledFont> unscaledFont;
275
0
  if (!fontResource) {
276
0
    gfxDevCrash(LogReason::NativeFontResourceNotFound) << "Failed to create NativeFontResource for FontKey " << aKey.mHandle;
277
0
  } else {
278
0
    // Instance data is only needed for GDI fonts which webrender does not
279
0
    // support.
280
0
    unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0);
281
0
    if (!unscaledFont) {
282
0
      gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to create UnscaledFont for FontKey " << aKey.mHandle;
283
0
    }
284
0
  }
285
0
  data.mUnscaledFont = unscaledFont;
286
0
  return unscaledFont;
287
0
}
288
289
static RefPtr<ScaledFont>
290
GetScaledFont(Translator* aTranslator, WrFontInstanceKey aKey)
291
0
{
292
0
  StaticMutexAutoLock lock(sFontDataTableLock);
293
0
  auto i = sBlobFontTable.find(aKey);
294
0
  if (i == sBlobFontTable.end()) {
295
0
    gfxDevCrash(LogReason::ScaledFontNotFound) << "Failed to get ScaledFont entry for FontInstanceKey " << aKey.mHandle;
296
0
    return nullptr;
297
0
  }
298
0
  FontInstanceData &data = i->second;
299
0
  if (data.mScaledFont) {
300
0
    return data.mScaledFont;
301
0
  }
302
0
  RefPtr<UnscaledFont> unscaled = GetUnscaledFont(aTranslator, data.mFontKey);
303
0
  if (!unscaled) {
304
0
    return nullptr;
305
0
  }
306
0
  RefPtr<ScaledFont> scaled =
307
0
    unscaled->CreateScaledFontFromWRFont(data.mSize,
308
0
                                         data.mOptions.ptrOr(nullptr),
309
0
                                         data.mPlatformOptions.ptrOr(nullptr),
310
0
                                         data.mVariations.get(),
311
0
                                         data.mNumVariations);
312
0
  if (!scaled) {
313
0
    gfxDevCrash(LogReason::ScaledFontNotFound) << "Failed to create ScaledFont for FontKey " << aKey.mHandle;
314
0
  }
315
0
  data.mScaledFont = scaled;
316
0
  return data.mScaledFont;
317
0
}
318
319
static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
320
                                gfx::IntSize aSize,
321
                                gfx::SurfaceFormat aFormat,
322
                                const uint16_t *aTileSize,
323
                                const mozilla::wr::TileOffset *aTileOffset,
324
                                const mozilla::wr::DeviceUintRect *aDirtyRect,
325
                                Range<uint8_t> aOutput)
326
0
{
327
0
  MOZ_ASSERT(aSize.width > 0 && aSize.height > 0);
328
0
  if (aSize.width <= 0 || aSize.height <= 0) {
329
0
    return false;
330
0
  }
331
0
332
0
  auto stride = aSize.width * gfx::BytesPerPixel(aFormat);
333
0
334
0
  if (aOutput.length() < static_cast<size_t>(aSize.height * stride)) {
335
0
    return false;
336
0
  }
337
0
338
0
  // In bindings.rs we allocate a buffer filled with opaque white.
339
0
  bool uninitialized = false;
340
0
341
0
  RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(
342
0
    gfx::BackendType::SKIA,
343
0
    aOutput.begin().get(),
344
0
    aSize,
345
0
    stride,
346
0
    aFormat,
347
0
    uninitialized
348
0
  );
349
0
350
0
  if (!dt) {
351
0
    return false;
352
0
  }
353
0
354
0
  auto origin = gfx::IntPoint(0, 0);
355
0
  if (aTileOffset) {
356
0
    origin = gfx::IntPoint(aTileOffset->x * *aTileSize, aTileOffset->y * *aTileSize);
357
0
    dt = gfx::Factory::CreateOffsetDrawTarget(dt, origin);
358
0
  }
359
0
360
0
  auto bounds = gfx::IntRect(origin, aSize);
361
0
362
0
  if (aDirtyRect) {
363
0
    Rect dirty(aDirtyRect->origin.x, aDirtyRect->origin.y, aDirtyRect->size.width, aDirtyRect->size.height);
364
0
    dt->PushClipRect(dirty);
365
0
    bounds = bounds.Intersect(IntRect(aDirtyRect->origin.x,
366
0
                                      aDirtyRect->origin.y,
367
0
                                      aDirtyRect->size.width,
368
0
                                      aDirtyRect->size.height));
369
0
  }
370
0
371
0
  struct Reader {
372
0
    const uint8_t *buf;
373
0
    size_t len;
374
0
    size_t pos;
375
0
376
0
    Reader(const uint8_t *buf, size_t len) : buf(buf), len(len), pos(0) {}
377
0
378
0
    size_t ReadSize() {
379
0
      size_t ret;
380
0
      MOZ_RELEASE_ASSERT(pos + sizeof(ret) <= len);
381
0
      memcpy(&ret, buf + pos, sizeof(ret));
382
0
      pos += sizeof(ret);
383
0
      return ret;
384
0
    }
385
0
    int ReadInt() {
386
0
      int ret;
387
0
      MOZ_RELEASE_ASSERT(pos + sizeof(ret) <= len);
388
0
      memcpy(&ret, buf + pos, sizeof(ret));
389
0
      pos += sizeof(ret);
390
0
      return ret;
391
0
    }
392
0
393
0
    IntRectAbsolute ReadBounds() {
394
0
      MOZ_RELEASE_ASSERT(pos + sizeof(int32_t) * 4 <= len);
395
0
      int32_t x1, y1, x2, y2;
396
0
      memcpy(&x1, buf + pos + 0 * sizeof(int32_t), sizeof(x1));
397
0
      memcpy(&y1, buf + pos + 1 * sizeof(int32_t), sizeof(y1));
398
0
      memcpy(&x2, buf + pos + 2 * sizeof(int32_t), sizeof(x2));
399
0
      memcpy(&y2, buf + pos + 3 * sizeof(int32_t), sizeof(y2));
400
0
      pos += sizeof(int32_t) * 4;
401
0
      return IntRectAbsolute(x1, y1, x2, y2);
402
0
    }
403
0
404
0
    layers::BlobFont ReadBlobFont() {
405
0
      MOZ_RELEASE_ASSERT(pos + sizeof(layers::BlobFont) <= len);
406
0
      layers::BlobFont ret;
407
0
      memcpy(&ret, buf + pos, sizeof(ret));
408
0
      pos += sizeof(ret);
409
0
      return ret;
410
0
    }
411
0
412
0
  };
413
0
414
0
  // We try hard to not have empty blobs but we can end up with
415
0
  // them because of CompositorHitTestInfo and merging.
416
0
  MOZ_RELEASE_ASSERT(aBlob.length() >= sizeof(size_t));
417
0
  size_t indexOffset = *(size_t*)(aBlob.end().get()-sizeof(size_t));
418
0
  MOZ_RELEASE_ASSERT(indexOffset <= aBlob.length() - sizeof(size_t));
419
0
  Reader reader(aBlob.begin().get()+indexOffset, aBlob.length()-sizeof(size_t)-indexOffset);
420
0
421
0
  bool ret = true;
422
0
  size_t offset = 0;
423
0
  auto absBounds = IntRectAbsolute::FromRect(bounds);
424
0
  while (reader.pos < reader.len) {
425
0
    size_t end = reader.ReadSize();
426
0
    size_t extra_end = reader.ReadSize();
427
0
    auto combinedBounds = absBounds.Intersect(reader.ReadBounds());
428
0
    if (combinedBounds.IsEmpty()) {
429
0
      offset = extra_end;
430
0
      continue;
431
0
    }
432
0
433
0
    layers::WebRenderTranslator translator(dt);
434
0
435
0
    MOZ_RELEASE_ASSERT(extra_end >= end);
436
0
    MOZ_RELEASE_ASSERT(extra_end < aBlob.length());
437
0
    Reader fontReader(aBlob.begin().get() + end, extra_end - end);
438
0
    size_t count = fontReader.ReadSize();
439
0
    for (size_t i = 0; i < count; i++) {
440
0
      layers::BlobFont blobFont = fontReader.ReadBlobFont();
441
0
      RefPtr<ScaledFont> scaledFont = GetScaledFont(&translator, blobFont.mFontInstanceKey);
442
0
      translator.AddScaledFont(blobFont.mScaledFontPtr, scaledFont);
443
0
    }
444
0
445
0
    Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);
446
0
    ret = translator.TranslateRecording((char*)blob.begin().get(), blob.length());
447
0
    MOZ_RELEASE_ASSERT(ret);
448
0
    offset = extra_end;
449
0
  }
450
0
451
0
  if (gfxPrefs::WebRenderBlobPaintFlashing()) {
452
0
    dt->SetTransform(gfx::Matrix());
453
0
    float r = float(rand()) / RAND_MAX;
454
0
    float g = float(rand()) / RAND_MAX;
455
0
    float b = float(rand()) / RAND_MAX;
456
0
    dt->FillRect(gfx::Rect(origin.x, origin.y, aSize.width, aSize.height), gfx::ColorPattern(gfx::Color(r, g, b, 0.5)));
457
0
  }
458
0
459
0
  if (aDirtyRect) {
460
0
    dt->PopClip();
461
0
  }
462
0
463
#if 0
464
  static int i = 0;
465
  char filename[40];
466
  sprintf(filename, "out%d.png", i++);
467
  gfxUtils::WriteAsPNG(dt, filename);
468
#endif
469
470
0
  return ret;
471
0
}
472
473
} // namespace
474
} // namespace
475
476
extern "C" {
477
478
bool wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob,
479
                        uint32_t width, uint32_t height,
480
                        mozilla::wr::ImageFormat aFormat,
481
                        const uint16_t *aTileSize,
482
                        const mozilla::wr::TileOffset *aTileOffset,
483
                        const mozilla::wr::DeviceUintRect *aDirtyRect,
484
                        mozilla::wr::MutByteSlice output)
485
0
{
486
0
  return mozilla::wr::Moz2DRenderCallback(mozilla::wr::ByteSliceToRange(blob),
487
0
                                          mozilla::gfx::IntSize(width, height),
488
0
                                          mozilla::wr::ImageFormatToSurfaceFormat(aFormat),
489
0
                                          aTileSize,
490
0
                                          aTileOffset,
491
0
                                          aDirtyRect,
492
0
                                          mozilla::wr::MutByteSliceToRange(output));
493
0
}
494
495
} // extern
496