| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/gfx/geometry/rrect_f.h" |
| |
| #include <iomanip> |
| #include <iostream> |
| #include <sstream> |
| |
| #include "base/values.h" |
| #include "third_party/skia/include/core/SkMatrix.h" |
| |
| namespace gfx { |
| |
| // Sets all x radii to x_rad, and all y radii to y_rad. If one of x_rad or |
| // y_rad are zero, sets ALL radii to zero. |
| RRectF::RRectF(float x, |
| float y, |
| float width, |
| float height, |
| float x_rad, |
| float y_rad) |
| : skrrect_(SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, width, height), |
| x_rad, |
| y_rad)) { |
| if (IsEmpty()) { |
| // Make sure that empty rects are created fully empty, not with some |
| // non-zero dimensions. |
| skrrect_ = SkRRect::MakeEmpty(); |
| } |
| } |
| |
| // Directly sets all four corners. |
| RRectF::RRectF(float x, |
| float y, |
| float width, |
| float height, |
| float upper_left_x, |
| float upper_left_y, |
| float upper_right_x, |
| float upper_right_y, |
| float lower_right_x, |
| float lower_right_y, |
| float lower_left_x, |
| float lower_left_y) { |
| SkVector radii[4] = { |
| {upper_left_x, upper_left_y}, |
| {upper_right_x, upper_right_y}, |
| {lower_right_x, lower_right_y}, |
| {lower_left_x, lower_left_y}, |
| }; |
| skrrect_.setRectRadii(SkRect::MakeXYWH(x, y, width, height), radii); |
| if (IsEmpty()) { |
| // Make sure that empty rects are created fully empty, not with some |
| // non-zero dimensions. |
| skrrect_ = SkRRect::MakeEmpty(); |
| } |
| } |
| |
| gfx::Vector2dF RRectF::GetSimpleRadii() const { |
| DCHECK(GetType() <= Type::kOval); |
| SkPoint result = skrrect_.getSimpleRadii(); |
| return gfx::Vector2dF(result.x(), result.y()); |
| } |
| |
| float RRectF::GetSimpleRadius() const { |
| DCHECK(GetType() <= Type::kOval); |
| SkPoint result = skrrect_.getSimpleRadii(); |
| DCHECK_EQ(result.x(), result.y()); |
| return result.x(); |
| } |
| |
| RRectF::Type RRectF::GetType() const { |
| SkPoint rad; |
| switch (skrrect_.getType()) { |
| case SkRRect::kEmpty_Type: |
| return Type::kEmpty; |
| case SkRRect::kRect_Type: |
| return Type::kRect; |
| case SkRRect::kSimple_Type: |
| rad = skrrect_.getSimpleRadii(); |
| if (rad.x() == rad.y()) { |
| return Type::kSingle; |
| } |
| return Type::kSimple; |
| case SkRRect::kOval_Type: |
| rad = skrrect_.getSimpleRadii(); |
| if (rad.x() == rad.y()) { |
| return Type::kSingle; |
| } |
| return Type::kOval; |
| case SkRRect::kNinePatch_Type: |
| case SkRRect::kComplex_Type: |
| default: |
| return Type::kComplex; |
| } |
| } |
| |
| gfx::Vector2dF RRectF::GetCornerRadii(Corner corner) const { |
| SkPoint result = skrrect_.radii(SkRRect::Corner(corner)); |
| return gfx::Vector2dF(result.x(), result.y()); |
| } |
| |
| void RRectF::GetAllRadii(SkVector radii[4]) const { |
| // Unfortunately, the only way to get all radii is one at a time. |
| radii[SkRRect::kUpperLeft_Corner] = |
| skrrect_.radii(SkRRect::kUpperLeft_Corner); |
| radii[SkRRect::kUpperRight_Corner] = |
| skrrect_.radii(SkRRect::kUpperRight_Corner); |
| radii[SkRRect::kLowerRight_Corner] = |
| skrrect_.radii(SkRRect::kLowerRight_Corner); |
| radii[SkRRect::kLowerLeft_Corner] = |
| skrrect_.radii(SkRRect::kLowerLeft_Corner); |
| } |
| |
| void RRectF::SetCornerRadii(Corner corner, float x_rad, float y_rad) { |
| // Unfortunately, the only way to set this is to create a new SkRRect. |
| SkVector radii[4]; |
| GetAllRadii(radii); |
| radii[SkRRect::Corner(corner)] = SkPoint::Make(x_rad, y_rad); |
| skrrect_.setRectRadii(skrrect_.rect(), radii); |
| } |
| |
| void RRectF::Scale(float x_scale, float y_scale) { |
| if (IsEmpty()) { |
| // SkRRect doesn't support scaling of empty rects. |
| return; |
| } |
| if (!x_scale || !y_scale) { |
| // SkRRect doesn't support scaling TO an empty rect. |
| skrrect_ = SkRRect::MakeEmpty(); |
| return; |
| } |
| SkMatrix scale = SkMatrix::Scale(x_scale, y_scale); |
| SkRRect result; |
| if (!skrrect_.transform(scale, &result)) { |
| skrrect_ = SkRRect::MakeEmpty(); |
| return; |
| } |
| skrrect_ = result; |
| } |
| |
| void RRectF::Offset(float horizontal, float vertical) { |
| skrrect_.offset(horizontal, vertical); |
| } |
| |
| const RRectF& RRectF::operator+=(const gfx::Vector2dF& offset) { |
| Offset(offset.x(), offset.y()); |
| return *this; |
| } |
| |
| const RRectF& RRectF::operator-=(const gfx::Vector2dF& offset) { |
| Offset(-offset.x(), -offset.y()); |
| return *this; |
| } |
| |
| std::string RRectF::ToString() const { |
| std::stringstream ss; |
| ss << std::fixed << std::setprecision(3); |
| ss << rect().origin().x() << "," << rect().origin().y() << " " |
| << rect().size().width() << "x" << rect().size().height(); |
| Type type = this->GetType(); |
| if (type <= Type::kRect) { |
| ss << ", rectangular"; |
| } else if (type <= Type::kSingle) { |
| ss << ", radius " << GetSimpleRadius(); |
| } else if (type <= Type::kSimple) { |
| gfx::Vector2dF radii = GetSimpleRadii(); |
| ss << ", x_rad " << radii.x() << ", y_rad " << radii.y(); |
| } else { |
| ss << ","; |
| const Corner corners[] = {Corner::kUpperLeft, Corner::kUpperRight, |
| Corner::kLowerRight, Corner::kLowerLeft}; |
| for (const auto& c : corners) { |
| auto this_corner = GetCornerRadii(c); |
| ss << " [" << this_corner.x() << " " << this_corner.y() << "]"; |
| } |
| } |
| return ss.str(); |
| } |
| |
| namespace { |
| inline bool AboveTol(float val1, float val2, float tolerance) { |
| return (std::abs(val1 - val2) > tolerance); |
| } |
| } // namespace |
| |
| bool RRectF::ApproximatelyEqual(const RRectF& rect, float tolerance) const { |
| if (AboveTol(skrrect_.rect().x(), rect.skrrect_.rect().x(), tolerance) || |
| AboveTol(skrrect_.rect().y(), rect.skrrect_.rect().y(), tolerance) || |
| AboveTol(skrrect_.width(), rect.skrrect_.width(), tolerance) || |
| AboveTol(skrrect_.height(), rect.skrrect_.height(), tolerance)) |
| return false; |
| for (int i = 0; i < 4; i++) { |
| SkVector r1 = skrrect_.radii(SkRRect::Corner(i)); |
| SkVector r2 = rect.skrrect_.radii(SkRRect::Corner(i)); |
| if (std::abs(r1.x() - r2.x()) > tolerance || |
| std::abs(r1.y() - r2.y()) > tolerance) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace gfx |