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