Fix SkMergeImageFilter crop rect computation.

The crop rect should be applied to the union of the input bounds, not
to the src input's bounds. These are often the same, since the saveLayer
offscreen size is computed as the union of all the required bounds, but
is not correct if the input primitive is not used (e.g., if all inputs
are connected to SkImageSources). But this will change as we
move to more accurate intermediate bounds computations (getting rid of the join() hacks as described in skbug.com/3194).

Since we can't know this without actually processing the inputs,
split SkMergeImageFilter processing into:

- filter all inputs
- applyCropRect to the union'ed bounds
- allocate the destination
- do the merge

BUG=3194

Review URL: https://codereview.chromium.org/1475793002
This commit is contained in:
senorblanco 2015-11-25 07:06:55 -08:00 committed by Commit bot
parent 43432f333a
commit 4a24398391
2 changed files with 62 additions and 21 deletions

View File

@ -15,7 +15,7 @@
///////////////////////////////////////////////////////////////////////////////
void SkMergeImageFilter::initAllocModes() {
int inputCount = countInputs();
int inputCount = this->countInputs();
if (inputCount) {
size_t size = sizeof(uint8_t) * inputCount;
if (size <= sizeof(fStorage)) {
@ -31,7 +31,7 @@ void SkMergeImageFilter::initAllocModes() {
void SkMergeImageFilter::initModes(const SkXfermode::Mode modes[]) {
if (modes) {
this->initAllocModes();
int inputCount = countInputs();
int inputCount = this->countInputs();
for (int i = 0; i < inputCount; ++i) {
fModes[i] = SkToU8(modes[i]);
}
@ -58,46 +58,64 @@ SkMergeImageFilter::~SkMergeImageFilter() {
bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
const Context& ctx,
SkBitmap* result, SkIPoint* offset) const {
if (countInputs() < 1) {
int inputCount = this->countInputs();
if (inputCount < 1) {
return false;
}
SkIRect bounds;
if (!this->applyCropRect(ctx, src, SkIPoint::Make(0, 0), &bounds)) {
SkAutoTDeleteArray<SkBitmap> inputs(new SkBitmap[inputCount]);
SkAutoTDeleteArray<SkIPoint> offsets(new SkIPoint[inputCount]);
bool didProduceResult = false;
// Filter all of the inputs.
for (int i = 0; i < inputCount; ++i) {
inputs[i] = src;
offsets[i].setZero();
if (!this->filterInput(i, proxy, src, ctx, &inputs[i], &offsets[i])) {
inputs[i].reset();
continue;
}
SkIRect srcBounds;
inputs[i].getBounds(&srcBounds);
srcBounds.offset(offsets[i]);
if (!didProduceResult) {
bounds = srcBounds;
didProduceResult = true;
} else {
bounds.join(srcBounds);
}
}
if (!didProduceResult) {
return false;
}
// Apply the crop rect to the union of the inputs' bounds.
if (!this->getCropRect().applyTo(bounds, ctx, &bounds)) {
return false;
}
const int x0 = bounds.left();
const int y0 = bounds.top();
// Allocate the destination buffer.
SkAutoTUnref<SkBaseDevice> dst(proxy->createDevice(bounds.width(), bounds.height()));
if (nullptr == dst) {
return false;
}
SkCanvas canvas(dst);
SkPaint paint;
bool didProduceResult = false;
int inputCount = countInputs();
// Composite all of the filter inputs.
for (int i = 0; i < inputCount; ++i) {
SkBitmap tmp;
SkBitmap input = src;
SkIPoint pos = SkIPoint::Make(0, 0);
if (!this->filterInput(i, proxy, src, ctx, &input, &pos)) {
continue;
}
SkPaint paint;
if (fModes) {
paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
} else {
paint.setXfermode(nullptr);
}
canvas.drawBitmap(input, SkIntToScalar(pos.x() - x0), SkIntToScalar(pos.y() - y0), &paint);
didProduceResult = true;
canvas.drawBitmap(inputs[i], SkIntToScalar(offsets[i].x() - x0),
SkIntToScalar(offsets[i].y() - y0), &paint);
}
if (!didProduceResult)
return false;
offset->fX = bounds.left();
offset->fY = bounds.top();
*result = dst->accessBitmap(false);
@ -134,7 +152,7 @@ void SkMergeImageFilter::flatten(SkWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
buffer.writeBool(fModes != nullptr);
if (fModes) {
buffer.writeByteArray(fModes, countInputs() * sizeof(fModes[0]));
buffer.writeByteArray(fModes, this->countInputs() * sizeof(fModes[0]));
}
}

View File

@ -642,6 +642,29 @@ DEF_TEST(ImageFilterComposedBlurFastBounds, reporter) {
REPORTER_ASSERT(reporter, boundsDst == expectedBounds);
}
DEF_TEST(ImageFilterMergeResultSize, reporter) {
SkBitmap greenBM;
greenBM.allocN32Pixels(20, 20);
greenBM.eraseColor(SK_ColorGREEN);
SkAutoTUnref<SkImage> greenImage(SkImage::NewFromBitmap(greenBM));
SkAutoTUnref<SkImageFilter> source(SkImageSource::Create(greenImage.get()));
SkAutoTUnref<SkImageFilter> merge(SkMergeImageFilter::Create(source.get(), source.get()));
SkBitmap bitmap;
bitmap.allocN32Pixels(1, 1);
bitmap.eraseColor(0);
const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(info, props));
SkImageFilter::DeviceProxy proxy(device);
SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 100, 100), nullptr,
SkImageFilter::kApprox_SizeConstraint);
SkBitmap result;
SkIPoint offset;
REPORTER_ASSERT(reporter, merge->filterImage(&proxy, bitmap, ctx, &result, &offset));
REPORTER_ASSERT(reporter, result.width() == 20 && result.height() == 20);
}
static void draw_blurred_rect(SkCanvas* canvas) {
SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(SkIntToScalar(8), 0));
SkPaint filterPaint;