зеркало из https://github.com/mozilla/moz-skia.git
Add SkRRect::transform.
Much like SkPath::transform, it transforms an SkRRect based on an SkMatrix. Unlike SkPath::transform, it will fail for matrices that contain perspective or skewing. Required by a future change (https://codereview.chromium.org/48623006) to speed up drawing large blurry rounded rectangles by using ninepatches. TODO: This could easily support 90 degree rotations, if desired. BUG=https://b.corp.google.com/issue?id=11174385 R=reed@google.com, robertphillips@google.com Review URL: https://codereview.chromium.org/52703003 git-svn-id: http://skia.googlecode.com/svn/trunk@12132 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
Родитель
a93f4e770f
Коммит
20e3cd2c9f
|
@ -12,6 +12,7 @@
|
|||
#include "SkPoint.h"
|
||||
|
||||
class SkPath;
|
||||
class SkMatrix;
|
||||
|
||||
// Path forward:
|
||||
// core work
|
||||
|
@ -259,6 +260,17 @@ public:
|
|||
*/
|
||||
size_t readFromMemory(const void* buffer, size_t length);
|
||||
|
||||
/**
|
||||
* Transform by the specified matrix, and put the result in dst.
|
||||
*
|
||||
* @param matrix SkMatrix specifying the transform. Must only contain
|
||||
* scale and/or translate, or this call will fail.
|
||||
* @param dst SkRRect to store the result. It is an error to use this,
|
||||
* which would make this function no longer const.
|
||||
* @return true on success, false on failure. If false, dst is unmodified.
|
||||
*/
|
||||
bool transform(const SkMatrix& matrix, SkRRect* dst) const;
|
||||
|
||||
private:
|
||||
SkRect fRect;
|
||||
// Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[]
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include "SkRRect.h"
|
||||
#include "SkMatrix.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -233,6 +234,85 @@ void SkRRect::computeType() const {
|
|||
fType = kComplex_Type;
|
||||
}
|
||||
|
||||
static bool matrix_only_scale_and_translate(const SkMatrix& matrix) {
|
||||
const SkMatrix::TypeMask m = (SkMatrix::TypeMask) (SkMatrix::kAffine_Mask
|
||||
| SkMatrix::kPerspective_Mask);
|
||||
return (matrix.getType() & m) == 0;
|
||||
}
|
||||
|
||||
bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const {
|
||||
if (NULL == dst) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assert that the caller is not trying to do this in place, which
|
||||
// would violate const-ness. Do not return false though, so that
|
||||
// if they know what they're doing and want to violate it they can.
|
||||
SkASSERT(dst != this);
|
||||
|
||||
if (matrix.isIdentity()) {
|
||||
*dst = *this;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If transform supported 90 degree rotations (which it could), we could
|
||||
// use SkMatrix::rectStaysRect() to check for a valid transformation.
|
||||
if (!matrix_only_scale_and_translate(matrix)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkRect newRect;
|
||||
if (!matrix.mapRect(&newRect, fRect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point, this is guaranteed to succeed, so we can modify dst.
|
||||
dst->fRect = newRect;
|
||||
|
||||
// Now scale each corner
|
||||
SkScalar xScale = matrix.getScaleX();
|
||||
const bool flipX = xScale < 0;
|
||||
if (flipX) {
|
||||
xScale = -xScale;
|
||||
}
|
||||
SkScalar yScale = matrix.getScaleY();
|
||||
const bool flipY = yScale < 0;
|
||||
if (flipY) {
|
||||
yScale = -yScale;
|
||||
}
|
||||
|
||||
// Scale the radii without respecting the flip.
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
dst->fRadii[i].fX = SkScalarMul(fRadii[i].fX, xScale);
|
||||
dst->fRadii[i].fY = SkScalarMul(fRadii[i].fY, yScale);
|
||||
}
|
||||
|
||||
// Now swap as necessary.
|
||||
if (flipX) {
|
||||
if (flipY) {
|
||||
// Swap with opposite corners
|
||||
SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]);
|
||||
SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]);
|
||||
} else {
|
||||
// Only swap in x
|
||||
SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]);
|
||||
SkTSwap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]);
|
||||
}
|
||||
} else if (flipY) {
|
||||
// Only swap in y
|
||||
SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]);
|
||||
SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]);
|
||||
}
|
||||
|
||||
// Since the only transforms that were allowed are scale and translate, the type
|
||||
// remains unchanged.
|
||||
dst->fType = fType;
|
||||
|
||||
SkDEBUGCODE(dst->validate();)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
*/
|
||||
|
||||
#include "Test.h"
|
||||
#include "SkMatrix.h"
|
||||
#include "SkRRect.h"
|
||||
|
||||
static const SkScalar kWidth = 100.0f;
|
||||
static const SkScalar kHeight = 100.0f;
|
||||
static const SkScalar kWidth = SkFloatToScalar(100.0f);
|
||||
static const SkScalar kHeight = SkFloatToScalar(100.0f);
|
||||
|
||||
static void test_inset(skiatest::Reporter* reporter) {
|
||||
SkRRect rr, rr2;
|
||||
|
@ -352,6 +353,206 @@ static void test_round_rect_contains_rect(skiatest::Reporter* reporter) {
|
|||
}
|
||||
}
|
||||
|
||||
// Called for a matrix that should cause SkRRect::transform to fail.
|
||||
static void assert_transform_failure(skiatest::Reporter* reporter, const SkRRect& orig,
|
||||
const SkMatrix& matrix) {
|
||||
// The test depends on the fact that the original is not empty.
|
||||
SkASSERT(!orig.isEmpty());
|
||||
SkRRect dst;
|
||||
dst.setEmpty();
|
||||
|
||||
const SkRRect copyOfDst = dst;
|
||||
const SkRRect copyOfOrig = orig;
|
||||
bool success = orig.transform(matrix, &dst);
|
||||
// This transform should fail.
|
||||
REPORTER_ASSERT(reporter, !success);
|
||||
// Since the transform failed, dst should be unchanged.
|
||||
REPORTER_ASSERT(reporter, copyOfDst == dst);
|
||||
// original should not be modified.
|
||||
REPORTER_ASSERT(reporter, copyOfOrig == orig);
|
||||
REPORTER_ASSERT(reporter, orig != dst);
|
||||
}
|
||||
|
||||
#define GET_RADII \
|
||||
const SkVector& origUL = orig.radii(SkRRect::kUpperLeft_Corner); \
|
||||
const SkVector& origUR = orig.radii(SkRRect::kUpperRight_Corner); \
|
||||
const SkVector& origLR = orig.radii(SkRRect::kLowerRight_Corner); \
|
||||
const SkVector& origLL = orig.radii(SkRRect::kLowerLeft_Corner); \
|
||||
const SkVector& dstUL = dst.radii(SkRRect::kUpperLeft_Corner); \
|
||||
const SkVector& dstUR = dst.radii(SkRRect::kUpperRight_Corner); \
|
||||
const SkVector& dstLR = dst.radii(SkRRect::kLowerRight_Corner); \
|
||||
const SkVector& dstLL = dst.radii(SkRRect::kLowerLeft_Corner)
|
||||
|
||||
// Called to test various transforms on a single SkRRect.
|
||||
static void test_transform_helper(skiatest::Reporter* reporter, const SkRRect& orig) {
|
||||
SkRRect dst;
|
||||
dst.setEmpty();
|
||||
|
||||
// The identity matrix will duplicate the rrect.
|
||||
bool success = orig.transform(SkMatrix::I(), &dst);
|
||||
REPORTER_ASSERT(reporter, success);
|
||||
REPORTER_ASSERT(reporter, orig == dst);
|
||||
|
||||
// Skew and Perspective make transform fail.
|
||||
SkMatrix matrix;
|
||||
matrix.reset();
|
||||
matrix.setSkewX(SkIntToScalar(2));
|
||||
assert_transform_failure(reporter, orig, matrix);
|
||||
|
||||
matrix.reset();
|
||||
matrix.setSkewY(SkIntToScalar(3));
|
||||
assert_transform_failure(reporter, orig, matrix);
|
||||
|
||||
matrix.reset();
|
||||
matrix.setPerspX(SkScalarToPersp(SkIntToScalar(4)));
|
||||
assert_transform_failure(reporter, orig, matrix);
|
||||
|
||||
matrix.reset();
|
||||
matrix.setPerspY(SkScalarToPersp(SkIntToScalar(5)));
|
||||
assert_transform_failure(reporter, orig, matrix);
|
||||
|
||||
// Rotation fails.
|
||||
matrix.reset();
|
||||
matrix.setRotate(SkIntToScalar(90));
|
||||
assert_transform_failure(reporter, orig, matrix);
|
||||
matrix.setRotate(SkIntToScalar(37));
|
||||
assert_transform_failure(reporter, orig, matrix);
|
||||
|
||||
// Translate will keep the rect moved, but otherwise the same.
|
||||
matrix.reset();
|
||||
SkScalar translateX = SkIntToScalar(32);
|
||||
SkScalar translateY = SkIntToScalar(15);
|
||||
matrix.setTranslateX(translateX);
|
||||
matrix.setTranslateY(translateY);
|
||||
dst.setEmpty();
|
||||
success = orig.transform(matrix, &dst);
|
||||
REPORTER_ASSERT(reporter, success);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
REPORTER_ASSERT(reporter,
|
||||
orig.radii((SkRRect::Corner) i) == dst.radii((SkRRect::Corner) i));
|
||||
}
|
||||
REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
|
||||
REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
|
||||
REPORTER_ASSERT(reporter, dst.rect().left() == orig.rect().left() + translateX);
|
||||
REPORTER_ASSERT(reporter, dst.rect().top() == orig.rect().top() + translateY);
|
||||
|
||||
// Keeping the translation, but adding skew will make transform fail.
|
||||
matrix.setSkewY(SkIntToScalar(7));
|
||||
assert_transform_failure(reporter, orig, matrix);
|
||||
|
||||
// Scaling in -x will flip the round rect horizontally.
|
||||
matrix.reset();
|
||||
matrix.setScaleX(SkIntToScalar(-1));
|
||||
dst.setEmpty();
|
||||
success = orig.transform(matrix, &dst);
|
||||
REPORTER_ASSERT(reporter, success);
|
||||
{
|
||||
GET_RADII;
|
||||
// Radii have swapped in x.
|
||||
REPORTER_ASSERT(reporter, origUL == dstUR);
|
||||
REPORTER_ASSERT(reporter, origUR == dstUL);
|
||||
REPORTER_ASSERT(reporter, origLR == dstLL);
|
||||
REPORTER_ASSERT(reporter, origLL == dstLR);
|
||||
}
|
||||
// Width and height remain the same.
|
||||
REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
|
||||
REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
|
||||
// Right and left have swapped (sort of)
|
||||
REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
|
||||
// Top has stayed the same.
|
||||
REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top());
|
||||
|
||||
// Keeping the scale, but adding a persp will make transform fail.
|
||||
matrix.setPerspX(SkScalarToPersp(SkIntToScalar(7)));
|
||||
assert_transform_failure(reporter, orig, matrix);
|
||||
|
||||
// Scaling in -y will flip the round rect vertically.
|
||||
matrix.reset();
|
||||
matrix.setScaleY(SkIntToScalar(-1));
|
||||
dst.setEmpty();
|
||||
success = orig.transform(matrix, &dst);
|
||||
REPORTER_ASSERT(reporter, success);
|
||||
{
|
||||
GET_RADII;
|
||||
// Radii have swapped in y.
|
||||
REPORTER_ASSERT(reporter, origUL == dstLL);
|
||||
REPORTER_ASSERT(reporter, origUR == dstLR);
|
||||
REPORTER_ASSERT(reporter, origLR == dstUR);
|
||||
REPORTER_ASSERT(reporter, origLL == dstUL);
|
||||
}
|
||||
// Width and height remain the same.
|
||||
REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
|
||||
REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
|
||||
// Top and bottom have swapped (sort of)
|
||||
REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
|
||||
// Left has stayed the same.
|
||||
REPORTER_ASSERT(reporter, orig.rect().left() == dst.rect().left());
|
||||
|
||||
// Scaling in -x and -y will swap in both directions.
|
||||
matrix.reset();
|
||||
matrix.setScaleY(SkIntToScalar(-1));
|
||||
matrix.setScaleX(SkIntToScalar(-1));
|
||||
dst.setEmpty();
|
||||
success = orig.transform(matrix, &dst);
|
||||
REPORTER_ASSERT(reporter, success);
|
||||
{
|
||||
GET_RADII;
|
||||
REPORTER_ASSERT(reporter, origUL == dstLR);
|
||||
REPORTER_ASSERT(reporter, origUR == dstLL);
|
||||
REPORTER_ASSERT(reporter, origLR == dstUL);
|
||||
REPORTER_ASSERT(reporter, origLL == dstUR);
|
||||
}
|
||||
// Width and height remain the same.
|
||||
REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
|
||||
REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
|
||||
REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
|
||||
REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
|
||||
|
||||
// Scale in both directions.
|
||||
SkScalar xScale = SkIntToScalar(3);
|
||||
SkScalar yScale = SkFloatToScalar(3.2f);
|
||||
matrix.reset();
|
||||
matrix.setScaleX(xScale);
|
||||
matrix.setScaleY(yScale);
|
||||
dst.setEmpty();
|
||||
success = orig.transform(matrix, &dst);
|
||||
REPORTER_ASSERT(reporter, success);
|
||||
// Radii are scaled.
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX,
|
||||
SkScalarMul(orig.radii((SkRRect::Corner) i).fX, xScale)));
|
||||
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
|
||||
SkScalarMul(orig.radii((SkRRect::Corner) i).fY, yScale)));
|
||||
}
|
||||
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
|
||||
SkScalarMul(orig.rect().width(), xScale)));
|
||||
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
|
||||
SkScalarMul(orig.rect().height(), yScale)));
|
||||
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
|
||||
SkScalarMul(orig.rect().left(), xScale)));
|
||||
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
|
||||
SkScalarMul(orig.rect().top(), yScale)));
|
||||
}
|
||||
|
||||
static void test_round_rect_transform(skiatest::Reporter* reporter) {
|
||||
SkRRect rrect;
|
||||
{
|
||||
SkRect r = { 0, 0, kWidth, kHeight };
|
||||
rrect.setRectXY(r, SkIntToScalar(4), SkIntToScalar(7));
|
||||
test_transform_helper(reporter, rrect);
|
||||
}
|
||||
{
|
||||
SkRect r = { SkIntToScalar(5), SkIntToScalar(15),
|
||||
SkIntToScalar(27), SkIntToScalar(34) };
|
||||
SkVector radii[4] = { { 0, SkIntToScalar(1) },
|
||||
{ SkIntToScalar(2), SkIntToScalar(3) },
|
||||
{ SkIntToScalar(4), SkIntToScalar(5) },
|
||||
{ SkIntToScalar(6), SkIntToScalar(7) } };
|
||||
rrect.setRectRadii(r, radii);
|
||||
test_transform_helper(reporter, rrect);
|
||||
}
|
||||
}
|
||||
|
||||
static void TestRoundRect(skiatest::Reporter* reporter) {
|
||||
test_round_rect_basic(reporter);
|
||||
test_round_rect_rects(reporter);
|
||||
|
@ -360,6 +561,7 @@ static void TestRoundRect(skiatest::Reporter* reporter) {
|
|||
test_round_rect_iffy_parameters(reporter);
|
||||
test_inset(reporter);
|
||||
test_round_rect_contains_rect(reporter);
|
||||
test_round_rect_transform(reporter);
|
||||
}
|
||||
|
||||
#include "TestClassDef.h"
|
||||
|
|
Загрузка…
Ссылка в новой задаче