Extend the geometry APIs.

Add a RRect structure for rounded rect.

Define helpers for manipulating transformation matrices.

Define output formatters for geometry datatypes.

BUG=
R=abarth@chromium.org

Review URL: https://codereview.chromium.org/1517853002 .
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/geometry.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/geometry.mojom.dart
index 90422dd..cdf5a0e 100644
--- a/mojo/dart/packages/mojo_services/lib/mojo/geometry.mojom.dart
+++ b/mojo/dart/packages/mojo_services/lib/mojo/geometry.mojom.dart
@@ -427,6 +427,172 @@
 }
 
 
+class RRect extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(56, 0)
+  ];
+  int x = 0;
+  int y = 0;
+  int width = 0;
+  int height = 0;
+  int topLeftRadiusX = 0;
+  int topLeftRadiusY = 0;
+  int topRightRadiusX = 0;
+  int topRightRadiusY = 0;
+  int bottomLeftRadiusX = 0;
+  int bottomLeftRadiusY = 0;
+  int bottomRightRadiusX = 0;
+  int bottomRightRadiusY = 0;
+
+  RRect() : super(kVersions.last.size);
+
+  static RRect deserialize(bindings.Message message) {
+    var decoder = new bindings.Decoder(message);
+    var result = decode(decoder);
+    if (decoder.excessHandles != null) {
+      decoder.excessHandles.forEach((h) => h.close());
+    }
+    return result;
+  }
+
+  static RRect decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    RRect result = new RRect();
+
+    var mainDataHeader = decoder0.decodeStructDataHeader();
+    if (mainDataHeader.version <= kVersions.last.version) {
+      // Scan in reverse order to optimize for more recent versions.
+      for (int i = kVersions.length - 1; i >= 0; --i) {
+        if (mainDataHeader.version >= kVersions[i].version) {
+          if (mainDataHeader.size == kVersions[i].size) {
+            // Found a match.
+            break;
+          }
+          throw new bindings.MojoCodecError(
+              'Header size doesn\'t correspond to known version size.');
+        }
+      }
+    } else if (mainDataHeader.size < kVersions.last.size) {
+      throw new bindings.MojoCodecError(
+        'Message newer than the last known version cannot be shorter than '
+        'required by the last known version.');
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.x = decoder0.decodeInt32(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.y = decoder0.decodeInt32(12);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.width = decoder0.decodeInt32(16);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.height = decoder0.decodeInt32(20);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.topLeftRadiusX = decoder0.decodeInt32(24);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.topLeftRadiusY = decoder0.decodeInt32(28);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.topRightRadiusX = decoder0.decodeInt32(32);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.topRightRadiusY = decoder0.decodeInt32(36);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.bottomLeftRadiusX = decoder0.decodeInt32(40);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.bottomLeftRadiusY = decoder0.decodeInt32(44);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.bottomRightRadiusX = decoder0.decodeInt32(48);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.bottomRightRadiusY = decoder0.decodeInt32(52);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    
+    encoder0.encodeInt32(x, 8);
+    
+    encoder0.encodeInt32(y, 12);
+    
+    encoder0.encodeInt32(width, 16);
+    
+    encoder0.encodeInt32(height, 20);
+    
+    encoder0.encodeInt32(topLeftRadiusX, 24);
+    
+    encoder0.encodeInt32(topLeftRadiusY, 28);
+    
+    encoder0.encodeInt32(topRightRadiusX, 32);
+    
+    encoder0.encodeInt32(topRightRadiusY, 36);
+    
+    encoder0.encodeInt32(bottomLeftRadiusX, 40);
+    
+    encoder0.encodeInt32(bottomLeftRadiusY, 44);
+    
+    encoder0.encodeInt32(bottomRightRadiusX, 48);
+    
+    encoder0.encodeInt32(bottomRightRadiusY, 52);
+  }
+
+  String toString() {
+    return "RRect("
+           "x: $x" ", "
+           "y: $y" ", "
+           "width: $width" ", "
+           "height: $height" ", "
+           "topLeftRadiusX: $topLeftRadiusX" ", "
+           "topLeftRadiusY: $topLeftRadiusY" ", "
+           "topRightRadiusX: $topRightRadiusX" ", "
+           "topRightRadiusY: $topRightRadiusY" ", "
+           "bottomLeftRadiusX: $bottomLeftRadiusX" ", "
+           "bottomLeftRadiusY: $bottomLeftRadiusY" ", "
+           "bottomRightRadiusX: $bottomRightRadiusX" ", "
+           "bottomRightRadiusY: $bottomRightRadiusY" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["x"] = x;
+    map["y"] = y;
+    map["width"] = width;
+    map["height"] = height;
+    map["topLeftRadiusX"] = topLeftRadiusX;
+    map["topLeftRadiusY"] = topLeftRadiusY;
+    map["topRightRadiusX"] = topRightRadiusX;
+    map["topRightRadiusY"] = topRightRadiusY;
+    map["bottomLeftRadiusX"] = bottomLeftRadiusX;
+    map["bottomLeftRadiusY"] = bottomLeftRadiusY;
+    map["bottomRightRadiusX"] = bottomRightRadiusX;
+    map["bottomRightRadiusY"] = bottomRightRadiusY;
+    return map;
+  }
+}
+
+
 class Transform extends bindings.Struct {
   static const List<bindings.StructDataHeader> kVersions = const [
     const bindings.StructDataHeader(16, 0)
diff --git a/mojo/services/geometry/cpp/BUILD.gn b/mojo/services/geometry/cpp/BUILD.gn
index 2a42aa0..783b8ef 100644
--- a/mojo/services/geometry/cpp/BUILD.gn
+++ b/mojo/services/geometry/cpp/BUILD.gn
@@ -9,7 +9,10 @@
   restrict_external_deps = false
   public_configs = [ "../../public/build/config:mojo_services" ]
   sources = [
+    "geometry_util.cc",
     "geometry_util.h",
+    "logging.cc",
+    "logging.h",
   ]
 
   deps = [
diff --git a/mojo/services/geometry/cpp/geometry_util.cc b/mojo/services/geometry/cpp/geometry_util.cc
new file mode 100644
index 0000000..ead6de7
--- /dev/null
+++ b/mojo/services/geometry/cpp/geometry_util.cc
@@ -0,0 +1,28 @@
+// Copyright 2014 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 <string.h>
+#include "mojo/services/geometry/cpp/geometry_util.h"
+
+namespace mojo {
+
+static const float kIdentityMatrix[]{
+    1.f, 0.f, 0.f, 0.f,  // comments to prevent
+    0.f, 1.f, 0.f, 0.f,  // auto formatter reflow
+    0.f, 0.f, 1.f, 0.f,  //
+    0.f, 0.f, 0.f, 1.f};
+
+void SetIdentityTransform(Transform* transform) {
+  transform->matrix.resize(16u);
+  memcpy(transform->matrix.data(), kIdentityMatrix, sizeof(kIdentityMatrix));
+}
+
+void SetTranslationTransform(Transform* transform, float x, float y, float z) {
+  SetIdentityTransform(transform);
+  transform->matrix[3] = x;
+  transform->matrix[7] = y;
+  transform->matrix[11] = z;
+}
+
+}  // namespace mojo
diff --git a/mojo/services/geometry/cpp/geometry_util.h b/mojo/services/geometry/cpp/geometry_util.h
index 3d4d35e..97271c9 100644
--- a/mojo/services/geometry/cpp/geometry_util.h
+++ b/mojo/services/geometry/cpp/geometry_util.h
@@ -5,7 +5,7 @@
 #ifndef MOJO_SERVICES_GEOMETRY_CPP_GEOMETRY_UTIL_H_
 #define MOJO_SERVICES_GEOMETRY_CPP_GEOMETRY_UTIL_H_
 
-#include "geometry/interfaces/geometry.mojom.h"
+#include "mojo/services/geometry/interfaces/geometry.mojom.h"
 
 namespace mojo {
 
@@ -34,6 +34,9 @@
   return !(lhs == rhs);
 }
 
+void SetIdentityTransform(Transform* transform);
+void SetTranslationTransform(Transform* transform, float x, float y, float z);
+
 }  // namespace mojo
 
 #endif  // MOJO_SERVICES_GEOMETRY_CPP_GEOMETRY_UTIL_H_
diff --git a/mojo/services/geometry/cpp/logging.cc b/mojo/services/geometry/cpp/logging.cc
new file mode 100644
index 0000000..c69435e
--- /dev/null
+++ b/mojo/services/geometry/cpp/logging.cc
@@ -0,0 +1,58 @@
+// Copyright 2015 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 <ostream>
+
+#include "mojo/services/geometry/cpp/logging.h"
+
+namespace mojo {
+
+std::ostream& operator<<(std::ostream& os, const mojo::Point& value) {
+  return os << "{x=" << value.x << ", y=" << value.y << "}";
+}
+
+std::ostream& operator<<(std::ostream& os, const mojo::Size& value) {
+  return os << "{width=" << value.width << ", height=" << value.height << "}";
+}
+
+std::ostream& operator<<(std::ostream& os, const mojo::Rect& value) {
+  return os << "{x=" << value.x << ", y=" << value.y
+            << ", width=" << value.width << ", height=" << value.height << "}";
+}
+
+std::ostream& operator<<(std::ostream& os, const mojo::RRect& value) {
+  return os << "{x=" << value.x << ", y=" << value.y
+            << ", width=" << value.width << ", height=" << value.height
+            << ", top_left_radius_x=" << value.top_left_radius_x
+            << ", top_left_radius_y=" << value.top_left_radius_y
+            << ", top_right_radius_x=" << value.top_right_radius_x
+            << ", top_right_radius_y=" << value.top_right_radius_y
+            << ", bottom_left_radius_x=" << value.bottom_left_radius_x
+            << ", bottom_left_radius_y=" << value.bottom_left_radius_y
+            << ", bottom_right_radius_x=" << value.bottom_right_radius_x
+            << ", bottom_right_radius_y=" << value.bottom_right_radius_y << "}";
+}
+
+std::ostream& operator<<(std::ostream& os, const mojo::Transform& value) {
+  if (value.matrix) {
+    os << "[";
+    for (size_t i = 0; i < 4; i++) {
+      if (i != 0)
+        os << ", ";
+      os << "[";
+      for (size_t j = 0; j < 4; j++) {
+        if (j != 0)
+          os << ", ";
+        os << value.matrix[i * 4 + j];
+      }
+      os << "]";
+    }
+    os << "]";
+  } else {
+    os << "null";
+  }
+  return os;
+}
+
+}  // namespace mojo
diff --git a/mojo/services/geometry/cpp/logging.h b/mojo/services/geometry/cpp/logging.h
new file mode 100644
index 0000000..19d5ed7
--- /dev/null
+++ b/mojo/services/geometry/cpp/logging.h
@@ -0,0 +1,22 @@
+// Copyright 2015 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_SERVICES_GEOMETRY_CPP_LOGGING_H_
+#define MOJO_SERVICES_GEOMETRY_CPP_LOGGING_H_
+
+#include <iosfwd>
+
+#include "mojo/services/geometry/interfaces/geometry.mojom.h"
+
+namespace mojo {
+
+std::ostream& operator<<(std::ostream& os, const mojo::Point& value);
+std::ostream& operator<<(std::ostream& os, const mojo::Size& value);
+std::ostream& operator<<(std::ostream& os, const mojo::Rect& value);
+std::ostream& operator<<(std::ostream& os, const mojo::RRect& value);
+std::ostream& operator<<(std::ostream& os, const mojo::Transform& value);
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_GEOMETRY_CPP_LOGGING_H_
diff --git a/mojo/services/geometry/interfaces/geometry.mojom b/mojo/services/geometry/interfaces/geometry.mojom
index a6d2d4e..1f882b6 100644
--- a/mojo/services/geometry/interfaces/geometry.mojom
+++ b/mojo/services/geometry/interfaces/geometry.mojom
@@ -34,6 +34,21 @@
   float height;
 };
 
+struct RRect {
+  int32 x;
+  int32 y;
+  int32 width;
+  int32 height;
+  int32 top_left_radius_x;
+  int32 top_left_radius_y;
+  int32 top_right_radius_x;
+  int32 top_right_radius_y;
+  int32 bottom_left_radius_x;
+  int32 bottom_left_radius_y;
+  int32 bottom_right_radius_x;
+  int32 bottom_right_radius_y;
+};
+
 struct Transform {
   // Row major order.
   array<float, 16> matrix;