Bug 1380014. Share fonts with WebRender. r=lsalzman
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Fri, 27 Oct 2017 18:21:27 -0400
changeset 688148 2a824a8a2277bc8f725b959be4b1962d8593b02f
parent 688147 006a45a50d903db5a3bfd9e4a70832ae3fd7519c
child 688149 6aeb83b2225071a5daeacc8ad2b02e92018fd7f6
push id86669
push userhikezoe@mozilla.com
push dateSat, 28 Oct 2017 10:13:18 +0000
reviewerslsalzman
bugs1380014
milestone58.0a1
Bug 1380014. Share fonts with WebRender. r=lsalzman This changes the serialization format a little bit. We now have an index at the end of the blob. This is currently used to store a list of the used font keys. In the future we'll add rects and can use it for invalidation.
gfx/2d/DrawEventRecorder.cpp
gfx/2d/DrawEventRecorder.h
gfx/2d/InlineTranslator.cpp
gfx/2d/InlineTranslator.h
gfx/2d/RecordedEvent.cpp
gfx/2d/RecordedEvent.h
gfx/2d/RecordedEventImpl.h
gfx/layers/wr/WebRenderCommandBuilder.cpp
gfx/webrender_bindings/Moz2DImageRenderer.cpp
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/src/moz2d_renderer.rs
--- a/gfx/2d/DrawEventRecorder.cpp
+++ b/gfx/2d/DrawEventRecorder.cpp
@@ -74,29 +74,61 @@ DrawEventRecorderFile::Close()
   mOutputStream.close();
 }
 
 DrawEventRecorderMemory::DrawEventRecorderMemory()
 {
   WriteHeader(mOutputStream);
 }
 
+DrawEventRecorderMemory::DrawEventRecorderMemory(const SerializeResourcesFn &aFn) :
+  mSerializeCallback(aFn)
+{
+  mExternalFonts = true;
+  WriteHeader(mOutputStream);
+}
+
+
 void
 DrawEventRecorderMemory::Flush()
 {
 }
 
