use doubles in matrix.invert() to keep more precision, needed for subtle

bugs when drawing stretched bitmaps (like ninepatch) at nasty scale factors
like 1.5 (where the inverse matrix steps 0.333333, 1.0, 1.666667, etc.)



git-svn-id: http://skia.googlecode.com/svn/trunk@354 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@android.com 2009-09-16 17:00:17 +00:00
parent ed881c2704
commit 0b9e2dbf2f
2 changed files with 40 additions and 14 deletions

View File

@ -12,6 +12,13 @@ public:
NinePatchView() {
SkImageDecoder::DecodeFile("/skimages/folder_background.9.png", &fBM);
// trim off the edge guide-lines
SkBitmap tmp;
SkIRect r;
r.set(1, 1, fBM.width() - 1, fBM.height() - 1);
fBM.extractSubset(&tmp, r);
fBM.swap(tmp);
}
protected:
@ -27,6 +34,8 @@ protected:
void drawBG(SkCanvas* canvas) {
canvas->drawColor(SK_ColorWHITE);
canvas->scale(1.5f, 1.5f);
canvas->drawBitmap(fBM, 0, 0);
SkIRect margins;

View File

@ -660,10 +660,18 @@ bool SkMatrix::postConcat(const SkMatrix& mat) {
///////////////////////////////////////////////////////////////////////////////
/* Matrix inversion is very expensive, but also the place where keeping
precision may be most important (here and matrix concat). Hence to avoid
bitmap blitting artifacts when walking the inverse, we use doubles for
the intermediate math, even though we know that is more expensive.
The fixed counter part is us using Sk64 for temp calculations.
*/
#ifdef SK_SCALAR_IS_FLOAT
typedef double SkDetScalar;
#define SkPerspMul(a, b) SkScalarMul(a, b)
#define SkScalarMulShift(a, b, s) SkScalarMul(a, b)
static float sk_inv_determinant(const float mat[9], int isPerspective,
#define SkScalarMulShift(a, b, s) SkDoubleToFloat((a) * (b))
static double sk_inv_determinant(const float mat[9], int isPerspective,
int* /* (only used in Fixed case) */) {
double det;
@ -680,9 +688,20 @@ bool SkMatrix::postConcat(const SkMatrix& mat) {
if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
return 0;
}
return (float)(1.0 / det);
return 1.0 / det;
}
static inline float SkDoubleToFloat(double x) {
return static_cast<float>(x);
}
// we declar a,b,c,d to all be doubles, because we want to perform
// double-precision muls and subtract, even though the original values are
// from the matrix, which are floats.
static float inline mul_diff_scale(double a, double b, double c, double d,
double scale) {
return SkDoubleToFloat((a * b - c * d) * scale);
}
#else
typedef SkFixed SkDetScalar;
#define SkPerspMul(a, b) SkFractMul(a, b)
#define SkScalarMulShift(a, b, s) SkMulShift(a, b, s)
static void set_muladdmul(Sk64* dst, int32_t a, int32_t b, int32_t c,
@ -733,7 +752,7 @@ bool SkMatrix::postConcat(const SkMatrix& mat) {
bool SkMatrix::invert(SkMatrix* inv) const {
int isPersp = has_perspective(*this);
int shift;
SkScalar scale = sk_inv_determinant(fMat, isPersp, &shift);
SkDetScalar scale = sk_inv_determinant(fMat, isPersp, &shift);
if (scale == 0) { // underflow
return false;
@ -808,17 +827,15 @@ bool SkMatrix::invert(SkMatrix* inv) const {
inv->fMat[kMScaleY] = SkMulShift(fMat[kMScaleX], scale, fixedShift);
inv->fMat[kMTransY] = SkMulShift(ty.getShiftRight(33 - clzNumer), scale, sk64shift);
#else
inv->fMat[kMScaleX] = SkScalarMul(fMat[kMScaleY], scale);
inv->fMat[kMSkewX] = SkScalarMul(-fMat[kMSkewX], scale);
if (!fixmuladdmulshiftmul(fMat[kMSkewX], fMat[kMTransY], -fMat[kMScaleY], fMat[kMTransX], shift, scale, &inv->fMat[kMTransX])) {
return false;
}
inv->fMat[kMScaleX] = SkDoubleToFloat(fMat[kMScaleY] * scale);
inv->fMat[kMSkewX] = SkDoubleToFloat(-fMat[kMSkewX] * scale);
inv->fMat[kMTransX] = mul_diff_scale(fMat[kMSkewX], fMat[kMTransY],
fMat[kMScaleY], fMat[kMTransX], scale);
inv->fMat[kMSkewY] = SkScalarMul(-fMat[kMSkewY], scale);
inv->fMat[kMScaleY] = SkScalarMul(fMat[kMScaleX], scale);
if (!fixmuladdmulshiftmul(fMat[kMSkewY], fMat[kMTransX], -fMat[kMScaleX], fMat[kMTransY], shift, scale, &inv->fMat[kMTransY])) {
return false;
}
inv->fMat[kMSkewY] = SkDoubleToFloat(-fMat[kMSkewY] * scale);
inv->fMat[kMScaleY] = SkDoubleToFloat(fMat[kMScaleX] * scale);
inv->fMat[kMTransY] = mul_diff_scale(fMat[kMSkewY], fMat[kMTransX],
fMat[kMScaleX], fMat[kMTransY], scale);
#endif
inv->fMat[kMPersp0] = 0;
inv->fMat[kMPersp1] = 0;