Improve Ganesh helpers.

Properly reset and flush the Ganesh context when needed.

When intermixing Ganesh rendering with other GL operations, the
Ganesh renderer's cached GPU state can get out of sync with the
actual state of the GL context, resulting in broken behavior.

To fix this problem, reset the Ganesh context when entering its
scope and flush it when exiting its scope.  This operation has a
small performance cost but only when entering and exiting the scope
which should be infrequent (ideally no more than once per frame).

Added a helper for creating SkImages from a texture and updated
the GaneshTextureSurface to use the new GrGLTextureInfo type
which was introduced in the latest Skia roll.

Moved helpers into the mojo::skia namespace.

R=abarth@google.com

Review URL: https://codereview.chromium.org/1534033002 .
diff --git a/mojo/skia/BUILD.gn b/mojo/skia/BUILD.gn
index 01459f2..60baa2f 100644
--- a/mojo/skia/BUILD.gn
+++ b/mojo/skia/BUILD.gn
@@ -8,8 +8,12 @@
     "ganesh_context.h",
     "ganesh_framebuffer_surface.cc",
     "ganesh_framebuffer_surface.h",
+    "ganesh_image_factory.cc",
+    "ganesh_image_factory.h",
     "ganesh_texture_surface.cc",
     "ganesh_texture_surface.h",
+    "type_converters.cc",
+    "type_converters.h",
   ]
 
   deps = [
@@ -18,6 +22,7 @@
     "//mojo/gpu",
     "//mojo/public/c/gpu",
     "//mojo/public/c/gpu:MGL",
+    "//mojo/services/geometry/interfaces",
     "//skia",
   ]
 }