+void
+DrawEventRecorderMemory::FlushItem(IntRect aRect)
+{
+  DetatchResources();
+  WriteElement(mIndex, mOutputStream.mLength);
+  mSerializeCallback(mOutputStream, mUnscaledFonts);
+  WriteElement(mIndex, mOutputStream.mLength);
+  ClearResources();
+}
+
+void
+DrawEventRecorderMemory::Finish()
+{
+  size_t indexOffset = mOutputStream.mLength;
+  // write out the index
+  mOutputStream.write(mIndex.mData, mIndex.mLength);
+  mIndex = MemStream();
+  // write out the offset of the Index to the end of the output stream
+  WriteElement(mOutputStream, indexOffset);
+  ClearResources();
+}
+
+
 size_t
 DrawEventRecorderMemory::RecordingSize()
 {
   return mOutputStream.mLength;
 }
 
 void
 DrawEventRecorderMemory::WipeRecording()
 {
   mOutputStream = MemStream();
+  mIndex = MemStream();
 
   WriteHeader(mOutputStream);
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/DrawEventRecorder.h
+++ b/gfx/2d/DrawEventRecorder.h
@@ -9,40 +9,51 @@
 #include "2D.h"
 #include "RecordedEvent.h"
 #include "RecordingTypes.h"
 #include <ostream>
 #include <fstream>
 
 #include <unordered_set>
 #include <unordered_map>
+#include <functional>
 
 namespace mozilla {
 namespace gfx {
 
 class PathRecording;
 
 class DrawEventRecorderPrivate : public DrawEventRecorder
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPrivate)
   DrawEventRecorderPrivate();
   virtual ~DrawEventRecorderPrivate() { }
-  virtual void Finish() {
+  virtual void Finish() { ClearResources(); }
+  virtual void FlushItem(IntRect) { }
+  void DetatchResources() {
     // The iteration is a bit awkward here because our iterator will
     // be invalidated by the removal
     for (auto font = mStoredFonts.begin(); font != mStoredFonts.end(); ) {
       auto oldFont = font++;
       (*oldFont)->RemoveUserData(reinterpret_cast<UserDataKey*>(this));
     }
     for (auto surface = mStoredSurfaces.begin(); surface != mStoredSurfaces.end(); ) {
       auto oldSurface = surface++;
       (*oldSurface)->RemoveUserData(reinterpret_cast<UserDataKey*>(this));
     }
+    mStoredFonts.clear();
+    mStoredSurfaces.clear();
+  }
 
+  void ClearResources() {
+    mUnscaledFonts.clear();
+    mStoredObjects.clear();
+    mStoredFontData.clear();
+    mUnscaledFontMap.clear();
   }
 
   template<class S>
   void WriteHeader(S& aStream) {
     WriteElement(aStream, kMagicInt);
     WriteElement(aStream, kMajorRevision);
     WriteElement(aStream, kMinorRevision);
   }
@@ -143,44 +154,57 @@ public:
   void Close();
 
 private:
   void Flush() override;
 
   std::ofstream mOutputStream;
 };
 
+typedef std::function<void(MemStream &aStream, std::vector<RefPtr<UnscaledFont>> &aUnscaledFonts)> SerializeResourcesFn;
+
 // WARNING: This should not be used in its existing state because
 // it is likely to OOM because of large continguous allocations.
 class DrawEventRecorderMemory final : public DrawEventRecorderPrivate
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderMemory, override)
 
   /**
    * Constructs a DrawEventRecorder that stores the recording in memory.
    */
   DrawEventRecorderMemory();
+  explicit DrawEventRecorderMemory(const SerializeResourcesFn &aSerialize);
 
   void RecordEvent(const RecordedEvent &aEvent) override;
 
   /**
    * @return the current size of the recording (in chars).
    */
   size_t RecordingSize();
 
   /**
    * Wipes the internal recording buffer, but the recorder does NOT forget which
    * objects it has recorded. This can be used so that a recording can be copied
    * and processed in chunks, releasing memory as it goes.
    */
   void WipeRecording();
+  void Finish() override;
+  void FlushItem(IntRect) override;
 
   MemStream mOutputStream;
+  /* The index stream is of the form:
+   * ItemIndex { size_t dataEnd; size_t extraDataEnd; }
+   * It gets concatenated to the end of mOutputStream in Finish()
+   * The last size_t in the stream is offset of the begining of the
+   * index.
+   */
+  MemStream mIndex;
 private:
+  SerializeResourcesFn mSerializeCallback;
   ~DrawEventRecorderMemory() {};
 
   void Flush() override;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
--- a/gfx/2d/InlineTranslator.cpp
+++ b/gfx/2d/InlineTranslator.cpp
@@ -47,16 +47,17 @@ InlineTranslator::TranslateRecording(cha
       return !eof();
     }
 
     char *mData;
     char *mEnd;
   };
   MemReader reader(aData, aLen);
 
+
   uint32_t magicInt;
   ReadElement(reader, magicInt);
   if (magicInt != mozilla::gfx::kMagicInt) {
     return false;
   }
 
   uint16_t majorRevision;
   ReadElement(reader, majorRevision);
--- a/gfx/2d/InlineTranslator.h
+++ b/gfx/2d/InlineTranslator.h
@@ -123,16 +123,17 @@ public:
 
   void AddScaledFont(ReferencePtr aRefPtr, ScaledFont *aScaledFont) final
   {
     mScaledFonts.Put(aRefPtr, aScaledFont);
   }
 
   void AddUnscaledFont(ReferencePtr aRefPtr, UnscaledFont *aUnscaledFont) final
   {
+    mUnscaledFontTable.push_back(aUnscaledFont);
     mUnscaledFonts.Put(aRefPtr, aUnscaledFont);
   }
 
   void AddNativeFontResource(uint64_t aKey,
                              NativeFontResource *aScaledFontResouce) final
   {
     mNativeFontResources.Put(aKey, aScaledFontResouce);
   }
