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:
parent
154beea859
commit
b87f798e5a
@ -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) {
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user