blob: dab46ae716b69ffb76f9adcf726342289ecda4cb [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkImageFilter_DEFINED
#define SkImageFilter_DEFINED
#include "SkFilterQuality.h"
#include "SkFlattenable.h"
#include "SkMatrix.h"
#include "SkRect.h"
#include "SkTemplates.h"
class SkBitmap;
class SkColorFilter;
class SkBaseDevice;
class SkSurfaceProps;
struct SkIPoint;
class GrFragmentProcessor;
class GrTexture;
/**
* Base class for image filters. If one is installed in the paint, then
* all drawing occurs as usual, but it is as if the drawing happened into an
* offscreen (before the xfermode is applied). This offscreen bitmap will
* then be handed to the imagefilter, who in turn creates a new bitmap which
* is what will finally be drawn to the device (using the original xfermode).
*/
class SK_API SkImageFilter : public SkFlattenable {
public:
SK_DECLARE_INST_COUNT(SkImageFilter)
class CropRect {
public:
enum CropEdge {
kHasLeft_CropEdge = 0x01,
kHasTop_CropEdge = 0x02,
kHasRight_CropEdge = 0x04,
kHasBottom_CropEdge = 0x08,
kHasAll_CropEdge = 0x0F,
};
CropRect() {}
explicit CropRect(const SkRect& rect, uint32_t flags = kHasAll_CropEdge) : fRect(rect), fFlags(flags) {}
uint32_t flags() const { return fFlags; }
const SkRect& rect() const { return fRect; }
private:
SkRect fRect;
uint32_t fFlags;
};
// This cache maps from (filter's unique ID + CTM + clipBounds + src bitmap generation ID) to
// (result, offset).
class Cache : public SkRefCnt {
public:
struct Key;
virtual ~Cache() {}
static Cache* Create(size_t maxBytes);
static Cache* Get();
virtual bool get(const Key& key, SkBitmap* result, SkIPoint* offset) const = 0;
virtual void set(const Key& key, const SkBitmap& result, const SkIPoint& offset) = 0;
};
class Context {
public:
Context(const SkMatrix& ctm, const SkIRect& clipBounds, Cache* cache) :
fCTM(ctm), fClipBounds(clipBounds), fCache(cache) {
}
const SkMatrix& ctm() const { return fCTM; }
const SkIRect& clipBounds() const { return fClipBounds; }
Cache* cache() const { return fCache; }
private:
SkMatrix fCTM;
SkIRect fClipBounds;
Cache* fCache;
};
class Proxy {
public:
virtual ~Proxy() {};
virtual SkBaseDevice* createDevice(int width, int height) = 0;
// returns true if the proxy can handle this filter natively
virtual bool canHandleImageFilter(const SkImageFilter*) = 0;
// returns true if the proxy handled the filter itself. if this returns
// false then the filter's code will be called.
virtual bool filterImage(const SkImageFilter*, const SkBitmap& src,
const Context&,
SkBitmap* result, SkIPoint* offset) = 0;
virtual const SkSurfaceProps* surfaceProps() const = 0;
};
/**
* Request a new (result) image to be created from the src image.
* If the src has no pixels (isNull()) then the request just wants to
* receive the config and width/height of the result.
*
* The matrix is the current matrix on the canvas.
*
* Offset is the amount to translate the resulting image relative to the
* src when it is drawn. This is an out-param.
*
* If the result image cannot be created, return false, in which case both
* the result and offset parameters will be ignored by the caller.
*/
bool filterImage(Proxy*, const SkBitmap& src, const Context&,
SkBitmap* result, SkIPoint* offset) const;
/**
* Given the src bounds of an image, this returns the bounds of the result
* image after the filter has been applied.
*/
bool filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const;
/**
* Returns true if the filter can be processed on the GPU. This is most
* often used for multi-pass effects, where intermediate results must be
* rendered to textures. For single-pass effects, use asFragmentProcessor().
* The default implementation returns asFragmentProcessor(NULL, NULL, SkMatrix::I(),
* SkIRect()).
*/
virtual bool canFilterImageGPU() const;
/**
* Process this image filter on the GPU. This is most often used for
* multi-pass effects, where intermediate results must be rendered to
* textures. For single-pass effects, use asFragmentProcessor(). src is the
* source image for processing, as a texture-backed bitmap. result is
* the destination bitmap, which should contain a texture-backed pixelref
* on success. offset is the amount to translate the resulting image
* relative to the src when it is drawn. The default implementation does
* single-pass processing using asFragmentProcessor().
*/
virtual bool filterImageGPU(Proxy*, const SkBitmap& src, const Context&,
SkBitmap* result, SkIPoint* offset) const;
/**
* Returns whether this image filter is a color filter and puts the color filter into the
* "filterPtr" parameter if it can. Does nothing otherwise.
* If this returns false, then the filterPtr is unchanged.
* If this returns true, then if filterPtr is not null, it must be set to a ref'd colorfitler
* (i.e. it may not be set to NULL).
*/
bool isColorFilterNode(SkColorFilter** filterPtr) const {
return this->onIsColorFilterNode(filterPtr);
}
// DEPRECATED : use isColorFilterNode() instead
bool asColorFilter(SkColorFilter** filterPtr) const {
return this->isColorFilterNode(filterPtr);
}
/**
* Returns true (and optionally returns a ref'd filter) if this imagefilter can be completely
* replaced by the returned colorfilter. i.e. the two effects will affect drawing in the
* same way.
*/
bool asAColorFilter(SkColorFilter** filterPtr) const {
return this->countInputs() > 0 &&
NULL == this->getInput(0) &&
this->isColorFilterNode(filterPtr);
}
/**
* Returns the number of inputs this filter will accept (some inputs can
* be NULL).
*/
int countInputs() const { return fInputCount; }
/**
* Returns the input filter at a given index, or NULL if no input is
* connected. The indices used are filter-specific.
*/
SkImageFilter* getInput(int i) const {
SkASSERT(i < fInputCount);
return fInputs[i];
}
/**
* Returns whether any edges of the crop rect have been set. The crop
* rect is set at construction time, and determines which pixels from the
* input image will be processed. The size of the crop rect should be
* used as the size of the destination image. The origin of this rect
* should be used to offset access to the input images, and should also
* be added to the "offset" parameter in onFilterImage and
* filterImageGPU(). (The latter ensures that the resulting buffer is
* drawn in the correct location.)
*/
bool cropRectIsSet() const { return fCropRect.flags() != 0x0; }
// Default impl returns union of all input bounds.
virtual void computeFastBounds(const SkRect&, SkRect*) const;
/**
* Create an SkMatrixImageFilter, which transforms its input by the given matrix.
*/
static SkImageFilter* CreateMatrixFilter(const SkMatrix& matrix,
SkFilterQuality,
SkImageFilter* input = NULL);
#if SK_SUPPORT_GPU
/**
* Wrap the given texture in a texture-backed SkBitmap.
*/
static void WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result);
/**
* Recursively evaluate this filter on the GPU. If the filter has no GPU
* implementation, it will be processed in software and uploaded to the GPU.
*/
bool getInputResultGPU(SkImageFilter::Proxy* proxy, const SkBitmap& src, const Context&,
SkBitmap* result, SkIPoint* offset) const;
#endif
SK_TO_STRING_PUREVIRT()
SK_DEFINE_FLATTENABLE_TYPE(SkImageFilter)
protected:
class Common {
public:
Common() {}
~Common();
/**
* Attempt to unflatten the cropRect and the expected number of input filters.
* If any number of input filters is valid, pass -1.
* If this fails (i.e. corrupt buffer or contents) then return false and common will
* be left uninitialized.
* If this returns true, then inputCount() is the number of found input filters, each
* of which may be NULL or a valid imagefilter.
*/
bool unflatten(SkReadBuffer&, int expectedInputs);
const CropRect& cropRect() const { return fCropRect; }
int inputCount() const { return fInputs.count(); }
SkImageFilter** inputs() const { return fInputs.get(); }
SkImageFilter* getInput(int index) const { return fInputs[index]; }
// If the caller wants a copy of the inputs, call this and it will transfer ownership
// of the unflattened input filters to the caller. This is just a short-cut for copying
// the inputs, calling ref() on each, and then waiting for Common's destructor to call
// unref() on each.
void detachInputs(SkImageFilter** inputs);
private:
CropRect fCropRect;
// most filters accept at most 2 input-filters
SkAutoSTArray<2, SkImageFilter*> fInputs;
void allocInputs(int count);
};
SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect = NULL);
virtual ~SkImageFilter();
/**
* Constructs a new SkImageFilter read from an SkReadBuffer object.
*
* @param inputCount The exact number of inputs expected for this SkImageFilter object.
* -1 can be used if the filter accepts any number of inputs.
* @param rb SkReadBuffer object from which the SkImageFilter is read.
*/
explicit SkImageFilter(int inputCount, SkReadBuffer& rb);
void flatten(SkWriteBuffer&) const override;
/**
* This is the virtual which should be overridden by the derived class
* to perform image filtering.
*
* src is the original primitive bitmap. If the filter has a connected
* input, it should recurse on that input and use that in place of src.
*
* The matrix is the current matrix on the canvas.
*
* Offset is the amount to translate the resulting image relative to the
* src when it is drawn. This is an out-param.
*
* If the result image cannot be created, this should false, in which
* case both the result and offset parameters will be ignored by the
* caller.
*/
virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
SkBitmap* result, SkIPoint* offset) const;
// Given the bounds of the destination rect to be filled in device
// coordinates (first parameter), and the CTM, compute (conservatively)
// which rect of the source image would be required (third parameter).
// Used for clipping and temp-buffer allocations, so the result need not
// be exact, but should never be smaller than the real answer. The default
// implementation recursively unions all input bounds, or returns false if
// no inputs.
virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const;
/**
* Return true (and return a ref'd colorfilter) if this node in the DAG is just a
* colorfilter w/o CropRect constraints.
*/
virtual bool onIsColorFilterNode(SkColorFilter** /*filterPtr*/) const {
return false;
}
/** Computes source bounds as the src bitmap bounds offset by srcOffset.
* Apply the transformed crop rect to the bounds if any of the
* corresponding edge flags are set. Intersects the result against the
* context's clipBounds, and returns the result in "bounds". If there is
* no intersection, returns false and leaves "bounds" unchanged.
*/
bool applyCropRect(const Context&, const SkBitmap& src, const SkIPoint& srcOffset,
SkIRect* bounds) const;
/** Same as the above call, except that if the resulting crop rect is not
* entirely contained by the source bitmap's bounds, it creates a new
* bitmap in "result" and pads the edges with transparent black. In that
* case, the srcOffset is modified to be the same as the bounds, since no
* further adjustment is needed by the caller. This version should only
* be used by filters which are not capable of processing a smaller
* source bitmap into a larger destination.
*/
bool applyCropRect(const Context&, Proxy* proxy, const SkBitmap& src, SkIPoint* srcOffset,
SkIRect* bounds, SkBitmap* result) const;
/**
* Returns true if the filter can be expressed a single-pass
* GrProcessor, used to process this filter on the GPU, or false if
* not.
*
* If effect is non-NULL, a new GrProcessor instance is stored
* in it. The caller assumes ownership of the stage, and it is up to the
* caller to unref it.
*
* The effect can assume its vertexCoords space maps 1-to-1 with texels
* in the texture. "matrix" is a transformation to apply to filter
* parameters before they are used in the effect. Note that this function
* will be called with (NULL, NULL, SkMatrix::I()) to query for support,
* so returning "true" indicates support for all possible matrices.
*/
virtual bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&,
const SkIRect& bounds) const;
private:
bool usesSrcInput() const { return fUsesSrcInput; }
typedef SkFlattenable INHERITED;
int fInputCount;
SkImageFilter** fInputs;
bool fUsesSrcInput;
CropRect fCropRect;
uint32_t fUniqueID; // Globally unique
};
/**
* Helper to unflatten the common data, and return NULL if we fail.
*/
#define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount) \
Common localVar; \
do { \
if (!localVar.unflatten(buffer, expectedCount)) { \
return NULL; \
} \
} while (0)
#endif