blob: baae6f8c674c9fc22b9eafbf4d5f68f7015eea80 [file] [log] [blame]
/*
* Copyright 2013 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkPictureImageFilter.h"
#include "SkDevice.h"
#include "SkCanvas.h"
#include "SkReadBuffer.h"
#include "SkSurfaceProps.h"
#include "SkWriteBuffer.h"
#include "SkValidationUtils.h"
SkPictureImageFilter::SkPictureImageFilter(const SkPicture* picture)
: INHERITED(0, 0, NULL)
, fPicture(SkSafeRef(picture))
, fCropRect(picture ? picture->cullRect() : SkRect::MakeEmpty())
, fPictureResolution(kDeviceSpace_PictureResolution)
, fFilterQuality(kLow_SkFilterQuality) {
}
SkPictureImageFilter::SkPictureImageFilter(const SkPicture* picture, const SkRect& cropRect,
PictureResolution pictureResolution,
SkFilterQuality filterQuality)
: INHERITED(0, 0, NULL)
, fPicture(SkSafeRef(picture))
, fCropRect(cropRect)
, fPictureResolution(pictureResolution)
, fFilterQuality(filterQuality) {
}
SkPictureImageFilter::~SkPictureImageFilter() {
SkSafeUnref(fPicture);
}
SkFlattenable* SkPictureImageFilter::CreateProc(SkReadBuffer& buffer) {
SkAutoTUnref<SkPicture> picture;
SkRect cropRect;
#ifdef SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS
if (buffer.isCrossProcess()) {
buffer.validate(!buffer.readBool());
} else
#endif
{
if (buffer.readBool()) {
picture.reset(SkPicture::CreateFromBuffer(buffer));
}
}
buffer.readRect(&cropRect);
PictureResolution pictureResolution;
if (buffer.isVersionLT(SkReadBuffer::kPictureImageFilterResolution_Version)) {
pictureResolution = kDeviceSpace_PictureResolution;
} else {
pictureResolution = (PictureResolution)buffer.readInt();
}
if (kLocalSpace_PictureResolution == pictureResolution) {
//filterLevel is only serialized if pictureResolution is LocalSpace
SkFilterQuality filterQuality;
if (buffer.isVersionLT(SkReadBuffer::kPictureImageFilterLevel_Version)) {
filterQuality = kLow_SkFilterQuality;
} else {
filterQuality = (SkFilterQuality)buffer.readInt();
}
return CreateForLocalSpace(picture, cropRect, filterQuality);
}
return Create(picture, cropRect);
}
void SkPictureImageFilter::flatten(SkWriteBuffer& buffer) const {
#ifdef SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS
if (buffer.isCrossProcess()) {
buffer.writeBool(false);
} else
#endif
{
bool hasPicture = (fPicture != NULL);
buffer.writeBool(hasPicture);
if (hasPicture) {
fPicture->flatten(buffer);
}
}
buffer.writeRect(fCropRect);
buffer.writeInt(fPictureResolution);
if (kLocalSpace_PictureResolution == fPictureResolution) {
buffer.writeInt(fFilterQuality);
}
}
bool SkPictureImageFilter::onFilterImage(Proxy* proxy, const SkBitmap&, const Context& ctx,
SkBitmap* result, SkIPoint* offset) const {
if (!fPicture) {
offset->fX = offset->fY = 0;
return true;
}
SkRect floatBounds;
ctx.ctm().mapRect(&floatBounds, fCropRect);
SkIRect bounds = floatBounds.roundOut();
if (!bounds.intersect(ctx.clipBounds())) {
return false;
}
if (bounds.isEmpty()) {
offset->fX = offset->fY = 0;
return true;
}
SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
if (NULL == device.get()) {
return false;
}
if (kDeviceSpace_PictureResolution == fPictureResolution ||
0 == (ctx.ctm().getType() & ~SkMatrix::kTranslate_Mask)) {
drawPictureAtDeviceResolution(proxy, device.get(), bounds, ctx);
} else {
drawPictureAtLocalResolution(proxy, device.get(), bounds, ctx);
}
*result = device.get()->accessBitmap(false);
offset->fX = bounds.fLeft;
offset->fY = bounds.fTop;
return true;
}
void SkPictureImageFilter::drawPictureAtDeviceResolution(Proxy* proxy, SkBaseDevice* device,
const SkIRect& deviceBounds,
const Context& ctx) const {
// Pass explicit surface props, as the simplified canvas constructor discards device properties.
// FIXME: switch back to the public constructor (and unfriend) after
// https://code.google.com/p/skia/issues/detail?id=3142 is fixed.
SkCanvas canvas(device, proxy->surfaceProps(), SkCanvas::kDefault_InitFlags);
canvas.translate(-SkIntToScalar(deviceBounds.fLeft), -SkIntToScalar(deviceBounds.fTop));
canvas.concat(ctx.ctm());
canvas.drawPicture(fPicture);
}
void SkPictureImageFilter::drawPictureAtLocalResolution(Proxy* proxy, SkBaseDevice* device,
const SkIRect& deviceBounds,
const Context& ctx) const {
SkMatrix inverseCtm;
if (!ctx.ctm().invert(&inverseCtm))
return;
SkRect localBounds = SkRect::Make(ctx.clipBounds());
inverseCtm.mapRect(&localBounds);
if (!localBounds.intersect(fCropRect))
return;
SkIRect localIBounds = localBounds.roundOut();
SkAutoTUnref<SkBaseDevice> localDevice(proxy->createDevice(localIBounds.width(), localIBounds.height()));
// Pass explicit surface props, as the simplified canvas constructor discards device properties.
// FIXME: switch back to the public constructor (and unfriend) after
// https://code.google.com/p/skia/issues/detail?id=3142 is fixed.
SkCanvas localCanvas(localDevice, proxy->surfaceProps(), SkCanvas::kDefault_InitFlags);
localCanvas.translate(-SkIntToScalar(localIBounds.fLeft), -SkIntToScalar(localIBounds.fTop));
localCanvas.drawPicture(fPicture);
// Pass explicit surface props, as the simplified canvas constructor discards device properties.
// FIXME: switch back to the public constructor (and unfriend) after
// https://code.google.com/p/skia/issues/detail?id=3142 is fixed.
SkCanvas canvas(device, proxy->surfaceProps(), SkCanvas::kDefault_InitFlags);
canvas.translate(-SkIntToScalar(deviceBounds.fLeft), -SkIntToScalar(deviceBounds.fTop));
canvas.concat(ctx.ctm());
SkPaint paint;
paint.setFilterQuality(fFilterQuality);
canvas.drawBitmap(localDevice.get()->accessBitmap(false), SkIntToScalar(localIBounds.fLeft),
SkIntToScalar(localIBounds.fTop), &paint);
//canvas.drawPicture(fPicture);
}
#ifndef SK_IGNORE_TO_STRING
void SkPictureImageFilter::toString(SkString* str) const {
str->appendf("SkPictureImageFilter: (");
str->appendf("crop: (%f,%f,%f,%f) ",
fCropRect.fLeft, fCropRect.fTop, fCropRect.fRight, fCropRect.fBottom);
if (fPicture) {
str->appendf("picture: (%f,%f,%f,%f)",
fPicture->cullRect().fLeft, fPicture->cullRect().fTop,
fPicture->cullRect().fRight, fPicture->cullRect().fBottom);
}
str->append(")");
}
#endif