diff --git a/mojo/skia/ganesh_context.cc b/mojo/skia/ganesh_context.cc
index 76b7c59..735a902 100644
--- a/mojo/skia/ganesh_context.cc
+++ b/mojo/skia/ganesh_context.cc
@@ -9,63 +9,80 @@
 #include "third_party/skia/include/gpu/gl/GrGLInterface.h"
 
 namespace mojo {
-namespace {
+namespace skia {
 
 // The limit of the number of GPU resources we hold in the GrContext's
 // GPU cache.
-const int kMaxGaneshResourceCacheCount = 2048;
+constexpr int kMaxGaneshResourceCacheCount = 2048;
 
 // The limit of the bytes allocated toward GPU resources in the GrContext's
 // GPU cache.
-const size_t kMaxGaneshResourceCacheBytes = 96 * 1024 * 1024;
-}
-
-GaneshContext::Scope::Scope(GaneshContext* context)
-    : previous_(gles2::GetGLContext()) {
-  auto gl = context->gl_context_->gl();
-  DCHECK(gl);
-  gles2::SetGLContext(gl);
-  DCHECK(gles2::GetGLContext());
-}
-
-GaneshContext::Scope::~Scope() {
-  gles2::SetGLContext(previous_);
-}
+constexpr size_t kMaxGaneshResourceCacheBytes = 96 * 1024 * 1024;
 
 GaneshContext::GaneshContext(base::WeakPtr<GLContext> gl_context)
     : gl_context_(gl_context) {
   DCHECK(gl_context_);
   gl_context_->AddObserver(this);
+
   Scope scope(this);
 
-  skia::RefPtr<GrGLInterface> interface =
-      skia::AdoptRef(skia_bindings::CreateMojoSkiaGLBinding());
+  ::skia::RefPtr<GrGLInterface> interface =
+      ::skia::AdoptRef(CreateMojoSkiaGLBinding());
   DCHECK(interface);
 
-  context_ = skia::AdoptRef(GrContext::Create(
+  gr_context_ = ::skia::AdoptRef(GrContext::Create(
       kOpenGL_GrBackend, reinterpret_cast<GrBackendContext>(interface.get())));
-  DCHECK(context_);
-  context_->setResourceCacheLimits(kMaxGaneshResourceCacheCount,
-                                   kMaxGaneshResourceCacheBytes);
+  DCHECK(gr_context_);
+
+  gr_context_->setResourceCacheLimits(kMaxGaneshResourceCacheCount,
+                                      kMaxGaneshResourceCacheBytes);
 }
 
 GaneshContext::~GaneshContext() {
-  if (context_) {
-    Scope scope(this);
-    context_.clear();
-  }
-  if (gl_context_.get())
+  if (gl_context_)
     gl_context_->RemoveObserver(this);
-}
 
-bool GaneshContext::InScope() const {
-  return gl_context_->IsCurrent();
+  ReleaseContext();
 }
 
 void GaneshContext::OnContextLost() {
-  context_->abandonContext();
-  context_.clear();
+  ReleaseContext();
+}
+
+void GaneshContext::ReleaseContext() {
+  Scope(this);
+  gr_context_->abandonContext();
+  gr_context_.clear();
   gl_context_.reset();
 }
 
+void GaneshContext::EnterScope() {
+  CHECK(!scope_entered_);
+  scope_entered_ = true;
+
+  if (gl_context_) {
+    previous_mgl_context_ = MGLGetCurrentContext();
+    gl_context_->MakeCurrent();
+
+    // Reset the Ganesh context when entering its scope in case the caller
+    // performed low-level GL operations which might interfere with Ganesh's
+    // state expectations.
+    if (gr_context_)
+      gr_context_->resetContext();
+  }
+}
+
+void GaneshContext::ExitScope() {
+  CHECK(scope_entered_);
+  scope_entered_ = false;
+
+  // Flush the Ganesh context when exiting its scope.
+  if (gr_context_)
+    gr_context_->flush();
+
+  MGLMakeCurrent(previous_mgl_context_);
+  previous_mgl_context_ = MGL_NO_CONTEXT;
+}
+
+}  // namespace skia
 }  // namespace mojo
diff --git a/mojo/skia/ganesh_context.h b/mojo/skia/ganesh_context.h
index 149981d..e3f72db 100644
--- a/mojo/skia/ganesh_context.h
+++ b/mojo/skia/ganesh_context.h
@@ -6,49 +6,85 @@
 #define MOJO_SKIA_GANESH_CONTEXT_H_
 
 #include "base/basictypes.h"
+#include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "mojo/gpu/gl_context.h"
 #include "skia/ext/refptr.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 
-namespace gpu {
-namespace gles2 {
-class GLES2Interface;
-}
-}
-
 namespace mojo {
+namespace skia {
 
+// Binds a Ganesh rendering context to a GL context.
+//
+// This object is not thread-safe.
 class GaneshContext : public GLContext::Observer {
  public:
+  // RAII style helper for executing code within a Ganesh environment.
+  //
+  // Note that Ganesh assumes that it owns the state of the GL Context
+  // for the duration while the scope is active.  Take care not to perform
+  // any significant low-level GL operations while in the Ganesh scope
+  // which might disrupt what Ganesh is doing!
+  //
+  // Recursively entering the scope of a particular GaneshContext is not
+  // allowed.
   class Scope {
    public:
-    explicit Scope(GaneshContext*);
-    ~Scope();
+    // Upon entry to the scope, makes the GL context active and resets
+    // the Ganesh context state.
+    explicit Scope(GaneshContext* context) : context_(context) {
+      DCHECK(context_);
+      context_->EnterScope();
+    }
+
+    // Upon exit from the scope, flushes the Ganesh context state and
+    // restores the prior GL context.
+    ~Scope() { context_->ExitScope(); }
+
+    // Gets the underlying GL context, may be null if the context was lost.
+    //
+    // Be careful when manipulating the GL context from within a Ganesh
+    // scope since the Ganesh renderer caches GL state.  Queries are safe
+    // but operations which modify the state of the GL context, such as binding
+    // textures, should be followed by a call to |GrContext::resetContext|
+    // before performing other Ganesh related actions within the scope.
+    const base::WeakPtr<GLContext>& gl_context() const {
+      return context_->gl_context_;
+    }
+
+    // Gets the Ganesh rendering context, may be null if the context was lost.
+    GrContext* gr_context() const { return context_->gr_context_.get(); }
 
    private:
-    gpu::gles2::GLES2Interface* previous_;
+    GaneshContext* context_;
+
+    DISALLOW_COPY_AND_ASSIGN(Scope);
   };
 
   explicit GaneshContext(base::WeakPtr<GLContext> gl_context);
   ~GaneshContext() override;
 
-  // Note: You must be in a GaneshContext::Scope to use GrContext.
-  GrContext* gr() const {
-    DCHECK(InScope());
-    return context_.get();
-  }
+  // Gets the underlying GL context.
+  const base::WeakPtr<GLContext>& gl_context() const { return gl_context_; }
 
  private:
-  bool InScope() const;
   void OnContextLost() override;
+  void ReleaseContext();
+
+  void EnterScope();
+  void ExitScope();
 
   base::WeakPtr<GLContext> gl_context_;
-  skia::RefPtr<GrContext> context_;
+  ::skia::RefPtr<GrContext> gr_context_;
+
+  bool scope_entered_ = false;
+  MGLContext previous_mgl_context_ = MGL_NO_CONTEXT;
 
   DISALLOW_COPY_AND_ASSIGN(GaneshContext);
 };
 
+}  // namespace skia
 }  // namespace mojo
 
 #endif  // MOJO_SKIA_GANESH_CONTEXT_H_
diff --git a/mojo/skia/ganesh_framebuffer_surface.cc b/mojo/skia/ganesh_framebuffer_surface.cc
index f56d11e..c8ad676 100644
--- a/mojo/skia/ganesh_framebuffer_surface.cc
+++ b/mojo/skia/ganesh_framebuffer_surface.cc
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "mojo/skia/ganesh_framebuffer_surface.h"
+
 #include <GLES2/gl2.h>
 
 #include "base/logging.h"
-#include "mojo/skia/ganesh_framebuffer_surface.h"
 
 namespace mojo {
+namespace skia {
 
-GaneshFramebufferSurface::GaneshFramebufferSurface(GaneshContext* context) {
-  DCHECK(context);
-  GaneshContext::Scope scope(context);
-
+GaneshFramebufferSurface::GaneshFramebufferSurface(
+    const GaneshContext::Scope& scope) {
   GLint samples = 0;
   glGetIntegerv(GL_SAMPLES, &samples);
   GLint stencil_bits = 0;
@@ -32,14 +32,13 @@
   desc.fSampleCnt = samples;
   desc.fStencilBits = stencil_bits;
   desc.fRenderTargetHandle = framebuffer_binding;
-  GrRenderTarget* render_target =
-      context->gr()->textureProvider()->wrapBackendRenderTarget(desc);
-  DCHECK(render_target);
 
-  surface_ = skia::AdoptRef(SkSurface::NewRenderTargetDirect(render_target));
+  surface_ = ::skia::AdoptRef(
+      SkSurface::NewFromBackendRenderTarget(scope.gr_context(), desc, nullptr));
   DCHECK(surface_);
 }
 
 GaneshFramebufferSurface::~GaneshFramebufferSurface() {}
 
+}  // namespace skia
 }  // namespace mojo
diff --git a/mojo/skia/ganesh_framebuffer_surface.h b/mojo/skia/ganesh_framebuffer_surface.h
index 6925b03..99ab149 100644
--- a/mojo/skia/ganesh_framebuffer_surface.h
+++ b/mojo/skia/ganesh_framebuffer_surface.h
@@ -5,14 +5,13 @@
 #ifndef MOJO_SKIA_GANESH_FRAMEBUFFER_SURFACE_H_
 #define MOJO_SKIA_GANESH_FRAMEBUFFER_SURFACE_H_
 
-#include <memory>
-
-#include "mojo/gpu/gl_texture.h"
+#include "base/macros.h"
 #include "mojo/skia/ganesh_context.h"
 #include "skia/ext/refptr.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
 namespace mojo {
+namespace skia {
 
 // This class represents an SkSurface backed by a GL framebuffer, which is
 // appropriate for use with Ganesh.  This is useful for rendering Skia
@@ -21,18 +20,19 @@
  public:
   // Creates a surface that wraps the currently bound GL framebuffer.
   // The size of the surface is determined by querying the current viewport.
-  explicit GaneshFramebufferSurface(GaneshContext* context);
+  explicit GaneshFramebufferSurface(const GaneshContext::Scope& scope);
   ~GaneshFramebufferSurface();
 
   SkSurface* surface() const { return surface_.get(); }
   SkCanvas* canvas() const { return surface_->getCanvas(); }
 
  private:
-  skia::RefPtr<SkSurface> surface_;
+  ::skia::RefPtr<SkSurface> surface_;
 
   DISALLOW_COPY_AND_ASSIGN(GaneshFramebufferSurface);
 };
 
+}  // namespace skia
 }  // namespace mojo
 
 #endif  // MOJO_SKIA_GANESH_FRAMEBUFFER_SURFACE_H_
diff --git a/mojo/skia/ganesh_image_factory.cc b/mojo/skia/ganesh_image_factory.cc
new file mode 100644
index 0000000..5300d99
--- /dev/null
+++ b/mojo/skia/ganesh_image_factory.cc
@@ -0,0 +1,95 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#endif
+
+#include "mojo/skia/ganesh_image_factory.h"
+
+#include "base/logging.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/GrTextureProvider.h"
+#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+
+namespace mojo {
+namespace skia {
+namespace {
+void ReleaseThunk(void* data) {
+  auto release_callback = static_cast<base::Closure*>(data);
+  release_callback->Run();
+  delete release_callback;
+}
+}  // namespace
+
+::skia::RefPtr<SkImage> CreateImageFromTexture(
+    const GaneshContext::Scope& scope,
+    uint32_t texture_id,
+    uint32_t width,
+    uint32_t height,
+    const base::Closure& release_callback) {
+  DCHECK(texture_id);
+  DCHECK(width);
+  DCHECK(height);
+
+  // TODO(jeffbrown): Give the caller more control over these parameters.
+  GrGLTextureInfo info;
+  info.fTarget = GL_TEXTURE_2D;
+  info.fID = texture_id;
+
+  GrBackendTextureDesc desc;
+  desc.fFlags = kRenderTarget_GrBackendTextureFlag;
+  desc.fWidth = width;
+  desc.fHeight = height;
+  desc.fConfig = kSkia8888_GrPixelConfig;
+  desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+  desc.fTextureHandle = reinterpret_cast<GrBackendObject>(&info);
+  return ::skia::AdoptRef(SkImage::NewFromTexture(
+      scope.gr_context(), desc, kPremul_SkAlphaType, &ReleaseThunk,
+      new base::Closure(release_callback)));
+}
+
+MailboxTextureImageGenerator::MailboxTextureImageGenerator(
+    const GLbyte mailbox_name[GL_MAILBOX_SIZE_CHROMIUM],
+    GLuint sync_point,
+    uint32_t width,
+    uint32_t height)
+    : SkImageGenerator(SkImageInfo::MakeN32Premul(width, height)),
+      sync_point_(sync_point) {
+  DCHECK(mailbox_name);
+  memcpy(mailbox_name_, mailbox_name, GL_MAILBOX_SIZE_CHROMIUM);
+}
+
+MailboxTextureImageGenerator::~MailboxTextureImageGenerator() {}
+
+GrTexture* MailboxTextureImageGenerator::onGenerateTexture(
+    GrContext* context,
+    const SkIRect* subset) {
+  if (sync_point_)
+    glWaitSyncPointCHROMIUM(sync_point_);
+
+  GLuint texture_id =
+      glCreateAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox_name_);
+  if (!texture_id)
+    return nullptr;
+
+  // TODO(jeffbrown): Give the caller more control over these parameters.
+  GrGLTextureInfo info;
+  info.fTarget = GL_TEXTURE_2D;
+  info.fID = texture_id;
+
+  GrBackendTextureDesc desc;
+  desc.fFlags = kRenderTarget_GrBackendTextureFlag;
+  desc.fWidth = getInfo().width();
+  desc.fHeight = getInfo().height();
+  desc.fConfig = kSkia8888_GrPixelConfig;
+  desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+  desc.fTextureHandle = reinterpret_cast<GrBackendObject>(&info);
+  return context->textureProvider()->wrapBackendTexture(desc,
+                                                        kAdopt_GrWrapOwnership);
+}
+
+}  // namespace skia
+}  // namespace mojo
diff --git a/mojo/skia/ganesh_image_factory.h b/mojo/skia/ganesh_image_factory.h
new file mode 100644
index 0000000..ae4bfc0
--- /dev/null
+++ b/mojo/skia/ganesh_image_factory.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SKIA_GANESH_IMAGE_FACTORY_H_
+#define MOJO_SKIA_GANESH_IMAGE_FACTORY_H_
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2extmojo.h>
+
+#include "base/callback.h"
+#include "mojo/skia/ganesh_context.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkImageGenerator.h"
+
+class SkImage;
+
+namespace mojo {
+namespace skia {
+
+// Creates an SkImage from a GL texture.
+// The underlying texture must be kept alive for as long as the SkImage exists.
+// Invokes |release_callback| when the SkImage is deleted.
+::skia::RefPtr<SkImage> CreateImageFromTexture(
+    const GaneshContext::Scope& scope,
+    uint32_t texture_id,
+    uint32_t width,
+    uint32_t height,
+    const base::Closure& release_callback);
+
+// Generates backing content for SkImages from a texture mailbox.
+// If |sync_point| is non-zero, inserts a sync point into the command stream
+// before the image is first drawn.
+// It is the responsibility of the client of this class to ensure that
+// the mailbox name is valid at the time when the image is being drawn.
+class MailboxTextureImageGenerator : public SkImageGenerator {
+ public:
+  MailboxTextureImageGenerator(
+      const GLbyte mailbox_name[GL_MAILBOX_SIZE_CHROMIUM],
+      GLuint sync_point,
+      uint32_t width,
+      uint32_t height);
+  ~MailboxTextureImageGenerator() override;
+
+  GrTexture* onGenerateTexture(GrContext* context,
+                               const SkIRect* subset) override;
+
+ private:
+  GLbyte mailbox_name_[GL_MAILBOX_SIZE_CHROMIUM];
+  GLuint sync_point_;
+};
+
+}  // namespace skia
+}  // namespace mojo
+
+#endif  // MOJO_SKIA_GANESH_IMAGE_FACTORY_H_
diff --git a/mojo/skia/ganesh_texture_surface.cc b/mojo/skia/ganesh_texture_surface.cc
index 2eccb81..f58d3ad 100644
--- a/mojo/skia/ganesh_texture_surface.cc
+++ b/mojo/skia/ganesh_texture_surface.cc
@@ -2,34 +2,39 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/logging.h"
 #include "mojo/skia/ganesh_texture_surface.h"
 
-namespace mojo {
+#include <GLES2/gl2.h>
 
-GaneshTextureSurface::GaneshTextureSurface(GaneshContext* context,
+#include "base/logging.h"
+#include "mojo/gpu/gl_texture.h"
+#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+
+namespace mojo {
+namespace skia {
+
+GaneshTextureSurface::GaneshTextureSurface(const GaneshContext::Scope& scope,
                                            std::unique_ptr<GLTexture> texture)
     : texture_(std::move(texture)) {
-  DCHECK(context);
   DCHECK(texture_);
   DCHECK(texture_->texture_id());
   DCHECK(texture_->size().width > 0);
   DCHECK(texture_->size().height > 0);
+  GrGLTextureInfo info;
+  info.fTarget = GL_TEXTURE_2D;
+  info.fID = texture_->texture_id();
 
   GrBackendTextureDesc desc;
   desc.fFlags = kRenderTarget_GrBackendTextureFlag;
+  desc.fOrigin = kTopLeft_GrSurfaceOrigin;
   desc.fWidth = texture_->size().width;
   desc.fHeight = texture_->size().height;
   desc.fConfig = kSkia8888_GrPixelConfig;
-  desc.fOrigin = kTopLeft_GrSurfaceOrigin;
-  desc.fTextureHandle = texture_->texture_id();
+  desc.fSampleCnt = 0;
+  desc.fTextureHandle = reinterpret_cast<GrBackendObject>(&info);
 
-  skia::RefPtr<GrTexture> gr_texture = skia::AdoptRef(
-      context->gr()->textureProvider()->wrapBackendTexture(desc));
-  DCHECK(gr_texture);
-
-  surface_ = skia::AdoptRef(
-      SkSurface::NewRenderTargetDirect(gr_texture->asRenderTarget()));
+  surface_ = ::skia::AdoptRef(
+      SkSurface::NewFromBackendTexture(scope.gr_context(), desc, nullptr));
   DCHECK(surface_);
 }
 
@@ -40,4 +45,5 @@
   return std::move(texture_);
 }
 
+}  // namespace skia
 }  // namespace mojo
diff --git a/mojo/skia/ganesh_texture_surface.h b/mojo/skia/ganesh_texture_surface.h
index cd3b05d..3cf4c36 100644
--- a/mojo/skia/ganesh_texture_surface.h
+++ b/mojo/skia/ganesh_texture_surface.h
@@ -7,12 +7,15 @@
 
 #include <memory>
 
-#include "mojo/gpu/gl_texture.h"
+#include "base/macros.h"
 #include "mojo/skia/ganesh_context.h"
 #include "skia/ext/refptr.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
 namespace mojo {
+class GLTexture;
+
+namespace skia {
 
 // This class represents an SkSurface backed by a GL texture, which is
 // appropriate for use with Ganesh.  This is useful for rendering Skia
@@ -20,7 +23,7 @@
 class GaneshTextureSurface {
  public:
   // Creates a surface that wraps the specified GL texture.
-  GaneshTextureSurface(GaneshContext* context,
+  GaneshTextureSurface(const GaneshContext::Scope& scope,
                        std::unique_ptr<GLTexture> texture);
   ~GaneshTextureSurface();
 
@@ -32,11 +35,12 @@
 
  private:
   std::unique_ptr<GLTexture> texture_;
-  skia::RefPtr<SkSurface> surface_;
+  ::skia::RefPtr<SkSurface> surface_;
 
   DISALLOW_COPY_AND_ASSIGN(GaneshTextureSurface);
 };
 
+}  // namespace skia
 }  // namespace mojo
 
 #endif  // MOJO_SKIA_GANESH_TEXTURE_SURFACE_H_
diff --git a/mojo/skia/gl_bindings_skia.cc b/mojo/skia/gl_bindings_skia.cc
index 68f8494..4ca10bd 100644
--- a/mojo/skia/gl_bindings_skia.cc
+++ b/mojo/skia/gl_bindings_skia.cc
@@ -12,7 +12,8 @@
 #include "mojo/public/c/gpu/GLES2/gl2extmojo.h"
 #include "third_party/skia/include/gpu/gl/GrGLInterface.h"
 
-namespace skia_bindings {
+namespace mojo {
+namespace skia {
 
 GrGLInterface* CreateMojoSkiaGLBinding() {
   GrGLInterface* interface = new GrGLInterface;
@@ -150,3 +151,4 @@
 }
 
 }  // namespace skia
+}  // namespace mojo
diff --git a/mojo/skia/gl_bindings_skia.h b/mojo/skia/gl_bindings_skia.h
index 524edc2..3eba0f9 100644
--- a/mojo/skia/gl_bindings_skia.h
+++ b/mojo/skia/gl_bindings_skia.h
@@ -9,12 +9,14 @@
 
 struct GrGLInterface;
 
-namespace skia_bindings {
+namespace mojo {
+namespace skia {
 
 // The GPU back-end for skia requires pointers to GL functions. This function
 // returns a binding for skia-gpu to the Mojo C GL entry points.
 GrGLInterface* CreateMojoSkiaGLBinding();
 
-}  // namespace skia_bindings
+}  // namespace skia
+}  // namespace mojo
 
 #endif  // MOJO_SKIA_GL_BINDINGS_SKIA_H_
diff --git a/mojo/skia/type_converters.cc b/mojo/skia/type_converters.cc
new file mode 100644
index 0000000..ddc9687
--- /dev/null
+++ b/mojo/skia/type_converters.cc
@@ -0,0 +1,99 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/skia/type_converters.h"
+
+namespace mojo {
+
+SkPoint TypeConverter<SkPoint, mojo::Point>::Convert(const mojo::Point& input) {
+  return SkPoint::Make(input.x, input.y);
+}
+
+mojo::Point TypeConverter<mojo::Point, SkPoint>::Convert(const SkPoint& input) {
+  mojo::Point output;
+  output.x = input.x();
+  output.y = input.y();
+  return output;
+}
+
+SkRect TypeConverter<SkRect, mojo::Rect>::Convert(const mojo::Rect& input) {
+  return SkRect::MakeXYWH(input.x, input.y, input.width, input.height);
+}
+
+mojo::Rect TypeConverter<mojo::Rect, SkRect>::Convert(const SkRect& input) {
+  mojo::Rect output;
+  output.x = input.x();
+  output.y = input.y();
+  output.width = input.width();
+  output.height = input.height();
+  return output;
+}
+
+SkRRect TypeConverter<SkRRect, mojo::RRect>::Convert(const mojo::RRect& input) {
+  SkVector radii[4] = {
+      {input.top_left_radius_x, input.top_left_radius_y},
+      {input.top_right_radius_x, input.top_right_radius_y},
+      {input.bottom_left_radius_x, input.bottom_left_radius_y},
+      {input.bottom_right_radius_x, input.bottom_right_radius_y}};
+  SkRRect output;
+  output.setRectRadii(
+      SkRect::MakeXYWH(input.x, input.y, input.width, input.height), radii);
+  return output;
+}
+
+mojo::RRect TypeConverter<mojo::RRect, SkRRect>::Convert(const SkRRect& input) {
+  mojo::RRect output;
+  output.x = input.rect().x();
+  output.y = input.rect().y();
+  output.width = input.rect().width();
+  output.height = input.rect().height();
+  output.top_left_radius_x = input.radii(SkRRect::kUpperLeft_Corner).x();
+  output.top_left_radius_y = input.radii(SkRRect::kUpperLeft_Corner).y();
+  output.top_right_radius_x = input.radii(SkRRect::kUpperRight_Corner).x();
+  output.top_right_radius_y = input.radii(SkRRect::kUpperRight_Corner).y();
+  output.bottom_left_radius_x = input.radii(SkRRect::kLowerLeft_Corner).x();
+  output.bottom_left_radius_y = input.radii(SkRRect::kLowerLeft_Corner).y();
+  output.bottom_right_radius_x = input.radii(SkRRect::kLowerRight_Corner).x();
+  output.bottom_right_radius_y = input.radii(SkRRect::kLowerRight_Corner).y();
+  return output;
+}
+
+SkMatrix TypeConverter<SkMatrix, mojo::TransformPtr>::Convert(
+    const mojo::TransformPtr& input) {
+  if (!input)
+    return SkMatrix::I();
+
+  // Drop 3D components during conversion from 4x4 to 3x3.
+  SkMatrix output;
+  output.setAll(input->matrix[0], input->matrix[1], input->matrix[3],
+                input->matrix[4], input->matrix[5], input->matrix[7],
+                input->matrix[12], input->matrix[13], input->matrix[15]);
+  return output;
+}
+
+mojo::TransformPtr TypeConverter<mojo::TransformPtr, SkMatrix>::Convert(
+    const SkMatrix& input) {
+  // Expand 3x3 to 4x4.
+  auto output = mojo::Transform::New();
+  output->matrix.resize(16u);
+  output->matrix[0] = input[0];
+  output->matrix[1] = input[1];
+  output->matrix[2] = 0.f;
+  output->matrix[3] = input[2];
+  output->matrix[4] = input[3];
+  output->matrix[5] = input[4];
+  output->matrix[6] = 0.f;
+  output->matrix[7] = input[5];
+  output->matrix[8] = 0.f;
+  output->matrix[9] = 0.f;
+  output->matrix[10] = 1.f;
+  output->matrix[11] = 0.f;
+  output->matrix[12] = input[6];
+  output->matrix[13] = input[7];
+  output->matrix[14] = 0.f;
+  output->matrix[15] = input[8];
+  return output.Pass();
+}
+
+}  // namespace mojo
diff --git a/mojo/skia/type_converters.h b/mojo/skia/type_converters.h
new file mode 100644
index 0000000..6f18878
--- /dev/null
+++ b/mojo/skia/type_converters.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SKIA_TYPE_CONVERTERS_H_
+#define MOJO_SKIA_TYPE_CONVERTERS_H_
+
+#include "mojo/services/geometry/interfaces/geometry.mojom.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkPoint.h"
+#include "third_party/skia/include/core/SkRRect.h"
+#include "third_party/skia/include/core/SkRect.h"
+
+namespace mojo {
+
+template <>
+struct TypeConverter<SkPoint, mojo::Point> {
+  static SkPoint Convert(const mojo::Point& input);
+};
+template <>
+struct TypeConverter<mojo::Point, SkPoint> {
+  static mojo::Point Convert(const SkPoint& input);
+};
+
+template <>
+struct TypeConverter<SkRect, mojo::Rect> {
+  static SkRect Convert(const mojo::Rect& input);
+};
+template <>
+struct TypeConverter<mojo::Rect, SkRect> {
+  static mojo::Rect Convert(const SkRect& input);
+};
+
+template <>
+struct TypeConverter<SkRRect, mojo::RRect> {
+  static SkRRect Convert(const mojo::RRect& input);
+};
+template <>
+struct TypeConverter<mojo::RRect, SkRRect> {
+  static mojo::RRect Convert(const SkRRect& input);
+};
+
+// Note: This transformation is lossy since Transform is 4x4 whereas
+// SkMatrix is only 3x3.
+template <>
+struct TypeConverter<SkMatrix, mojo::TransformPtr> {
+  static SkMatrix Convert(const mojo::TransformPtr& input);
+};
+template <>
+struct TypeConverter<mojo::TransformPtr, SkMatrix> {
+  static mojo::TransformPtr Convert(const SkMatrix& input);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SKIA_TYPE_CONVERTERS_H_