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 // ...or any positive non-zero small epsilon 76 SkMScalar w = 0.00001f; 77 SkMScalar t = (w - h1.w()) / (h2.w() - h1.w()); 78 79 SkMScalar x = (SK_MScalar1 - t) * h1.x() + t * h2.x(); 80 SkMScalar y = (SK_MScalar1 - t) * h1.y() + t * h2.y(); 81 SkMScalar z = (SK_MScalar1 - 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(SkMScalarToFloat(transform.matrix().get(0, 3)), 114 SkMScalarToFloat(transform.matrix().get(1, 3))); 115 } 116 117 // Apply the transform, but retain the result in homogeneous coordinates. 118 119 SkMScalar 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 SkMScalar 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(SkMScalarToFloat(transform.matrix().get(0, 3)), 144 SkMScalarToFloat(transform.matrix().get(1, 3))); 145 } 146 147 // Perform the projection, but retain the result in homogeneous coordinates. 148 gfx::QuadF q = gfx::QuadF(src_rect); 149 HomogeneousCoordinate h1 = ProjectHomogeneousPoint(transform, q.p1()); 150 HomogeneousCoordinate h2 = ProjectHomogeneousPoint(transform, q.p2()); 151 HomogeneousCoordinate h3 = ProjectHomogeneousPoint(transform, q.p3()); 152 HomogeneousCoordinate h4 = ProjectHomogeneousPoint(transform, q.p4()); 153 154 return ComputeEnclosingClippedRect(h1, h2, h3, h4); 155 } 156 157 void MathUtil::MapClippedQuad(const gfx::Transform& transform, 158 const gfx::QuadF& src_quad, 159 gfx::PointF clipped_quad[8], 160 int* num_vertices_in_clipped_quad) { 161 HomogeneousCoordinate h1 = 162 MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p1())); 163 HomogeneousCoordinate h2 = 164 MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p2())); 165 HomogeneousCoordinate h3 = 166 MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p3())); 167 HomogeneousCoordinate h4 = 168 MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p4())); 169 170 // The order of adding the vertices to the array is chosen so that 171 // clockwise / counter-clockwise orientation is retained. 172 173 *num_vertices_in_clipped_quad = 0; 174 175 if (!h1.ShouldBeClipped()) { 176 AddVertexToClippedQuad( 177 h1.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad); 178 } 179 180 if (h1.ShouldBeClipped() ^ h2.ShouldBeClipped()) { 181 AddVertexToClippedQuad( 182 ComputeClippedPointForEdge(h1, h2).CartesianPoint2d(), 183 clipped_quad, 184 num_vertices_in_clipped_quad); 185 } 186 187 if (!h2.ShouldBeClipped()) { 188 AddVertexToClippedQuad( 189 h2.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad); 190 } 191 192 if (h2.ShouldBeClipped() ^ h3.ShouldBeClipped()) { 193 AddVertexToClippedQuad( 194 ComputeClippedPointForEdge(h2, h3).CartesianPoint2d(), 195 clipped_quad, 196 num_vertices_in_clipped_quad); 197 } 198 199 if (!h3.ShouldBeClipped()) { 200 AddVertexToClippedQuad( 201 h3.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad); 202 } 203 204 if (h3.ShouldBeClipped() ^ h4.ShouldBeClipped()) { 205 AddVertexToClippedQuad( 206 ComputeClippedPointForEdge(h3, h4).CartesianPoint2d(), 207 clipped_quad, 208 num_vertices_in_clipped_quad); 209 } 210 211 if (!h4.ShouldBeClipped()) { 212 AddVertexToClippedQuad( 213 h4.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad); 214 } 215 216 if (h4.ShouldBeClipped() ^ h1.ShouldBeClipped()) { 217 AddVertexToClippedQuad( 218 ComputeClippedPointForEdge(h4, h1).CartesianPoint2d(), 219 clipped_quad, 220 num_vertices_in_clipped_quad); 221 } 222 223 DCHECK_LE(*num_vertices_in_clipped_quad, 8); 224 } 225 226 gfx::RectF MathUtil::ComputeEnclosingRectOfVertices(gfx::PointF vertices[], 227 int num_vertices) { 228 if (num_vertices < 2) 229 return gfx::RectF(); 230 231 float xmin = std::numeric_limits<float>::max(); 232 float xmax = -std::numeric_limits<float>::max(); 233 float ymin = std::numeric_limits<float>::max(); 234 float ymax = -std::numeric_limits<float>::max(); 235 236 for (int i = 0; i < num_vertices; ++i) 237 ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax, vertices[i]); 238 239 return gfx::RectF(gfx::PointF(xmin, ymin), 240 gfx::SizeF(xmax - xmin, ymax - ymin)); 241 } 242 243 gfx::RectF MathUtil::ComputeEnclosingClippedRect( 244 const HomogeneousCoordinate& h1, 245 const HomogeneousCoordinate& h2, 246 const HomogeneousCoordinate& h3, 247 const HomogeneousCoordinate& h4) { 248 // This function performs clipping as necessary and computes the enclosing 2d 249 // gfx::RectF of the vertices. Doing these two steps simultaneously allows us 250 // to avoid the overhead of storing an unknown number of clipped vertices. 251 252 // If no vertices on the quad are clipped, then we can simply return the 253 // enclosing rect directly. 254 bool something_clipped = h1.ShouldBeClipped() || h2.ShouldBeClipped() || 255 h3.ShouldBeClipped() || h4.ShouldBeClipped(); 256 if (!something_clipped) { 257 gfx::QuadF mapped_quad = gfx::QuadF(h1.CartesianPoint2d(), 258 h2.CartesianPoint2d(), 259 h3.CartesianPoint2d(), 260 h4.CartesianPoint2d()); 261 return mapped_quad.BoundingBox(); 262 } 263 264 bool everything_clipped = h1.ShouldBeClipped() && h2.ShouldBeClipped() && 265 h3.ShouldBeClipped() && h4.ShouldBeClipped(); 266 if (everything_clipped) 267 return gfx::RectF(); 268 269 float xmin = std::numeric_limits<float>::max(); 270 float xmax = -std::numeric_limits<float>::max(); 271 float ymin = std::numeric_limits<float>::max(); 272 float ymax = -std::numeric_limits<float>::max(); 273 274 if (!h1.ShouldBeClipped()) 275 ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax, 276 h1.CartesianPoint2d()); 277 278 if (h1.ShouldBeClipped() ^ h2.ShouldBeClipped()) 279 ExpandBoundsToIncludePoint(&xmin, 280 &xmax, 281 &ymin, 282 &ymax, 283 ComputeClippedPointForEdge(h1, h2) 284 .CartesianPoint2d()); 285 286 if (!h2.ShouldBeClipped()) 287 ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax, 288 h2.CartesianPoint2d()); 289 290 if (h2.ShouldBeClipped() ^ h3.ShouldBeClipped()) 291 ExpandBoundsToIncludePoint(&xmin, 292 &xmax, 293 &ymin, 294 &ymax, 295 ComputeClippedPointForEdge(h2, h3) 296 .CartesianPoint2d()); 297 298 if (!h3.ShouldBeClipped()) 299 ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax, 300 h3.CartesianPoint2d()); 301 302 if (h3.ShouldBeClipped() ^ h4.ShouldBeClipped()) 303 ExpandBoundsToIncludePoint(&xmin, 304 &xmax, 305 &ymin, 306 &ymax, 307 ComputeClippedPointForEdge(h3, h4) 308 .CartesianPoint2d()); 309 310 if (!h4.ShouldBeClipped()) 311 ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax, 312 h4.CartesianPoint2d()); 313 314 if (h4.ShouldBeClipped() ^ h1.ShouldBeClipped()) 315 ExpandBoundsToIncludePoint(&xmin, 316 &xmax, 317 &ymin, 318 &ymax, 319 ComputeClippedPointForEdge(h4, h1) 320 .CartesianPoint2d()); 321 322 return gfx::RectF(gfx::PointF(xmin, ymin), 323 gfx::SizeF(xmax - xmin, ymax - ymin)); 324 } 325 326 gfx::QuadF MathUtil::MapQuad(const gfx::Transform& transform, 327 const gfx::QuadF& q, 328 bool* clipped) { 329 if (transform.IsIdentityOrTranslation()) { 330 gfx::QuadF mapped_quad(q); 331 mapped_quad += 332 gfx::Vector2dF(SkMScalarToFloat(transform.matrix().get(0, 3)), 333 SkMScalarToFloat(transform.matrix().get(1, 3))); 334 *clipped = false; 335 return mapped_quad; 336 } 337 338 HomogeneousCoordinate h1 = 339 MapHomogeneousPoint(transform, gfx::Point3F(q.p1())); 340 HomogeneousCoordinate h2 = 341 MapHomogeneousPoint(transform, gfx::Point3F(q.p2())); 342 HomogeneousCoordinate h3 = 343 MapHomogeneousPoint(transform, gfx::Point3F(q.p3())); 344 HomogeneousCoordinate h4 = 345 MapHomogeneousPoint(transform, gfx::Point3F(q.p4())); 346 347 *clipped = h1.ShouldBeClipped() || h2.ShouldBeClipped() || 348 h3.ShouldBeClipped() || h4.ShouldBeClipped(); 349 350 // Result will be invalid if clipped == true. But, compute it anyway just in 351 // case, to emulate existing behavior. 352 return gfx::QuadF(h1.CartesianPoint2d(), 353 h2.CartesianPoint2d(), 354 h3.CartesianPoint2d(), 355 h4.CartesianPoint2d()); 356 } 357 358 gfx::PointF MathUtil::MapPoint(const gfx::Transform& transform, 359 gfx::PointF p, 360 bool* clipped) { 361 HomogeneousCoordinate h = MapHomogeneousPoint(transform, gfx::Point3F(p)); 362 363 if (h.w() > 0) { 364 *clipped = false; 365 return h.CartesianPoint2d(); 366 } 367 368 // The cartesian coordinates will be invalid after dividing by w. 369 *clipped = true; 370 371 // Avoid dividing by w if w == 0. 372 if (!h.w()) 373 return gfx::PointF(); 374 375 // This return value will be invalid because clipped == true, but (1) users of 376 // this code should be ignoring the return value when clipped == true anyway, 377 // and (2) this behavior is more consistent with existing behavior of WebKit 378 // transforms if the user really does not ignore the return value. 379 return h.CartesianPoint2d(); 380 } 381 382 gfx::Point3F MathUtil::MapPoint(const gfx::Transform& transform, 383 const gfx::Point3F& p, 384 bool* clipped) { 385 HomogeneousCoordinate h = MapHomogeneousPoint(transform, p); 386 387 if (h.w() > 0) { 388 *clipped = false; 389 return h.CartesianPoint3d(); 390 } 391 392 // The cartesian coordinates will be invalid after dividing by w. 393 *clipped = true; 394 395 // Avoid dividing by w if w == 0. 396 if (!h.w()) 397 return gfx::Point3F(); 398 399 // This return value will be invalid because clipped == true, but (1) users of 400 // this code should be ignoring the return value when clipped == true anyway, 401 // and (2) this behavior is more consistent with existing behavior of WebKit 402 // transforms if the user really does not ignore the return value. 403 return h.CartesianPoint3d(); 404 } 405 406 gfx::QuadF MathUtil::ProjectQuad(const gfx::Transform& transform, 407 const gfx::QuadF& q, 408 bool* clipped) { 409 gfx::QuadF projected_quad; 410 bool clipped_point; 411 projected_quad.set_p1(ProjectPoint(transform, q.p1(), &clipped_point)); 412 *clipped = clipped_point; 413 projected_quad.set_p2(ProjectPoint(transform, q.p2(), &clipped_point)); 414 *clipped |= clipped_point; 415 projected_quad.set_p3(ProjectPoint(transform, q.p3(), &clipped_point)); 416 *clipped |= clipped_point; 417 projected_quad.set_p4(ProjectPoint(transform, q.p4(), &clipped_point)); 418 *clipped |= clipped_point; 419 420 return projected_quad; 421 } 422 423 gfx::PointF MathUtil::ProjectPoint(const gfx::Transform& transform, 424 gfx::PointF p, 425 bool* clipped) { 426 HomogeneousCoordinate h = ProjectHomogeneousPoint(transform, p); 427 428 if (h.w() > 0) { 429 // The cartesian coordinates will be valid in this case. 430 *clipped = false; 431 return h.CartesianPoint2d(); 432 } 433 434 // The cartesian coordinates will be invalid after dividing by w. 435 *clipped = true; 436 437 // Avoid dividing by w if w == 0. 438 if (!h.w()) 439 return gfx::PointF(); 440 441 // This return value will be invalid because clipped == true, but (1) users of 442 // this code should be ignoring the return value when clipped == true anyway, 443 // and (2) this behavior is more consistent with existing behavior of WebKit 444 // transforms if the user really does not ignore the return value. 445 return h.CartesianPoint2d(); 446 } 447 448 gfx::RectF MathUtil::ScaleRectProportional(const gfx::RectF& input_outer_rect, 449 const gfx::RectF& scale_outer_rect, 450 const gfx::RectF& scale_inner_rect) { 451 gfx::RectF output_inner_rect = input_outer_rect; 452 float scale_rect_to_input_scale_x = 453 scale_outer_rect.width() / input_outer_rect.width(); 454 float scale_rect_to_input_scale_y = 455 scale_outer_rect.height() / input_outer_rect.height(); 456 457 gfx::Vector2dF top_left_diff = 458 scale_inner_rect.origin() - scale_outer_rect.origin(); 459 gfx::Vector2dF bottom_right_diff = 460 scale_inner_rect.bottom_right() - scale_outer_rect.bottom_right(); 461 output_inner_rect.Inset(top_left_diff.x() / scale_rect_to_input_scale_x, 462 top_left_diff.y() / scale_rect_to_input_scale_y, 463 -bottom_right_diff.x() / scale_rect_to_input_scale_x, 464 -bottom_right_diff.y() / scale_rect_to_input_scale_y); 465 return output_inner_rect; 466 } 467 468 static inline float ScaleOnAxis(double a, double b, double c) { 469 if (!b && !c) 470 return a; 471 if (!a && !c) 472 return b; 473 if (!a && !b) 474 return c; 475 476 // Do the sqrt as a double to not lose precision. 477 return static_cast<float>(std::sqrt(a * a + b * b + c * c)); 478 } 479 480 gfx::Vector2dF MathUtil::ComputeTransform2dScaleComponents( 481 const gfx::Transform& transform, 482 float fallback_value) { 483 if (transform.HasPerspective()) 484 return gfx::Vector2dF(fallback_value, fallback_value); 485 float x_scale = ScaleOnAxis(transform.matrix().getDouble(0, 0), 486 transform.matrix().getDouble(1, 0), 487 transform.matrix().getDouble(2, 0)); 488 float y_scale = ScaleOnAxis(transform.matrix().getDouble(0, 1), 489 transform.matrix().getDouble(1, 1), 490 transform.matrix().getDouble(2, 1)); 491 return gfx::Vector2dF(x_scale, y_scale); 492 } 493 494 float MathUtil::SmallestAngleBetweenVectors(gfx::Vector2dF v1, 495 gfx::Vector2dF v2) { 496 double dot_product = gfx::DotProduct(v1, v2) / v1.Length() / v2.Length(); 497 // Clamp to compensate for rounding errors. 498 dot_product = std::max(-1.0, std::min(1.0, dot_product)); 499 return static_cast<float>(Rad2Deg(std::acos(dot_product))); 500 } 501 502 gfx::Vector2dF MathUtil::ProjectVector(gfx::Vector2dF source, 503 gfx::Vector2dF destination) { 504 float projected_length = 505 gfx::DotProduct(source, destination) / destination.LengthSquared(); 506 return gfx::Vector2dF(projected_length * destination.x(), 507 projected_length * destination.y()); 508 } 509 510 scoped_ptr<base::Value> MathUtil::AsValue(gfx::Size s) { 511 scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue()); 512 res->SetDouble("width", s.width()); 513 res->SetDouble("height", s.height()); 514 return res.PassAs<base::Value>(); 515 } 516 517 scoped_ptr<base::Value> MathUtil::AsValue(gfx::SizeF s) { 518 scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue()); 519 res->SetDouble("width", s.width()); 520 res->SetDouble("height", s.height()); 521 return res.PassAs<base::Value>(); 522 } 523 524 scoped_ptr<base::Value> MathUtil::AsValue(gfx::Rect r) { 525 scoped_ptr<base::ListValue> res(new base::ListValue()); 526 res->AppendInteger(r.x()); 527 res->AppendInteger(r.y()); 528 res->AppendInteger(r.width()); 529 res->AppendInteger(r.height()); 530 return res.PassAs<base::Value>(); 531 } 532 533 bool MathUtil::FromValue(const base::Value* raw_value, gfx::Rect* out_rect) { 534 const base::ListValue* value = NULL; 535 if (!raw_value->GetAsList(&value)) 536 return false; 537 538 if (value->GetSize() != 4) 539 return false; 540 541 int x, y, w, h; 542 bool ok = true; 543 ok &= value->GetInteger(0, &x); 544 ok &= value->GetInteger(1, &y); 545 ok &= value->GetInteger(2, &w); 546 ok &= value->GetInteger(3, &h); 547 if (!ok) 548 return false; 549 550 *out_rect = gfx::Rect(x, y, w, h); 551 return true; 552 } 553 554 scoped_ptr<base::Value> MathUtil::AsValue(gfx::PointF pt) { 555 scoped_ptr<base::ListValue> res(new base::ListValue()); 556 res->AppendDouble(pt.x()); 557 res->AppendDouble(pt.y()); 558 return res.PassAs<base::Value>(); 559 } 560 561 scoped_ptr<base::Value> MathUtil::AsValue(const gfx::QuadF& q) { 562 scoped_ptr<base::ListValue> res(new base::ListValue()); 563 res->AppendDouble(q.p1().x()); 564 res->AppendDouble(q.p1().y()); 565 res->AppendDouble(q.p2().x()); 566 res->AppendDouble(q.p2().y()); 567 res->AppendDouble(q.p3().x()); 568 res->AppendDouble(q.p3().y()); 569 res->AppendDouble(q.p4().x()); 570 res->AppendDouble(q.p4().y()); 571 return res.PassAs<base::Value>(); 572 } 573 574 scoped_ptr<base::Value> MathUtil::AsValue(const gfx::RectF& rect) { 575 scoped_ptr<base::ListValue> res(new base::ListValue()); 576 res->AppendDouble(rect.x()); 577 res->AppendDouble(rect.y()); 578 res->AppendDouble(rect.width()); 579 res->AppendDouble(rect.height()); 580 return res.PassAs<base::Value>(); 581 } 582 583 scoped_ptr<base::Value> MathUtil::AsValue(const gfx::Transform& transform) { 584 scoped_ptr<base::ListValue> res(new base::ListValue()); 585 const SkMatrix44& m = transform.matrix(); 586 for (int row = 0; row < 4; ++row) { 587 for (int col = 0; col < 4; ++col) 588 res->AppendDouble(m.getDouble(row, col)); 589 } 590 return res.PassAs<base::Value>(); 591 } 592 593 scoped_ptr<base::Value> MathUtil::AsValue(const gfx::BoxF& box) { 594 scoped_ptr<base::ListValue> res(new base::ListValue()); 595 res->AppendInteger(box.x()); 596 res->AppendInteger(box.y()); 597 res->AppendInteger(box.z()); 598 res->AppendInteger(box.width()); 599 res->AppendInteger(box.height()); 600 res->AppendInteger(box.depth()); 601 return res.PassAs<base::Value>(); 602 } 603 604 scoped_ptr<base::Value> MathUtil::AsValueSafely(double value) { 605 return scoped_ptr<base::Value>(base::Value::CreateDoubleValue( 606 std::min(value, std::numeric_limits<double>::max()))); 607 } 608 609 scoped_ptr<base::Value> MathUtil::AsValueSafely(float value) { 610 return scoped_ptr<base::Value>(base::Value::CreateDoubleValue( 611 std::min(value, std::numeric_limits<float>::max()))); 612 } 613 614 } // namespace cc 615