static void test_wacky_bitmapshader(skiatest::Reporter* reporter, int width, int height) { SkBitmap dev; dev.allocN32Pixels(0x56F, 0x4f6); dev.eraseColor(SK_ColorTRANSPARENT); // necessary, so we know if we draw to it SkMatrix matrix; SkCanvas c(dev); matrix.setAll(-119.34097f, -43.436558f, 93489.945f, 43.436558f, -119.34097f, 123.98426f, 0, 0, SK_Scalar1); c.concat(matrix); SkBitmap bm; if (bm.tryAllocN32Pixels(width, height)) { bm.eraseColor(SK_ColorRED); } else { SkASSERT(false); return; } matrix.setAll(0.0078740157f, 0, SkIntToScalar(249), 0, 0.0078740157f, SkIntToScalar(239), 0, 0, SK_Scalar1); SkPaint paint; paint.setShader(bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, &matrix)); SkRect r = SkRect::MakeXYWH(681, 239, 695, 253); c.drawRect(r, paint); for (int y = 0; y < dev.height(); ++y) { for (int x = 0; x < dev.width(); ++x) { if (SK_ColorTRANSPARENT == *dev.getAddr32(x, y)) { REPORTER_ASSERT(reporter, false); return; } } } }
extern void SetTransform(RenderingContext*ctx, float*m) { SkMatrix matrix; matrix.setAll(m[0], m[1], m[2], m[3], m[4], m[5], 0, 0, 1); ctx->Canvas->setMatrix(ConvertPerspexMatrix(m)); }
// Test drawing text with some unusual matricies. // We measure success by not crashing or asserting. DEF_TEST(DrawText_weirdMatricies, r) { auto surface = SkSurface::MakeRasterN32Premul(100,100); auto canvas = surface->getCanvas(); SkPaint paint; paint.setAntiAlias(true); paint.setLCDRenderText(true); struct { SkScalar textSize; SkScalar matrix[9]; } testCases[] = { // 2x2 singular {10, { 0, 0, 0, 0, 0, 0, 0, 0, 1}}, {10, { 0, 0, 0, 0, 1, 0, 0, 0, 1}}, {10, { 0, 0, 0, 1, 0, 0, 0, 0, 1}}, {10, { 0, 0, 0, 1, 1, 0, 0, 0, 1}}, {10, { 0, 1, 0, 0, 1, 0, 0, 0, 1}}, {10, { 1, 0, 0, 0, 0, 0, 0, 0, 1}}, {10, { 1, 0, 0, 1, 0, 0, 0, 0, 1}}, {10, { 1, 1, 0, 0, 0, 0, 0, 0, 1}}, {10, { 1, 1, 0, 1, 1, 0, 0, 0, 1}}, // See https://bugzilla.mozilla.org/show_bug.cgi?id=1305085 . { 1, {10, 20, 0, 20, 40, 0, 0, 0, 1}}, }; for (const auto& testCase : testCases) { paint.setTextSize(testCase.textSize); const SkScalar(&m)[9] = testCase.matrix; SkMatrix mat; mat.setAll(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); canvas->setMatrix(mat); canvas->drawString("Hamburgefons", 10, 10, paint); } }
void draw(SkCanvas* canvas) { SkMatrix matrix; matrix.setAll(1, 0, 0, 0, 1, 0, 0, 0, 0); if (matrix.invert(&matrix)) { SkScalar factor[2] = {2, 2}; bool result = matrix.getMinMaxScales(factor); SkDebugf("matrix.getMinMaxScales() %s %g %g\n", result ? "true" : "false", factor[0], factor[1]); } }
static void test_wacky_bitmapshader(skiatest::Reporter* reporter, int width, int height, bool shouldBeDrawn) { SkBitmap dev; dev.allocN32Pixels(0x56F, 0x4f6); dev.eraseColor(SK_ColorTRANSPARENT); // necessary, so we know if we draw to it SkMatrix matrix; SkCanvas c(dev); matrix.setAll(-119.34097f, -43.436558f, 93489.945f, 43.436558f, -119.34097f, 123.98426f, 0, 0, SK_Scalar1); c.concat(matrix); SkBitmap bm; if (bm.tryAllocN32Pixels(width, height)) { // allow this to fail silently, to test the code downstream } bm.eraseColor(SK_ColorRED); matrix.setAll(0.0078740157f, 0, SkIntToScalar(249), 0, 0.0078740157f, SkIntToScalar(239), 0, 0, SK_Scalar1); SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &matrix); SkPaint paint; paint.setShader(s)->unref(); SkRect r = SkRect::MakeXYWH(681, 239, 695, 253); c.drawRect(r, paint); assert_ifDrawnTo(reporter, dev, shouldBeDrawn); }
void WRasterImage::Impl::applyTransform(const WTransform& t) { SkMatrix sm; sm.setAll(SkDoubleToScalar(t.m11()), SkDoubleToScalar(t.m12()), SkDoubleToScalar(t.dx()), SkDoubleToScalar(t.m21()), SkDoubleToScalar(t.m22()), SkDoubleToScalar(t.dy()), 0, 0, SkDoubleToScalar(1.0)); canvas_->concat(sm); }
// Solves linear system to extract klm // P.K = k (similarly for l, m) // Where P is matrix of control points // K is coefficients for the line K // k is vector of values of K evaluated at the control points // Solving for K, thus K = P^(-1) . k static void calc_cubic_klm(const SkPoint p[4], const SkScalar controlK[4], const SkScalar controlL[4], const SkScalar controlM[4], SkScalar k[3], SkScalar l[3], SkScalar m[3]) { SkMatrix matrix; matrix.setAll(p[0].fX, p[0].fY, 1.f, p[1].fX, p[1].fY, 1.f, p[2].fX, p[2].fY, 1.f); SkMatrix inverse; if (matrix.invert(&inverse)) { inverse.mapHomogeneousPoints(k, controlK, 1); inverse.mapHomogeneousPoints(l, controlL, 1); inverse.mapHomogeneousPoints(m, controlM, 1); } }
static SkMatrix Matrix3DToSkia(const gfx3DMatrix& aMatrix) { SkMatrix transform; transform.setAll(aMatrix._11, aMatrix._21, aMatrix._41, aMatrix._12, aMatrix._22, aMatrix._42, aMatrix._14, aMatrix._24, aMatrix._44); return transform; }
void draw(SkCanvas* canvas) { SkPaint p; p.setAntiAlias(true); SkMatrix m; int pos = 0; for (SkScalar sx : { 1, 2 } ) { for (SkScalar kx : { 0, 1 } ) { m.setAll(sx, kx, 16, 0, 1, 32, 0, 0, 1); bool isSimilarity = m.isSimilarity(); bool preservesRightAngles = m.preservesRightAngles(); SkString str; str.printf("sx: %g kx: %g %s %s", sx, kx, isSimilarity ? "sim" : "", preservesRightAngles ? "right" : ""); SkAutoCanvasRestore autoRestore(canvas, true); canvas->concat(m); canvas->drawString(str, 0, pos, p); pos += 20; } } }
void onOnceBeforeDraw() override { SkBitmap emptyBmp; SkBitmap blueBmp; blueBmp.allocN32Pixels(10, 10); blueBmp.eraseColor(SK_ColorBLUE); SkMatrix badMatrix; badMatrix.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0); #if 0 // This crashes pipe! // Empty bitmap. fPaints.push_back().setColor(SK_ColorGREEN); fPaints.back().setShader(SkShader::CreateBitmapShader(emptyBmp, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref(); #endif // Non-invertible local matrix. fPaints.push_back().setColor(SK_ColorGREEN); fPaints.back().setShader(SkShader::CreateBitmapShader(blueBmp, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &badMatrix))->unref(); }
// Test out the case where an oval already off in space is translated/scaled // further off into space - yielding numerical issues when the rect & radii // are transformed separatly // BUG=skia:2696 static void test_issue_2696(skiatest::Reporter* reporter) { SkRRect rrect; SkRect r = { 28443.8594f, 53.1428604f, 28446.7148f, 56.0000038f }; rrect.setOval(r); SkMatrix xform; xform.setAll(2.44f, 0.0f, 485411.7f, 0.0f, 2.44f, -438.7f, 0.0f, 0.0f, 1.0f); SkRRect dst; bool success = rrect.transform(xform, &dst); REPORTER_ASSERT(reporter, success); SkScalar halfWidth = SkScalarHalf(dst.width()); SkScalar halfHeight = SkScalarHalf(dst.height()); for (int i = 0; i < 4; ++i) { REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight)); } }
static void test_matrix_homogeneous(skiatest::Reporter* reporter) { SkMatrix mat; const float kRotation0 = 15.5f; const float kRotation1 = -50.f; const float kScale0 = 5000.f; const int kTripleCount = 1000; const int kMatrixCount = 1000; SkRandom rand; SkScalar randTriples[3*kTripleCount]; for (int i = 0; i < 3*kTripleCount; ++i) { randTriples[i] = rand.nextRangeF(-3000.f, 3000.f); } SkMatrix mats[kMatrixCount]; for (int i = 0; i < kMatrixCount; ++i) { for (int j = 0; j < 9; ++j) { mats[i].set(j, rand.nextRangeF(-3000.f, 3000.f)); } } // identity { mat.reset(); SkScalar dst[3*kTripleCount]; mat.mapHomogeneousPoints(dst, randTriples, kTripleCount); REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(randTriples, dst, kTripleCount*3)); } // zero matrix { mat.setAll(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f); SkScalar dst[3*kTripleCount]; mat.mapHomogeneousPoints(dst, randTriples, kTripleCount); SkScalar zeros[3] = {0.f, 0.f, 0.f}; for (int i = 0; i < kTripleCount; ++i) { REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(&dst[i*3], zeros, 3)); } } // zero point { SkScalar zeros[3] = {0.f, 0.f, 0.f}; for (int i = 0; i < kMatrixCount; ++i) { SkScalar dst[3]; mats[i].mapHomogeneousPoints(dst, zeros, 1); REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(dst, zeros, 3)); } } // doesn't crash with null dst, src, count == 0 { mats[0].mapHomogeneousPoints(NULL, NULL, 0); } // uniform scale of point { mat.setScale(kScale0, kScale0); SkScalar dst[3]; SkScalar src[3] = {randTriples[0], randTriples[1], 1.f}; SkPoint pnt; pnt.set(src[0], src[1]); mat.mapHomogeneousPoints(dst, src, 1); mat.mapPoints(&pnt, &pnt, 1); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1)); } // rotation of point { mat.setRotate(kRotation0); SkScalar dst[3]; SkScalar src[3] = {randTriples[0], randTriples[1], 1.f}; SkPoint pnt; pnt.set(src[0], src[1]); mat.mapHomogeneousPoints(dst, src, 1); mat.mapPoints(&pnt, &pnt, 1); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1)); } // rotation, scale, rotation of point { mat.setRotate(kRotation1); mat.postScale(kScale0, kScale0); mat.postRotate(kRotation0); SkScalar dst[3]; SkScalar src[3] = {randTriples[0], randTriples[1], 1.f}; SkPoint pnt; pnt.set(src[0], src[1]); mat.mapHomogeneousPoints(dst, src, 1); mat.mapPoints(&pnt, &pnt, 1); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1)); } // compare with naive approach { for (int i = 0; i < kMatrixCount; ++i) { for (int j = 0; j < kTripleCount; ++j) { SkScalar dst[3]; mats[i].mapHomogeneousPoints(dst, &randTriples[j*3], 1); REPORTER_ASSERT(reporter, naive_homogeneous_mapping(mats[i], &randTriples[j*3], dst)); } } } }
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()); }
virtual void onDraw(SkCanvas* inputCanvas) override { SkScalar textSizes[] = { 9.0f, 9.0f*2.0f, 9.0f*5.0f, 9.0f*2.0f*5.0f }; SkScalar scales[] = { 2.0f*5.0f, 5.0f, 2.0f, 1.0f }; // set up offscreen rendering with distance field text GrContext* ctx = inputCanvas->getGrContext(); SkISize size = onISize(); SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), kPremul_SkAlphaType, inputCanvas->imageInfo().refColorSpace()); SkSurfaceProps props(SkSurfaceProps::kUseDeviceIndependentFonts_Flag, SkSurfaceProps::kLegacyFontHost_InitType); auto surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, 0, &props)); SkCanvas* canvas = surface ? surface->getCanvas() : inputCanvas; // init our new canvas with the old canvas's matrix canvas->setMatrix(inputCanvas->getTotalMatrix()); // apply global scale to test glyph positioning canvas->scale(1.05f, 1.05f); canvas->clear(0xffffffff); SkPaint paint; paint.setAntiAlias(true); SkFont font(ToolUtils::create_portable_typeface("serif", SkFontStyle())); font.setSubpixel(true); const char* text = "Hamburgefons"; const size_t textLen = strlen(text); // check scaling up SkScalar x = SkIntToScalar(0); SkScalar y = SkIntToScalar(78); for (size_t i = 0; i < SK_ARRAY_COUNT(textSizes); ++i) { SkAutoCanvasRestore acr(canvas, true); canvas->translate(x, y); canvas->scale(scales[i], scales[i]); font.setSize(textSizes[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); y += font.getMetrics(nullptr)*scales[i]; } // check rotation for (size_t i = 0; i < 5; ++i) { SkScalar rotX = SkIntToScalar(10); SkScalar rotY = y; SkAutoCanvasRestore acr(canvas, true); canvas->translate(SkIntToScalar(10 + i * 200), -80); canvas->rotate(SkIntToScalar(i * 5), rotX, rotY); for (int ps = 6; ps <= 32; ps += 3) { font.setSize(SkIntToScalar(ps)); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, rotX, rotY, font, paint); rotY += font.getMetrics(nullptr); } } // check scaling down font.setEdging(SkFont::Edging::kSubpixelAntiAlias); x = SkIntToScalar(680); y = SkIntToScalar(20); size_t arraySize = SK_ARRAY_COUNT(textSizes); for (size_t i = 0; i < arraySize; ++i) { SkAutoCanvasRestore acr(canvas, true); canvas->translate(x, y); SkScalar scaleFactor = SkScalarInvert(scales[arraySize - i - 1]); canvas->scale(scaleFactor, scaleFactor); font.setSize(textSizes[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); y += font.getMetrics(nullptr)*scaleFactor; } // check pos text { SkAutoCanvasRestore acr(canvas, true); canvas->scale(2.0f, 2.0f); SkAutoTArray<SkGlyphID> glyphs(SkToInt(textLen)); int count = font.textToGlyphs(text, textLen, SkTextEncoding::kUTF8, glyphs.get(), textLen); SkAutoTArray<SkPoint> pos(count); font.setSize(textSizes[0]); font.getPos(glyphs.get(), count, pos.get(), {340, 75}); auto blob = SkTextBlob::MakeFromPosText(glyphs.get(), count * sizeof(SkGlyphID), pos.get(), font, SkTextEncoding::kGlyphID); canvas->drawTextBlob(blob, 0, 0, paint); } // check gamma-corrected blending const SkColor fg[] = { 0xFFFFFFFF, 0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFF000000, }; paint.setColor(0xFFF7F3F7); SkRect r = SkRect::MakeLTRB(670, 215, 820, 397); canvas->drawRect(r, paint); x = SkIntToScalar(680); y = SkIntToScalar(235); font.setSize(SkIntToScalar(19)); for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) { paint.setColor(fg[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, x, y, font, paint); y += font.getMetrics(nullptr); } paint.setColor(0xFF181C18); r = SkRect::MakeLTRB(820, 215, 970, 397); canvas->drawRect(r, paint); x = SkIntToScalar(830); y = SkIntToScalar(235); font.setSize(SkIntToScalar(19)); for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) { paint.setColor(fg[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, x, y, font, paint); y += font.getMetrics(nullptr); } // check skew { font.setEdging(SkFont::Edging::kAntiAlias); SkAutoCanvasRestore acr(canvas, true); canvas->skew(0.0f, 0.151515f); font.setSize(SkIntToScalar(32)); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 745, 70, font, paint); } { font.setEdging(SkFont::Edging::kSubpixelAntiAlias); SkAutoCanvasRestore acr(canvas, true); canvas->skew(0.5f, 0.0f); font.setSize(SkIntToScalar(32)); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 580, 125, font, paint); } // check perspective { font.setEdging(SkFont::Edging::kAntiAlias); SkAutoCanvasRestore acr(canvas, true); SkMatrix persp; persp.setAll(0.9839f, 0, 0, 0.2246f, 0.6829f, 0, 0.0002352f, -0.0003844f, 1); canvas->concat(persp); canvas->translate(1100, -295); font.setSize(37.5f); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); } { font.setSubpixel(false); font.setEdging(SkFont::Edging::kAlias); SkAutoCanvasRestore acr(canvas, true); SkMatrix persp; persp.setAll(0.9839f, 0, 0, 0.2246f, 0.6829f, 0, 0.0002352f, -0.0003844f, 1); canvas->concat(persp); canvas->translate(1075, -245); canvas->scale(375, 375); font.setSize(0.1f); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); } // check color emoji if (fEmojiTypeface) { SkFont emoiFont; emoiFont.setSubpixel(true); emoiFont.setTypeface(fEmojiTypeface); emoiFont.setSize(SkIntToScalar(19)); canvas->drawSimpleText(fEmojiText, strlen(fEmojiText), SkTextEncoding::kUTF8, 670, 90, emoiFont, paint); } // render offscreen buffer if (surface) { SkAutoCanvasRestore acr(inputCanvas, true); // since we prepended this matrix already, we blit using identity inputCanvas->resetMatrix(); inputCanvas->drawImage(surface->makeImageSnapshot().get(), 0, 0, nullptr); } }
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()); }
void GrGpuGL::flushViewMatrix(DrawType type) { const GrGLRenderTarget* rt = static_cast<const GrGLRenderTarget*>(this->getDrawState().getRenderTarget()); SkISize viewportSize; const GrGLIRect& viewport = rt->getViewport(); viewportSize.set(viewport.fWidth, viewport.fHeight); const SkMatrix& vm = this->getDrawState().getViewMatrix(); if (kStencilPath_DrawType == type) { if (fHWPathMatrixState.fViewMatrix != vm || fHWPathMatrixState.fRTSize != viewportSize) { // rescale the coords from skia's "device" coords to GL's normalized coords, // and perform a y-flip. SkMatrix m; m.setScale(SkIntToScalar(2) / rt->width(), SkIntToScalar(-2) / rt->height()); m.postTranslate(-SK_Scalar1, SK_Scalar1); m.preConcat(vm); // GL wants a column-major 4x4. GrGLfloat mv[] = { // col 0 SkScalarToFloat(m[SkMatrix::kMScaleX]), SkScalarToFloat(m[SkMatrix::kMSkewY]), 0, SkScalarToFloat(m[SkMatrix::kMPersp0]), // col 1 SkScalarToFloat(m[SkMatrix::kMSkewX]), SkScalarToFloat(m[SkMatrix::kMScaleY]), 0, SkScalarToFloat(m[SkMatrix::kMPersp1]), // col 2 0, 0, 0, 0, // col3 SkScalarToFloat(m[SkMatrix::kMTransX]), SkScalarToFloat(m[SkMatrix::kMTransY]), 0.0f, SkScalarToFloat(m[SkMatrix::kMPersp2]) }; GL_CALL(MatrixMode(GR_GL_PROJECTION)); GL_CALL(LoadMatrixf(mv)); fHWPathMatrixState.fViewMatrix = vm; fHWPathMatrixState.fRTSize = viewportSize; } } else if (!fCurrentProgram->fViewMatrix.cheapEqualTo(vm) || fCurrentProgram->fViewportSize != viewportSize) { SkMatrix m; m.setAll( SkIntToScalar(2) / viewportSize.fWidth, 0, -SK_Scalar1, 0,-SkIntToScalar(2) / viewportSize.fHeight, SK_Scalar1, 0, 0, SkMatrix::I()[8]); m.setConcat(m, vm); // ES doesn't allow you to pass true to the transpose param, // so do our own transpose GrGLfloat mt[] = { SkScalarToFloat(m[SkMatrix::kMScaleX]), SkScalarToFloat(m[SkMatrix::kMSkewY]), SkScalarToFloat(m[SkMatrix::kMPersp0]), SkScalarToFloat(m[SkMatrix::kMSkewX]), SkScalarToFloat(m[SkMatrix::kMScaleY]), SkScalarToFloat(m[SkMatrix::kMPersp1]), SkScalarToFloat(m[SkMatrix::kMTransX]), SkScalarToFloat(m[SkMatrix::kMTransY]), SkScalarToFloat(m[SkMatrix::kMPersp2]) }; fCurrentProgram->fUniformManager.setMatrix3f( fCurrentProgram->fUniformHandles.fViewMatrixUni, mt); fCurrentProgram->fViewMatrix = vm; fCurrentProgram->fViewportSize = viewportSize; } }
SkMatrix makeMatrix() { SkMatrix matrix; matrix.reset(); RandomSetMatrix setMatrix = (RandomSetMatrix) fRand.nextRangeU(0, kRandomSetMatrix_Last); if (fPrintName) { SkDebugf("%.*s%s\n", fPathDepth * 3, fTab, gRandomSetMatrixNames[setMatrix]); } switch (setMatrix) { case kSetIdentity: break; case kSetTranslateX: matrix.setTranslateX(makeScalar()); break; case kSetTranslateY: matrix.setTranslateY(makeScalar()); break; case kSetTranslate: matrix.setTranslate(makeScalar(), makeScalar()); break; case kSetScaleX: matrix.setScaleX(makeScalar()); break; case kSetScaleY: matrix.setScaleY(makeScalar()); break; case kSetScale: matrix.setScale(makeScalar(), makeScalar()); break; case kSetScaleTranslate: matrix.setScale(makeScalar(), makeScalar(), makeScalar(), makeScalar()); break; case kSetSkewX: matrix.setSkewX(makeScalar()); break; case kSetSkewY: matrix.setSkewY(makeScalar()); break; case kSetSkew: matrix.setSkew(makeScalar(), makeScalar()); break; case kSetSkewTranslate: matrix.setSkew(makeScalar(), makeScalar(), makeScalar(), makeScalar()); break; case kSetRotate: matrix.setRotate(makeScalar()); break; case kSetRotateTranslate: matrix.setRotate(makeScalar(), makeScalar(), makeScalar()); break; case kSetPerspectiveX: matrix.setPerspX(makeScalar()); break; case kSetPerspectiveY: matrix.setPerspY(makeScalar()); break; case kSetAll: matrix.setAll(makeScalar(), makeScalar(), makeScalar(), makeScalar(), makeScalar(), makeScalar(), makeScalar(), makeScalar(), makeScalar()); break; } return matrix; }
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); } } }
bool GetSpotShadowTransform(const SkPoint3& lightPos, SkScalar lightRadius, const SkMatrix& ctm, const SkPoint3& zPlaneParams, const SkRect& pathBounds, SkMatrix* shadowTransform, SkScalar* radius) { auto heightFunc = [zPlaneParams] (SkScalar x, SkScalar y) { return zPlaneParams.fX*x + zPlaneParams.fY*y + zPlaneParams.fZ; }; SkScalar occluderHeight = heightFunc(pathBounds.centerX(), pathBounds.centerY()); if (!ctm.hasPerspective()) { SkScalar scale; SkVector translate; SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, lightPos.fZ, lightRadius, radius, &scale, &translate); shadowTransform->setScaleTranslate(scale, scale, translate.fX, translate.fY); shadowTransform->preConcat(ctm); } else { if (SkScalarNearlyZero(pathBounds.width()) || SkScalarNearlyZero(pathBounds.height())) { return false; } // get rotated quad in 3D SkPoint pts[4]; ctm.mapRectToQuad(pts, pathBounds); // No shadows for bowties or other degenerate cases if (!SkIsConvexPolygon(pts, 4)) { return false; } SkPoint3 pts3D[4]; SkScalar z = heightFunc(pathBounds.fLeft, pathBounds.fTop); pts3D[0].set(pts[0].fX, pts[0].fY, z); z = heightFunc(pathBounds.fRight, pathBounds.fTop); pts3D[1].set(pts[1].fX, pts[1].fY, z); z = heightFunc(pathBounds.fRight, pathBounds.fBottom); pts3D[2].set(pts[2].fX, pts[2].fY, z); z = heightFunc(pathBounds.fLeft, pathBounds.fBottom); pts3D[3].set(pts[3].fX, pts[3].fY, z); // project from light through corners to z=0 plane for (int i = 0; i < 4; ++i) { SkScalar dz = lightPos.fZ - pts3D[i].fZ; // light shouldn't be below or at a corner's z-location if (dz <= SK_ScalarNearlyZero) { return false; } SkScalar zRatio = pts3D[i].fZ / dz; pts3D[i].fX -= (lightPos.fX - pts3D[i].fX)*zRatio; pts3D[i].fY -= (lightPos.fY - pts3D[i].fY)*zRatio; pts3D[i].fZ = SK_Scalar1; } // Generate matrix that projects from [-1,1]x[-1,1] square to projected quad SkPoint3 h0, h1, h2; // Compute homogenous crossing point between top and bottom edges (gives new x-axis). h0 = (pts3D[1].cross(pts3D[0])).cross(pts3D[2].cross(pts3D[3])); // Compute homogenous crossing point between left and right edges (gives new y-axis). h1 = (pts3D[0].cross(pts3D[3])).cross(pts3D[1].cross(pts3D[2])); // Compute homogenous crossing point between diagonals (gives new origin). h2 = (pts3D[0].cross(pts3D[2])).cross(pts3D[1].cross(pts3D[3])); // If h2 is a vector (z=0 in 2D homogeneous space), that means that at least // two of the quad corners are coincident and we don't have a realistic projection if (SkScalarNearlyZero(h2.fZ)) { return false; } // In some cases the crossing points are in the wrong direction // to map (-1,-1) to pts3D[0], so we need to correct for that. // Want h0 to be to the right of the left edge. SkVector3 v = pts3D[3] - pts3D[0]; SkVector3 w = h0 - pts3D[0]; SkScalar perpDot = v.fX*w.fY - v.fY*w.fX; if (perpDot > 0) { h0 = -h0; } // Want h1 to be above the bottom edge. v = pts3D[1] - pts3D[0]; perpDot = v.fX*w.fY - v.fY*w.fX; if (perpDot < 0) { h1 = -h1; } shadowTransform->setAll(h0.fX / h2.fZ, h1.fX / h2.fZ, h2.fX / h2.fZ, h0.fY / h2.fZ, h1.fY / h2.fZ, h2.fY / h2.fZ, h0.fZ / h2.fZ, h1.fZ / h2.fZ, 1); // generate matrix that transforms from bounds to [-1,1]x[-1,1] square SkMatrix toHomogeneous; SkScalar xScale = 2/(pathBounds.fRight - pathBounds.fLeft); SkScalar yScale = 2/(pathBounds.fBottom - pathBounds.fTop); toHomogeneous.setAll(xScale, 0, -xScale*pathBounds.fLeft - 1, 0, yScale, -yScale*pathBounds.fTop - 1, 0, 0, 1); shadowTransform->preConcat(toHomogeneous); *radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius); } return true; }
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(SK_Scalar1 / 1000); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinScale()); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxScale()); success = perspX.getMinMaxScales(scales); REPORTER_ASSERT(reporter, !success); // skbug.com/4718 SkMatrix big; big.setAll(2.39394089e+36f, 8.85347779e+36f, 9.26526204e+36f, 3.9159619e+36f, 1.44823453e+37f, 1.51559342e+37f, 0.f, 0.f, 1.f); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinScale()); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxScale()); success = big.getMinMaxScales(scales); REPORTER_ASSERT(reporter, !success); SkMatrix perspY; perspY.reset(); perspY.setPerspY(-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, d / maxScale < gVectorScaleTol); REPORTER_ASSERT(reporter, minScale / d < gVectorScaleTol); if (max < d) { max = d; } if (min > d) { min = d; } } REPORTER_ASSERT(reporter, max / maxScale >= gCloseScaleTol); REPORTER_ASSERT(reporter, 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()); // 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()); }