blob: 188905e5d7313f343db3527e55b12820c44962f7 [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
senorblanco8c874ee2015-03-20 06:38:17 -070011#include "SkFilterQuality.h"
reed@google.com894aa9a2011-09-23 14:49:49 +000012#include "SkFlattenable.h"
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000013#include "SkMatrix.h"
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000014#include "SkRect.h"
reed2c55d7b2015-06-09 08:18:39 -070015#include "SkSurfaceProps.h"
reedb959ec72014-07-17 07:03:09 -070016#include "SkTemplates.h"
reed@google.com894aa9a2011-09-23 14:49:49 +000017
joshualittb0a8a372014-09-23 09:50:21 -070018class GrFragmentProcessor;
tomhudson@google.comd0c1a062012-07-12 17:23:52 +000019class GrTexture;
reed2c55d7b2015-06-09 08:18:39 -070020class SkBaseDevice;
21class SkBitmap;
22class SkColorFilter;
23struct SkIPoint;
reed@google.com15356a62011-11-03 19:29:08 +000024
25/**
reed@google.com15356a62011-11-03 19:29:08 +000026 * Base class for image filters. If one is installed in the paint, then
27 * all drawing occurs as usual, but it is as if the drawing happened into an
28 * offscreen (before the xfermode is applied). This offscreen bitmap will
29 * then be handed to the imagefilter, who in turn creates a new bitmap which
30 * is what will finally be drawn to the device (using the original xfermode).
reed@google.com15356a62011-11-03 19:29:08 +000031 */
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +000032class SK_API SkImageFilter : public SkFlattenable {
reed@google.com894aa9a2011-09-23 14:49:49 +000033public:
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; }
robertphillips157bcd02015-06-26 08:07:39 -070047#ifndef SK_IGNORE_TO_STRING
48 void toString(SkString* str) const;
49#endif
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +000050 private:
51 SkRect fRect;
52 uint32_t fFlags;
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000053 };
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000054
senorblanco55b6d8b2014-07-30 11:26:46 -070055 // This cache maps from (filter's unique ID + CTM + clipBounds + src bitmap generation ID) to
56 // (result, offset).
senorblancobe129b22014-08-08 07:14:35 -070057 class Cache : public SkRefCnt {
senorblanco55b6d8b2014-07-30 11:26:46 -070058 public:
59 struct Key;
senorblancobe129b22014-08-08 07:14:35 -070060 virtual ~Cache() {}
61 static Cache* Create(size_t maxBytes);
62 static Cache* Get();
senorblanco55b6d8b2014-07-30 11:26:46 -070063 virtual bool get(const Key& key, SkBitmap* result, SkIPoint* offset) const = 0;
64 virtual void set(const Key& key, const SkBitmap& result, const SkIPoint& offset) = 0;
reed67ca2a92015-05-20 13:22:58 -070065 virtual void purge() {}
senorblanco55b6d8b2014-07-30 11:26:46 -070066 };
67
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000068 class Context {
69 public:
senorblancobe129b22014-08-08 07:14:35 -070070 Context(const SkMatrix& ctm, const SkIRect& clipBounds, Cache* cache) :
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +000071 fCTM(ctm), fClipBounds(clipBounds), fCache(cache) {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000072 }
73 const SkMatrix& ctm() const { return fCTM; }
74 const SkIRect& clipBounds() const { return fClipBounds; }
senorblancobe129b22014-08-08 07:14:35 -070075 Cache* cache() const { return fCache; }
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000076 private:
77 SkMatrix fCTM;
78 SkIRect fClipBounds;
reed2c55d7b2015-06-09 08:18:39 -070079 Cache* fCache;
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000080 };
81
reed@google.com76dd2772012-01-05 21:15:07 +000082 class Proxy {
83 public:
robertphillipsefbffed2015-06-22 12:06:08 -070084 Proxy(SkBaseDevice* device) : fDevice(device) { }
mtklein2766c002015-06-26 11:45:03 -070085
reed2c55d7b2015-06-09 08:18:39 -070086 SkBaseDevice* createDevice(int width, int height);
robertphillipsefbffed2015-06-22 12:06:08 -070087
88 // Returns true if the proxy handled the filter itself. If this returns
reed@google.com76dd2772012-01-05 21:15:07 +000089 // false then the filter's code will be called.
reed2c55d7b2015-06-09 08:18:39 -070090 bool filterImage(const SkImageFilter*, const SkBitmap& src, const SkImageFilter::Context&,
91 SkBitmap* result, SkIPoint* offset);
robertphillipsefbffed2015-06-22 12:06:08 -070092
reed2c55d7b2015-06-09 08:18:39 -070093 private:
robertphillipsefbffed2015-06-22 12:06:08 -070094 SkBaseDevice* fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +000095 };
mtklein2766c002015-06-26 11:45:03 -070096
reed@google.com894aa9a2011-09-23 14:49:49 +000097
98 /**
99 * Request a new (result) image to be created from the src image.
100 * If the src has no pixels (isNull()) then the request just wants to
101 * receive the config and width/height of the result.
102 *
103 * The matrix is the current matrix on the canvas.
104 *
105 * Offset is the amount to translate the resulting image relative to the
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000106 * src when it is drawn. This is an out-param.
reed@google.com894aa9a2011-09-23 14:49:49 +0000107 *
108 * If the result image cannot be created, return false, in which case both
109 * the result and offset parameters will be ignored by the caller.
110 */
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000111 bool filterImage(Proxy*, const SkBitmap& src, const Context&,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000112 SkBitmap* result, SkIPoint* offset) const;
reed@google.com15356a62011-11-03 19:29:08 +0000113
114 /**
reed@google.com32d25b62011-12-20 16:19:00 +0000115 * Given the src bounds of an image, this returns the bounds of the result
116 * image after the filter has been applied.
117 */
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000118 bool filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const;
reed@google.com32d25b62011-12-20 16:19:00 +0000119
120 /**
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +0000121 * Returns true if the filter can be processed on the GPU. This is most
122 * often used for multi-pass effects, where intermediate results must be
joshualittb0a8a372014-09-23 09:50:21 -0700123 * rendered to textures. For single-pass effects, use asFragmentProcessor().
124 * The default implementation returns asFragmentProcessor(NULL, NULL, SkMatrix::I(),
senorblanco@chromium.org7938bae2013-10-18 20:08:14 +0000125 * SkIRect()).
reed@google.com15356a62011-11-03 19:29:08 +0000126 */
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +0000127 virtual bool canFilterImageGPU() const;
reed@google.com894aa9a2011-09-23 14:49:49 +0000128
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000129 /**
skia.committer@gmail.com32840172013-04-09 07:01:27 +0000130 * Process this image filter on the GPU. This is most often used for
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000131 * multi-pass effects, where intermediate results must be rendered to
joshualittb0a8a372014-09-23 09:50:21 -0700132 * textures. For single-pass effects, use asFragmentProcessor(). src is the
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000133 * source image for processing, as a texture-backed bitmap. result is
134 * the destination bitmap, which should contain a texture-backed pixelref
skia.committer@gmail.comde2e4e82013-07-11 07:01:01 +0000135 * on success. offset is the amount to translate the resulting image
commit-bot@chromium.org7b320702013-07-10 21:22:18 +0000136 * relative to the src when it is drawn. The default implementation does
joshualittb0a8a372014-09-23 09:50:21 -0700137 * single-pass processing using asFragmentProcessor().
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000138 */
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000139 virtual bool filterImageGPU(Proxy*, const SkBitmap& src, const Context&,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000140 SkBitmap* result, SkIPoint* offset) const;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000141
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000142 /**
sugoi@google.coma1c511b2013-02-21 15:02:28 +0000143 * Returns whether this image filter is a color filter and puts the color filter into the
sugoi@google.com4b6d4322013-02-21 20:26:50 +0000144 * "filterPtr" parameter if it can. Does nothing otherwise.
145 * If this returns false, then the filterPtr is unchanged.
146 * If this returns true, then if filterPtr is not null, it must be set to a ref'd colorfitler
147 * (i.e. it may not be set to NULL).
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000148 */
reedcedc36f2015-03-08 04:42:52 -0700149 bool isColorFilterNode(SkColorFilter** filterPtr) const {
150 return this->onIsColorFilterNode(filterPtr);
151 }
152
153 // DEPRECATED : use isColorFilterNode() instead
154 bool asColorFilter(SkColorFilter** filterPtr) const {
155 return this->isColorFilterNode(filterPtr);
156 }
157
158 /**
159 * Returns true (and optionally returns a ref'd filter) if this imagefilter can be completely
160 * replaced by the returned colorfilter. i.e. the two effects will affect drawing in the
161 * same way.
162 */
163 bool asAColorFilter(SkColorFilter** filterPtr) const {
164 return this->countInputs() > 0 &&
165 NULL == this->getInput(0) &&
166 this->isColorFilterNode(filterPtr);
167 }
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000168
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000169 /**
170 * Returns the number of inputs this filter will accept (some inputs can
171 * be NULL).
172 */
173 int countInputs() const { return fInputCount; }
174
175 /**
176 * Returns the input filter at a given index, or NULL if no input is
177 * connected. The indices used are filter-specific.
178 */
179 SkImageFilter* getInput(int i) const {
180 SkASSERT(i < fInputCount);
181 return fInputs[i];
182 }
183
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000184 /**
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +0000185 * Returns whether any edges of the crop rect have been set. The crop
186 * rect is set at construction time, and determines which pixels from the
187 * input image will be processed. The size of the crop rect should be
188 * used as the size of the destination image. The origin of this rect
189 * should be used to offset access to the input images, and should also
190 * be added to the "offset" parameter in onFilterImage and
191 * filterImageGPU(). (The latter ensures that the resulting buffer is
192 * drawn in the correct location.)
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000193 */
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +0000194 bool cropRectIsSet() const { return fCropRect.flags() != 0x0; }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000195
reedb3fe1b82015-06-23 08:29:20 -0700196 CropRect getCropRect() const { return fCropRect; }
197
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +0000198 // Default impl returns union of all input bounds.
199 virtual void computeFastBounds(const SkRect&, SkRect*) const;
200
senorblanco8c874ee2015-03-20 06:38:17 -0700201 /**
202 * Create an SkMatrixImageFilter, which transforms its input by the given matrix.
203 */
204 static SkImageFilter* CreateMatrixFilter(const SkMatrix& matrix,
205 SkFilterQuality,
206 SkImageFilter* input = NULL);
207
kkinnunen45095172014-07-29 06:12:49 -0700208#if SK_SUPPORT_GPU
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000209 /**
210 * Wrap the given texture in a texture-backed SkBitmap.
211 */
212 static void WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result);
213
214 /**
215 * Recursively evaluate this filter on the GPU. If the filter has no GPU
216 * implementation, it will be processed in software and uploaded to the GPU.
217 */
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000218 bool getInputResultGPU(SkImageFilter::Proxy* proxy, const SkBitmap& src, const Context&,
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000219 SkBitmap* result, SkIPoint* offset) const;
220#endif
221
robertphillipsf3f5bad2014-12-19 13:49:15 -0800222 SK_TO_STRING_PUREVIRT()
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +0000223 SK_DEFINE_FLATTENABLE_TYPE(SkImageFilter)
224
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000225protected:
reedb959ec72014-07-17 07:03:09 -0700226 class Common {
227 public:
228 Common() {}
229 ~Common();
230
reed9fa60da2014-08-21 07:59:51 -0700231 /**
232 * Attempt to unflatten the cropRect and the expected number of input filters.
233 * If any number of input filters is valid, pass -1.
234 * If this fails (i.e. corrupt buffer or contents) then return false and common will
235 * be left uninitialized.
236 * If this returns true, then inputCount() is the number of found input filters, each
237 * of which may be NULL or a valid imagefilter.
238 */
239 bool unflatten(SkReadBuffer&, int expectedInputs);
reedb959ec72014-07-17 07:03:09 -0700240
reed9fa60da2014-08-21 07:59:51 -0700241 const CropRect& cropRect() const { return fCropRect; }
reedb959ec72014-07-17 07:03:09 -0700242 int inputCount() const { return fInputs.count(); }
243 SkImageFilter** inputs() const { return fInputs.get(); }
244
reed9fa60da2014-08-21 07:59:51 -0700245 SkImageFilter* getInput(int index) const { return fInputs[index]; }
246
reedb959ec72014-07-17 07:03:09 -0700247 // If the caller wants a copy of the inputs, call this and it will transfer ownership
248 // of the unflattened input filters to the caller. This is just a short-cut for copying
249 // the inputs, calling ref() on each, and then waiting for Common's destructor to call
250 // unref() on each.
251 void detachInputs(SkImageFilter** inputs);
252
253 private:
254 CropRect fCropRect;
255 // most filters accept at most 2 input-filters
256 SkAutoSTArray<2, SkImageFilter*> fInputs;
257
258 void allocInputs(int count);
259 };
260
senorblanco24e06d52015-03-18 12:11:33 -0700261 SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect = NULL);
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000262
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000263 virtual ~SkImageFilter();
264
commit-bot@chromium.orgc84728d2013-12-04 20:07:47 +0000265 /**
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000266 * Constructs a new SkImageFilter read from an SkReadBuffer object.
commit-bot@chromium.orgc84728d2013-12-04 20:07:47 +0000267 *
268 * @param inputCount The exact number of inputs expected for this SkImageFilter object.
269 * -1 can be used if the filter accepts any number of inputs.
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000270 * @param rb SkReadBuffer object from which the SkImageFilter is read.
commit-bot@chromium.orgc84728d2013-12-04 20:07:47 +0000271 */
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000272 explicit SkImageFilter(int inputCount, SkReadBuffer& rb);
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000273
mtklein36352bf2015-03-25 18:17:31 -0700274 void flatten(SkWriteBuffer&) const override;
reed@google.com32d25b62011-12-20 16:19:00 +0000275
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000276 /**
277 * This is the virtual which should be overridden by the derived class
278 * to perform image filtering.
279 *
280 * src is the original primitive bitmap. If the filter has a connected
281 * input, it should recurse on that input and use that in place of src.
282 *
283 * The matrix is the current matrix on the canvas.
284 *
285 * Offset is the amount to translate the resulting image relative to the
286 * src when it is drawn. This is an out-param.
287 *
288 * If the result image cannot be created, this should false, in which
289 * case both the result and offset parameters will be ignored by the
290 * caller.
291 */
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000292 virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000293 SkBitmap* result, SkIPoint* offset) const;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000294 // Given the bounds of the destination rect to be filled in device
295 // coordinates (first parameter), and the CTM, compute (conservatively)
296 // which rect of the source image would be required (third parameter).
297 // Used for clipping and temp-buffer allocations, so the result need not
298 // be exact, but should never be smaller than the real answer. The default
299 // implementation recursively unions all input bounds, or returns false if
300 // no inputs.
301 virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const;
reed@google.com894aa9a2011-09-23 14:49:49 +0000302
reedcedc36f2015-03-08 04:42:52 -0700303 /**
304 * Return true (and return a ref'd colorfilter) if this node in the DAG is just a
305 * colorfilter w/o CropRect constraints.
306 */
307 virtual bool onIsColorFilterNode(SkColorFilter** /*filterPtr*/) const {
308 return false;
309 }
310
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000311 /** Computes source bounds as the src bitmap bounds offset by srcOffset.
312 * Apply the transformed crop rect to the bounds if any of the
313 * corresponding edge flags are set. Intersects the result against the
314 * context's clipBounds, and returns the result in "bounds". If there is
315 * no intersection, returns false and leaves "bounds" unchanged.
316 */
317 bool applyCropRect(const Context&, const SkBitmap& src, const SkIPoint& srcOffset,
318 SkIRect* bounds) const;
319
320 /** Same as the above call, except that if the resulting crop rect is not
321 * entirely contained by the source bitmap's bounds, it creates a new
322 * bitmap in "result" and pads the edges with transparent black. In that
323 * case, the srcOffset is modified to be the same as the bounds, since no
324 * further adjustment is needed by the caller. This version should only
325 * be used by filters which are not capable of processing a smaller
326 * source bitmap into a larger destination.
327 */
328 bool applyCropRect(const Context&, Proxy* proxy, const SkBitmap& src, SkIPoint* srcOffset,
329 SkIRect* bounds, SkBitmap* result) const;
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000330
senorblanco@chromium.org1aa68722013-10-17 19:35:09 +0000331 /**
332 * Returns true if the filter can be expressed a single-pass
joshualittb0a8a372014-09-23 09:50:21 -0700333 * GrProcessor, used to process this filter on the GPU, or false if
senorblanco@chromium.org1aa68722013-10-17 19:35:09 +0000334 * not.
335 *
joshualittb0a8a372014-09-23 09:50:21 -0700336 * If effect is non-NULL, a new GrProcessor instance is stored
senorblanco@chromium.org1aa68722013-10-17 19:35:09 +0000337 * in it. The caller assumes ownership of the stage, and it is up to the
338 * caller to unref it.
339 *
340 * The effect can assume its vertexCoords space maps 1-to-1 with texels
341 * in the texture. "matrix" is a transformation to apply to filter
342 * parameters before they are used in the effect. Note that this function
343 * will be called with (NULL, NULL, SkMatrix::I()) to query for support,
344 * so returning "true" indicates support for all possible matrices.
345 */
joshualittb0a8a372014-09-23 09:50:21 -0700346 virtual bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&,
347 const SkIRect& bounds) const;
senorblanco@chromium.org1aa68722013-10-17 19:35:09 +0000348
reed@google.com894aa9a2011-09-23 14:49:49 +0000349private:
reed67ca2a92015-05-20 13:22:58 -0700350 friend class SkGraphics;
351 static void PurgeCache();
352
senorblanco55b6d8b2014-07-30 11:26:46 -0700353 bool usesSrcInput() const { return fUsesSrcInput; }
354
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +0000355 typedef SkFlattenable INHERITED;
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000356 int fInputCount;
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000357 SkImageFilter** fInputs;
senorblanco55b6d8b2014-07-30 11:26:46 -0700358 bool fUsesSrcInput;
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +0000359 CropRect fCropRect;
senorblanco55b6d8b2014-07-30 11:26:46 -0700360 uint32_t fUniqueID; // Globally unique
reed@google.com894aa9a2011-09-23 14:49:49 +0000361};
362
reed9fa60da2014-08-21 07:59:51 -0700363/**
364 * Helper to unflatten the common data, and return NULL if we fail.
365 */
366#define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount) \
367 Common localVar; \
368 do { \
369 if (!localVar.unflatten(buffer, expectedCount)) { \
370 return NULL; \
371 } \
372 } while (0)
373
reed@google.com894aa9a2011-09-23 14:49:49 +0000374#endif