diff options
-rw-r--r-- | src/core/SkMatrix.cpp | 49 | ||||
-rw-r--r-- | src/core/SkMatrixUtils.h | 32 | ||||
-rw-r--r-- | tests/DrawBitmapRectTest.cpp | 86 |
3 files changed, 167 insertions, 0 deletions
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp index 8dee0cb229..4845f59592 100644 --- a/src/core/SkMatrix.cpp +++ b/src/core/SkMatrix.cpp @@ -1802,3 +1802,52 @@ void SkMatrix::toDumpString(SkString* str) const { SkFractToFloat(fMat[6]), SkFractToFloat(fMat[7]), SkFractToFloat(fMat[8])); #endif } + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkMatrixUtils.h" + +bool SkTreatAsSprite(const SkMatrix& mat, const SkRect& src, + unsigned subpixelBits) { + if (mat.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) { + return false; + } + + // quick success check + if (!subpixelBits && !(mat.getType() & ~SkMatrix::kTranslate_Mask)) { + return true; + } + + // mapRect supports negative scales, so we eliminate those first + if (mat.getScaleX() < 0 || mat.getScaleY() < 0) { + return false; + } + + SkRect dst; + SkIRect isrc, idst; + + mat.mapRect(&dst, src); + + { + SkRect tmp = src; + tmp.offset(mat.getTranslateX(), mat.getTranslateY()); + tmp.round(&isrc); + } + + if (subpixelBits) { + isrc.fLeft <<= subpixelBits; + isrc.fTop <<= subpixelBits; + isrc.fRight <<= subpixelBits; + isrc.fBottom <<= subpixelBits; + + const float scale = 1 << subpixelBits; + dst.fLeft *= scale; + dst.fTop *= scale; + dst.fRight *= scale; + dst.fBottom *= scale; + } + + dst.round(&idst); + return isrc == idst; +} + diff --git a/src/core/SkMatrixUtils.h b/src/core/SkMatrixUtils.h new file mode 100644 index 0000000000..db7370c97c --- /dev/null +++ b/src/core/SkMatrixUtils.h @@ -0,0 +1,32 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkMatrixUtils_DEFINED +#define SkMatrixUtils_DEFINED + +#include "SkMatrix.h" + +/** + * Number of subpixel bits used in skia's bilerp. + * See SkBitmapProcState_procs.h and SkBitmapProcState_filter.h + */ +#define kSkSubPixelBitsForBilerp 4 + +/** + * Given a matrix and a src-rect, return true if the computed dst-rect would + * align such that there is a 1-to-1 coorspondence between src and dst pixels. + * This can be called by drawing code to see if drawBitmap can be turned into + * drawSprite (which is faster). + * + * The "closeness" test is based on the subpixelBits parameter. Pass 0 for + * round-to-nearest behavior (e.g. nearest neighbor sampling). Pass the number + * of subpixel-bits to simulate filtering. + */ +bool SkTreatAsSprite(const SkMatrix&, const SkRect& src, unsigned subpixelBits); + + +#endif diff --git a/tests/DrawBitmapRectTest.cpp b/tests/DrawBitmapRectTest.cpp index 7dc51c04ef..0a8d730a97 100644 --- a/tests/DrawBitmapRectTest.cpp +++ b/tests/DrawBitmapRectTest.cpp @@ -9,6 +9,90 @@ #include "SkBitmap.h" #include "SkCanvas.h" #include "SkShader.h" +#include "SkRandom.h" +#include "SkMatrixUtils.h" + +static void rand_matrix(SkMatrix* mat, SkRandom& rand, unsigned mask) { + mat->setIdentity(); + if (mask & SkMatrix::kTranslate_Mask) { + mat->postTranslate(rand.nextSScalar1(), rand.nextSScalar1()); + } + if (mask & SkMatrix::kScale_Mask) { + mat->postScale(rand.nextSScalar1(), rand.nextSScalar1()); + } + if (mask & SkMatrix::kAffine_Mask) { + mat->postRotate(rand.nextSScalar1() * 360); + } + if (mask & SkMatrix::kPerspective_Mask) { + mat->setPerspX(rand.nextSScalar1()); + mat->setPerspY(rand.nextSScalar1()); + } +} + +static void rand_rect(SkRect* r, SkRandom& rand) { + r->set(rand.nextSScalar1() * 1000, rand.nextSScalar1() * 1000, + rand.nextSScalar1() * 1000, rand.nextSScalar1() * 1000); + r->sort(); +} + +static void test_treatAsSprite(skiatest::Reporter* reporter) { + const unsigned bilerBits = kSkSubPixelBitsForBilerp; + + SkMatrix mat; + SkRect r; + SkRandom rand; + + // assert: translate-only no-filter can always be treated as sprite + for (int i = 0; i < 1000; ++i) { + rand_matrix(&mat, rand, SkMatrix::kTranslate_Mask); + for (int j = 0; j < 1000; ++j) { + rand_rect(&r, rand); + REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, r, 0)); + } + } + + // assert: rotate/perspect is never treated as sprite + for (int i = 0; i < 1000; ++i) { + rand_matrix(&mat, rand, SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask); + for (int j = 0; j < 1000; ++j) { + rand_rect(&r, rand); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, 0)); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, bilerBits)); + } + } + + r.set(10, 10, 500, 600); + + const SkScalar tooMuchSubpixel = SkFloatToScalar(100.1f); + mat.setTranslate(tooMuchSubpixel, 0); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, bilerBits)); + mat.setTranslate(0, tooMuchSubpixel); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, bilerBits)); + + const SkScalar tinySubPixel = SkFloatToScalar(100.02f); + mat.setTranslate(tinySubPixel, 0); + REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, r, bilerBits)); + mat.setTranslate(0, tinySubPixel); + REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, r, bilerBits)); + + const SkScalar twoThirds = SK_Scalar1 * 2 / 3; + const SkScalar bigScale = SkScalarDiv(r.width() + twoThirds, r.width()); + mat.setScale(bigScale, bigScale); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, false)); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, bilerBits)); + + const SkScalar oneThird = SK_Scalar1 / 3; + const SkScalar smallScale = SkScalarDiv(r.width() + oneThird, r.width()); + mat.setScale(smallScale, smallScale); + REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, r, false)); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, bilerBits)); + + const SkScalar oneFortyth = SK_Scalar1 / 40; + const SkScalar tinyScale = SkScalarDiv(r.width() + oneFortyth, r.width()); + mat.setScale(tinyScale, tinyScale); + REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, r, false)); + REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, r, bilerBits)); +} static void assert_ifDrawnTo(skiatest::Reporter* reporter, const SkBitmap& bm, bool shouldBeDrawn) { @@ -179,6 +263,8 @@ static void TestDrawBitmapRect(skiatest::Reporter* reporter) { test_nan_antihair(reporter); test_giantrepeat_crbug118018(reporter); + + test_treatAsSprite(reporter); } #include "TestClassDef.h" |