1/*
2 * Copyright 2013 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkPictureImageFilter.h"
9#include "SkDevice.h"
10#include "SkCanvas.h"
11#include "SkReadBuffer.h"
12#include "SkSurfaceProps.h"
13#include "SkWriteBuffer.h"
14#include "SkValidationUtils.h"
15
16SkPictureImageFilter::SkPictureImageFilter(const SkPicture* picture)
17    : INHERITED(0, 0, NULL)
18    , fPicture(SkSafeRef(picture))
19    , fCropRect(picture ? picture->cullRect() : SkRect::MakeEmpty())
20    , fPictureResolution(kDeviceSpace_PictureResolution)
21    , fFilterQuality(kLow_SkFilterQuality) {
22}
23
24SkPictureImageFilter::SkPictureImageFilter(const SkPicture* picture, const SkRect& cropRect,
25                                           PictureResolution pictureResolution,
26                                           SkFilterQuality filterQuality)
27    : INHERITED(0, 0, NULL)
28    , fPicture(SkSafeRef(picture))
29    , fCropRect(cropRect)
30    , fPictureResolution(pictureResolution)
31    , fFilterQuality(filterQuality) {
32}
33
34SkPictureImageFilter::~SkPictureImageFilter() {
35    SkSafeUnref(fPicture);
36}
37
38SkFlattenable* SkPictureImageFilter::CreateProc(SkReadBuffer& buffer) {
39    SkAutoTUnref<SkPicture> picture;
40    SkRect cropRect;
41
42#ifdef SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS
43    if (buffer.isCrossProcess()) {
44        buffer.validate(!buffer.readBool());
45    } else
46#endif
47    {
48        if (buffer.readBool()) {
49            picture.reset(SkPicture::CreateFromBuffer(buffer));
50        }
51    }
52    buffer.readRect(&cropRect);
53    PictureResolution pictureResolution;
54    if (buffer.isVersionLT(SkReadBuffer::kPictureImageFilterResolution_Version)) {
55        pictureResolution = kDeviceSpace_PictureResolution;
56    } else {
57        pictureResolution = (PictureResolution)buffer.readInt();
58    }
59
60    if (kLocalSpace_PictureResolution == pictureResolution) {
61        //filterLevel is only serialized if pictureResolution is LocalSpace
62        SkFilterQuality filterQuality;
63        if (buffer.isVersionLT(SkReadBuffer::kPictureImageFilterLevel_Version)) {
64            filterQuality = kLow_SkFilterQuality;
65        } else {
66            filterQuality = (SkFilterQuality)buffer.readInt();
67        }
68        return CreateForLocalSpace(picture, cropRect, filterQuality);
69    }
70    return Create(picture, cropRect);
71}
72
73void SkPictureImageFilter::flatten(SkWriteBuffer& buffer) const {
74#ifdef SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS
75    if (buffer.isCrossProcess()) {
76        buffer.writeBool(false);
77    } else
78#endif
79    {
80        bool hasPicture = (fPicture != NULL);
81        buffer.writeBool(hasPicture);
82        if (hasPicture) {
83            fPicture->flatten(buffer);
84        }
85    }
86    buffer.writeRect(fCropRect);
87    buffer.writeInt(fPictureResolution);
88    if (kLocalSpace_PictureResolution == fPictureResolution) {
89        buffer.writeInt(fFilterQuality);
90    }
91}
92
93bool SkPictureImageFilter::onFilterImage(Proxy* proxy, const SkBitmap&, const Context& ctx,
94                                         SkBitmap* result, SkIPoint* offset) const {
95    if (!fPicture) {
96        offset->fX = offset->fY = 0;
97        return true;
98    }
99
100    SkRect floatBounds;
101    ctx.ctm().mapRect(&floatBounds, fCropRect);
102    SkIRect bounds = floatBounds.roundOut();
103    if (!bounds.intersect(ctx.clipBounds())) {
104        return false;
105    }
106
107    if (bounds.isEmpty()) {
108        offset->fX = offset->fY = 0;
109        return true;
110    }
111
112    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
113    if (NULL == device.get()) {
114        return false;
115    }
116
117    if (kDeviceSpace_PictureResolution == fPictureResolution ||
118        0 == (ctx.ctm().getType() & ~SkMatrix::kTranslate_Mask)) {
119        drawPictureAtDeviceResolution(proxy, device.get(), bounds, ctx);
120    } else {
121        drawPictureAtLocalResolution(proxy, device.get(), bounds, ctx);
122    }
123
124    *result = device.get()->accessBitmap(false);
125    offset->fX = bounds.fLeft;
126    offset->fY = bounds.fTop;
127    return true;
128}
129
130void SkPictureImageFilter::drawPictureAtDeviceResolution(Proxy* proxy, SkBaseDevice* device,
131                                                         const SkIRect& deviceBounds,
132                                                         const Context& ctx) const {
133    // Pass explicit surface props, as the simplified canvas constructor discards device properties.
134    // FIXME: switch back to the public constructor (and unfriend) after
135    //        https://code.google.com/p/skia/issues/detail?id=3142 is fixed.
136    SkCanvas canvas(device, proxy->surfaceProps(), SkCanvas::kDefault_InitFlags);
137
138    canvas.translate(-SkIntToScalar(deviceBounds.fLeft), -SkIntToScalar(deviceBounds.fTop));
139    canvas.concat(ctx.ctm());
140    canvas.drawPicture(fPicture);
141}
142
143void SkPictureImageFilter::drawPictureAtLocalResolution(Proxy* proxy, SkBaseDevice* device,
144                                                        const SkIRect& deviceBounds,
145                                                        const Context& ctx) const {
146    SkMatrix inverseCtm;
147    if (!ctx.ctm().invert(&inverseCtm))
148        return;
149    SkRect localBounds = SkRect::Make(ctx.clipBounds());
150    inverseCtm.mapRect(&localBounds);
151    if (!localBounds.intersect(fCropRect))
152        return;
153    SkIRect localIBounds = localBounds.roundOut();
154    SkAutoTUnref<SkBaseDevice> localDevice(proxy->createDevice(localIBounds.width(), localIBounds.height()));
155
156    // Pass explicit surface props, as the simplified canvas constructor discards device properties.
157    // FIXME: switch back to the public constructor (and unfriend) after
158    //        https://code.google.com/p/skia/issues/detail?id=3142 is fixed.
159    SkCanvas localCanvas(localDevice, proxy->surfaceProps(), SkCanvas::kDefault_InitFlags);
160    localCanvas.translate(-SkIntToScalar(localIBounds.fLeft), -SkIntToScalar(localIBounds.fTop));
161    localCanvas.drawPicture(fPicture);
162
163    // Pass explicit surface props, as the simplified canvas constructor discards device properties.
164    // FIXME: switch back to the public constructor (and unfriend) after
165    //        https://code.google.com/p/skia/issues/detail?id=3142 is fixed.
166    SkCanvas canvas(device, proxy->surfaceProps(), SkCanvas::kDefault_InitFlags);
167
168    canvas.translate(-SkIntToScalar(deviceBounds.fLeft), -SkIntToScalar(deviceBounds.fTop));
169    canvas.concat(ctx.ctm());
170    SkPaint paint;
171    paint.setFilterQuality(fFilterQuality);
172    canvas.drawBitmap(localDevice.get()->accessBitmap(false), SkIntToScalar(localIBounds.fLeft),
173                      SkIntToScalar(localIBounds.fTop), &paint);
174    //canvas.drawPicture(fPicture);
175}
176
177#ifndef SK_IGNORE_TO_STRING
178void SkPictureImageFilter::toString(SkString* str) const {
179    str->appendf("SkPictureImageFilter: (");
180    str->appendf("crop: (%f,%f,%f,%f) ",
181                 fCropRect.fLeft, fCropRect.fTop, fCropRect.fRight, fCropRect.fBottom);
182    if (fPicture) {
183        str->appendf("picture: (%f,%f,%f,%f)",
184                     fPicture->cullRect().fLeft, fPicture->cullRect().fTop,
185                     fPicture->cullRect().fRight, fPicture->cullRect().fBottom);
186    }
187    str->append(")");
188}
189#endif
190