gecko/layout/svg/nsSVGFilterInstance.h
2014-01-08 10:30:03 +01:00

342 lines
13 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __NS_SVGFILTERINSTANCE_H__
#define __NS_SVGFILTERINSTANCE_H__
#include "gfxMatrix.h"
#include "gfxPoint.h"
#include "gfxRect.h"
#include "nsCOMPtr.h"
#include "nsHashKeys.h"
#include "nsPoint.h"
#include "nsRect.h"
#include "nsSize.h"
#include "nsSVGFilters.h"
#include "nsSVGNumber2.h"
#include "nsSVGNumberPair.h"
#include "nsTArray.h"
#include "nsIFrame.h"
#include "mozilla/gfx/2D.h"
class gfxASurface;
class gfxImageSurface;
class nsIFrame;
class nsSVGFilterPaintCallback;
namespace mozilla {
namespace dom {
class SVGFilterElement;
}
}
/**
* This class performs all filter processing.
*
* We build a graph of the filter image data flow, essentially
* converting the filter graph to SSA. This lets us easily propagate
* analysis data (such as bounding-boxes) over the filter primitive graph.
*
* Definition of "filter space": filter space is a coordinate system that is
* aligned with the user space of the filtered element, with its origin located
* at the top left of the filter region (as specified by our ctor's
* aFilterRegion, and returned by our GetFilterRegion, specifically), and with
* one unit equal in size to one pixel of the offscreen surface into which the
* filter output would/will be painted.
*
* The definition of "filter region" can be found here:
* http://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion
*/
class nsSVGFilterInstance
{
typedef mozilla::gfx::Point3D Point3D;
typedef mozilla::gfx::IntRect IntRect;
typedef mozilla::gfx::SourceSurface SourceSurface;
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
public:
/**
* @param aTargetFrame The frame of the filtered element under consideration.
* @param aPaintCallback [optional] The callback that Render() should use to
* paint. Only required if you will call Render().
* @param aFilterElement The filter element referenced by aTargetFrame's
* element.
* @param aTargetBBox The filtered element's bbox, in the filtered element's
* user space.
* @param aFilterRegion The "filter region", in the filtered element's user
* space. The caller must have already expanded the region out so that its
* edges coincide with pixel boundaries in the offscreen surface that
* would/will be created to paint the filter output.
* @param aFilterSpaceSize The size of the user specified "filter region",
* in filter space units.
* @param aFilterSpaceToDeviceSpaceTransform The transform from filter
* space to outer-<svg> device space.
* @param aTargetBounds The pre-filter paint bounds of the filtered element,
* in filter space.
* @param aPostFilterDirtyRect [optional] The bounds of the post-filter area
* that has to be repainted, in filter space. Only required if you will
* call ComputeSourceNeededRect() or Render().
* @param aPreFilterDirtyRect [optional] The bounds of the pre-filter area of
* the filtered element that changed, in filter space. Only required if you
* will call ComputePostFilterDirtyRect().
* @param aPrimitiveUnits The value from the 'primitiveUnits' attribute.
*/
nsSVGFilterInstance(nsIFrame *aTargetFrame,
nsSVGFilterPaintCallback *aPaintCallback,
const mozilla::dom::SVGFilterElement *aFilterElement,
const gfxRect &aTargetBBox,
const gfxRect& aFilterRegion,
const nsIntSize& aFilterSpaceSize,
const gfxMatrix &aFilterSpaceToDeviceSpaceTransform,
const gfxMatrix &aFilterSpaceToFrameSpaceInCSSPxTransform,
const nsIntRect& aTargetBounds,
const nsIntRect& aPostFilterDirtyRect,
const nsIntRect& aPreFilterDirtyRect,
uint16_t aPrimitiveUnits,
nsIFrame* aTransformRoot) :
mTargetFrame(aTargetFrame),
mPaintCallback(aPaintCallback),
mFilterElement(aFilterElement),
mTargetBBox(aTargetBBox),
mFilterSpaceToDeviceSpaceTransform(aFilterSpaceToDeviceSpaceTransform),
mFilterSpaceToFrameSpaceInCSSPxTransform(aFilterSpaceToFrameSpaceInCSSPxTransform),
mFilterRegion(aFilterRegion),
mFilterSpaceBounds(nsIntPoint(0, 0), aFilterSpaceSize),
mTargetBounds(aTargetBounds),
mPostFilterDirtyRect(aPostFilterDirtyRect),
mPreFilterDirtyRect(aPreFilterDirtyRect),
mPrimitiveUnits(aPrimitiveUnits),
mTransformRoot(aTransformRoot) {
}
/**
* Returns the user specified "filter region", in the filtered element's user
* space, after it has been adjusted out (if necessary) so that its edges
* coincide with pixel boundaries of the offscreen surface into which the
* filtered output would/will be painted.
*/
gfxRect GetFilterRegion() const { return mFilterRegion; }
/**
* Returns the size of the user specified "filter region", in filter space.
* The size will be {filterRes.x by filterRes.y}, whether the user specified
* the filter's filterRes attribute explicitly, or the implementation chose
* the filterRes values. (The top-left of the filter region is the origin of
* filter space, which is why this method returns an nsIntSize and not an
* nsIntRect.)
*/
uint32_t GetFilterResX() const { return mFilterSpaceBounds.width; }
uint32_t GetFilterResY() const { return mFilterSpaceBounds.height; }
/**
* Draws the filter output into aContext. The area that
* needs to be painted must have been specified before calling this method
* by passing it as the aPostFilterDirtyRect argument to the
* nsSVGFilterInstance constructor.
*/
nsresult Render(gfxContext* aContext);
/**
* Sets the aPostFilterDirtyRect outparam to the post-filter bounds in filter
* space of the area that would be dirtied by mTargetFrame when a given
* pre-filter area of mTargetFrame is dirtied. The pre-filter area must have
* been specified before calling this method by passing it as the
* aPreFilterDirtyRect argument to the nsSVGFilterInstance constructor.
*/
nsresult ComputePostFilterDirtyRect(nsIntRect* aPostFilterDirtyRect);
/**
* Sets the aPostFilterExtents outparam to the post-filter bounds in filter
* space for the whole filter output. This is not necessarily equivalent to
* the area that would be dirtied in the result when the entire pre-filter
* area is dirtied, because some filter primitives can generate output
* without any input.
*/
nsresult ComputePostFilterExtents(nsIntRect* aPostFilterExtents);
/**
* Sets the aDirty outparam to the pre-filter bounds in filter space of the
* area of mTargetFrame that is needed in order to paint the filtered output
* for a given post-filter dirtied area. The post-filter area must have been
* specified before calling this method by passing it as the aPostFilterDirtyRect
* argument to the nsSVGFilterInstance constructor.
*/
nsresult ComputeSourceNeededRect(nsIntRect* aDirty);
float GetPrimitiveNumber(uint8_t aCtxType, const nsSVGNumber2 *aNumber) const
{
return GetPrimitiveNumber(aCtxType, aNumber->GetAnimValue());
}
float GetPrimitiveNumber(uint8_t aCtxType, const nsSVGNumberPair *aNumberPair,
nsSVGNumberPair::PairIndex aIndex) const
{
return GetPrimitiveNumber(aCtxType, aNumberPair->GetAnimValue(aIndex));
}
/**
* Converts a userSpaceOnUse/objectBoundingBoxUnits unitless point
* into filter space, depending on the value of mPrimitiveUnits. (For
* objectBoundingBoxUnits, the bounding box offset is applied to the point.)
*/
Point3D ConvertLocation(const Point3D& aPoint) const;
/**
* Returns the transform from the filtered element's user space to filter
* space. This will be a simple translation and/or scale.
*/
gfxMatrix GetUserSpaceToFilterSpaceTransform() const;
/**
* Returns the transform from filter space to outer-<svg> device space.
*/
gfxMatrix GetFilterSpaceToDeviceSpaceTransform() const {
return mFilterSpaceToDeviceSpaceTransform;
}
gfxPoint FilterSpaceToUserSpace(const gfxPoint& aPt) const;
/**
* Returns the transform from filter space to frame space, in CSS px. This
* transform does not transform to frame space in its normal app units, since
* app units are ints, requiring appropriate rounding which can't be done by
* a transform matrix. Callers have to do that themselves as appropriate for
* their needs.
*/
gfxMatrix GetFilterSpaceToFrameSpaceInCSSPxTransform() const {
return mFilterSpaceToFrameSpaceInCSSPxTransform;
}
int32_t AppUnitsPerCSSPixel() const {
return mTargetFrame->PresContext()->AppUnitsPerCSSPixel();
}
private:
struct SourceInfo {
// Specifies which parts of the source need to be rendered.
// Set by ComputeNeededBoxes().
nsIntRect mNeededBounds;
// The surface that contains the input rendering.
// Set by BuildSourceImage / BuildSourcePaint.
mozilla::RefPtr<SourceSurface> mSourceSurface;
// The position and size of mSourceSurface in filter space.
// Set by BuildSourceImage / BuildSourcePaint.
IntRect mSurfaceRect;
};
/**
* Creates a SourceSurface for either the FillPaint or StrokePaint graph
* nodes
*/
nsresult BuildSourcePaint(SourceInfo *aPrimitive,
gfxASurface* aTargetSurface,
DrawTarget* aTargetDT);
/**
* Creates a SourceSurface for either the FillPaint and StrokePaint graph
* nodes, fills its contents and assigns it to mFillPaint.mSourceSurface and
* mStrokePaint.mSourceSurface respectively.
*/
nsresult BuildSourcePaints(gfxASurface* aTargetSurface,
DrawTarget* aTargetDT);
/**
* Creates the SourceSurface for the SourceGraphic graph node, paints its
* contents, and assigns it to mSourceGraphic.mSourceSurface.
*/
nsresult BuildSourceImage(gfxASurface* aTargetSurface,
DrawTarget* aTargetDT);
/**
* Build the list of FilterPrimitiveDescriptions that describes the filter's
* filter primitives and their connections. This populates
* mPrimitiveDescriptions and mInputImages.
*/
nsresult BuildPrimitives();
/**
* Computes the filter space bounds of the areas that we actually *need* from
* the filter sources, based on the value of mPostFilterDirtyRect.
* This sets mNeededBounds on the corresponding SourceInfo structs.
*/
void ComputeNeededBoxes();
/**
* Computes the filter primitive subregion for the given primitive.
*/
IntRect ComputeFilterPrimitiveSubregion(nsSVGFE* aFilterElement,
const nsTArray<int32_t>& aInputIndices);
/**
* Takes the input indices of a filter primitive and returns for each input
* whether the input's output is tainted.
*/
void GetInputsAreTainted(const nsTArray<int32_t>& aInputIndices,
nsTArray<bool>& aOutInputsAreTainted);
/**
* Scales a numeric filter primitive length in the X, Y or "XY" directions
* into a length in filter space (no offset is applied).
*/
float GetPrimitiveNumber(uint8_t aCtxType, float aValue) const;
gfxRect UserSpaceToFilterSpace(const gfxRect& aUserSpace) const;
/**
* The frame for the element that is currently being filtered.
*/
nsIFrame* mTargetFrame;
nsSVGFilterPaintCallback* mPaintCallback;
const mozilla::dom::SVGFilterElement* mFilterElement;
/**
* The SVG bbox of the element that is being filtered, in user space.
*/
gfxRect mTargetBBox;
gfxMatrix mFilterSpaceToDeviceSpaceTransform;
gfxMatrix mFilterSpaceToFrameSpaceInCSSPxTransform;
gfxRect mFilterRegion;
nsIntRect mFilterSpaceBounds;
/**
* Pre-filter paint bounds of the element that is being filtered, in filter
* space.
*/
nsIntRect mTargetBounds;
/**
* If set, this is the filter space bounds of the outer-<svg> device space
* bounds of the dirty area that needs to be repainted. (As bounds-of-bounds,
* this may be a fair bit bigger than we actually need, unfortunately.)
*/
nsIntRect mPostFilterDirtyRect;
/**
* If set, this is the filter space bounds of the outer-<svg> device bounds
* of the pre-filter area of the filtered element that changed. (As
* bounds-of-bounds, this may be a fair bit bigger than we actually need,
* unfortunately.)
*/
nsIntRect mPreFilterDirtyRect;
/**
* The 'primitiveUnits' attribute value (objectBoundingBox or userSpaceOnUse).
*/
uint16_t mPrimitiveUnits;
SourceInfo mSourceGraphic;
SourceInfo mFillPaint;
SourceInfo mStrokePaint;
nsIFrame* mTransformRoot;
nsTArray<mozilla::RefPtr<SourceSurface>> mInputImages;
nsTArray<FilterPrimitiveDescription> mPrimitiveDescriptions;
};
#endif