blob: e7ba5e6ad92a881c8e043c58f5b41992a5d35d54 [file] [log] [blame]
reed@google.com894aa9a2011-09-23 14:49:49 +00001/*
2 * Copyright 2011 Google Inc.
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#ifndef SkImageFilter_DEFINED
9#define SkImageFilter_DEFINED
10
11#include "SkFlattenable.h"
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000012#include "SkMatrix.h"
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000013#include "SkRect.h"
reedb959ec72014-07-17 07:03:09 -070014#include "SkTemplates.h"
reed@google.com894aa9a2011-09-23 14:49:49 +000015
reed@google.com15356a62011-11-03 19:29:08 +000016class SkBitmap;
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +000017class SkColorFilter;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000018class SkBaseDevice;
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000019struct SkIPoint;
bsalomon97b9ab72014-07-08 06:52:35 -070020class GrEffect;
tomhudson@google.comd0c1a062012-07-12 17:23:52 +000021class GrTexture;
reed@google.com15356a62011-11-03 19:29:08 +000022
23/**
reed@google.com15356a62011-11-03 19:29:08 +000024 * Base class for image filters. If one is installed in the paint, then
25 * all drawing occurs as usual, but it is as if the drawing happened into an
26 * offscreen (before the xfermode is applied). This offscreen bitmap will
27 * then be handed to the imagefilter, who in turn creates a new bitmap which
28 * is what will finally be drawn to the device (using the original xfermode).
reed@google.com15356a62011-11-03 19:29:08 +000029 */
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +000030class SK_API SkImageFilter : public SkFlattenable {
reed@google.com894aa9a2011-09-23 14:49:49 +000031public:
robertphillips@google.com0456e0b2012-06-27 14:03:26 +000032 SK_DECLARE_INST_COUNT(SkImageFilter)
33
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +000034 class CropRect {
35 public:
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000036 enum CropEdge {
37 kHasLeft_CropEdge = 0x01,
38 kHasTop_CropEdge = 0x02,
39 kHasRight_CropEdge = 0x04,
40 kHasBottom_CropEdge = 0x08,
41 kHasAll_CropEdge = 0x0F,
42 };
43 CropRect() {}
44 explicit CropRect(const SkRect& rect, uint32_t flags = kHasAll_CropEdge) : fRect(rect), fFlags(flags) {}
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +000045 uint32_t flags() const { return fFlags; }
46 const SkRect& rect() const { return fRect; }
47 private:
48 SkRect fRect;
49 uint32_t fFlags;
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000050 };
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000051
senorblanco@chromium.orga49c0a52014-04-23 23:07:00 +000052 class SK_API Cache : public SkRefCnt {
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +000053 public:
54 // By default, we cache only image filters with 2 or more children.
mtkleinbd7746d2014-07-09 07:52:32 -070055 // Values less than 2 mean always cache; values greater than 2 are not supported.
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +000056 static Cache* Create(int minChildren = 2);
57 virtual ~Cache() {}
58 virtual bool get(const SkImageFilter* key, SkBitmap* result, SkIPoint* offset) = 0;
59 virtual void set(const SkImageFilter* key,
60 const SkBitmap& result, const SkIPoint& offset) = 0;
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +000061 virtual void remove(const SkImageFilter* key) = 0;
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +000062 };
63
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000064 class Context {
65 public:
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +000066 Context(const SkMatrix& ctm, const SkIRect& clipBounds, Cache* cache) :
67 fCTM(ctm), fClipBounds(clipBounds), fCache(cache) {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000068 }
69 const SkMatrix& ctm() const { return fCTM; }
70 const SkIRect& clipBounds() const { return fClipBounds; }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +000071 Cache* cache() const { return fCache; }
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000072 private:
73 SkMatrix fCTM;
74 SkIRect fClipBounds;
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +000075 Cache* fCache;
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000076 };
77
reed@google.com76dd2772012-01-05 21:15:07 +000078 class Proxy {
79 public:
reed@google.com8926b162012-03-23 15:36:36 +000080 virtual ~Proxy() {};
81
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000082 virtual SkBaseDevice* createDevice(int width, int height) = 0;
reed@google.com8926b162012-03-23 15:36:36 +000083 // returns true if the proxy can handle this filter natively
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +000084 virtual bool canHandleImageFilter(const SkImageFilter*) = 0;
reed@google.com76dd2772012-01-05 21:15:07 +000085 // returns true if the proxy handled the filter itself. if this returns
86 // false then the filter's code will be called.
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +000087 virtual bool filterImage(const SkImageFilter*, const SkBitmap& src,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000088 const Context&,
reed@google.com76dd2772012-01-05 21:15:07 +000089 SkBitmap* result, SkIPoint* offset) = 0;
90 };
reed@google.com894aa9a2011-09-23 14:49:49 +000091
92 /**
93 * Request a new (result) image to be created from the src image.
94 * If the src has no pixels (isNull()) then the request just wants to
95 * receive the config and width/height of the result.
96 *
97 * The matrix is the current matrix on the canvas.
98 *
99 * Offset is the amount to translate the resulting image relative to the
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000100 * src when it is drawn. This is an out-param.
reed@google.com894aa9a2011-09-23 14:49:49 +0000101 *
102 * If the result image cannot be created, return false, in which case both
103 * the result and offset parameters will be ignored by the caller.
104 */
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000105 bool filterImage(Proxy*, const SkBitmap& src, const Context&,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000106 SkBitmap* result, SkIPoint* offset) const;
reed@google.com15356a62011-11-03 19:29:08 +0000107
108 /**
reed@google.com32d25b62011-12-20 16:19:00 +0000109 * Given the src bounds of an image, this returns the bounds of the result
110 * image after the filter has been applied.
111 */
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000112 bool filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const;
reed@google.com32d25b62011-12-20 16:19:00 +0000113
114 /**
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +0000115 * Returns true if the filter can be processed on the GPU. This is most
116 * often used for multi-pass effects, where intermediate results must be
bsalomon@google.com8ea78d82012-10-24 20:11:30 +0000117 * rendered to textures. For single-pass effects, use asNewEffect().
senorblanco@chromium.org7938bae2013-10-18 20:08:14 +0000118 * The default implementation returns asNewEffect(NULL, NULL, SkMatrix::I(),
119 * SkIRect()).
reed@google.com15356a62011-11-03 19:29:08 +0000120 */
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +0000121 virtual bool canFilterImageGPU() const;
reed@google.com894aa9a2011-09-23 14:49:49 +0000122
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000123 /**
skia.committer@gmail.com32840172013-04-09 07:01:27 +0000124 * Process this image filter on the GPU. This is most often used for
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000125 * multi-pass effects, where intermediate results must be rendered to
126 * textures. For single-pass effects, use asNewEffect(). src is the
127 * source image for processing, as a texture-backed bitmap. result is
128 * the destination bitmap, which should contain a texture-backed pixelref
skia.committer@gmail.comde2e4e82013-07-11 07:01:01 +0000129 * on success. offset is the amount to translate the resulting image
commit-bot@chromium.org7b320702013-07-10 21:22:18 +0000130 * relative to the src when it is drawn. The default implementation does
131 * single-pass processing using asNewEffect().
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000132 */
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000133 virtual bool filterImageGPU(Proxy*, const SkBitmap& src, const Context&,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000134 SkBitmap* result, SkIPoint* offset) const;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000135
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000136 /**
sugoi@google.coma1c511b2013-02-21 15:02:28 +0000137 * Returns whether this image filter is a color filter and puts the color filter into the
sugoi@google.com4b6d4322013-02-21 20:26:50 +0000138 * "filterPtr" parameter if it can. Does nothing otherwise.
139 * If this returns false, then the filterPtr is unchanged.
140 * If this returns true, then if filterPtr is not null, it must be set to a ref'd colorfitler
141 * (i.e. it may not be set to NULL).
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000142 */
sugoi@google.com4b6d4322013-02-21 20:26:50 +0000143 virtual bool asColorFilter(SkColorFilter** filterPtr) const;
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000144
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000145 /**
146 * Returns the number of inputs this filter will accept (some inputs can
147 * be NULL).
148 */
149 int countInputs() const { return fInputCount; }
150
151 /**
152 * Returns the input filter at a given index, or NULL if no input is
153 * connected. The indices used are filter-specific.
154 */
155 SkImageFilter* getInput(int i) const {
156 SkASSERT(i < fInputCount);
157 return fInputs[i];
158 }
159
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000160 /**
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +0000161 * Returns whether any edges of the crop rect have been set. The crop
162 * rect is set at construction time, and determines which pixels from the
163 * input image will be processed. The size of the crop rect should be
164 * used as the size of the destination image. The origin of this rect
165 * should be used to offset access to the input images, and should also
166 * be added to the "offset" parameter in onFilterImage and
167 * filterImageGPU(). (The latter ensures that the resulting buffer is
168 * drawn in the correct location.)
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000169 */
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +0000170 bool cropRectIsSet() const { return fCropRect.flags() != 0x0; }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000171
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +0000172 // Default impl returns union of all input bounds.
173 virtual void computeFastBounds(const SkRect&, SkRect*) const;
174
kkinnunen45095172014-07-29 06:12:49 -0700175#if SK_SUPPORT_GPU
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000176 /**
177 * Wrap the given texture in a texture-backed SkBitmap.
178 */
179 static void WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result);
180
181 /**
182 * Recursively evaluate this filter on the GPU. If the filter has no GPU
183 * implementation, it will be processed in software and uploaded to the GPU.
184 */
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000185 bool getInputResultGPU(SkImageFilter::Proxy* proxy, const SkBitmap& src, const Context&,
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000186 SkBitmap* result, SkIPoint* offset) const;
187#endif
188
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +0000189 /**
190 * Set an external cache to be used for all image filter processing. This
191 * will replace the default intra-frame cache.
192 */
193 static void SetExternalCache(Cache* cache);
194
195 /**
196 * Returns the currently-set external cache, or NULL if none is set.
197 */
198 static Cache* GetExternalCache();
199
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +0000200 SK_DEFINE_FLATTENABLE_TYPE(SkImageFilter)
201
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000202protected:
reedb959ec72014-07-17 07:03:09 -0700203 class Common {
204 public:
205 Common() {}
206 ~Common();
207
208 bool unflatten(SkReadBuffer&, int expectedInputs = -1);
209
210 CropRect cropRect() const { return fCropRect; }
211 int inputCount() const { return fInputs.count(); }
212 SkImageFilter** inputs() const { return fInputs.get(); }
213
214 // If the caller wants a copy of the inputs, call this and it will transfer ownership
215 // of the unflattened input filters to the caller. This is just a short-cut for copying
216 // the inputs, calling ref() on each, and then waiting for Common's destructor to call
217 // unref() on each.
218 void detachInputs(SkImageFilter** inputs);
219
220 private:
221 CropRect fCropRect;
222 // most filters accept at most 2 input-filters
223 SkAutoSTArray<2, SkImageFilter*> fInputs;
224
225 void allocInputs(int count);
226 };
227
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +0000228 SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect = NULL);
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000229
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000230 virtual ~SkImageFilter();
231
commit-bot@chromium.orgc84728d2013-12-04 20:07:47 +0000232 /**
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000233 * Constructs a new SkImageFilter read from an SkReadBuffer object.
commit-bot@chromium.orgc84728d2013-12-04 20:07:47 +0000234 *
235 * @param inputCount The exact number of inputs expected for this SkImageFilter object.
236 * -1 can be used if the filter accepts any number of inputs.
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000237 * @param rb SkReadBuffer object from which the SkImageFilter is read.
commit-bot@chromium.orgc84728d2013-12-04 20:07:47 +0000238 */
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000239 explicit SkImageFilter(int inputCount, SkReadBuffer& rb);
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000240
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000241 virtual void flatten(SkWriteBuffer& wb) const SK_OVERRIDE;
reed@google.com32d25b62011-12-20 16:19:00 +0000242
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000243 /**
244 * This is the virtual which should be overridden by the derived class
245 * to perform image filtering.
246 *
247 * src is the original primitive bitmap. If the filter has a connected
248 * input, it should recurse on that input and use that in place of src.
249 *
250 * The matrix is the current matrix on the canvas.
251 *
252 * Offset is the amount to translate the resulting image relative to the
253 * src when it is drawn. This is an out-param.
254 *
255 * If the result image cannot be created, this should false, in which
256 * case both the result and offset parameters will be ignored by the
257 * caller.
258 */
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000259 virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000260 SkBitmap* result, SkIPoint* offset) const;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000261 // Given the bounds of the destination rect to be filled in device
262 // coordinates (first parameter), and the CTM, compute (conservatively)
263 // which rect of the source image would be required (third parameter).
264 // Used for clipping and temp-buffer allocations, so the result need not
265 // be exact, but should never be smaller than the real answer. The default
266 // implementation recursively unions all input bounds, or returns false if
267 // no inputs.
268 virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const;
reed@google.com894aa9a2011-09-23 14:49:49 +0000269
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000270 /** Computes source bounds as the src bitmap bounds offset by srcOffset.
271 * Apply the transformed crop rect to the bounds if any of the
272 * corresponding edge flags are set. Intersects the result against the
273 * context's clipBounds, and returns the result in "bounds". If there is
274 * no intersection, returns false and leaves "bounds" unchanged.
275 */
276 bool applyCropRect(const Context&, const SkBitmap& src, const SkIPoint& srcOffset,
277 SkIRect* bounds) const;
278
279 /** Same as the above call, except that if the resulting crop rect is not
280 * entirely contained by the source bitmap's bounds, it creates a new
281 * bitmap in "result" and pads the edges with transparent black. In that
282 * case, the srcOffset is modified to be the same as the bounds, since no
283 * further adjustment is needed by the caller. This version should only
284 * be used by filters which are not capable of processing a smaller
285 * source bitmap into a larger destination.
286 */
287 bool applyCropRect(const Context&, Proxy* proxy, const SkBitmap& src, SkIPoint* srcOffset,
288 SkIRect* bounds, SkBitmap* result) const;
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000289
senorblanco@chromium.org1aa68722013-10-17 19:35:09 +0000290 /**
291 * Returns true if the filter can be expressed a single-pass
292 * GrEffect, used to process this filter on the GPU, or false if
293 * not.
294 *
295 * If effect is non-NULL, a new GrEffect instance is stored
296 * in it. The caller assumes ownership of the stage, and it is up to the
297 * caller to unref it.
298 *
299 * The effect can assume its vertexCoords space maps 1-to-1 with texels
300 * in the texture. "matrix" is a transformation to apply to filter
301 * parameters before they are used in the effect. Note that this function
302 * will be called with (NULL, NULL, SkMatrix::I()) to query for support,
303 * so returning "true" indicates support for all possible matrices.
304 */
bsalomon97b9ab72014-07-08 06:52:35 -0700305 virtual bool asNewEffect(GrEffect** effect,
senorblanco@chromium.org7938bae2013-10-18 20:08:14 +0000306 GrTexture*,
307 const SkMatrix& matrix,
308 const SkIRect& bounds) const;
senorblanco@chromium.org1aa68722013-10-17 19:35:09 +0000309
reed@google.com894aa9a2011-09-23 14:49:49 +0000310private:
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +0000311 typedef SkFlattenable INHERITED;
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000312 int fInputCount;
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000313 SkImageFilter** fInputs;
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +0000314 CropRect fCropRect;
reed@google.com894aa9a2011-09-23 14:49:49 +0000315};
316
317#endif