Consistently round fOffset in SkOffsetImageFilter

Since SkScalarRoundToInt rounds differently depending on the sign of the
value, care must be taken to perform rounding before any potential
change to the sign - like in SkOffsetImageFilter::onFilterNodeBounds.

Bug: chromium:778204
Change-Id: I3debff7565f45022c7b8566662927149850b1bea
Reviewed-on: https://skia-review.googlesource.com/64020
Reviewed-by: Stephen White <senorblanco@chromium.org>
Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Fredrik Söderquist 2017-10-26 13:54:16 +02:00 committed by Skia Commit-Bot
parent 154beea859
commit b87f798e5a
2 changed files with 26 additions and 8 deletions

View File

@ -15,6 +15,11 @@
#include "SkSpecialSurface.h"
#include "SkWriteBuffer.h"
static SkIPoint map_offset_vector(const SkMatrix& ctm, const SkVector& offset) {
SkVector vec = ctm.mapVector(offset.fX, offset.fY);
return SkIPoint::Make(SkScalarRoundToInt(vec.fX), SkScalarRoundToInt(vec.fY));
}
sk_sp<SkImageFilter> SkOffsetImageFilter::Make(SkScalar dx, SkScalar dy,
sk_sp<SkImageFilter> input,
const CropRect* cropRect) {
@ -34,12 +39,11 @@ sk_sp<SkSpecialImage> SkOffsetImageFilter::onFilterImage(SkSpecialImage* source,
return nullptr;
}
SkVector vec;
ctx.ctm().mapVectors(&vec, &fOffset, 1);
SkIPoint vec = map_offset_vector(ctx.ctm(), fOffset);
if (!this->cropRectIsSet()) {
offset->fX = srcOffset.fX + SkScalarRoundToInt(vec.fX);
offset->fY = srcOffset.fY + SkScalarRoundToInt(vec.fY);
offset->fX = srcOffset.fX + vec.fX;
offset->fY = srcOffset.fY + vec.fY;
return input;
} else {
SkIRect bounds;
@ -65,7 +69,7 @@ sk_sp<SkSpecialImage> SkOffsetImageFilter::onFilterImage(SkSpecialImage* source,
canvas->translate(SkIntToScalar(srcOffset.fX - bounds.fLeft),
SkIntToScalar(srcOffset.fY - bounds.fTop));
input->draw(canvas, vec.x(), vec.y(), &paint);
input->draw(canvas, vec.fX, vec.fY, &paint);
offset->fX = bounds.fLeft;
offset->fY = bounds.fTop;
@ -92,13 +96,12 @@ SkRect SkOffsetImageFilter::computeFastBounds(const SkRect& src) const {
SkIRect SkOffsetImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
MapDirection direction) const {
SkVector vec;
ctm.mapVectors(&vec, &fOffset, 1);
SkIPoint vec = map_offset_vector(ctm, fOffset);
if (kReverse_MapDirection == direction) {
vec.negate();
}
return src.makeOffset(SkScalarCeilToInt(vec.fX), SkScalarCeilToInt(vec.fY));
return src.makeOffset(vec.fX, vec.fY);
}
sk_sp<SkFlattenable> SkOffsetImageFilter::CreateProc(SkReadBuffer& buffer) {

View File

@ -1998,6 +1998,21 @@ DEF_TEST(XfermodeImageFilterBounds, reporter) {
REPORTER_ASSERT(reporter, bounds.isEmpty());
}
DEF_TEST(OffsetImageFilterBounds, reporter) {
SkIRect src = SkIRect::MakeXYWH(0, 0, 100, 100);
sk_sp<SkImageFilter> offset(SkOffsetImageFilter::Make(-50.5f, -50.5f, nullptr));
SkIRect expectedForward = SkIRect::MakeXYWH(-50, -50, 100, 100);
SkIRect boundsForward = offset->filterBounds(src, SkMatrix::I(),
SkImageFilter::kForward_MapDirection);
REPORTER_ASSERT(reporter, boundsForward == expectedForward);
SkIRect expectedReverse = SkIRect::MakeXYWH(50, 50, 100, 100);
SkIRect boundsReverse = offset->filterBounds(src, SkMatrix::I(),
SkImageFilter::kReverse_MapDirection);
REPORTER_ASSERT(reporter, boundsReverse == expectedReverse);
}
static void test_arithmetic_bounds(skiatest::Reporter* reporter, float k1, float k2, float k3,
float k4, sk_sp<SkImageFilter> background,
sk_sp<SkImageFilter> foreground,