Do not use GrBicubic effect when downscaling. Also, don't use glTexStorage as it interferes with deleyed mipmap generation.
R=robertphillips@google.com Author: bsalomon@google.com Review URL: https://codereview.chromium.org/105353002 git-svn-id: http://skia.googlecode.com/svn/trunk@12576 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
c9c0b1e90e
commit
cea9abb001
@ -573,6 +573,14 @@ public:
|
||||
SkDEVCODE(void dump() const;)
|
||||
SkDEVCODE(void toString(SkString*) const;)
|
||||
|
||||
/**
|
||||
* Calculates the minimum stretching factor of the matrix. If the matrix has
|
||||
* perspective -1 is returned.
|
||||
*
|
||||
* @return minumum strecthing factor
|
||||
*/
|
||||
SkScalar getMinStretch() const;
|
||||
|
||||
/**
|
||||
* Calculates the maximum stretching factor of the matrix. If the matrix has
|
||||
* perspective -1 is returned.
|
||||
|
@ -358,13 +358,12 @@ GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint&
|
||||
SkMatrix matrix;
|
||||
matrix.setIDiv(fRawBitmap.width(), fRawBitmap.height());
|
||||
|
||||
if (this->hasLocalMatrix()) {
|
||||
SkMatrix inverse;
|
||||
if (!this->getLocalMatrix().invert(&inverse)) {
|
||||
return NULL;
|
||||
}
|
||||
matrix.preConcat(inverse);
|
||||
}
|
||||
|
||||
SkShader::TileMode tm[] = {
|
||||
(TileMode)fState.fTileModeX,
|
||||
(TileMode)fState.fTileModeY,
|
||||
@ -384,9 +383,21 @@ GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint&
|
||||
textureFilterMode = GrTextureParams::kMipMap_FilterMode;
|
||||
break;
|
||||
case SkPaint::kHigh_FilterLevel:
|
||||
// Minification can look bad with the bicubic effect. This is an overly aggressive
|
||||
// check for MIP fallbacks. It doesn't consider the fact that minification in the local
|
||||
// matrix could be offset by the view matrix and vice versa. We also don't know whether
|
||||
// the draw has explicit local coords (e.g. drawVertices) where the scale factor is
|
||||
// unknown and varies.
|
||||
if (context->getMatrix().getMinStretch() >= SK_Scalar1 &&
|
||||
this->getLocalMatrix().getMaxStretch() <= SK_Scalar1) {
|
||||
// fall back to no filtering here; we will install another
|
||||
// shader that will do the HQ filtering.
|
||||
textureFilterMode = GrTextureParams::kNone_FilterMode;
|
||||
} else {
|
||||
// Fall back to mip-mapping.
|
||||
paintFilterLevel = SkPaint::kMedium_FilterLevel;
|
||||
textureFilterMode = GrTextureParams::kMipMap_FilterMode;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SkErrorInternals::SetError( kInvalidPaint_SkError,
|
||||
|
@ -1857,45 +1857,71 @@ bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkScalar SkMatrix::getMaxStretch() const {
|
||||
TypeMask mask = this->getType();
|
||||
enum MinOrMax {
|
||||
kMin_MinOrMax,
|
||||
kMax_MinOrMax
|
||||
};
|
||||
|
||||
if (this->hasPerspective()) {
|
||||
template <MinOrMax MIN_OR_MAX> SkScalar get_stretch_factor(SkMatrix::TypeMask typeMask,
|
||||
const SkScalar m[9]) {
|
||||
if (typeMask & SkMatrix::kPerspective_Mask) {
|
||||
return -SK_Scalar1;
|
||||
}
|
||||
if (this->isIdentity()) {
|
||||
if (SkMatrix::kIdentity_Mask == typeMask) {
|
||||
return SK_Scalar1;
|
||||
}
|
||||
if (!(mask & kAffine_Mask)) {
|
||||
return SkMaxScalar(SkScalarAbs(fMat[kMScaleX]),
|
||||
SkScalarAbs(fMat[kMScaleY]));
|
||||
if (!(typeMask & SkMatrix::kAffine_Mask)) {
|
||||
if (kMin_MinOrMax == MIN_OR_MAX) {
|
||||
return SkMinScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
|
||||
SkScalarAbs(m[SkMatrix::kMScaleY]));
|
||||
} else {
|
||||
return SkMaxScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
|
||||
SkScalarAbs(m[SkMatrix::kMScaleY]));
|
||||
}
|
||||
}
|
||||
// ignore the translation part of the matrix, just look at 2x2 portion.
|
||||
// compute singular values, take largest abs value.
|
||||
// compute singular values, take largest or smallest abs value.
|
||||
// [a b; b c] = A^T*A
|
||||
SkScalar a = SkScalarMul(fMat[kMScaleX], fMat[kMScaleX]) +
|
||||
SkScalarMul(fMat[kMSkewY], fMat[kMSkewY]);
|
||||
SkScalar b = SkScalarMul(fMat[kMScaleX], fMat[kMSkewX]) +
|
||||
SkScalarMul(fMat[kMScaleY], fMat[kMSkewY]);
|
||||
SkScalar c = SkScalarMul(fMat[kMSkewX], fMat[kMSkewX]) +
|
||||
SkScalarMul(fMat[kMScaleY], fMat[kMScaleY]);
|
||||
SkScalar a = SkScalarMul(m[SkMatrix::kMScaleX], m[SkMatrix::kMScaleX]) +
|
||||
SkScalarMul(m[SkMatrix::kMSkewY], m[SkMatrix::kMSkewY]);
|
||||
SkScalar b = SkScalarMul(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewX]) +
|
||||
SkScalarMul(m[SkMatrix::kMScaleY], m[SkMatrix::kMSkewY]);
|
||||
SkScalar c = SkScalarMul(m[SkMatrix::kMSkewX], m[SkMatrix::kMSkewX]) +
|
||||
SkScalarMul(m[SkMatrix::kMScaleY], m[SkMatrix::kMScaleY]);
|
||||
// eigenvalues of A^T*A are the squared singular values of A.
|
||||
// characteristic equation is det((A^T*A) - l*I) = 0
|
||||
// l^2 - (a + c)l + (ac-b^2)
|
||||
// solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
|
||||
// and roots are guaraunteed to be pos and real).
|
||||
SkScalar largerRoot;
|
||||
// and roots are guaranteed to be pos and real).
|
||||
SkScalar chosenRoot;
|
||||
SkScalar bSqd = SkScalarMul(b,b);
|
||||
// if upper left 2x2 is orthogonal save some math
|
||||
if (bSqd <= SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
|
||||
largerRoot = SkMaxScalar(a, c);
|
||||
if (kMin_MinOrMax == MIN_OR_MAX) {
|
||||
chosenRoot = SkMinScalar(a, c);
|
||||
} else {
|
||||
chosenRoot = SkMaxScalar(a, c);
|
||||
}
|
||||
} else {
|
||||
SkScalar aminusc = a - c;
|
||||
SkScalar apluscdiv2 = SkScalarHalf(a + c);
|
||||
SkScalar x = SkScalarHalf(SkScalarSqrt(SkScalarMul(aminusc, aminusc) + 4 * bSqd));
|
||||
largerRoot = apluscdiv2 + x;
|
||||
if (kMin_MinOrMax == MIN_OR_MAX) {
|
||||
chosenRoot = apluscdiv2 - x;
|
||||
} else {
|
||||
chosenRoot = apluscdiv2 + x;
|
||||
}
|
||||
return SkScalarSqrt(largerRoot);
|
||||
}
|
||||
SkASSERT(chosenRoot >= 0);
|
||||
return SkScalarSqrt(chosenRoot);
|
||||
}
|
||||
|
||||
SkScalar SkMatrix::getMinStretch() const {
|
||||
return get_stretch_factor<kMin_MinOrMax>(this->getType(), fMat);
|
||||
}
|
||||
|
||||
SkScalar SkMatrix::getMaxStretch() const {
|
||||
return get_stretch_factor<kMax_MinOrMax>(this->getType(), fMat);
|
||||
}
|
||||
|
||||
static void reset_identity_matrix(SkMatrix* identity) {
|
||||
|
@ -1194,8 +1194,10 @@ void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,
|
||||
tileFilterPad = 1;
|
||||
textureFilterMode = GrTextureParams::kMipMap_FilterMode;
|
||||
break;
|
||||
case SkPaint::kHigh_FilterLevel:
|
||||
if (flags & SkCanvas::kBleed_DrawBitmapRectFlag) {
|
||||
case SkPaint::kHigh_FilterLevel: {
|
||||
// Minification can look bad with the bicubic effect.
|
||||
if (fContext->getMatrix().getMinStretch() >= SK_Scalar1 &&
|
||||
(flags & SkCanvas::kBleed_DrawBitmapRectFlag)) {
|
||||
// We will install an effect that does the filtering in the shader.
|
||||
textureFilterMode = GrTextureParams::kNone_FilterMode;
|
||||
tileFilterPad = GrBicubicEffect::kFilterTexelPad;
|
||||
@ -1207,6 +1209,7 @@ void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,
|
||||
tileFilterPad = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
SkErrorInternals::SetError( kInvalidPaint_SkError,
|
||||
"Sorry, I don't understand the filtering "
|
||||
|
@ -552,7 +552,12 @@ bool GrGpuGL::uploadTexData(const GrGLTexture::Desc& desc,
|
||||
SkAutoSMalloc<128 * 128> tempStorage;
|
||||
|
||||
// paletted textures cannot be partially updated
|
||||
bool useTexStorage = isNewTexture &&
|
||||
// We currently lazily create MIPMAPs when the we see a draw with
|
||||
// GrTextureParams::kMipMap_FilterMode. Using texture storage requires that the
|
||||
// MIP levels are all created when the texture is created. So for now we don't use
|
||||
// texture storage.
|
||||
bool useTexStorage = false &&
|
||||
isNewTexture &&
|
||||
desc.fConfig != kIndex_8_GrPixelConfig &&
|
||||
this->glCaps().texStorageSupport();
|
||||
|
||||
@ -638,8 +643,7 @@ bool GrGpuGL::uploadTexData(const GrGLTexture::Desc& desc,
|
||||
desc.fWidth == width && desc.fHeight == height) {
|
||||
CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
|
||||
if (useTexStorage) {
|
||||
// We never resize or change formats of textures. We don't use
|
||||
// mipmaps currently.
|
||||
// We never resize or change formats of textures.
|
||||
GL_ALLOC_CALL(this->glInterface(),
|
||||
TexStorage2D(GR_GL_TEXTURE_2D,
|
||||
1, // levels
|
||||
|
@ -130,36 +130,43 @@ static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
|
||||
REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
|
||||
}
|
||||
|
||||
static void test_matrix_max_stretch(skiatest::Reporter* reporter) {
|
||||
static void test_matrix_min_max_stretch(skiatest::Reporter* reporter) {
|
||||
SkMatrix identity;
|
||||
identity.reset();
|
||||
REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMinStretch());
|
||||
REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch());
|
||||
|
||||
SkMatrix scale;
|
||||
scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
|
||||
REPORTER_ASSERT(reporter, SK_Scalar1 * 2 == scale.getMinStretch());
|
||||
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 / 4 == rot90Scale.getMinStretch());
|
||||
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);
|
||||
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMinStretch() ,SK_ScalarNearlyZero));
|
||||
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMaxStretch(), SK_ScalarNearlyZero));
|
||||
|
||||
SkMatrix translate;
|
||||
translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
|
||||
REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMinStretch());
|
||||
REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch());
|
||||
|
||||
SkMatrix perspX;
|
||||
perspX.reset();
|
||||
perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
|
||||
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinStretch());
|
||||
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch());
|
||||
|
||||
SkMatrix perspY;
|
||||
perspY.reset();
|
||||
perspY.setPerspX(SkScalarToPersp(-SK_Scalar1 / 500));
|
||||
perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500));
|
||||
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinStretch());
|
||||
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch());
|
||||
|
||||
SkMatrix baseMats[] = {scale, rot90Scale, rotate,
|
||||
@ -178,25 +185,22 @@ static void test_matrix_max_stretch(skiatest::Reporter* reporter) {
|
||||
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());
|
||||
SkScalar minStretch = mat.getMinStretch();
|
||||
SkScalar maxStretch = mat.getMaxStretch();
|
||||
REPORTER_ASSERT(reporter, (minStretch < 0) == (maxStretch < 0));
|
||||
REPORTER_ASSERT(reporter, (maxStretch < 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;
|
||||
// test a bunch of vectors. All should be scaled by between minStretch and maxStretch
|
||||
// (modulo some error) and we should find a vector that is scaled by almost each.
|
||||
static const SkScalar gVectorStretchTol = (105 * SK_Scalar1) / 100;
|
||||
static const SkScalar gClosestStretchTol = (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();
|
||||
@ -209,12 +213,17 @@ static void test_matrix_max_stretch(skiatest::Reporter* reporter) {
|
||||
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);
|
||||
REPORTER_ASSERT(reporter, SkScalarDiv(d, maxStretch) < gVectorStretchTol);
|
||||
REPORTER_ASSERT(reporter, SkScalarDiv(minStretch, d) < gVectorStretchTol);
|
||||
if (max < d) {
|
||||
max = d;
|
||||
}
|
||||
if (min > d) {
|
||||
min = d;
|
||||
}
|
||||
REPORTER_ASSERT(reporter, SkScalarDiv(max, stretch) >= gMaxStretchTol);
|
||||
}
|
||||
REPORTER_ASSERT(reporter, SkScalarDiv(max, maxStretch) >= gClosestStretchTol);
|
||||
REPORTER_ASSERT(reporter, SkScalarDiv(minStretch, min) >= gClosestStretchTol);
|
||||
}
|
||||
}
|
||||
|
||||
@ -797,7 +806,7 @@ static void TestMatrix(skiatest::Reporter* reporter) {
|
||||
REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
|
||||
#endif
|
||||
|
||||
test_matrix_max_stretch(reporter);
|
||||
test_matrix_min_max_stretch(reporter);
|
||||
test_matrix_is_similarity(reporter);
|
||||
test_matrix_recttorect(reporter);
|
||||
test_matrix_decomposition(reporter);
|
||||
|
Loading…
Reference in New Issue
Block a user