void SkDrawMatrix::onEndElement(SkAnimateMaker& ) { if (matrix.count() > 0) { SkScalar* vals = matrix.begin(); fMatrix.setScaleX(vals[0]); fMatrix.setSkewX(vals[1]); fMatrix.setTranslateX(vals[2]); fMatrix.setSkewY(vals[3]); fMatrix.setScaleY(vals[4]); fMatrix.setTranslateY(vals[5]); fMatrix.setPerspX(SkScalarToPersp(vals[6])); fMatrix.setPerspY(SkScalarToPersp(vals[7])); // fMatrix.setPerspW(SkScalarToPersp(vals[8])); goto setConcat; } if (fChildHasID == false) { { for (SkMatrixPart** part = fParts.begin(); part < fParts.end(); part++) delete *part; } fParts.reset(); setConcat: fConcat = fMatrix; fDirty = false; } }
bool SkDrawMatrix::setProperty(int index, SkScriptValue& scriptValue) { SkScalar number = scriptValue.fOperand.fScalar; switch (index) { case SK_PROPERTY(translate): // SkScalar xy[2]; SkASSERT(scriptValue.fType == SkType_Array); SkASSERT(scriptValue.fOperand.fArray->getType() == SkType_Float); SkASSERT(scriptValue.fOperand.fArray->count() == 2); // SkParse::FindScalars(scriptValue.fOperand.fString->c_str(), xy, 2); fMatrix.setTranslateX((*scriptValue.fOperand.fArray)[0].fScalar); fMatrix.setTranslateY((*scriptValue.fOperand.fArray)[1].fScalar); return true; case SK_PROPERTY(perspectX): fMatrix.setPerspX(SkScalarToPersp((number))); break; case SK_PROPERTY(perspectY): fMatrix.setPerspY(SkScalarToPersp((number))); break; case SK_PROPERTY(rotate): { SkMatrix temp; temp.setRotate(number, 0, 0); fMatrix.setScaleX(temp.getScaleX()); fMatrix.setScaleY(temp.getScaleY()); fMatrix.setSkewX(temp.getSkewX()); fMatrix.setSkewY(temp.getSkewY()); } break; case SK_PROPERTY(scale): fMatrix.setScaleX(number); fMatrix.setScaleY(number); break; case SK_PROPERTY(scaleX): fMatrix.setScaleX(number); break; case SK_PROPERTY(scaleY): fMatrix.setScaleY(number); break; case SK_PROPERTY(skewX): fMatrix.setSkewX(number); break; case SK_PROPERTY(skewY): fMatrix.setSkewY(number); break; case SK_PROPERTY(translateX): fMatrix.setTranslateX(number); break; case SK_PROPERTY(translateY): fMatrix.setTranslateY(number); break; default: SkASSERT(0); return false; } fConcat = fMatrix; return true; }
virtual void onDraw(SkCanvas* canvas) { this->makePath(); // do perspective drawPaint as the background; SkPaint bkgnrd; SkPoint center = SkPoint::Make(SkIntToScalar(100), SkIntToScalar(100)); SkColor colors[] = {SK_ColorBLACK, SK_ColorCYAN, SK_ColorYELLOW, SK_ColorWHITE}; SkScalar pos[] = {0, SK_ScalarHalf / 2, 3 * SK_ScalarHalf / 2, SK_Scalar1}; SkShader* s = SkGradientShader::CreateRadial(center, SkIntToScalar(1000), colors, pos, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode); bkgnrd.setShader(s)->unref(); canvas->save(); canvas->translate(SkIntToScalar(100), SkIntToScalar(100)); SkMatrix mat; mat.reset(); mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 1000)); canvas->concat(mat); canvas->drawPaint(bkgnrd); canvas->restore(); // draw the paths in perspective SkMatrix persp; persp.reset(); persp.setPerspX(SkScalarToPersp(-SK_Scalar1 / 1800)); persp.setPerspY(SkScalarToPersp(SK_Scalar1 / 500)); canvas->concat(persp); canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); const SkScalar scale = SkIntToScalar(5)/4; showFour(canvas, SK_Scalar1, false); canvas->translate(SkIntToScalar(450), 0); showFour(canvas, scale, false); canvas->translate(SkIntToScalar(-450), SkIntToScalar(450)); showFour(canvas, SK_Scalar1, true); canvas->translate(SkIntToScalar(450), 0); showFour(canvas, scale, true); }
DEF_TEST(Matrix, reporter) { SkMatrix mat, inverse, iden1, iden2; mat.reset(); mat.setTranslate(SK_Scalar1, SK_Scalar1); REPORTER_ASSERT(reporter, mat.invert(&inverse)); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); mat.setScale(SkIntToScalar(2), SkIntToScalar(4)); REPORTER_ASSERT(reporter, mat.invert(&inverse)); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); test_flatten(reporter, mat); mat.setScale(SK_Scalar1/2, SkIntToScalar(2)); REPORTER_ASSERT(reporter, mat.invert(&inverse)); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); test_flatten(reporter, mat); mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0); mat.postRotate(SkIntToScalar(25)); REPORTER_ASSERT(reporter, mat.invert(NULL)); REPORTER_ASSERT(reporter, mat.invert(&inverse)); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); iden2.setConcat(inverse, mat); REPORTER_ASSERT(reporter, is_identity(iden2)); test_flatten(reporter, mat); test_flatten(reporter, iden2); mat.setScale(0, SK_Scalar1); REPORTER_ASSERT(reporter, !mat.invert(NULL)); REPORTER_ASSERT(reporter, !mat.invert(&inverse)); mat.setScale(SK_Scalar1, 0); REPORTER_ASSERT(reporter, !mat.invert(NULL)); REPORTER_ASSERT(reporter, !mat.invert(&inverse)); // rectStaysRect test { static const struct { SkScalar m00, m01, m10, m11; bool mStaysRect; } gRectStaysRectSamples[] = { { 0, 0, 0, 0, false }, { 0, 0, 0, SK_Scalar1, false }, { 0, 0, SK_Scalar1, 0, false }, { 0, 0, SK_Scalar1, SK_Scalar1, false }, { 0, SK_Scalar1, 0, 0, false }, { 0, SK_Scalar1, 0, SK_Scalar1, false }, { 0, SK_Scalar1, SK_Scalar1, 0, true }, { 0, SK_Scalar1, SK_Scalar1, SK_Scalar1, false }, { SK_Scalar1, 0, 0, 0, false }, { SK_Scalar1, 0, 0, SK_Scalar1, true }, { SK_Scalar1, 0, SK_Scalar1, 0, false }, { SK_Scalar1, 0, SK_Scalar1, SK_Scalar1, false }, { SK_Scalar1, SK_Scalar1, 0, 0, false }, { SK_Scalar1, SK_Scalar1, 0, SK_Scalar1, false }, { SK_Scalar1, SK_Scalar1, SK_Scalar1, 0, false }, { SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, false } }; for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) { SkMatrix m; m.reset(); m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00); m.set(SkMatrix::kMSkewX, gRectStaysRectSamples[i].m01); m.set(SkMatrix::kMSkewY, gRectStaysRectSamples[i].m10); m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11); REPORTER_ASSERT(reporter, m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect); } } mat.reset(); mat.set(SkMatrix::kMScaleX, SkIntToScalar(1)); mat.set(SkMatrix::kMSkewX, SkIntToScalar(2)); mat.set(SkMatrix::kMTransX, SkIntToScalar(3)); mat.set(SkMatrix::kMSkewY, SkIntToScalar(4)); mat.set(SkMatrix::kMScaleY, SkIntToScalar(5)); mat.set(SkMatrix::kMTransY, SkIntToScalar(6)); SkScalar affine[6]; REPORTER_ASSERT(reporter, mat.asAffine(affine)); #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e) REPORTER_ASSERT(reporter, affineEqual(ScaleX)); REPORTER_ASSERT(reporter, affineEqual(SkewY)); REPORTER_ASSERT(reporter, affineEqual(SkewX)); REPORTER_ASSERT(reporter, affineEqual(ScaleY)); REPORTER_ASSERT(reporter, affineEqual(TransX)); REPORTER_ASSERT(reporter, affineEqual(TransY)); #undef affineEqual mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2)); REPORTER_ASSERT(reporter, !mat.asAffine(affine)); SkMatrix mat2; mat2.reset(); mat.reset(); SkScalar zero = 0; mat.set(SkMatrix::kMSkewX, -zero); REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2)); mat2.reset(); mat.reset(); mat.set(SkMatrix::kMSkewX, SK_ScalarNaN); mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN); REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2)); test_matrix_min_max_scale(reporter); test_matrix_is_similarity(reporter); test_matrix_recttorect(reporter); test_matrix_decomposition(reporter); test_matrix_homogeneous(reporter); }
static void test_matrix_is_similarity(skiatest::Reporter* reporter) { SkMatrix mat; // identity mat.setIdentity(); REPORTER_ASSERT(reporter, mat.isSimilarity()); // translation only mat.reset(); mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // scale with same size mat.reset(); mat.setScale(SkIntToScalar(15), SkIntToScalar(15)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // scale with one negative mat.reset(); mat.setScale(SkIntToScalar(-15), SkIntToScalar(15)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // scale with different size mat.reset(); mat.setScale(SkIntToScalar(15), SkIntToScalar(20)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // scale with same size at a pivot point mat.reset(); mat.setScale(SkIntToScalar(15), SkIntToScalar(15), SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // scale with different size at a pivot point mat.reset(); mat.setScale(SkIntToScalar(15), SkIntToScalar(20), SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // skew with same size mat.reset(); mat.setSkew(SkIntToScalar(15), SkIntToScalar(15)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // skew with different size mat.reset(); mat.setSkew(SkIntToScalar(15), SkIntToScalar(20)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // skew with same size at a pivot point mat.reset(); mat.setSkew(SkIntToScalar(15), SkIntToScalar(15), SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // skew with different size at a pivot point mat.reset(); mat.setSkew(SkIntToScalar(15), SkIntToScalar(20), SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // perspective x mat.reset(); mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // perspective y mat.reset(); mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // rotate for (int angle = 0; angle < 360; ++angle) { mat.reset(); mat.setRotate(SkIntToScalar(angle)); REPORTER_ASSERT(reporter, mat.isSimilarity()); } // see if there are any accumulated precision issues mat.reset(); for (int i = 1; i < 360; i++) { mat.postRotate(SkIntToScalar(1)); } REPORTER_ASSERT(reporter, mat.isSimilarity()); // rotate + translate mat.reset(); mat.setRotate(SkIntToScalar(30)); mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // rotate + uniform scale mat.reset(); mat.setRotate(SkIntToScalar(30)); mat.postScale(SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // rotate + non-uniform scale mat.reset(); mat.setRotate(SkIntToScalar(30)); mat.postScale(SkIntToScalar(3), SkIntToScalar(2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // all zero mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // all zero except perspective mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // scales zero, only skews mat.setAll(0, SK_Scalar1, 0, SK_Scalar1, 0, 0, 0, 0, SkMatrix::I()[8]); REPORTER_ASSERT(reporter, mat.isSimilarity()); }
static void test_matrix_min_max_scale(skiatest::Reporter* reporter) { SkScalar scales[2]; bool success; SkMatrix identity; identity.reset(); REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMinScale()); REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxScale()); success = identity.getMinMaxScales(scales); REPORTER_ASSERT(reporter, success && SK_Scalar1 == scales[0] && SK_Scalar1 == scales[1]); SkMatrix scale; scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4); REPORTER_ASSERT(reporter, SK_Scalar1 * 2 == scale.getMinScale()); REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxScale()); success = scale.getMinMaxScales(scales); REPORTER_ASSERT(reporter, success && SK_Scalar1 * 2 == scales[0] && SK_Scalar1 * 4 == scales[1]); SkMatrix rot90Scale; rot90Scale.setRotate(90 * SK_Scalar1); rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2); REPORTER_ASSERT(reporter, SK_Scalar1 / 4 == rot90Scale.getMinScale()); REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxScale()); success = rot90Scale.getMinMaxScales(scales); REPORTER_ASSERT(reporter, success && SK_Scalar1 / 4 == scales[0] && SK_Scalar1 / 2 == scales[1]); SkMatrix rotate; rotate.setRotate(128 * SK_Scalar1); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMinScale(), SK_ScalarNearlyZero)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMaxScale(), SK_ScalarNearlyZero)); success = rotate.getMinMaxScales(scales); REPORTER_ASSERT(reporter, success); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, scales[0], SK_ScalarNearlyZero)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, scales[1], SK_ScalarNearlyZero)); SkMatrix translate; translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1); REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMinScale()); REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxScale()); success = translate.getMinMaxScales(scales); REPORTER_ASSERT(reporter, success && SK_Scalar1 == scales[0] && SK_Scalar1 == scales[1]); SkMatrix perspX; perspX.reset(); perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000)); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinScale()); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxScale()); // Verify that getMinMaxScales() doesn't update the scales array on failure. scales[0] = -5; scales[1] = -5; success = perspX.getMinMaxScales(scales); REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1 == scales[1]); SkMatrix perspY; perspY.reset(); perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500)); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinScale()); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxScale()); scales[0] = -5; scales[1] = -5; success = perspY.getMinMaxScales(scales); REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1 == scales[1]); SkMatrix baseMats[] = {scale, rot90Scale, rotate, translate, perspX, perspY}; SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)]; for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) { mats[i] = baseMats[i]; bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]); REPORTER_ASSERT(reporter, invertable); } SkRandom rand; for (int m = 0; m < 1000; ++m) { SkMatrix mat; mat.reset(); for (int i = 0; i < 4; ++i) { int x = rand.nextU() % SK_ARRAY_COUNT(mats); mat.postConcat(mats[x]); } SkScalar minScale = mat.getMinScale(); SkScalar maxScale = mat.getMaxScale(); REPORTER_ASSERT(reporter, (minScale < 0) == (maxScale < 0)); REPORTER_ASSERT(reporter, (maxScale < 0) == mat.hasPerspective()); SkScalar scales[2]; bool success = mat.getMinMaxScales(scales); REPORTER_ASSERT(reporter, success == !mat.hasPerspective()); REPORTER_ASSERT(reporter, !success || (scales[0] == minScale && scales[1] == maxScale)); if (mat.hasPerspective()) { m -= 1; // try another non-persp matrix continue; } // test a bunch of vectors. All should be scaled by between minScale and maxScale // (modulo some error) and we should find a vector that is scaled by almost each. static const SkScalar gVectorScaleTol = (105 * SK_Scalar1) / 100; static const SkScalar gCloseScaleTol = (97 * SK_Scalar1) / 100; SkScalar max = 0, min = SK_ScalarMax; SkVector vectors[1000]; for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) { vectors[i].fX = rand.nextSScalar1(); vectors[i].fY = rand.nextSScalar1(); if (!vectors[i].normalize()) { i -= 1; continue; } } mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors)); for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) { SkScalar d = vectors[i].length(); REPORTER_ASSERT(reporter, SkScalarDiv(d, maxScale) < gVectorScaleTol); REPORTER_ASSERT(reporter, SkScalarDiv(minScale, d) < gVectorScaleTol); if (max < d) { max = d; } if (min > d) { min = d; } } REPORTER_ASSERT(reporter, SkScalarDiv(max, maxScale) >= gCloseScaleTol); REPORTER_ASSERT(reporter, SkScalarDiv(minScale, min) >= gCloseScaleTol); } }
static void test_matrix_is_similarity(skiatest::Reporter* reporter) { SkMatrix mat; // identity mat.setIdentity(); REPORTER_ASSERT(reporter, mat.isSimilarity()); // translation only mat.reset(); mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // scale with same size mat.reset(); mat.setScale(SkIntToScalar(15), SkIntToScalar(15)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // scale with one negative mat.reset(); mat.setScale(SkIntToScalar(-15), SkIntToScalar(15)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // scale with different size mat.reset(); mat.setScale(SkIntToScalar(15), SkIntToScalar(20)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // scale with same size at a pivot point mat.reset(); mat.setScale(SkIntToScalar(15), SkIntToScalar(15), SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // scale with different size at a pivot point mat.reset(); mat.setScale(SkIntToScalar(15), SkIntToScalar(20), SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // skew with same size mat.reset(); mat.setSkew(SkIntToScalar(15), SkIntToScalar(15)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // skew with different size mat.reset(); mat.setSkew(SkIntToScalar(15), SkIntToScalar(20)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // skew with same size at a pivot point mat.reset(); mat.setSkew(SkIntToScalar(15), SkIntToScalar(15), SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // skew with different size at a pivot point mat.reset(); mat.setSkew(SkIntToScalar(15), SkIntToScalar(20), SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // perspective x mat.reset(); mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // perspective y mat.reset(); mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); #ifdef SK_SCALAR_IS_FLOAT /* We bypass the following tests for SK_SCALAR_IS_FIXED build. * The long discussion can be found in this issue: * http://codereview.appspot.com/5999050/ * In short, we haven't found a perfect way to fix the precision * issue, i.e. the way we use tolerance in isSimilarityTransformation * is incorrect. The situation becomes worse in fixed build, so * we disabled rotation related tests for fixed build. */ // rotate for (int angle = 0; angle < 360; ++angle) { mat.reset(); mat.setRotate(SkIntToScalar(angle)); REPORTER_ASSERT(reporter, mat.isSimilarity()); } // see if there are any accumulated precision issues mat.reset(); for (int i = 1; i < 360; i++) { mat.postRotate(SkIntToScalar(1)); } REPORTER_ASSERT(reporter, mat.isSimilarity()); // rotate + translate mat.reset(); mat.setRotate(SkIntToScalar(30)); mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // rotate + uniform scale mat.reset(); mat.setRotate(SkIntToScalar(30)); mat.postScale(SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // rotate + non-uniform scale mat.reset(); mat.setRotate(SkIntToScalar(30)); mat.postScale(SkIntToScalar(3), SkIntToScalar(2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); #endif // all zero mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // all zero except perspective mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // scales zero, only skews mat.setAll(0, SK_Scalar1, 0, SK_Scalar1, 0, 0, 0, 0, SkMatrix::I()[8]); REPORTER_ASSERT(reporter, mat.isSimilarity()); }
static void test_matrix_max_stretch(skiatest::Reporter* reporter) { SkMatrix identity; identity.reset(); REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch()); SkMatrix scale; scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4); REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxStretch()); SkMatrix rot90Scale; rot90Scale.setRotate(90 * SK_Scalar1); rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2); REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxStretch()); SkMatrix rotate; rotate.setRotate(128 * SK_Scalar1); REPORTER_ASSERT(reporter, SkScalarAbs(SK_Scalar1 - rotate.getMaxStretch()) <= SK_ScalarNearlyZero); SkMatrix translate; translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1); REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch()); SkMatrix perspX; perspX.reset(); perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000)); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch()); SkMatrix perspY; perspY.reset(); perspY.setPerspX(SkScalarToPersp(-SK_Scalar1 / 500)); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch()); SkMatrix baseMats[] = {scale, rot90Scale, rotate, translate, perspX, perspY}; SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)]; for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) { mats[i] = baseMats[i]; bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]); REPORTER_ASSERT(reporter, invertable); } SkMWCRandom rand; for (int m = 0; m < 1000; ++m) { SkMatrix mat; mat.reset(); for (int i = 0; i < 4; ++i) { int x = rand.nextU() % SK_ARRAY_COUNT(mats); mat.postConcat(mats[x]); } SkScalar stretch = mat.getMaxStretch(); if ((stretch < 0) != mat.hasPerspective()) { stretch = mat.getMaxStretch(); } REPORTER_ASSERT(reporter, (stretch < 0) == mat.hasPerspective()); if (mat.hasPerspective()) { m -= 1; // try another non-persp matrix continue; } // test a bunch of vectors. None should be scaled by more than stretch // (modulo some error) and we should find a vector that is scaled by // almost stretch. static const SkScalar gStretchTol = (105 * SK_Scalar1) / 100; static const SkScalar gMaxStretchTol = (97 * SK_Scalar1) / 100; SkScalar max = 0; SkVector vectors[1000]; for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) { vectors[i].fX = rand.nextSScalar1(); vectors[i].fY = rand.nextSScalar1(); if (!vectors[i].normalize()) { i -= 1; continue; } } mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors)); for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) { SkScalar d = vectors[i].length(); REPORTER_ASSERT(reporter, SkScalarDiv(d, stretch) < gStretchTol); if (max < d) { max = d; } } REPORTER_ASSERT(reporter, SkScalarDiv(max, stretch) >= gMaxStretchTol); } }
void SkScalerContextRec::getMatrixFrom2x2(SkMatrix* dst) const { dst->setAll(fPost2x2[0][0], fPost2x2[0][1], 0, fPost2x2[1][0], fPost2x2[1][1], 0, 0, 0, SkScalarToPersp(SK_Scalar1)); }
static void test_matrix_is_similarity(skiatest::Reporter* reporter) { SkMatrix mat; // identity mat.setIdentity(); REPORTER_ASSERT(reporter, mat.isSimilarity()); // translation only mat.reset(); mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // scale with same size mat.reset(); mat.setScale(SkIntToScalar(15), SkIntToScalar(15)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // scale with one negative mat.reset(); mat.setScale(SkIntToScalar(-15), SkIntToScalar(15)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // scale with different size mat.reset(); mat.setScale(SkIntToScalar(15), SkIntToScalar(20)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // scale with same size at a pivot point mat.reset(); mat.setScale(SkIntToScalar(15), SkIntToScalar(15), SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // scale with different size at a pivot point mat.reset(); mat.setScale(SkIntToScalar(15), SkIntToScalar(20), SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // skew with same size mat.reset(); mat.setSkew(SkIntToScalar(15), SkIntToScalar(15)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // skew with different size mat.reset(); mat.setSkew(SkIntToScalar(15), SkIntToScalar(20)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // skew with same size at a pivot point mat.reset(); mat.setSkew(SkIntToScalar(15), SkIntToScalar(15), SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // skew with different size at a pivot point mat.reset(); mat.setSkew(SkIntToScalar(15), SkIntToScalar(20), SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // perspective x mat.reset(); mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // perspective y mat.reset(); mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // rotate for (int angle = 0; angle < 360; ++angle) { mat.reset(); mat.setRotate(SkIntToScalar(angle)); #ifndef SK_CPU_ARM64 REPORTER_ASSERT(reporter, mat.isSimilarity()); #else // 64-bit ARM devices built with -O2 and -ffp-contract=fast have a loss // of precision and require that we have a higher tolerance REPORTER_ASSERT(reporter, mat.isSimilarity(SK_ScalarNearlyZero + 0.00010113f)); #endif } // see if there are any accumulated precision issues mat.reset(); for (int i = 1; i < 360; i++) { mat.postRotate(SkIntToScalar(1)); } REPORTER_ASSERT(reporter, mat.isSimilarity()); // rotate + translate mat.reset(); mat.setRotate(SkIntToScalar(30)); mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // rotate + uniform scale mat.reset(); mat.setRotate(SkIntToScalar(30)); mat.postScale(SkIntToScalar(2), SkIntToScalar(2)); REPORTER_ASSERT(reporter, mat.isSimilarity()); // rotate + non-uniform scale mat.reset(); mat.setRotate(SkIntToScalar(30)); mat.postScale(SkIntToScalar(3), SkIntToScalar(2)); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // all zero mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // all zero except perspective mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1); REPORTER_ASSERT(reporter, !mat.isSimilarity()); // scales zero, only skews mat.setAll(0, SK_Scalar1, 0, SK_Scalar1, 0, 0, 0, 0, SkMatrix::I()[8]); REPORTER_ASSERT(reporter, mat.isSimilarity()); }
void TestMatrix(skiatest::Reporter* reporter) { SkMatrix mat, inverse, iden1, iden2; mat.reset(); mat.setTranslate(SK_Scalar1, SK_Scalar1); mat.invert(&inverse); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); mat.setScale(SkIntToScalar(2), SkIntToScalar(2)); mat.invert(&inverse); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); test_flatten(reporter, mat); mat.setScale(SK_Scalar1/2, SK_Scalar1/2); mat.invert(&inverse); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); test_flatten(reporter, mat); mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0); mat.postRotate(SkIntToScalar(25)); REPORTER_ASSERT(reporter, mat.invert(NULL)); mat.invert(&inverse); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); iden2.setConcat(inverse, mat); REPORTER_ASSERT(reporter, is_identity(iden2)); test_flatten(reporter, mat); test_flatten(reporter, iden2); // rectStaysRect test { static const struct { SkScalar m00, m01, m10, m11; bool mStaysRect; } gRectStaysRectSamples[] = { { 0, 0, 0, 0, false }, { 0, 0, 0, SK_Scalar1, false }, { 0, 0, SK_Scalar1, 0, false }, { 0, 0, SK_Scalar1, SK_Scalar1, false }, { 0, SK_Scalar1, 0, 0, false }, { 0, SK_Scalar1, 0, SK_Scalar1, false }, { 0, SK_Scalar1, SK_Scalar1, 0, true }, { 0, SK_Scalar1, SK_Scalar1, SK_Scalar1, false }, { SK_Scalar1, 0, 0, 0, false }, { SK_Scalar1, 0, 0, SK_Scalar1, true }, { SK_Scalar1, 0, SK_Scalar1, 0, false }, { SK_Scalar1, 0, SK_Scalar1, SK_Scalar1, false }, { SK_Scalar1, SK_Scalar1, 0, 0, false }, { SK_Scalar1, SK_Scalar1, 0, SK_Scalar1, false }, { SK_Scalar1, SK_Scalar1, SK_Scalar1, 0, false }, { SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, false } }; for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) { SkMatrix m; m.reset(); m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00); m.set(SkMatrix::kMSkewX, gRectStaysRectSamples[i].m01); m.set(SkMatrix::kMSkewY, gRectStaysRectSamples[i].m10); m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11); REPORTER_ASSERT(reporter, m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect); } } mat.reset(); mat.set(SkMatrix::kMScaleX, SkIntToScalar(1)); mat.set(SkMatrix::kMSkewX, SkIntToScalar(2)); mat.set(SkMatrix::kMTransX, SkIntToScalar(3)); mat.set(SkMatrix::kMSkewY, SkIntToScalar(4)); mat.set(SkMatrix::kMScaleY, SkIntToScalar(5)); mat.set(SkMatrix::kMTransY, SkIntToScalar(6)); SkScalar affine[6]; REPORTER_ASSERT(reporter, mat.asAffine(affine)); #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e) REPORTER_ASSERT(reporter, affineEqual(ScaleX)); REPORTER_ASSERT(reporter, affineEqual(SkewY)); REPORTER_ASSERT(reporter, affineEqual(SkewX)); REPORTER_ASSERT(reporter, affineEqual(ScaleY)); REPORTER_ASSERT(reporter, affineEqual(TransX)); REPORTER_ASSERT(reporter, affineEqual(TransY)); #undef affineEqual mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2)); REPORTER_ASSERT(reporter, !mat.asAffine(affine)); SkMatrix mat2; mat2.reset(); mat.reset(); SkScalar zero = 0; mat.set(SkMatrix::kMSkewX, -zero); REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2)); mat2.reset(); mat.reset(); mat.set(SkMatrix::kMSkewX, SK_ScalarNaN); mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN); // fixed pt doesn't have the property that NaN does not equal itself. #ifdef SK_SCALAR_IS_FIXED REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2)); #else REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2)); #endif test_matrix_max_stretch(reporter); }
void GrPathUtils::QuadUVMatrix::set(const GrPoint qPts[3]) { // can't make this static, no cons :( SkMatrix UVpts; #ifndef SK_SCALAR_IS_FLOAT GrCrash("Expected scalar is float."); #endif SkMatrix m; // We want M such that M * xy_pt = uv_pt // We know M * control_pts = [0 1/2 1] // [0 0 1] // [1 1 1] // We invert the control pt matrix and post concat to both sides to get M. UVpts.setAll(0, SK_ScalarHalf, SK_Scalar1, 0, 0, SK_Scalar1, SkScalarToPersp(SK_Scalar1), SkScalarToPersp(SK_Scalar1), SkScalarToPersp(SK_Scalar1)); m.setAll(qPts[0].fX, qPts[1].fX, qPts[2].fX, qPts[0].fY, qPts[1].fY, qPts[2].fY, SkScalarToPersp(SK_Scalar1), SkScalarToPersp(SK_Scalar1), SkScalarToPersp(SK_Scalar1)); if (!m.invert(&m)) { // The quad is degenerate. Hopefully this is rare. Find the pts that are // farthest apart to compute a line (unless it is really a pt). SkScalar maxD = qPts[0].distanceToSqd(qPts[1]); int maxEdge = 0; SkScalar d = qPts[1].distanceToSqd(qPts[2]); if (d > maxD) { maxD = d; maxEdge = 1; } d = qPts[2].distanceToSqd(qPts[0]); if (d > maxD) { maxD = d; maxEdge = 2; } // We could have a tolerance here, not sure if it would improve anything if (maxD > 0) { // Set the matrix to give (u = 0, v = distance_to_line) GrVec lineVec = qPts[(maxEdge + 1)%3] - qPts[maxEdge]; // when looking from the point 0 down the line we want positive // distances to be to the left. This matches the non-degenerate // case. lineVec.setOrthog(lineVec, GrPoint::kLeft_Side); lineVec.dot(qPts[0]); // first row fM[0] = 0; fM[1] = 0; fM[2] = 0; // second row fM[3] = lineVec.fX; fM[4] = lineVec.fY; fM[5] = -lineVec.dot(qPts[maxEdge]); } else { // It's a point. It should cover zero area. Just set the matrix such // that (u, v) will always be far away from the quad. fM[0] = 0; fM[1] = 0; fM[2] = 100.f; fM[3] = 0; fM[4] = 0; fM[5] = 100.f; } } else { m.postConcat(UVpts); // The matrix should not have perspective. SkDEBUGCODE(static const SkScalar gTOL = SkFloatToScalar(1.f / 100.f)); GrAssert(SkScalarAbs(m.get(SkMatrix::kMPersp0)) < gTOL); GrAssert(SkScalarAbs(m.get(SkMatrix::kMPersp1)) < gTOL); // It may not be normalized to have 1.0 in the bottom right float m33 = m.get(SkMatrix::kMPersp2); if (1.f != m33) { m33 = 1.f / m33; fM[0] = m33 * m.get(SkMatrix::kMScaleX); fM[1] = m33 * m.get(SkMatrix::kMSkewX); fM[2] = m33 * m.get(SkMatrix::kMTransX); fM[3] = m33 * m.get(SkMatrix::kMSkewY); fM[4] = m33 * m.get(SkMatrix::kMScaleY); fM[5] = m33 * m.get(SkMatrix::kMTransY); } else { fM[0] = m.get(SkMatrix::kMScaleX); fM[1] = m.get(SkMatrix::kMSkewX); fM[2] = m.get(SkMatrix::kMTransX); fM[3] = m.get(SkMatrix::kMSkewY); fM[4] = m.get(SkMatrix::kMScaleY); fM[5] = m.get(SkMatrix::kMTransY); } } }