--- a/gfx/2d/RecordedEvent.cpp
+++ b/gfx/2d/RecordedEvent.cpp
@@ -112,16 +112,50 @@ RecordedEvent::GetEventName(EventType aT
     return "UnscaledFontCreation";
   case UNSCALEDFONTDESTRUCTION:
     return "UnscaledFontDestruction";
   default:
     return "Unknown";
   }
 }
 
+template<class S>
+void RecordedEvent::RecordUnscaledFontImpl(UnscaledFont *aUnscaledFont, S& aOutput) {
+  RecordedFontData fontData(aUnscaledFont);
+  RecordedFontDetails fontDetails;
+  if (fontData.GetFontDetails(fontDetails)) {
+    // Try to serialise the whole font, just in case this is a web font that
+    // is not present on the system.
+    WriteElement(aOutput, fontData.mType);
+    fontData.RecordToStream(aOutput);
+
+    auto r = RecordedUnscaledFontCreation(aUnscaledFont, fontDetails);
+    WriteElement(aOutput, r.mType);
+    r.RecordToStream(aOutput);
+  } else {
+    // If that fails, record just the font description and try to load it from
+    // the system on the other side.
+    RecordedFontDescriptor fontDesc(aUnscaledFont);
+    if (fontDesc.IsValid()) {
+      WriteElement(aOutput, fontDesc.RecordedEvent::mType);
+      fontDesc.RecordToStream(aOutput);
+    } else {
+      gfxWarning() << "DrawTargetRecording::FillGlyphs failed to serialise UnscaledFont";
+    }
+  }
+}
+
+void RecordedEvent::RecordUnscaledFont(UnscaledFont *aUnscaledFont, std::ostream *aOutput) {
+  RecordUnscaledFontImpl(aUnscaledFont, *aOutput);
+}
+
+void RecordedEvent::RecordUnscaledFont(UnscaledFont *aUnscaledFont, MemStream &aOutput) {
+  RecordUnscaledFontImpl(aUnscaledFont, aOutput);
+}
+
 already_AddRefed<DrawTarget>
 Translator::CreateDrawTarget(ReferencePtr aRefPtr, const IntSize &aSize,
                              SurfaceFormat aFormat)
 {
   RefPtr<DrawTarget> newDT =
     GetReferenceDrawTarget()->CreateSimilarDrawTarget(aSize, aFormat);
   AddDrawTarget(aRefPtr, newDT);
   return newDT.forget();
--- a/gfx/2d/RecordedEvent.h
+++ b/gfx/2d/RecordedEvent.h
@@ -308,16 +308,20 @@ public:
   template<class S, class F>
   static bool DoWithEvent(S &aStream, EventType aType, F f);
 
   EventType GetType() const { return (EventType)mType; }
 protected:
   friend class DrawEventRecorderPrivate;
   friend class DrawEventRecorderFile;
   friend class DrawEventRecorderMemory;
+  static void RecordUnscaledFont(UnscaledFont *aUnscaledFont, std::ostream *aOutput);
+  static void RecordUnscaledFont(UnscaledFont *aUnscaledFont, MemStream &aOutput);
+  template<class S>
+  static void RecordUnscaledFontImpl(UnscaledFont *aUnscaledFont, S &aOutput);
 
   MOZ_IMPLICIT RecordedEvent(int32_t aType) : mType(aType)
   {}
 
   int32_t mType;
   std::vector<Float> mDashPatternStorage;
 };
 
