aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-05-09 13:53:38 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-05-09 13:53:38 +0000
commita17773f0b0db6b195004d438ec6ceb9c99465482 (patch)
treefa6c33436a31c2685de90f53ebb06b239839f3fd
parent263918ca01b0fffcf09492c5a5e6b57680703b10 (diff)
Disable filtering in draw Bitmap operation when pixels are aligned
The check is now performed in the callers of internalDrawBitmap() to avoid retrieve the texture with wrong filter mode when the pixels were aligned. This bug was spotted in Chrome for Android: Chrome for Androids's Canvas drawImage loses fidelity in canvases larger than 64k pixels. BUG=chromium:231916 R=bsalomon@google.com, robertphillips@google.com Signed-off-by: Samuel Iglesias Gonsalvez <siglesias@igalia.com> Author: siglesias@igalia.com Review URL: https://codereview.chromium.org/264303008 git-svn-id: http://skia.googlecode.com/svn/trunk@14665 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--expectations/gm/ignored-tests.txt4
-rw-r--r--include/gpu/SkGpuDevice.h3
-rw-r--r--src/gpu/SkGpuDevice.cpp164
3 files changed, 102 insertions, 69 deletions
diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt
index 8a4dcc97e1..9afd2ef44e 100644
--- a/expectations/gm/ignored-tests.txt
+++ b/expectations/gm/ignored-tests.txt
@@ -37,3 +37,7 @@
# This change removes an API that this GM was testing. If/when it lands and sticks,
# I will likely just delete the GM.
canvas-layer-state
+
+# bsalomon: https://codereview.chromium.org/264303008/
+# bsalomon@ will rebaseline this test
+ninepatch-stretch
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 8042ed30f7..15951aa927 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -208,7 +208,8 @@ private:
const GrTextureParams& params,
const SkPaint& paint,
SkCanvas::DrawBitmapRectFlags flags,
- bool bicubic);
+ bool bicubic,
+ bool needsTextureDomain);
void drawTiledBitmap(const SkBitmap& bitmap,
const SkRect& srcRect,
const SkIRect& clippedSrcRect,
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 483a1496c1..d43552e0d7 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1140,6 +1140,74 @@ static inline void clamped_outset_with_offset(SkIRect* iRect,
}
}
+static bool has_aligned_samples(const SkRect& srcRect,
+ const SkRect& transformedRect) {
+ // detect pixel disalignment
+ if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) -
+ transformedRect.left()) < COLOR_BLEED_TOLERANCE &&
+ SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) -
+ transformedRect.top()) < COLOR_BLEED_TOLERANCE &&
+ SkScalarAbs(transformedRect.width() - srcRect.width()) <
+ COLOR_BLEED_TOLERANCE &&
+ SkScalarAbs(transformedRect.height() - srcRect.height()) <
+ COLOR_BLEED_TOLERANCE) {
+ return true;
+ }
+ return false;
+}
+
+static bool may_color_bleed(const SkRect& srcRect,
+ const SkRect& transformedRect,
+ const SkMatrix& m) {
+ // Only gets called if has_aligned_samples returned false.
+ // So we can assume that sampling is axis aligned but not texel aligned.
+ SkASSERT(!has_aligned_samples(srcRect, transformedRect));
+ SkRect innerSrcRect(srcRect), innerTransformedRect,
+ outerTransformedRect(transformedRect);
+ innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
+ m.mapRect(&innerTransformedRect, innerSrcRect);
+
+ // The gap between outerTransformedRect and innerTransformedRect
+ // represents the projection of the source border area, which is
+ // problematic for color bleeding. We must check whether any
+ // destination pixels sample the border area.
+ outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
+ innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
+ SkIRect outer, inner;
+ outerTransformedRect.round(&outer);
+ innerTransformedRect.round(&inner);
+ // If the inner and outer rects round to the same result, it means the
+ // border does not overlap any pixel centers. Yay!
+ return inner != outer;
+}
+
+static bool needs_texture_domain(const SkBitmap& bitmap,
+ const SkRect& srcRect,
+ GrTextureParams &params,
+ const SkMatrix& contextMatrix,
+ bool bicubic) {
+ bool needsTextureDomain = false;
+
+ if (bicubic || params.filterMode() != GrTextureParams::kNone_FilterMode) {
+ // Need texture domain if drawing a sub rect
+ needsTextureDomain = srcRect.width() < bitmap.width() ||
+ srcRect.height() < bitmap.height();
+ if (!bicubic && needsTextureDomain && contextMatrix.rectStaysRect()) {
+ // sampling is axis-aligned
+ SkRect transformedRect;
+ contextMatrix.mapRect(&transformedRect, srcRect);
+
+ if (has_aligned_samples(srcRect, transformedRect)) {
+ params.setFilterMode(GrTextureParams::kNone_FilterMode);
+ needsTextureDomain = false;
+ } else {
+ needsTextureDomain = may_color_bleed(srcRect, transformedRect, contextMatrix);
+ }
+ }
+ }
+ return needsTextureDomain;
+}
+
void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,
const SkBitmap& bitmap,
const SkRect* srcRectPtr,
@@ -1278,7 +1346,18 @@ void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,
doBicubic);
} else {
// take the simple case
- this->internalDrawBitmap(bitmap, srcRect, params, paint, flags, doBicubic);
+ bool needsTextureDomain = needs_texture_domain(bitmap,
+ srcRect,
+ params,
+ fContext->getMatrix(),
+ doBicubic);
+ this->internalDrawBitmap(bitmap,
+ srcRect,
+ params,
+ paint,
+ flags,
+ doBicubic,
+ needsTextureDomain);
}
}
@@ -1349,54 +1428,24 @@ void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap,
if (bitmap.extractSubset(&tmpB, iTileR)) {
// now offset it to make it "local" to our tmp bitmap
tileR.offset(-offset.fX, -offset.fY);
-
- this->internalDrawBitmap(tmpB, tileR, params, paint, flags, bicubic);
+ GrTextureParams paramsTemp = params;
+ bool needsTextureDomain = needs_texture_domain(bitmap,
+ srcRect,
+ paramsTemp,
+ fContext->getMatrix(),
+ bicubic);
+ this->internalDrawBitmap(tmpB,
+ tileR,
+ paramsTemp,
+ paint,
+ flags,
+ bicubic,
+ needsTextureDomain);
}
}
}
}
-static bool has_aligned_samples(const SkRect& srcRect,
- const SkRect& transformedRect) {
- // detect pixel disalignment
- if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) -
- transformedRect.left()) < COLOR_BLEED_TOLERANCE &&
- SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) -
- transformedRect.top()) < COLOR_BLEED_TOLERANCE &&
- SkScalarAbs(transformedRect.width() - srcRect.width()) <
- COLOR_BLEED_TOLERANCE &&
- SkScalarAbs(transformedRect.height() - srcRect.height()) <
- COLOR_BLEED_TOLERANCE) {
- return true;
- }
- return false;
-}
-
-static bool may_color_bleed(const SkRect& srcRect,
- const SkRect& transformedRect,
- const SkMatrix& m) {
- // Only gets called if has_aligned_samples returned false.
- // So we can assume that sampling is axis aligned but not texel aligned.
- SkASSERT(!has_aligned_samples(srcRect, transformedRect));
- SkRect innerSrcRect(srcRect), innerTransformedRect,
- outerTransformedRect(transformedRect);
- innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
- m.mapRect(&innerTransformedRect, innerSrcRect);
-
- // The gap between outerTransformedRect and innerTransformedRect
- // represents the projection of the source border area, which is
- // problematic for color bleeding. We must check whether any
- // destination pixels sample the border area.
- outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
- innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
- SkIRect outer, inner;
- outerTransformedRect.round(&outer);
- innerTransformedRect.round(&inner);
- // If the inner and outer rects round to the same result, it means the
- // border does not overlap any pixel centers. Yay!
- return inner != outer;
-}
-
/*
* This is called by drawBitmap(), which has to handle images that may be too
@@ -1410,7 +1459,8 @@ void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,
const GrTextureParams& params,
const SkPaint& paint,
SkCanvas::DrawBitmapRectFlags flags,
- bool bicubic) {
+ bool bicubic,
+ bool needsTextureDomain) {
SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&
bitmap.height() <= fContext->getMaxTextureSize());
@@ -1429,31 +1479,9 @@ void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,
SkScalarMul(srcRect.fRight, wInv),
SkScalarMul(srcRect.fBottom, hInv));
- bool needsTextureDomain = false;
- if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) &&
- (bicubic || params.filterMode() != GrTextureParams::kNone_FilterMode)) {
- // Need texture domain if drawing a sub rect
- needsTextureDomain = srcRect.width() < bitmap.width() ||
- srcRect.height() < bitmap.height();
- if (!bicubic && needsTextureDomain && fContext->getMatrix().rectStaysRect()) {
- const SkMatrix& matrix = fContext->getMatrix();
- // sampling is axis-aligned
- SkRect transformedRect;
- matrix.mapRect(&transformedRect, srcRect);
-
- if (has_aligned_samples(srcRect, transformedRect)) {
- // We could also turn off filtering here (but we already did a cache lookup with
- // params).
- needsTextureDomain = false;
- } else {
- needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix);
- }
- }
- }
-
SkRect textureDomain = SkRect::MakeEmpty();
SkAutoTUnref<GrEffectRef> effect;
- if (needsTextureDomain) {
+ if (needsTextureDomain && !(flags & SkCanvas::kBleed_DrawBitmapRectFlag)) {
// Use a constrained texture domain to avoid color bleeding
SkScalar left, top, right, bottom;
if (srcRect.width() > SK_Scalar1) {