Home | History | Annotate | Download | only in base
      1 // Copyright 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "cc/base/math_util.h"
      6 
      7 #include <algorithm>
      8 #include <cmath>
      9 #include <limits>
     10 
     11 #include "base/values.h"
     12 #include "ui/gfx/quad_f.h"
     13 #include "ui/gfx/rect.h"
     14 #include "ui/gfx/rect_conversions.h"
     15 #include "ui/gfx/rect_f.h"
     16 #include "ui/gfx/transform.h"
     17 #include "ui/gfx/vector2d_f.h"
     18 
     19 namespace cc {
     20 
     21 const double MathUtil::kPiDouble = 3.14159265358979323846;
     22 const float MathUtil::kPiFloat = 3.14159265358979323846f;
     23 
     24 static HomogeneousCoordinate ProjectHomogeneousPoint(
     25     const gfx::Transform& transform,
     26     gfx::PointF p) {
     27   // In this case, the layer we are trying to project onto is perpendicular to
     28   // ray (point p and z-axis direction) that we are trying to project. This
     29   // happens when the layer is rotated so that it is infinitesimally thin, or
     30   // when it is co-planar with the camera origin -- i.e. when the layer is
     31   // invisible anyway.
     32   if (!transform.matrix().get(2, 2))
     33     return HomogeneousCoordinate(0.0, 0.0, 0.0, 1.0);
     34 
     35   SkMScalar z = -(transform.matrix().get(2, 0) * p.x() +
     36              transform.matrix().get(2, 1) * p.y() +
     37              transform.matrix().get(2, 3)) /
     38              transform.matrix().get(2, 2);
     39   HomogeneousCoordinate result(p.x(), p.y(), z, 1.0);
     40   transform.matrix().mapMScalars(result.vec, result.vec);
     41   return result;
     42 }
     43 
     44 static HomogeneousCoordinate MapHomogeneousPoint(
     45     const gfx::Transform& transform,
     46     const gfx::Point3F& p) {
     47   HomogeneousCoordinate result(p.x(), p.y(), p.z(), 1.0);
     48   transform.matrix().mapMScalars(result.vec, result.vec);
     49   return result;
     50 }
     51 
     52 static HomogeneousCoordinate ComputeClippedPointForEdge(
     53     const HomogeneousCoordinate& h1,
     54     const HomogeneousCoordinate& h2) {
     55   // Points h1 and h2 form a line in 4d, and any point on that line can be
     56   // represented as an interpolation between h1 and h2:
     57   //    p = (1-t) h1 + (t) h2
     58   //
     59   // We want to compute point p such that p.w == epsilon, where epsilon is a
     60   // small non-zero number. (but the smaller the number is, the higher the risk
     61   // of overflow)
     62   // To do this, we solve for t in the following equation:
     63   //    p.w = epsilon = (1-t) * h1.w + (t) * h2.w
     64   //
     65   // Once paramter t is known, the rest of p can be computed via
     66   //    p = (1-t) h1 + (t) h2.
     67 
     68   // Technically this is a special case of the following assertion, but its a
     69   // good idea to keep it an explicit sanity check here.
     70   DCHECK_NE(h2.w(), h1.w());
     71   // Exactly one of h1 or h2 (but not both) must be on the negative side of the
     72   // w plane when this is called.
     73   DCHECK(h1.ShouldBeClipped() ^ h2.ShouldBeClipped());
     74 
     75   SkMScalar w = 0.00001;  // or any positive non-zero small epsilon
     76 
     77   SkMScalar t = (w - h1.w()) / (h2.w() - h1.w());
     78 
     79   SkMScalar x = (1 - t) * h1.x() + t * h2.x();
     80   SkMScalar y = (1 - t) * h1.y() + t * h2.y();
     81   SkMScalar z = (1 - t) * h1.z() + t * h2.z();
     82 
     83   return HomogeneousCoordinate(x, y, z, w);
     84 }
     85 
     86 static inline void ExpandBoundsToIncludePoint(float* xmin,
     87                                               float* xmax,
     88                                               float* ymin,
     89                                               float* ymax,
     90                                               gfx::PointF p) {
     91   *xmin = std::min(p.x(), *xmin);
     92   *xmax = std::max(p.x(), *xmax);
     93   *ymin = std::min(p.y(), *ymin);
     94   *ymax = std::max(p.y(), *ymax);
     95 }
     96 
     97 static inline void AddVertexToClippedQuad(gfx::PointF new_vertex,
     98                                           gfx::PointF clipped_quad[8],
     99                                           int* num_vertices_in_clipped_quad) {
    100   clipped_quad[*num_vertices_in_clipped_quad] = new_vertex;
    101   (*num_vertices_in_clipped_quad)++;
    102 }
    103 
    104 gfx::Rect MathUtil::MapClippedRect(const gfx::Transform& transform,
    105                                    gfx::Rect src_rect) {
    106   return gfx::ToEnclosingRect(MapClippedRect(transform, gfx::RectF(src_rect)));
    107 }
    108 
    109 gfx::RectF MathUtil::MapClippedRect(const gfx::Transform& transform,
    110                                     const gfx::RectF& src_rect) {
    111   if (transform.IsIdentityOrTranslation())
    112     return src_rect +
    113            gfx::Vector2dF(
    114                static_cast<float>(transform.matrix().getDouble(0, 3)),
    115                static_cast<float>(transform.matrix().getDouble(1, 3)));
    116 
    117   // Apply the transform, but retain the result in homogeneous coordinates.
    118 
    119   double quad[4 * 2];  // input: 4 x 2D points
    120   quad[0] = src_rect.x();
    121   quad[1] = src_rect.y();
    122   quad[2] = src_rect.right();
    123   quad[3] = src_rect.y();
    124   quad[4] = src_rect.right();
    125   quad[5] = src_rect.bottom();
    126   quad[6] = src_rect.x();
    127   quad[7] = src_rect.bottom();
    128 
    129   double result[4 * 4];  // output: 4 x 4D homogeneous points
    130   transform.matrix().map2(quad, 4, result);
    131 
    132   HomogeneousCoordinate hc0(result[0], result[1], result[2], result[3]);
    133   HomogeneousCoordinate hc1(result[4], result[5], result[6], result[7]);
    134   HomogeneousCoordinate hc2(result[8], result[9], result[10], result[11]);
    135   HomogeneousCoordinate hc3(result[12], result[13], result[14], result[15]);
    136   return ComputeEnclosingClippedRect(hc0, hc1, hc2, hc3);
    137 }
    138 
    139 gfx::RectF MathUtil::ProjectClippedRect(const gfx::Transform& transform,
    140                                         const gfx::RectF& src_rect) {
    141   if (transform.IsIdentityOrTranslation()) {
    142     return src_rect +
    143            gfx::Vector2dF(
    144                static_cast<float>(transform.matrix().getDouble(0, 3)),
    145                static_cast<float>(transform.matrix().getDouble(1, 3)));
    146   }
    147 
    148   // Perform the projection, but retain the result in homogeneous coordinates.
    149   gfx::QuadF q = gfx::QuadF(src_rect);
    150   HomogeneousCoordinate h1 = ProjectHomogeneousPoint(transform, q.p1());
    151   HomogeneousCoordinate h2 = ProjectHomogeneousPoint(transform, q.p2());
    152   HomogeneousCoordinate h3 = ProjectHomogeneousPoint(transform, q.p3());
    153   HomogeneousCoordinate h4 = ProjectHomogeneousPoint(transform, q.p4());
    154 
    155   return ComputeEnclosingClippedRect(h1, h2, h3, h4);
    156 }
    157 
    158 void MathUtil::MapClippedQuad(const gfx::Transform& transform,
    159                               const gfx::QuadF& src_quad,
    160                               gfx::PointF clipped_quad[8],
    161                               int* num_vertices_in_clipped_quad) {
    162   HomogeneousCoordinate h1 =
    163       MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p1()));
    164   HomogeneousCoordinate h2 =
    165       MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p2()));
    166   HomogeneousCoordinate h3 =
    167       MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p3()));
    168   HomogeneousCoordinate h4 =
    169       MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p4()));
    170 
    171   // The order of adding the vertices to the array is chosen so that
    172   // clockwise / counter-clockwise orientation is retained.
    173 
    174   *num_vertices_in_clipped_quad = 0;
    175 
    176   if (!h1.ShouldBeClipped()) {
    177     AddVertexToClippedQuad(
    178         h1.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad);
    179   }
    180 
    181   if (h1.ShouldBeClipped() ^ h2.ShouldBeClipped()) {
    182     AddVertexToClippedQuad(
    183         ComputeClippedPointForEdge(h1, h2).CartesianPoint2d(),
    184         clipped_quad,
    185         num_vertices_in_clipped_quad);
    186   }
    187 
    188   if (!h2.ShouldBeClipped()) {
    189     AddVertexToClippedQuad(
    190         h2.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad);
    191   }
    192 
    193   if (h2.ShouldBeClipped() ^ h3.ShouldBeClipped()) {
    194     AddVertexToClippedQuad(
    195         ComputeClippedPointForEdge(h2, h3).CartesianPoint2d(),
    196         clipped_quad,
    197         num_vertices_in_clipped_quad);
    198   }
    199 
    200   if (!h3.ShouldBeClipped()) {
    201     AddVertexToClippedQuad(
    202         h3.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad);
    203   }
    204 
    205   if (h3.ShouldBeClipped() ^ h4.ShouldBeClipped()) {
    206     AddVertexToClippedQuad(
    207         ComputeClippedPointForEdge(h3, h4).CartesianPoint2d(),
    208         clipped_quad,
    209         num_vertices_in_clipped_quad);
    210   }
    211 
    212   if (!h4.ShouldBeClipped()) {
    213     AddVertexToClippedQuad(
    214         h4.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad);
    215   }
    216 
    217   if (h4.ShouldBeClipped() ^ h1.ShouldBeClipped()) {
    218     AddVertexToClippedQuad(
    219         ComputeClippedPointForEdge(h4, h1).CartesianPoint2d(),
    220         clipped_quad,
    221         num_vertices_in_clipped_quad);
    222   }
    223 
    224   DCHECK_LE(*num_vertices_in_clipped_quad, 8);
    225 }
    226 
    227 gfx::RectF MathUtil::ComputeEnclosingRectOfVertices(gfx::PointF vertices[],
    228                                                     int num_vertices) {
    229   if (num_vertices < 2)
    230     return gfx::RectF();
    231 
    232   float xmin = std::numeric_limits<float>::max();
    233   float xmax = -std::numeric_limits<float>::max();
    234   float ymin = std::numeric_limits<float>::max();
    235   float ymax = -std::numeric_limits<float>::max();
    236 
    237   for (int i = 0; i < num_vertices; ++i)
    238     ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax, vertices[i]);
    239 
    240   return gfx::RectF(gfx::PointF(xmin, ymin),
    241                     gfx::SizeF(xmax - xmin, ymax - ymin));
    242 }
    243 
    244 gfx::RectF MathUtil::ComputeEnclosingClippedRect(
    245     const HomogeneousCoordinate& h1,
    246     const HomogeneousCoordinate& h2,
    247     const HomogeneousCoordinate& h3,
    248     const HomogeneousCoordinate& h4) {
    249   // This function performs clipping as necessary and computes the enclosing 2d
    250   // gfx::RectF of the vertices. Doing these two steps simultaneously allows us
    251   // to avoid the overhead of storing an unknown number of clipped vertices.
    252 
    253   // If no vertices on the quad are clipped, then we can simply return the
    254   // enclosing rect directly.
    255   bool something_clipped = h1.ShouldBeClipped() || h2.ShouldBeClipped() ||
    256                            h3.ShouldBeClipped() || h4.ShouldBeClipped();
    257   if (!something_clipped) {
    258     gfx::QuadF mapped_quad = gfx::QuadF(h1.CartesianPoint2d(),
    259                                         h2.CartesianPoint2d(),
    260                                         h3.CartesianPoint2d(),
    261                                         h4.CartesianPoint2d());
    262     return mapped_quad.BoundingBox();
    263   }
    264 
    265   bool everything_clipped = h1.ShouldBeClipped() && h2.ShouldBeClipped() &&
    266                             h3.ShouldBeClipped() && h4.ShouldBeClipped();
    267   if (everything_clipped)
    268     return gfx::RectF();
    269 
    270   float xmin = std::numeric_limits<float>::max();
    271   float xmax = -std::numeric_limits<float>::max();
    272   float ymin = std::numeric_limits<float>::max();
    273   float ymax = -std::numeric_limits<float>::max();
    274 
    275   if (!h1.ShouldBeClipped())
    276     ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax,
    277                                h1.CartesianPoint2d());
    278 
    279   if (h1.ShouldBeClipped() ^ h2.ShouldBeClipped())
    280     ExpandBoundsToIncludePoint(&xmin,
    281                                &xmax,
    282                                &ymin,
    283                                &ymax,
    284                                ComputeClippedPointForEdge(h1, h2)
    285                                    .CartesianPoint2d());
    286 
    287   if (!h2.ShouldBeClipped())
    288     ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax,
    289                                h2.CartesianPoint2d());
    290 
    291   if (h2.ShouldBeClipped() ^ h3.ShouldBeClipped())
    292     ExpandBoundsToIncludePoint(&xmin,
    293                                &xmax,
    294                                &ymin,
    295                                &ymax,
    296                                ComputeClippedPointForEdge(h2, h3)
    297                                    .CartesianPoint2d());
    298 
    299   if (!h3.ShouldBeClipped())
    300     ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax,
    301                                h3.CartesianPoint2d());
    302 
    303   if (h3.ShouldBeClipped() ^ h4.ShouldBeClipped())
    304     ExpandBoundsToIncludePoint(&xmin,
    305                                &xmax,
    306                                &ymin,
    307                                &ymax,
    308                                ComputeClippedPointForEdge(h3, h4)
    309                                    .CartesianPoint2d());
    310 
    311   if (!h4.ShouldBeClipped())
    312     ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax,
    313                                h4.CartesianPoint2d());
    314 
    315   if (h4.ShouldBeClipped() ^ h1.ShouldBeClipped())
    316     ExpandBoundsToIncludePoint(&xmin,
    317                                &xmax,
    318                                &ymin,
    319                                &ymax,
    320                                ComputeClippedPointForEdge(h4, h1)
    321                                    .CartesianPoint2d());
    322 
    323   return gfx::RectF(gfx::PointF(xmin, ymin),
    324                     gfx::SizeF(xmax - xmin, ymax - ymin));
    325 }
    326 
    327 gfx::QuadF MathUtil::MapQuad(const gfx::Transform& transform,
    328                              const gfx::QuadF& q,
    329                              bool* clipped) {
    330   if (transform.IsIdentityOrTranslation()) {
    331     gfx::QuadF mapped_quad(q);
    332     mapped_quad +=
    333         gfx::Vector2dF(static_cast<float>(transform.matrix().getDouble(0, 3)),
    334                        static_cast<float>(transform.matrix().getDouble(1, 3)));
    335     *clipped = false;
    336     return mapped_quad;
    337   }
    338 
    339   HomogeneousCoordinate h1 =
    340       MapHomogeneousPoint(transform, gfx::Point3F(q.p1()));
    341   HomogeneousCoordinate h2 =
    342       MapHomogeneousPoint(transform, gfx::Point3F(q.p2()));
    343   HomogeneousCoordinate h3 =
    344       MapHomogeneousPoint(transform, gfx::Point3F(q.p3()));
    345   HomogeneousCoordinate h4 =
    346       MapHomogeneousPoint(transform, gfx::Point3F(q.p4()));
    347 
    348   *clipped = h1.ShouldBeClipped() || h2.ShouldBeClipped() ||
    349             h3.ShouldBeClipped() || h4.ShouldBeClipped();
    350 
    351   // Result will be invalid if clipped == true. But, compute it anyway just in
    352   // case, to emulate existing behavior.
    353   return gfx::QuadF(h1.CartesianPoint2d(),
    354                     h2.CartesianPoint2d(),
    355                     h3.CartesianPoint2d(),
    356                     h4.CartesianPoint2d());
    357 }
    358 
    359 gfx::PointF MathUtil::MapPoint(const gfx::Transform& transform,
    360                                gfx::PointF p,
    361                                bool* clipped) {
    362   HomogeneousCoordinate h = MapHomogeneousPoint(transform, gfx::Point3F(p));
    363 
    364   if (h.w() > 0) {
    365     *clipped = false;
    366     return h.CartesianPoint2d();
    367   }
    368 
    369   // The cartesian coordinates will be invalid after dividing by w.
    370   *clipped = true;
    371 
    372   // Avoid dividing by w if w == 0.
    373   if (!h.w())
    374     return gfx::PointF();
    375 
    376   // This return value will be invalid because clipped == true, but (1) users of
    377   // this code should be ignoring the return value when clipped == true anyway,
    378   // and (2) this behavior is more consistent with existing behavior of WebKit
    379   // transforms if the user really does not ignore the return value.
    380   return h.CartesianPoint2d();
    381 }
    382 
    383 gfx::Point3F MathUtil::MapPoint(const gfx::Transform& transform,
    384                                 const gfx::Point3F& p,
    385                                 bool* clipped) {
    386   HomogeneousCoordinate h = MapHomogeneousPoint(transform, p);
    387 
    388   if (h.w() > 0) {
    389     *clipped = false;
    390     return h.CartesianPoint3d();
    391   }
    392 
    393   // The cartesian coordinates will be invalid after dividing by w.
    394   *clipped = true;
    395 
    396   // Avoid dividing by w if w == 0.
    397   if (!h.w())
    398     return gfx::Point3F();
    399 
    400   // This return value will be invalid because clipped == true, but (1) users of
    401   // this code should be ignoring the return value when clipped == true anyway,
    402   // and (2) this behavior is more consistent with existing behavior of WebKit
    403   // transforms if the user really does not ignore the return value.
    404   return h.CartesianPoint3d();
    405 }
    406 
    407 gfx::QuadF MathUtil::ProjectQuad(const gfx::Transform& transform,
    408                                  const gfx::QuadF& q,
    409                                  bool* clipped) {
    410   gfx::QuadF projected_quad;
    411   bool clipped_point;
    412   projected_quad.set_p1(ProjectPoint(transform, q.p1(), &clipped_point));
    413   *clipped = clipped_point;
    414   projected_quad.set_p2(ProjectPoint(transform, q.p2(), &clipped_point));
    415   *clipped |= clipped_point;
    416   projected_quad.set_p3(ProjectPoint(transform, q.p3(), &clipped_point));
    417   *clipped |= clipped_point;
    418   projected_quad.set_p4(ProjectPoint(transform, q.p4(), &clipped_point));
    419   *clipped |= clipped_point;
    420 
    421   return projected_quad;
    422 }
    423 
    424 gfx::PointF MathUtil::ProjectPoint(const gfx::Transform& transform,
    425                                    gfx::PointF p,
    426                                    bool* clipped) {
    427   HomogeneousCoordinate h = ProjectHomogeneousPoint(transform, p);
    428 
    429   if (h.w() > 0) {
    430     // The cartesian coordinates will be valid in this case.
    431     *clipped = false;
    432     return h.CartesianPoint2d();
    433   }
    434 
    435   // The cartesian coordinates will be invalid after dividing by w.
    436   *clipped = true;
    437 
    438   // Avoid dividing by w if w == 0.
    439   if (!h.w())
    440     return gfx::PointF();
    441 
    442   // This return value will be invalid because clipped == true, but (1) users of
    443   // this code should be ignoring the return value when clipped == true anyway,
    444   // and (2) this behavior is more consistent with existing behavior of WebKit
    445   // transforms if the user really does not ignore the return value.
    446   return h.CartesianPoint2d();
    447 }
    448 
    449 static inline float ScaleOnAxis(double a, double b, double c) {
    450   return std::sqrt(a * a + b * b + c * c);
    451 }
    452 
    453 gfx::Vector2dF MathUtil::ComputeTransform2dScaleComponents(
    454     const gfx::Transform& transform,
    455     float fallback_value) {
    456   if (transform.HasPerspective())
    457     return gfx::Vector2dF(fallback_value, fallback_value);
    458   float x_scale = ScaleOnAxis(transform.matrix().getDouble(0, 0),
    459                               transform.matrix().getDouble(1, 0),
    460                               transform.matrix().getDouble(2, 0));
    461   float y_scale = ScaleOnAxis(transform.matrix().getDouble(0, 1),
    462                               transform.matrix().getDouble(1, 1),
    463                               transform.matrix().getDouble(2, 1));
    464   return gfx::Vector2dF(x_scale, y_scale);
    465 }
    466 
    467 float MathUtil::SmallestAngleBetweenVectors(gfx::Vector2dF v1,
    468                                             gfx::Vector2dF v2) {
    469   double dot_product = gfx::DotProduct(v1, v2) / v1.Length() / v2.Length();
    470   // Clamp to compensate for rounding errors.
    471   dot_product = std::max(-1.0, std::min(1.0, dot_product));
    472   return static_cast<float>(Rad2Deg(std::acos(dot_product)));
    473 }
    474 
    475 gfx::Vector2dF MathUtil::ProjectVector(gfx::Vector2dF source,
    476                                        gfx::Vector2dF destination) {
    477   float projected_length =
    478       gfx::DotProduct(source, destination) / destination.LengthSquared();
    479   return gfx::Vector2dF(projected_length * destination.x(),
    480                         projected_length * destination.y());
    481 }
    482 
    483 scoped_ptr<base::Value> MathUtil::AsValue(gfx::Size s) {
    484   scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
    485   res->SetDouble("width", s.width());
    486   res->SetDouble("height", s.height());
    487   return res.PassAs<base::Value>();
    488 }
    489 
    490 scoped_ptr<base::Value> MathUtil::AsValue(gfx::SizeF s) {
    491   scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
    492   res->SetDouble("width", s.width());
    493   res->SetDouble("height", s.height());
    494   return res.PassAs<base::Value>();
    495 }
    496 
    497 scoped_ptr<base::Value> MathUtil::AsValue(gfx::Rect r) {
    498   scoped_ptr<base::ListValue> res(new base::ListValue());
    499   res->AppendInteger(r.x());
    500   res->AppendInteger(r.y());
    501   res->AppendInteger(r.width());
    502   res->AppendInteger(r.height());
    503   return res.PassAs<base::Value>();
    504 }
    505 
    506 bool MathUtil::FromValue(const base::Value* raw_value, gfx::Rect* out_rect) {
    507   const base::ListValue* value = NULL;
    508   if (!raw_value->GetAsList(&value))
    509     return false;
    510 
    511   if (value->GetSize() != 4)
    512     return false;
    513 
    514   int x, y, w, h;
    515   bool ok = true;
    516   ok &= value->GetInteger(0, &x);
    517   ok &= value->GetInteger(1, &y);
    518   ok &= value->GetInteger(2, &w);
    519   ok &= value->GetInteger(3, &h);
    520   if (!ok)
    521     return false;
    522 
    523   *out_rect = gfx::Rect(x, y, w, h);
    524   return true;
    525 }
    526 
    527 scoped_ptr<base::Value> MathUtil::AsValue(gfx::PointF pt) {
    528   scoped_ptr<base::ListValue> res(new base::ListValue());
    529   res->AppendDouble(pt.x());
    530   res->AppendDouble(pt.y());
    531   return res.PassAs<base::Value>();
    532 }
    533 
    534 scoped_ptr<base::Value> MathUtil::AsValue(const gfx::QuadF& q) {
    535   scoped_ptr<base::ListValue> res(new base::ListValue());
    536   res->AppendDouble(q.p1().x());
    537   res->AppendDouble(q.p1().y());
    538   res->AppendDouble(q.p2().x());
    539   res->AppendDouble(q.p2().y());
    540   res->AppendDouble(q.p3().x());
    541   res->AppendDouble(q.p3().y());
    542   res->AppendDouble(q.p4().x());
    543   res->AppendDouble(q.p4().y());
    544   return res.PassAs<base::Value>();
    545 }
    546 
    547 scoped_ptr<base::Value> MathUtil::AsValue(const gfx::RectF& rect) {
    548   scoped_ptr<base::ListValue> res(new base::ListValue());
    549   res->AppendDouble(rect.x());
    550   res->AppendDouble(rect.y());
    551   res->AppendDouble(rect.width());
    552   res->AppendDouble(rect.height());
    553   return res.PassAs<base::Value>();
    554 }
    555 
    556 scoped_ptr<base::Value> MathUtil::AsValue(const gfx::Transform& transform) {
    557   scoped_ptr<base::ListValue> res(new base::ListValue());
    558   const SkMatrix44& m = transform.matrix();
    559   for (int row = 0; row < 4; ++row) {
    560     for (int col = 0; col < 4; ++col)
    561       res->AppendDouble(m.getDouble(row, col));
    562   }
    563   return res.PassAs<base::Value>();
    564 }
    565 
    566 scoped_ptr<base::Value> MathUtil::AsValueSafely(double value) {
    567   return scoped_ptr<base::Value>(base::Value::CreateDoubleValue(
    568       std::min(value, std::numeric_limits<double>::max())));
    569 }
    570 
    571 scoped_ptr<base::Value> MathUtil::AsValueSafely(float value) {
    572   return scoped_ptr<base::Value>(base::Value::CreateDoubleValue(
    573       std::min(value, std::numeric_limits<float>::max())));
    574 }
    575 
    576 }  // namespace cc
    577