--- a/gfx/2d/RecordedEventImpl.h
+++ b/gfx/2d/RecordedEventImpl.h
@@ -17,16 +17,17 @@
 #include "SFNTData.h"
 
 namespace mozilla {
 namespace gfx {
 
 template<class Derived>
 class RecordedEventDerived : public RecordedEvent {
   using RecordedEvent::RecordedEvent;
+  public:
   void RecordToStream(std::ostream &aStream) const {
     static_cast<const Derived*>(this)->Record(aStream);
   }
   void RecordToStream(EventStream& aStream) const {
     static_cast<const Derived*>(this)->Record(aStream);
   }
   void RecordToStream(MemStream &aStream) const {
     SizeCollector size;
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -20,16 +20,18 @@
 #include "nsDisplayListInvalidation.h"
 #include "WebRenderCanvasRenderer.h"
 #include "LayersLogging.h"
 #include "LayerTreeInvalidation.h"
 
 namespace mozilla {
 namespace layers {
 
+using namespace gfx;
+
 void WebRenderCommandBuilder::Destroy()
 {
   mLastCanvasDatas.Clear();
   RemoveUnusedAndResetWebRenderUserData();
 }
 
 void
 WebRenderCommandBuilder::EmptyTransaction()
@@ -495,22 +497,30 @@ WebRenderCommandBuilder::GenerateFallbac
 
   if (needPaint || !fallbackData->GetKey()) {
     gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK ?
                                                       gfx::SurfaceFormat::A8 : gfx::SurfaceFormat::B8G8R8A8;
     if (gfxPrefs::WebRenderBlobImages()) {
       bool snapped;
       bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(clippedBounds);
 
-      RefPtr<gfx::DrawEventRecorderMemory> recorder = MakeAndAddRef<gfx::DrawEventRecorderMemory>();
+      RefPtr<gfx::DrawEventRecorderMemory> recorder = MakeAndAddRef<gfx::DrawEventRecorderMemory>([&] (MemStream &aStream, std::vector<RefPtr<UnscaledFont>> &aUnscaledFonts) {
+          size_t count = aUnscaledFonts.size();
+          aStream.write((const char*)&count, sizeof(count));
+          for (auto unscaled : aUnscaledFonts) {
+            wr::FontKey key = mManager->WrBridge()->GetFontKeyForUnscaledFont(unscaled);
+            aStream.write((const char*)&key, sizeof(key));
+          }
+        });
       RefPtr<gfx::DrawTarget> dummyDt =
         gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
       RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt, paintSize.ToUnknownSize());
       PaintItemByDrawTarget(aItem, dt, paintRect, offset, aDisplayListBuilder,
                             fallbackData->mBasicLayerManager, mManager, scale);
+      recorder->FlushItem(IntRect());
       recorder->Finish();
 
       Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, recorder->mOutputStream.mLength);
       wr::ImageKey key = mManager->WrBridge()->GetNextImageKey();
       wr::ImageDescriptor descriptor(paintSize.ToUnknownSize(), 0, dt->GetFormat(), isOpaque);
       if (!aResources.AddBlobImage(key, descriptor, bytes)) {
         return nullptr;
       }
--- a/gfx/webrender_bindings/Moz2DImageRenderer.cpp
+++ b/gfx/webrender_bindings/Moz2DImageRenderer.cpp
@@ -8,28 +8,107 @@
 #include "mozilla/Range.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/InlineTranslator.h"
 #include "mozilla/gfx/RecordedEvent.h"
 #include "WebRenderTypes.h"
 #include "webrender_ffi.h"
 
 #include <iostream>
+#include <unordered_map>
 
 #ifdef MOZ_ENABLE_FREETYPE
 #include "mozilla/ThreadLocal.h"
 #endif
 
+namespace std {
+  template <>
+    struct hash<mozilla::wr::FontKey>{
+      public :
+        size_t operator()(const mozilla::wr::FontKey &key ) const
+        {
+          return hash<size_t>()(mozilla::wr::AsUint64(key));
+        }
+    };
+};
+
+
+
 namespace mozilla {
+
+using namespace gfx;
+
 namespace wr {
 
 #ifdef MOZ_ENABLE_FREETYPE
 static MOZ_THREAD_LOCAL(FT_Library) sFTLibrary;
 #endif
 
+struct FontTemplate {
+  void *mData;
+  size_t mSize;
+  int mIndex;
+  const VecU8 *mVec;
+};
+
+// we need to do special things for linux so that we have fonts per backend
+std::unordered_map<FontKey, FontTemplate> sFontDataTable;
+
+extern "C" {
+void
+AddFontData(wr::FontKey aKey, void *aData, size_t aSize, int aIndex, ArcVecU8 *aVec) {
+  auto i = sFontDataTable.find(aKey);
+  if (i == sFontDataTable.end()) {
+    FontTemplate font;
+    font.mData = aData;
+    font.mSize = aSize;
+    font.mIndex = aIndex;
+    font.mVec = wr_add_ref_arc(aVec);
+    sFontDataTable[aKey] = font;
+  }
+}
+
+void
+DeleteFontData(wr::FontKey aKey) {
+  auto i = sFontDataTable.find(aKey);
+  if (i != sFontDataTable.end()) {
+    sFontDataTable.erase(i);
+    wr_dec_ref_arc(i->second.mVec);
+  }
+}
+}
+
+RefPtr<UnscaledFont>
+GetUnscaledFont(Translator *aTranslator, wr::FontKey key) {
+  MOZ_ASSERT(sFontDataTable.find(key) != sFontDataTable.end());
+  auto data = sFontDataTable[key];
+  FontType type =
+#ifdef XP_MACOSX
+    FontType::MAC;
+#elif XP_WIN
+    FontType::DWRITE;
+#elif ANDROID
+    FontType::FREETYPE;
+#else
+    FontType::FONTCONFIG;
+#endif
+  // makes a copy of the data
+  RefPtr<NativeFontResource> fontResource = Factory::CreateNativeFontResource((uint8_t*)data.mData, data.mSize,
+                                                                              aTranslator->GetReferenceDrawTarget()->GetBackendType(),
+                                                                              type,
+                                                                              aTranslator->GetFontContext());
+  RefPtr<UnscaledFont> unscaledFont;
+  if (fontResource) {
+    // Instance data is only needed for GDI fonts which webrender does not
+    // support.
+    unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0);
+  }
+  return unscaledFont;
+}
+
 static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
                                 gfx::IntSize aSize,
                                 gfx::SurfaceFormat aFormat,
                                 const uint16_t *aTileSize,
                                 const mozilla::wr::TileOffset *aTileOffset,
                                 Range<uint8_t> aOutput)
 {
   MOZ_ASSERT(aSize.width > 0 && aSize.height > 0);
@@ -81,19 +160,53 @@ static bool Moz2DRenderCallback(const Ra
     gfx::Tile tile;
     tile.mDrawTarget = dt;
     tile.mTileOrigin = gfx::IntPoint(aTileOffset->x * *aTileSize, aTileOffset->y * *aTileSize);
     tileset.mTiles = &tile;
     tileset.mTileCount = 1;
     dt = gfx::Factory::CreateTiledDrawTarget(tileset);
   }
 
-  gfx::InlineTranslator translator(dt, fontContext);
+  struct Reader {
+    const uint8_t *buf;
+    size_t len;
+    size_t pos;
+
+    Reader(const uint8_t *buf, size_t len) : buf(buf), len(len), pos(0) {}
+
+    size_t ReadSize() {
+      size_t ret;
+      MOZ_RELEASE_ASSERT(pos + sizeof(ret) <= len);
+      memcpy(&ret, buf + pos, sizeof(ret));
+      pos += sizeof(ret);
+      return ret;
+    }
+  };
+  //XXX: Make safe
+  size_t indexOffset = *(size_t*)(aBlob.end().get()-sizeof(size_t));
+  Reader reader(aBlob.begin().get()+indexOffset, aBlob.length()-sizeof(size_t)-indexOffset);
 
-  auto ret = translator.TranslateRecording((char*)aBlob.begin().get(), aBlob.length());
+  bool ret;
+  size_t offset = 0;
+  while (reader.pos < reader.len) {
+    size_t end = reader.ReadSize();
+    size_t extra_end = reader.ReadSize();
+
+    gfx::InlineTranslator translator(dt, fontContext);
+
+    size_t count = *(size_t*)(aBlob.begin().get() + end);
+    for (size_t i = 0; i < count; i++) {
+      wr::FontKey key = *(wr::FontKey*)(aBlob.begin() + end + sizeof(count) + sizeof(wr::FontKey)*i).get();
+      RefPtr<UnscaledFont> font = GetUnscaledFont(&translator, key);
+      translator.AddUnscaledFont(0, font);
+    }
+    Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);
+    ret = translator.TranslateRecording((char*)blob.begin().get(), blob.length());
+    offset = extra_end;
+  }
 
 #if 0
   static int i = 0;
   char filename[40];
   sprintf(filename, "out%d.png", i++);
   gfxUtils::WriteAsPNG(dt, filename);
 #endif
 
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -33,17 +33,17 @@ type WrEpoch = Epoch;
 /// cbindgen:derive-neq=true
 type WrIdNamespace = IdNamespace;
 
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrPipelineId = PipelineId;
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrImageKey = ImageKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
-type WrFontKey = FontKey;
+pub type WrFontKey = FontKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrFontInstanceKey = FontInstanceKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrYuvColorSpace = YuvColorSpace;
 
 fn make_slice<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
     if ptr.is_null() {
         &[]
--- a/gfx/webrender_bindings/src/moz2d_renderer.rs
+++ b/gfx/webrender_bindings/src/moz2d_renderer.rs
@@ -1,13 +1,15 @@
+#![allow(improper_ctypes)] // this is needed so that rustc doesn't complain about passing the &Arc<Vec> to an extern function
 use webrender_api::*;
 use bindings::{ByteSlice, MutByteSlice, wr_moz2d_render_cb};
 use rayon::ThreadPool;
 
 use std::collections::hash_map::{HashMap, Entry};
+use std::mem;
 use std::ptr;
 use std::sync::mpsc::{channel, Sender, Receiver};
 use std::sync::Arc;
 
 pub struct Moz2dImageRenderer {
     blob_commands: HashMap<ImageKey, (Arc<BlobImageData>, Option<TileSize>)>,
 
     // The images rendered in the current frame (not kept here between frames)
@@ -21,32 +23,74 @@ pub struct Moz2dImageRenderer {
 
 fn option_to_nullable<T>(option: &Option<T>) -> *const T {
     match option {
         &Some(ref x) => { x as *const T }
         &None => { ptr::null() }
     }
 }
 
+fn to_usize(slice: &[u8]) -> usize {
+    convert_from_bytes(slice)
+}
+
+fn convert_from_bytes<T>(slice: &[u8]) -> T {
+    assert!(mem::size_of::<T>() <= slice.len());
+    let mut ret: T;
+    unsafe {
+        ret = mem::uninitialized();
+        ptr::copy_nonoverlapping(slice.as_ptr(),
+                                 &mut ret as *mut T as *mut u8,
+                                 mem::size_of::<T>());
+    }
+    ret
+}
+
+struct BufReader<'a>
+{
+    buf: &'a[u8],
+    pos: usize,
+}
+
+impl<'a> BufReader<'a> {
+    fn new(buf: &'a[u8]) -> BufReader<'a> {
+        BufReader{ buf: buf, pos: 0 }
+    }
+
+    fn read<T>(&mut self) -> T {
+        let ret = convert_from_bytes(&self.buf[self.pos..]);
+        self.pos += mem::size_of::<T>();
+        ret
+    }
+
+    fn read_font_key(&mut self) -> FontKey {
+        self.read()
+    }
+
+    fn read_usize(&mut self) -> usize {
+        self.read()
+    }
+}
+
 impl BlobImageRenderer for Moz2dImageRenderer {
     fn add(&mut self, key: ImageKey, data: BlobImageData, tiling: Option<TileSize>) {
         self.blob_commands.insert(key, (Arc::new(data), tiling));
     }
 
     fn update(&mut self, key: ImageKey, data: BlobImageData, _dirty_rect: Option<DeviceUintRect>) {
         let entry = self.blob_commands.get_mut(&key).unwrap();
         entry.0 = Arc::new(data);
     }
 
     fn delete(&mut self, key: ImageKey) {
         self.blob_commands.remove(&key);
     }
 
     fn request(&mut self,
-               _resources: &BlobImageResources,
+               resources: &BlobImageResources,
                request: BlobImageRequest,
                descriptor: &BlobImageDescriptor,
                _dirty_rect: Option<DeviceUintRect>) {
         debug_assert!(!self.rendered_images.contains_key(&request));
         // TODO: implement tiling.
 
         // Add None in the map of rendered images. This makes it possible to differentiate
         // between commands that aren't finished yet (entry in the map is equal to None) and
@@ -56,16 +100,39 @@ impl BlobImageRenderer for Moz2dImageRen
 
         let tx = self.tx.clone();
         let descriptor = descriptor.clone();
         let blob = &self.blob_commands[&request.key];
         let tile_size = blob.1;
         let commands = Arc::clone(&blob.0);
 
 
+        fn process_fonts(mut extra_data: BufReader, resources: &BlobImageResources) {
+            let font_count = extra_data.read_usize();
+            for _ in 0..font_count {
+                let key = extra_data.read_font_key();
+                let template = resources.get_font_data(key);
+                if let &FontTemplate::Raw(ref data, ref index) = template {
+                    unsafe { AddFontData(key, data.as_ptr(), data.len(), *index, data); }
+                }
+                resources.get_font_data(key);
+            }
+        }
+        let index_offset_pos = commands.len()-mem::size_of::<usize>();
+
+        let index_offset = to_usize(&commands[index_offset_pos..]);
+        {
+            let mut index = BufReader::new(&commands[index_offset..index_offset_pos]);
+            while index.pos < index.buf.len() {
+                let end = index.read_usize();
+                let extra_end = index.read_usize();
+                process_fonts(BufReader::new(&commands[end..extra_end]), resources);
+            }
+        }
+
         self.workers.spawn(move || {
             let buf_size = (descriptor.width
                 * descriptor.height
                 * descriptor.format.bytes_per_pixel()) as usize;
             let mut output = vec![255u8; buf_size];
 
             let result = unsafe {
                 if wr_moz2d_render_cb(
@@ -115,24 +182,31 @@ impl BlobImageRenderer for Moz2dImageRen
                 return result
             }
             self.rendered_images.insert(req, Some(result));
         }
 
         // If we break out of the loop above it means the channel closed unexpectedly.
         Err(BlobImageError::Other("Channel closed".into()))
     }
-
-    fn delete_font(&mut self, _font: FontKey) {
+    fn delete_font(&mut self, font: FontKey) {
+        unsafe { DeleteFontData(font); }
     }
 
     fn delete_font_instance(&mut self, _key: FontInstanceKey) {
     }
 }
 
+use bindings::WrFontKey;
+extern "C" {
+    #[allow(improper_ctypes)]
+    fn AddFontData(key: WrFontKey, data: *const u8, size: usize, index: u32, vec: &Arc<Vec<u8>>);
+    fn DeleteFontData(key: WrFontKey);
+}
+
 impl Moz2dImageRenderer {
     pub fn new(workers: Arc<ThreadPool>) -> Self {
         let (tx, rx) = channel();
         Moz2dImageRenderer {
             blob_commands: HashMap::new(),
             rendered_images: HashMap::new(),
             workers: workers,
             tx: tx,