#include "include/core/SkRRect.h"
#include "src/core/SkRectPriv.h"
#include "src/core/SkScaleToSides.h"
namespace pk {
void SkRRect::setOval(const SkRect& oval) {
if (!this->initializeRect(oval)) {
return;
}
SkScalar xRad = SkRectPriv::HalfWidth(fRect);
SkScalar yRad = SkRectPriv::HalfHeight(fRect);
if (xRad == 0.0f || yRad == 0.0f) {
memset(fRadii, 0, sizeof(fRadii));
fType = kRect_Type;
} else {
for (int i = 0; i < 4; ++i) {
fRadii[i].set(xRad, yRad);
}
fType = kOval_Type;
}
PkASSERT(this->isValid());
}
void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
if (!this->initializeRect(rect)) {
return;
}
if (!SkScalarsAreFinite(xRad, yRad)) {
xRad = yRad = 0; }
if (fRect.width() < xRad + xRad || fRect.height() < yRad + yRad) {
SkScalar scale = std::min(sk_ieee_float_divide(fRect.width(), xRad + xRad),
sk_ieee_float_divide(fRect.height(), yRad + yRad));
xRad *= scale;
yRad *= scale;
}
if (xRad <= 0 || yRad <= 0) {
this->setRect(rect);
return;
}
for (int i = 0; i < 4; ++i) {
fRadii[i].set(xRad, yRad);
}
fType = kSimple_Type;
if (xRad >= PkScalarHalf(fRect.width()) && yRad >= PkScalarHalf(fRect.height())) {
fType = kOval_Type;
}
}
static double compute_min_scale(double rad1, double rad2, double limit, double curMin) {
if ((rad1 + rad2) > limit) {
return std::min(curMin, limit / (rad1 + rad2));
}
return curMin;
}
static bool clamp_to_zero(SkVector radii[4]) {
bool allCornersSquare = true;
for (int i = 0; i < 4; ++i) {
if (radii[i].fX <= 0 || radii[i].fY <= 0) {
radii[i].fX = 0;
radii[i].fY = 0;
} else {
allCornersSquare = false;
}
}
return allCornersSquare;
}
void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
if (!this->initializeRect(rect)) {
return;
}
if (!SkScalarsAreFinite(&radii[0].fX, 8)) {
this->setRect(rect); return;
}
memcpy(fRadii, radii, sizeof(fRadii));
if (clamp_to_zero(fRadii)) {
this->setRect(rect);
return;
}
this->scaleRadii();
if (!this->isValid()) {
this->setRect(rect);
return;
}
}
bool SkRRect::initializeRect(const SkRect& rect) {
if (!rect.isFinite()) {
*this = SkRRect();
return false;
}
fRect = rect.makeSorted();
if (fRect.isEmpty()) {
memset(fRadii, 0, sizeof(fRadii));
fType = kEmpty_Type;
return false;
}
return true;
}
static void flush_to_zero(SkScalar& a, SkScalar& b) {
if (a + b == a) {
b = 0;
} else if (a + b == b) {
a = 0;
}
}
bool SkRRect::scaleRadii() {
double scale = 1.0;
double width = (double)fRect.fRight - (double)fRect.fLeft;
double height = (double)fRect.fBottom - (double)fRect.fTop;
scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, width, scale);
scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, height, scale);
scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, width, scale);
scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, height, scale);
flush_to_zero(fRadii[0].fX, fRadii[1].fX);
flush_to_zero(fRadii[1].fY, fRadii[2].fY);
flush_to_zero(fRadii[2].fX, fRadii[3].fX);
flush_to_zero(fRadii[3].fY, fRadii[0].fY);
if (scale < 1.0) {
SkScaleToSides::AdjustRadii(width, scale, &fRadii[0].fX, &fRadii[1].fX);
SkScaleToSides::AdjustRadii(height, scale, &fRadii[1].fY, &fRadii[2].fY);
SkScaleToSides::AdjustRadii(width, scale, &fRadii[2].fX, &fRadii[3].fX);
SkScaleToSides::AdjustRadii(height, scale, &fRadii[3].fY, &fRadii[0].fY);
}
clamp_to_zero(fRadii);
this->computeType();
return scale < 1.0;
}
static bool radii_are_nine_patch(const SkVector radii[4]) {
return radii[SkRRect::kUpperLeft_Corner].fX == radii[SkRRect::kLowerLeft_Corner].fX &&
radii[SkRRect::kUpperLeft_Corner].fY == radii[SkRRect::kUpperRight_Corner].fY &&
radii[SkRRect::kUpperRight_Corner].fX == radii[SkRRect::kLowerRight_Corner].fX &&
radii[SkRRect::kLowerLeft_Corner].fY == radii[SkRRect::kLowerRight_Corner].fY;
}
void SkRRect::computeType() {
if (fRect.isEmpty()) {
PkASSERT(fRect.isSorted());
for (size_t i = 0; i < PK_ARRAY_COUNT(fRadii); ++i) {
PkASSERT((fRadii[i] == SkVector{0, 0}));
}
fType = kEmpty_Type;
PkASSERT(this->isValid());
return;
}
bool allRadiiEqual = true; bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;
for (int i = 1; i < 4; ++i) {
if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
allCornersSquare = false;
}
if (fRadii[i].fX != fRadii[i - 1].fX || fRadii[i].fY != fRadii[i - 1].fY) {
allRadiiEqual = false;
}
}
if (allCornersSquare) {
fType = kRect_Type;
PkASSERT(this->isValid());
return;
}
if (allRadiiEqual) {
if (fRadii[0].fX >= PkScalarHalf(fRect.width()) &&
fRadii[0].fY >= PkScalarHalf(fRect.height())) {
fType = kOval_Type;
} else {
fType = kSimple_Type;
}
PkASSERT(this->isValid());
return;
}
if (radii_are_nine_patch(fRadii)) {
fType = kNinePatch_Type;
} else {
fType = kComplex_Type;
}
if (!this->isValid()) {
this->setRect(this->rect());
PkASSERT(this->isValid());
}
}
static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) {
return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min) &&
rad >= 0;
}
bool SkRRect::isValid() const {
if (!AreRectAndRadiiValid(fRect, fRadii)) {
return false;
}
bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
bool allRadiiSame = true;
for (int i = 1; i < 4; ++i) {
if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
allRadiiZero = false;
}
if (fRadii[i].fX != fRadii[i - 1].fX || fRadii[i].fY != fRadii[i - 1].fY) {
allRadiiSame = false;
}
if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
allCornersSquare = false;
}
}
bool patchesOfNine = radii_are_nine_patch(fRadii);
if (fType < 0 || fType > kLastType) {
return false;
}
switch (fType) {
case kEmpty_Type:
if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
return false;
}
break;
case kRect_Type:
if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
return false;
}
break;
case kOval_Type:
if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
return false;
}
for (int i = 0; i < 4; ++i) {
if (!SkScalarNearlyEqual(fRadii[i].fX, SkRectPriv::HalfWidth(fRect)) ||
!SkScalarNearlyEqual(fRadii[i].fY, SkRectPriv::HalfHeight(fRect))) {
return false;
}
}
break;
case kSimple_Type:
if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
return false;
}
break;
case kNinePatch_Type:
if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
!patchesOfNine) {
return false;
}
break;
case kComplex_Type:
if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
patchesOfNine) {
return false;
}
break;
}
return true;
}
bool SkRRect::AreRectAndRadiiValid(const SkRect& rect, const SkVector radii[4]) {
if (!rect.isFinite() || !rect.isSorted()) {
return false;
}
for (int i = 0; i < 4; ++i) {
if (!are_radius_check_predicates_valid(radii[i].fX, rect.fLeft, rect.fRight) ||
!are_radius_check_predicates_valid(radii[i].fY, rect.fTop, rect.fBottom)) {
return false;
}
}
return true;
}
}