summaryrefslogtreecommitdiff
path: root/gfx/2d
diff options
context:
space:
mode:
authorPale Moon <git-repo@palemoon.org>2016-09-01 13:39:08 +0200
committerPale Moon <git-repo@palemoon.org>2016-09-01 13:39:08 +0200
commit3d8ce1a11a7347cc94a937719c4bc8df46fb8d14 (patch)
tree8c26ca375a6312751c00a27e1653fb6f189f0463 /gfx/2d
parente449bdb1ec3a82f204bffdd9c3c54069d086eee3 (diff)
downloadpalemoon-gre-3d8ce1a11a7347cc94a937719c4bc8df46fb8d14.tar.gz
Base import of Tycho code (warning: huge commit)
Diffstat (limited to 'gfx/2d')
-rw-r--r--gfx/2d/2D.h811
-rw-r--r--gfx/2d/BaseCoord.h110
-rw-r--r--gfx/2d/BaseMargin.h73
-rw-r--r--gfx/2d/BasePoint.h38
-rw-r--r--gfx/2d/BasePoint3D.h6
-rw-r--r--gfx/2d/BasePoint4D.h8
-rw-r--r--gfx/2d/BaseRect.h158
-rw-r--r--gfx/2d/BaseSize.h10
-rw-r--r--gfx/2d/Blur.cpp145
-rw-r--r--gfx/2d/Blur.h14
-rw-r--r--gfx/2d/BlurNEON.cpp288
-rw-r--r--gfx/2d/BlurSSE2.cpp86
-rw-r--r--gfx/2d/BorrowedContext.h134
-rw-r--r--gfx/2d/Coord.h142
-rw-r--r--gfx/2d/DataSourceSurface.cpp (renamed from gfx/2d/ScaledFontFreetype.h)17
-rw-r--r--gfx/2d/DataSourceSurfaceWrapper.h39
-rw-r--r--gfx/2d/DataSurfaceHelpers.cpp285
-rw-r--r--gfx/2d/DataSurfaceHelpers.h106
-rw-r--r--gfx/2d/DrawCommand.h504
-rw-r--r--gfx/2d/DrawEventRecorder.cpp2
-rw-r--r--gfx/2d/DrawEventRecorder.h10
-rw-r--r--gfx/2d/DrawTarget.cpp39
-rw-r--r--gfx/2d/DrawTargetCG.cpp1270
-rw-r--r--gfx/2d/DrawTargetCG.h126
-rw-r--r--gfx/2d/DrawTargetCairo.cpp1115
-rw-r--r--gfx/2d/DrawTargetCairo.h112
-rw-r--r--gfx/2d/DrawTargetCapture.cpp197
-rw-r--r--gfx/2d/DrawTargetCapture.h163
-rw-r--r--gfx/2d/DrawTargetD2D.cpp501
-rw-r--r--gfx/2d/DrawTargetD2D.h43
-rw-r--r--gfx/2d/DrawTargetD2D1.cpp1462
-rw-r--r--gfx/2d/DrawTargetD2D1.h240
-rw-r--r--gfx/2d/DrawTargetDual.cpp16
-rw-r--r--gfx/2d/DrawTargetDual.h72
-rw-r--r--gfx/2d/DrawTargetRecording.cpp162
-rw-r--r--gfx/2d/DrawTargetRecording.h65
-rw-r--r--gfx/2d/DrawTargetSkia.cpp654
-rw-r--r--gfx/2d/DrawTargetSkia.h89
-rw-r--r--gfx/2d/DrawTargetTiled.cpp296
-rw-r--r--gfx/2d/DrawTargetTiled.h208
-rw-r--r--gfx/2d/ExtendInputEffectD2D1.cpp211
-rw-r--r--gfx/2d/ExtendInputEffectD2D1.h88
-rw-r--r--gfx/2d/Factory.cpp578
-rw-r--r--gfx/2d/FilterNodeD2D1.cpp1082
-rw-r--r--gfx/2d/FilterNodeD2D1.h133
-rw-r--r--gfx/2d/FilterNodeSoftware.cpp3545
-rw-r--r--gfx/2d/FilterNodeSoftware.h724
-rw-r--r--gfx/2d/FilterProcessing.cpp235
-rw-r--r--gfx/2d/FilterProcessing.h141
-rw-r--r--gfx/2d/FilterProcessingSIMD-inl.h1081
-rw-r--r--gfx/2d/FilterProcessingSSE2.cpp112
-rw-r--r--gfx/2d/FilterProcessingScalar.cpp244
-rw-r--r--gfx/2d/Filters.h512
-rw-r--r--gfx/2d/GenericRefCounted.h131
-rw-r--r--gfx/2d/GradientStopsD2D.h10
-rw-r--r--gfx/2d/Helpers.h97
-rw-r--r--gfx/2d/HelpersCairo.h156
-rw-r--r--gfx/2d/HelpersD2D.h215
-rw-r--r--gfx/2d/HelpersSkia.h234
-rw-r--r--gfx/2d/ImageScaling.cpp9
-rw-r--r--gfx/2d/ImageScalingSSE2.cpp2
-rw-r--r--gfx/2d/Logging.h584
-rw-r--r--gfx/2d/MacIOSurface.cpp536
-rw-r--r--gfx/2d/MacIOSurface.h152
-rw-r--r--gfx/2d/Makefile.in64
-rw-r--r--gfx/2d/Matrix.cpp192
-rw-r--r--gfx/2d/Matrix.h767
-rw-r--r--gfx/2d/Path.cpp530
-rw-r--r--gfx/2d/PathAnalysis.h57
-rw-r--r--gfx/2d/PathCG.cpp120
-rw-r--r--gfx/2d/PathCG.h20
-rw-r--r--gfx/2d/PathCairo.cpp361
-rw-r--r--gfx/2d/PathCairo.h114
-rw-r--r--gfx/2d/PathD2D.cpp120
-rw-r--r--gfx/2d/PathD2D.h24
-rw-r--r--gfx/2d/PathHelpers.cpp243
-rw-r--r--gfx/2d/PathHelpers.h297
-rw-r--r--gfx/2d/PathRecording.cpp4
-rw-r--r--gfx/2d/PathRecording.h14
-rw-r--r--gfx/2d/PathSkia.cpp74
-rw-r--r--gfx/2d/PathSkia.h17
-rw-r--r--gfx/2d/PatternHelpers.h137
-rw-r--r--gfx/2d/Point.h135
-rw-r--r--gfx/2d/QuartzSupport.h17
-rw-r--r--gfx/2d/QuartzSupport.mm510
-rw-r--r--gfx/2d/RadialGradientEffectD2D1.cpp51
-rw-r--r--gfx/2d/RadialGradientEffectD2D1.h6
-rw-r--r--gfx/2d/RecordedEvent.cpp363
-rw-r--r--gfx/2d/RecordedEvent.h288
-rw-r--r--gfx/2d/Rect.h105
-rw-r--r--gfx/2d/SIMD.h1180
-rw-r--r--gfx/2d/SVGTurbulenceRenderer-inl.h360
-rw-r--r--gfx/2d/Scale.cpp21
-rw-r--r--gfx/2d/ScaleFactor.h28
-rw-r--r--gfx/2d/ScaledFontBase.cpp140
-rw-r--r--gfx/2d/ScaledFontBase.h20
-rw-r--r--gfx/2d/ScaledFontCairo.cpp65
-rw-r--r--gfx/2d/ScaledFontCairo.h56
-rw-r--r--gfx/2d/ScaledFontDWrite.cpp26
-rw-r--r--gfx/2d/ScaledFontDWrite.h11
-rw-r--r--gfx/2d/ScaledFontFreetype.cpp53
-rw-r--r--gfx/2d/ScaledFontMac.cpp177
-rw-r--r--gfx/2d/ScaledFontMac.h5
-rw-r--r--gfx/2d/ScaledFontWin.h3
-rw-r--r--gfx/2d/ShadersD2D.fx56
-rw-r--r--gfx/2d/ShadersD2D.h7299
-rw-r--r--gfx/2d/ShadersD2D1.h1354
-rw-r--r--gfx/2d/ShadersD2D1.hlsl24
-rw-r--r--gfx/2d/SourceSurfaceCG.cpp150
-rw-r--r--gfx/2d/SourceSurfaceCG.h70
-rw-r--r--gfx/2d/SourceSurfaceCairo.cpp15
-rw-r--r--gfx/2d/SourceSurfaceCairo.h10
-rw-r--r--gfx/2d/SourceSurfaceD2D.cpp106
-rw-r--r--gfx/2d/SourceSurfaceD2D.h6
-rw-r--r--gfx/2d/SourceSurfaceD2D1.cpp229
-rw-r--r--gfx/2d/SourceSurfaceD2D1.h95
-rw-r--r--gfx/2d/SourceSurfaceD2DTarget.cpp92
-rw-r--r--gfx/2d/SourceSurfaceD2DTarget.h17
-rw-r--r--gfx/2d/SourceSurfaceDual.h3
-rw-r--r--gfx/2d/SourceSurfaceRawData.cpp63
-rw-r--r--gfx/2d/SourceSurfaceRawData.h34
-rw-r--r--gfx/2d/SourceSurfaceSkia.cpp75
-rw-r--r--gfx/2d/SourceSurfaceSkia.h19
-rw-r--r--gfx/2d/StackArray.h30
-rw-r--r--gfx/2d/Tools.h130
-rw-r--r--gfx/2d/Types.h330
-rw-r--r--gfx/2d/UserData.h2
-rw-r--r--gfx/2d/convolver.cpp550
-rw-r--r--gfx/2d/convolver.h19
-rw-r--r--gfx/2d/convolverSSE2.cpp476
-rw-r--r--gfx/2d/convolverSSE2.h70
-rwxr-xr-xgfx/2d/genshaders.sh10
-rw-r--r--gfx/2d/gfx2d.vcxproj9
-rw-r--r--gfx/2d/image_operations.cpp252
-rw-r--r--gfx/2d/image_operations.h128
-rw-r--r--gfx/2d/moz.build131
-rw-r--r--gfx/2d/unittest/Main.cpp2
-rw-r--r--gfx/2d/unittest/SanityChecks.cpp30
-rw-r--r--gfx/2d/unittest/SanityChecks.h24
-rw-r--r--gfx/2d/unittest/TestBase.cpp88
-rw-r--r--gfx/2d/unittest/TestBase.h2
-rw-r--r--gfx/2d/unittest/TestBugs.cpp86
-rw-r--r--gfx/2d/unittest/TestBugs.h18
-rw-r--r--gfx/2d/unittest/TestDrawTargetBase.cpp210
-rw-r--r--gfx/2d/unittest/TestDrawTargetBase.h68
-rw-r--r--gfx/2d/unittest/TestDrawTargetD2D.cpp24
-rw-r--r--gfx/2d/unittest/TestDrawTargetD2D.h28
-rw-r--r--gfx/2d/unittest/TestPoint.cpp84
-rw-r--r--gfx/2d/unittest/TestPoint.h26
-rw-r--r--gfx/2d/unittest/TestScaling.cpp490
-rw-r--r--gfx/2d/unittest/TestScaling.h36
151 files changed, 31737 insertions, 8736 deletions
diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h
index c1625e418..b1e0e3e7b 100644
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -11,11 +11,20 @@
#include "Rect.h"
#include "Matrix.h"
#include "UserData.h"
+
+// GenericRefCountedBase allows us to hold on to refcounted objects of any type
+// (contrary to RefCounted<T> which requires knowing the type T) and, in particular,
+// without having a dependency on that type. This is used for DrawTargetSkia
+// to be able to hold on to a GLContext.
+#include "mozilla/GenericRefCounted.h"
+
// This RefPtr class isn't ideal for usage in Azure, as it doesn't allow T**
// outparams using the &-operator. But it will have to do as there's no easy
// solution.
#include "mozilla/RefPtr.h"
+#include "mozilla/DebugOnly.h"
+
#ifdef MOZ_ENABLE_FREETYPE
#include <string>
#endif
@@ -28,9 +37,16 @@ typedef _cairo_scaled_font cairo_scaled_font_t;
struct ID3D10Device1;
struct ID3D10Texture2D;
+struct ID3D11Texture2D;
+struct ID3D11Device;
+struct ID2D1Device;
struct IDWriteRenderingParams;
class GrContext;
+struct GrGLInterface;
+
+struct CGContext;
+typedef struct CGContext *CGContextRef;
namespace mozilla {
@@ -40,10 +56,13 @@ class SourceSurface;
class DataSourceSurface;
class DrawTarget;
class DrawEventRecorder;
+class FilterNode;
+class LogForwarder;
struct NativeSurface {
NativeSurfaceType mType;
SurfaceFormat mFormat;
+ gfx::IntSize mSize;
void *mSurface;
};
@@ -52,58 +71,41 @@ struct NativeFont {
void *mFont;
};
-/*
+/**
* This structure is used to send draw options that are universal to all drawing
- * operations. It consists of the following:
- *
- * mAlpha - Alpha value by which the mask generated by this operation is
- * multiplied.
- * mCompositionOp - The operator that indicates how the source and destination
- * patterns are blended.
- * mAntiAliasMode - The AntiAlias mode used for this drawing operation.
- * mSnapping - Whether this operation is snapped to pixel boundaries.
+ * operations.
*/
struct DrawOptions {
- DrawOptions(Float aAlpha = 1.0f,
- CompositionOp aCompositionOp = OP_OVER,
- AntialiasMode aAntialiasMode = AA_DEFAULT,
- Snapping aSnapping = SNAP_NONE)
+ /// For constructor parameter description, see member data documentation.
+ explicit DrawOptions(Float aAlpha = 1.0f,
+ CompositionOp aCompositionOp = CompositionOp::OP_OVER,
+ AntialiasMode aAntialiasMode = AntialiasMode::DEFAULT)
: mAlpha(aAlpha)
, mCompositionOp(aCompositionOp)
, mAntialiasMode(aAntialiasMode)
- , mSnapping(aSnapping)
{}
- Float mAlpha;
- CompositionOp mCompositionOp : 8;
- AntialiasMode mAntialiasMode : 3;
- Snapping mSnapping : 1;
+ Float mAlpha; /**< Alpha value by which the mask generated by this
+ operation is multiplied. */
+ CompositionOp mCompositionOp; /**< The operator that indicates how the source and
+ destination patterns are blended. */
+ AntialiasMode mAntialiasMode; /**< The AntiAlias mode used for this drawing
+ operation. */
};
-/*
+/**
* This structure is used to send stroke options that are used in stroking
- * operations. It consists of the following:
- *
- * mLineWidth - Width of the stroke in userspace.
- * mLineJoin - Join style used for joining lines.
- * mLineCap - Cap style used for capping lines.
- * mMiterLimit - Miter limit in units of linewidth
- * mDashPattern - Series of on/off userspace lengths defining dash.
- * Owned by the caller; must live at least as long as
- * this StrokeOptions.
- * mDashPattern != null <=> mDashLength > 0.
- * mDashLength - Number of on/off lengths in mDashPattern.
- * mDashOffset - Userspace offset within mDashPattern at which stroking
- * begins.
+ * operations.
*/
struct StrokeOptions {
- StrokeOptions(Float aLineWidth = 1.0f,
- JoinStyle aLineJoin = JOIN_MITER_OR_BEVEL,
- CapStyle aLineCap = CAP_BUTT,
- Float aMiterLimit = 10.0f,
- size_t aDashLength = 0,
- const Float* aDashPattern = 0,
- Float aDashOffset = 0.f)
+ /// For constructor parameter description, see member data documentation.
+ explicit StrokeOptions(Float aLineWidth = 1.0f,
+ JoinStyle aLineJoin = JoinStyle::MITER_OR_BEVEL,
+ CapStyle aLineCap = CapStyle::BUTT,
+ Float aMiterLimit = 10.0f,
+ size_t aDashLength = 0,
+ const Float* aDashPattern = 0,
+ Float aDashOffset = 0.f)
: mLineWidth(aLineWidth)
, mMiterLimit(aMiterLimit)
, mDashPattern(aDashLength > 0 ? aDashPattern : 0)
@@ -115,36 +117,40 @@ struct StrokeOptions {
MOZ_ASSERT(aDashLength == 0 || aDashPattern);
}
- Float mLineWidth;
- Float mMiterLimit;
- const Float* mDashPattern;
- size_t mDashLength;
- Float mDashOffset;
- JoinStyle mLineJoin : 4;
- CapStyle mLineCap : 3;
+ Float mLineWidth; //!< Width of the stroke in userspace.
+ Float mMiterLimit; //!< Miter limit in units of linewidth
+ const Float* mDashPattern; /**< Series of on/off userspace lengths defining dash.
+ Owned by the caller; must live at least as long as
+ this StrokeOptions.
+ mDashPattern != null <=> mDashLength > 0. */
+ size_t mDashLength; //!< Number of on/off lengths in mDashPattern.
+ Float mDashOffset; /**< Userspace offset within mDashPattern at which
+ stroking begins. */
+ JoinStyle mLineJoin; //!< Join style used for joining lines.
+ CapStyle mLineCap; //!< Cap style used for capping lines.
};
-/*
+/**
* This structure supplies additional options for calls to DrawSurface.
- *
- * mFilter - Filter used when resampling source surface region to the
- * destination region.
- * aSamplingBounds - This indicates whether the implementation is allowed
- * to sample pixels outside the source rectangle as
- * specified in DrawSurface on the surface.
*/
struct DrawSurfaceOptions {
- DrawSurfaceOptions(Filter aFilter = FILTER_LINEAR,
- SamplingBounds aSamplingBounds = SAMPLING_UNBOUNDED)
+ /// For constructor parameter description, see member data documentation.
+ explicit DrawSurfaceOptions(Filter aFilter = Filter::LINEAR,
+ SamplingBounds aSamplingBounds = SamplingBounds::UNBOUNDED)
: mFilter(aFilter)
, mSamplingBounds(aSamplingBounds)
{ }
- Filter mFilter : 3;
- SamplingBounds mSamplingBounds : 1;
+ Filter mFilter; /**< Filter used when resampling source surface
+ region to the destination region. */
+ SamplingBounds mSamplingBounds; /**< This indicates whether the implementation is
+ allowed to sample pixels outside the source
+ rectangle as specified in DrawSurface on
+ the surface. */
+
};
-/*
+/**
* This class is used to store gradient stops, it can only be used with a
* matching DrawTarget. Not adhering to this condition will make a draw call
* fail.
@@ -152,15 +158,17 @@ struct DrawSurfaceOptions {
class GradientStops : public RefCounted<GradientStops>
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStops)
virtual ~GradientStops() {}
virtual BackendType GetBackendType() const = 0;
+ virtual bool IsValid() const { return true; }
protected:
GradientStops() {}
};
-/*
+/**
* This is the base class for 'patterns'. Patterns describe the pixels used as
* the source for a masked composition operation that is done by the different
* drawing commands. These objects are not backend specific, however for
@@ -180,16 +188,21 @@ protected:
class ColorPattern : public Pattern
{
public:
- ColorPattern(const Color &aColor)
+ // Explicit because consumers should generally use ToDeviceColor when
+ // creating a ColorPattern.
+ explicit ColorPattern(const Color &aColor)
: mColor(aColor)
{}
- virtual PatternType GetType() const { return PATTERN_COLOR; }
+ virtual PatternType GetType() const override
+ {
+ return PatternType::COLOR;
+ }
Color mColor;
};
-/*
+/**
* This class is used for Linear Gradient Patterns, the gradient stops are
* stored in a separate object and are backend dependent. This class itself
* may be used on the stack.
@@ -197,14 +210,7 @@ public:
class LinearGradientPattern : public Pattern
{
public:
- /*
- * aBegin Start of the linear gradient
- * aEnd End of the linear gradient - NOTE: In the case of a zero length
- * gradient it will act as the color of the last stop.
- * aStops GradientStops object for this gradient, this should match the
- * backend type of the draw target this pattern will be used with.
- * aMatrix A matrix that transforms the pattern into user space
- */
+ /// For constructor parameter description, see member data documentation.
LinearGradientPattern(const Point &aBegin,
const Point &aEnd,
GradientStops *aStops,
@@ -216,15 +222,23 @@ public:
{
}
- virtual PatternType GetType() const { return PATTERN_LINEAR_GRADIENT; }
+ virtual PatternType GetType() const override
+ {
+ return PatternType::LINEAR_GRADIENT;
+ }
- Point mBegin;
- Point mEnd;
- RefPtr<GradientStops> mStops;
- Matrix mMatrix;
+ Point mBegin; //!< Start of the linear gradient
+ Point mEnd; /**< End of the linear gradient - NOTE: In the case
+ of a zero length gradient it will act as the
+ color of the last stop. */
+ RefPtr<GradientStops> mStops; /**< GradientStops object for this gradient, this
+ should match the backend type of the draw
+ target this pattern will be used with. */
+ Matrix mMatrix; /**< A matrix that transforms the pattern into
+ user space */
};
-/*
+/**
* This class is used for Radial Gradient Patterns, the gradient stops are
* stored in a separate object and are backend dependent. This class itself
* may be used on the stack.
@@ -232,13 +246,7 @@ public:
class RadialGradientPattern : public Pattern
{
public:
- /*
- * aBegin Start of the linear gradient
- * aEnd End of the linear gradient
- * aStops GradientStops object for this gradient, this should match the
- * backend type of the draw target this pattern will be used with.
- * aMatrix A matrix that transforms the pattern into user space
- */
+ /// For constructor parameter description, see member data documentation.
RadialGradientPattern(const Point &aCenter1,
const Point &aCenter2,
Float aRadius1,
@@ -254,160 +262,243 @@ public:
{
}
- virtual PatternType GetType() const { return PATTERN_RADIAL_GRADIENT; }
+ virtual PatternType GetType() const override
+ {
+ return PatternType::RADIAL_GRADIENT;
+ }
- Point mCenter1;
- Point mCenter2;
- Float mRadius1;
- Float mRadius2;
- RefPtr<GradientStops> mStops;
- Matrix mMatrix;
+ Point mCenter1; //!< Center of the inner (focal) circle.
+ Point mCenter2; //!< Center of the outer circle.
+ Float mRadius1; //!< Radius of the inner (focal) circle.
+ Float mRadius2; //!< Radius of the outer circle.
+ RefPtr<GradientStops> mStops; /**< GradientStops object for this gradient, this
+ should match the backend type of the draw target
+ this pattern will be used with. */
+ Matrix mMatrix; //!< A matrix that transforms the pattern into user space
};
-/*
+/**
* This class is used for Surface Patterns, they wrap a surface and a
* repetition mode for the surface. This may be used on the stack.
*/
class SurfacePattern : public Pattern
{
public:
- /*
- * aSourceSurface Surface to use for drawing
- * aExtendMode This determines how the image is extended outside the bounds
- * of the image.
- * aMatrix A matrix that transforms the pattern into user space
- * aFilter Resampling filter used for resampling the image.
- */
+ /// For constructor parameter description, see member data documentation.
SurfacePattern(SourceSurface *aSourceSurface, ExtendMode aExtendMode,
- const Matrix &aMatrix = Matrix(), Filter aFilter = FILTER_LINEAR)
+ const Matrix &aMatrix = Matrix(), Filter aFilter = Filter::GOOD,
+ const IntRect &aSamplingRect = IntRect())
: mSurface(aSourceSurface)
, mExtendMode(aExtendMode)
, mFilter(aFilter)
, mMatrix(aMatrix)
+ , mSamplingRect(aSamplingRect)
{}
- virtual PatternType GetType() const { return PATTERN_SURFACE; }
+ virtual PatternType GetType() const override
+ {
+ return PatternType::SURFACE;
+ }
+
+ RefPtr<SourceSurface> mSurface; //!< Surface to use for drawing
+ ExtendMode mExtendMode; /**< This determines how the image is extended
+ outside the bounds of the image */
+ Filter mFilter; //!< Resampling filter for resampling the image.
+ Matrix mMatrix; //!< Transforms the pattern into user space
- RefPtr<SourceSurface> mSurface;
- ExtendMode mExtendMode;
- Filter mFilter;
- Matrix mMatrix;
+ IntRect mSamplingRect; /**< Rect that must not be sampled outside of,
+ or an empty rect if none has been specified. */
};
-/*
+class StoredPattern;
+class DrawTargetCaptureImpl;
+
+/**
* This is the base class for source surfaces. These objects are surfaces
- * which may be used as a source in a SurfacePattern of a DrawSurface call.
+ * which may be used as a source in a SurfacePattern or a DrawSurface call.
* They cannot be drawn to directly.
*/
class SourceSurface : public RefCounted<SourceSurface>
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurface)
virtual ~SourceSurface() {}
virtual SurfaceType GetType() const = 0;
virtual IntSize GetSize() const = 0;
virtual SurfaceFormat GetFormat() const = 0;
- /* This returns false if some event has made this source surface invalid for
+ /** This returns false if some event has made this source surface invalid for
* usage with current DrawTargets. For example in the case of Direct2D this
* could return false if we have switched devices since this surface was
* created.
*/
virtual bool IsValid() const { return true; }
- /*
+ /**
* This function will get a DataSourceSurface for this surface, a
* DataSourceSurface's data can be accessed directly.
*/
virtual TemporaryRef<DataSourceSurface> GetDataSurface() = 0;
+
+ /** Tries to get this SourceSurface's native surface. This will fail if aType
+ * is not the type of this SourceSurface's native surface.
+ */
+ virtual void *GetNativeSurface(NativeSurfaceType aType) {
+ return nullptr;
+ }
+
+ void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
+ mUserData.Add(key, userData, destroy);
+ }
+ void *GetUserData(UserDataKey *key) {
+ return mUserData.Get(key);
+ }
+
+protected:
+ friend class DrawTargetCaptureImpl;
+ friend class StoredPattern;
+
+ // This is for internal use, it ensures the SourceSurface's data remains
+ // valid during the lifetime of the SourceSurface.
+ // @todo XXX - We need something better here :(. But we may be able to get rid
+ // of CreateWrappingDataSourceSurface in the future.
+ virtual void GuaranteePersistance() {}
+
+ UserData mUserData;
};
class DataSourceSurface : public SourceSurface
{
public:
- virtual SurfaceType GetType() const { return SURFACE_DATA; }
- /*
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurface, override)
+ DataSourceSurface()
+ : mIsMapped(false)
+ {
+ }
+
+#ifdef DEBUG
+ virtual ~DataSourceSurface()
+ {
+ MOZ_ASSERT(!mIsMapped, "Someone forgot to call Unmap()");
+ }
+#endif
+
+ struct MappedSurface {
+ uint8_t *mData;
+ int32_t mStride;
+ };
+
+ enum MapType {
+ READ,
+ WRITE,
+ READ_WRITE
+ };
+
+ virtual SurfaceType GetType() const override { return SurfaceType::DATA; }
+ /** @deprecated
* Get the raw bitmap data of the surface.
* Can return null if there was OOM allocating surface data.
*/
virtual uint8_t *GetData() = 0;
- /*
+ /** @deprecated
* Stride of the surface, distance in bytes between the start of the image
* data belonging to row y and row y+1. This may be negative.
* Can return 0 if there was OOM allocating surface data.
*/
virtual int32_t Stride() = 0;
- /*
- * This function is called after modifying the data on the source surface
- * directly through the data pointer.
+ /**
+ * The caller is responsible for ensuring aMappedSurface is not null.
*/
- virtual void MarkDirty() {}
+ virtual bool Map(MapType, MappedSurface *aMappedSurface)
+ {
+ aMappedSurface->mData = GetData();
+ aMappedSurface->mStride = Stride();
+ mIsMapped = !!aMappedSurface->mData;
+ return mIsMapped;
+ }
- virtual TemporaryRef<DataSourceSurface> GetDataSurface() { RefPtr<DataSourceSurface> temp = this; return temp.forget(); }
+ virtual void Unmap()
+ {
+ MOZ_ASSERT(mIsMapped);
+ mIsMapped = false;
+ }
+
+ /**
+ * Returns a DataSourceSurface with the same data as this one, but
+ * guaranteed to have surface->GetType() == SurfaceType::DATA.
+ */
+ virtual TemporaryRef<DataSourceSurface> GetDataSurface() override;
+
+protected:
+ bool mIsMapped;
};
-/* This is an abstract object that accepts path segments. */
+/** This is an abstract object that accepts path segments. */
class PathSink : public RefCounted<PathSink>
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathSink)
virtual ~PathSink() {}
- /* Move the current point in the path, any figure currently being drawn will
+ /** Move the current point in the path, any figure currently being drawn will
* be considered closed during fill operations, however when stroking the
* closing line segment will not be drawn.
*/
virtual void MoveTo(const Point &aPoint) = 0;
- /* Add a linesegment to the current figure */
+ /** Add a linesegment to the current figure */
virtual void LineTo(const Point &aPoint) = 0;
- /* Add a cubic bezier curve to the current figure */
+ /** Add a cubic bezier curve to the current figure */
virtual void BezierTo(const Point &aCP1,
const Point &aCP2,
const Point &aCP3) = 0;
- /* Add a quadratic bezier curve to the current figure */
+ /** Add a quadratic bezier curve to the current figure */
virtual void QuadraticBezierTo(const Point &aCP1,
const Point &aCP2) = 0;
- /* Close the current figure, this will essentially generate a line segment
+ /** Close the current figure, this will essentially generate a line segment
* from the current point to the starting point for the current figure
*/
virtual void Close() = 0;
- /* Add an arc to the current figure */
+ /** Add an arc to the current figure */
virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
float aEndAngle, bool aAntiClockwise = false) = 0;
- /* Point the current subpath is at - or where the next subpath will start
+ /** Point the current subpath is at - or where the next subpath will start
* if there is no active subpath.
*/
virtual Point CurrentPoint() const = 0;
};
class PathBuilder;
+class FlattenedPath;
-/* The path class is used to create (sets of) figures of any shape that can be
+/** The path class is used to create (sets of) figures of any shape that can be
* filled or stroked to a DrawTarget
*/
class Path : public RefCounted<Path>
{
public:
- virtual ~Path() {}
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(Path)
+ virtual ~Path();
virtual BackendType GetBackendType() const = 0;
- /* This returns a PathBuilder object that contains a copy of the contents of
+ /** This returns a PathBuilder object that contains a copy of the contents of
* this path and is still writable.
*/
- virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const = 0;
+ virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const = 0;
virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
- FillRule aFillRule = FILL_WINDING) const = 0;
+ FillRule aFillRule = FillRule::FILL_WINDING) const = 0;
- /* This function checks if a point lies within a path. It allows passing a
+ /** This function checks if a point lies within a path. It allows passing a
* transform that will transform the path to the coordinate space in which
* aPoint is given.
*/
virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const = 0;
- /* This function checks if a point lies within the stroke of a path using the
+ /** This function checks if a point lies within the stroke of a path using the
* specified strokeoptions. It allows passing a transform that will transform
* the path to the coordinate space in which aPoint is given.
*/
@@ -415,13 +506,13 @@ public:
const Point &aPoint,
const Matrix &aTransform) const = 0;
- /* This functions gets the bounds of this path. These bounds are not
+ /** This functions gets the bounds of this path. These bounds are not
* guaranteed to be tight. A transform may be specified that gives the bounds
* after application of the transform.
*/
virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const = 0;
- /* This function gets the bounds of the stroke of this path using the
+ /** This function gets the bounds of the stroke of this path using the
* specified strokeoptions. These bounds are not guaranteed to be tight.
* A transform may be specified that gives the bounds after application of
* the transform.
@@ -429,22 +520,41 @@ public:
virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
const Matrix &aTransform = Matrix()) const = 0;
- /* This gets the fillrule this path's builder was created with. This is not
+ /** Take the contents of this path and stream it to another sink, this works
+ * regardless of the backend that might be used for the destination sink.
+ */
+ virtual void StreamToSink(PathSink *aSink) const = 0;
+
+ /** This gets the fillrule this path's builder was created with. This is not
* mutable.
*/
virtual FillRule GetFillRule() const = 0;
+
+ virtual Float ComputeLength();
+
+ virtual Point ComputePointAtLength(Float aLength,
+ Point* aTangent = nullptr);
+
+protected:
+ Path();
+ void EnsureFlattenedPath();
+
+ RefPtr<FlattenedPath> mFlattenedPath;
};
-/* The PathBuilder class allows path creation. Once finish is called on the
+/** The PathBuilder class allows path creation. Once finish is called on the
* pathbuilder it may no longer be written to.
*/
class PathBuilder : public PathSink
{
public:
- /* Finish writing to the path and return a Path object that can be used for
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilder)
+ /** Finish writing to the path and return a Path object that can be used for
* drawing. Future use of the builder results in a crash!
*/
virtual TemporaryRef<Path> Finish() = 0;
+
+ virtual BackendType GetBackendType() const = 0;
};
struct Glyph
@@ -453,44 +563,43 @@ struct Glyph
Point mPosition;
};
-/* This class functions as a glyph buffer that can be drawn to a DrawTarget.
- * XXX - This should probably contain the guts of gfxTextRun in the future as
+/** This class functions as a glyph buffer that can be drawn to a DrawTarget.
+ * @todo XXX - This should probably contain the guts of gfxTextRun in the future as
* roc suggested. But for now it's a simple container for a glyph vector.
*/
struct GlyphBuffer
{
- // A pointer to a buffer of glyphs. Managed by the caller.
- const Glyph *mGlyphs;
- // Number of glyphs mGlyphs points to.
- uint32_t mNumGlyphs;
+ const Glyph *mGlyphs; //!< A pointer to a buffer of glyphs. Managed by the caller.
+ uint32_t mNumGlyphs; //!< Number of glyphs mGlyphs points to.
};
-/* This class is an abstraction of a backend/platform specific font object
+/** This class is an abstraction of a backend/platform specific font object
* at a particular size. It is passed into text drawing calls to describe
* the font used for the drawing call.
*/
class ScaledFont : public RefCounted<ScaledFont>
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFont)
virtual ~ScaledFont() {}
typedef void (*FontFileDataOutput)(const uint8_t *aData, uint32_t aLength, uint32_t aIndex, Float aGlyphSize, void *aBaton);
virtual FontType GetType() const = 0;
- /* This allows getting a path that describes the outline of a set of glyphs.
+ /** This allows getting a path that describes the outline of a set of glyphs.
* A target is passed in so that the guarantee is made the returned path
* can be used with any DrawTarget that has the same backend as the one
* passed in.
*/
virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) = 0;
- /* This copies the path describing the glyphs into a PathBuilder. We use this
+ /** This copies the path describing the glyphs into a PathBuilder. We use this
* API rather than a generic API to append paths because it allows easier
* implementation in some backends, and more efficient implementation in
* others.
*/
- virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder) = 0;
+ virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint = nullptr) = 0;
virtual bool GetFontFileData(FontFileDataOutput, void *) { return false; }
@@ -507,21 +616,7 @@ protected:
UserData mUserData;
};
-#ifdef MOZ_ENABLE_FREETYPE
-/**
- * Describes a font
- * Used to pass the key informatin from a gfxFont into Azure
- * XXX Should be replaced by a more long term solution, perhaps Bug 738014
- */
-struct FontOptions
-{
- std::string mName;
- FontStyle mStyle;
-};
-#endif
-
-
-/* This class is designed to allow passing additional glyph rendering
+/** This class is designed to allow passing additional glyph rendering
* parameters to the glyph drawing functions. This is an empty wrapper class
* merely used to allow holding on to and passing around platform specific
* parameters. This is because different platforms have unique rendering
@@ -530,6 +625,7 @@ struct FontOptions
class GlyphRenderingOptions : public RefCounted<GlyphRenderingOptions>
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptions)
virtual ~GlyphRenderingOptions() {}
virtual FontType GetType() const = 0;
@@ -538,7 +634,9 @@ protected:
GlyphRenderingOptions() {}
};
-/* This is the main class used for all the drawing. It is created through the
+class DrawTargetCapture;
+
+/** This is the main class used for all the drawing. It is created through the
* factory and accepts drawing commands. The results of drawing to a target
* may be used either through a Snapshot or by flushing the target and directly
* accessing the backing store a DrawTarget was created with.
@@ -546,10 +644,13 @@ protected:
class DrawTarget : public RefCounted<DrawTarget>
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTarget)
DrawTarget() : mTransformDirty(false), mPermitSubpixelAA(false) {}
virtual ~DrawTarget() {}
- virtual BackendType GetType() const = 0;
+ virtual DrawTargetType GetType() const = 0;
+
+ virtual BackendType GetBackendType() const = 0;
/**
* Returns a SourceSurface which is a snapshot of the current contents of the DrawTarget.
* Multiple calls to Snapshot() without any drawing operations in between will
@@ -558,22 +659,40 @@ public:
virtual TemporaryRef<SourceSurface> Snapshot() = 0;
virtual IntSize GetSize() = 0;
- /* Ensure that the DrawTarget backend has flushed all drawing operations to
+ /**
+ * If possible returns the bits to this DrawTarget for direct manipulation. While
+ * the bits is locked any modifications to this DrawTarget is forbidden.
+ * Release takes the original data pointer for safety.
+ */
+ virtual bool LockBits(uint8_t** aData, IntSize* aSize,
+ int32_t* aStride, SurfaceFormat* aFormat) { return false; }
+ virtual void ReleaseBits(uint8_t* aData) {}
+
+ /** Ensure that the DrawTarget backend has flushed all drawing operations to
* this draw target. This must be called before using the backing surface of
* this draw target outside of GFX 2D code.
*/
virtual void Flush() = 0;
- /*
+ /**
+ * Realize a DrawTargetCapture onto the draw target.
+ *
+ * @param aSource Capture DrawTarget to draw
+ * @param aTransform Transform to apply when replaying commands
+ */
+ virtual void DrawCapturedDT(DrawTargetCapture *aCaptureDT,
+ const Matrix& aTransform);
+
+ /**
* Draw a surface to the draw target. Possibly doing partial drawing or
* applying scaling. No sampling happens outside the source.
*
- * aSurface Source surface to draw
- * aDest Destination rectangle that this drawing operation should draw to
- * aSource Source rectangle in aSurface coordinates, this area of aSurface
- * will be stretched to the size of aDest.
- * aOptions General draw options that are applied to the operation
- * aSurfOptions DrawSurface options that are applied
+ * @param aSurface Source surface to draw
+ * @param aDest Destination rectangle that this drawing operation should draw to
+ * @param aSource Source rectangle in aSurface coordinates, this area of aSurface
+ * will be stretched to the size of aDest.
+ * @param aOptions General draw options that are applied to the operation
+ * @param aSurfOptions DrawSurface options that are applied
*/
virtual void DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
@@ -581,19 +700,32 @@ public:
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
const DrawOptions &aOptions = DrawOptions()) = 0;
- /*
+ /**
+ * Draw the output of a FilterNode to the DrawTarget.
+ *
+ * @param aNode FilterNode to draw
+ * @param aSourceRect Source rectangle in FilterNode space to draw
+ * @param aDestPoint Destination point on the DrawTarget to draw the
+ * SourceRectangle of the filter output to
+ */
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /**
* Blend a surface to the draw target with a shadow. The shadow is drawn as a
* gaussian blur using a specified sigma. The shadow is clipped to the size
* of the input surface, so the input surface should contain a transparent
* border the size of the approximate coverage of the blur (3 * aSigma).
* NOTE: This function works in device space!
*
- * aSurface Source surface to draw.
- * aDest Destination point that this drawing operation should draw to.
- * aColor Color of the drawn shadow
- * aOffset Offset of the shadow
- * aSigma Sigma used for the guassian filter kernel
- * aOperator Composition operator used
+ * @param aSurface Source surface to draw.
+ * @param aDest Destination point that this drawing operation should draw to.
+ * @param aColor Color of the drawn shadow
+ * @param aOffset Offset of the shadow
+ * @param aSigma Sigma used for the guassian filter kernel
+ * @param aOperator Composition operator used
*/
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
const Point &aDest,
@@ -602,57 +734,70 @@ public:
Float aSigma,
CompositionOp aOperator) = 0;
- /*
+ /**
* Clear a rectangle on the draw target to transparent black. This will
* respect the clipping region and transform.
*
- * aRect Rectangle to clear
+ * @param aRect Rectangle to clear
*/
virtual void ClearRect(const Rect &aRect) = 0;
- /*
+ /**
* This is essentially a 'memcpy' between two surfaces. It moves a pixel
* aligned area from the source surface unscaled directly onto the
* drawtarget. This ignores both transform and clip.
*
- * aSurface Surface to copy from
- * aSourceRect Source rectangle to be copied
- * aDest Destination point to copy the surface to
+ * @param aSurface Surface to copy from
+ * @param aSourceRect Source rectangle to be copied
+ * @param aDest Destination point to copy the surface to
*/
virtual void CopySurface(SourceSurface *aSurface,
const IntRect &aSourceRect,
const IntPoint &aDestination) = 0;
- /*
+ /** @see CopySurface
+ * Same as CopySurface, except uses itself as the source.
+ *
+ * Some backends may be able to optimize this better
+ * than just taking a snapshot and using CopySurface.
+ */
+ virtual void CopyRect(const IntRect &aSourceRect,
+ const IntPoint &aDestination)
+ {
+ RefPtr<SourceSurface> source = Snapshot();
+ CopySurface(source, aSourceRect, aDestination);
+ }
+
+ /**
* Fill a rectangle on the DrawTarget with a certain source pattern.
*
- * aRect Rectangle that forms the mask of this filling operation
- * aPattern Pattern that forms the source of this filling operation
- * aOptions Options that are applied to this operation
+ * @param aRect Rectangle that forms the mask of this filling operation
+ * @param aPattern Pattern that forms the source of this filling operation
+ * @param aOptions Options that are applied to this operation
*/
virtual void FillRect(const Rect &aRect,
const Pattern &aPattern,
const DrawOptions &aOptions = DrawOptions()) = 0;
- /*
+ /**
* Stroke a rectangle on the DrawTarget with a certain source pattern.
*
- * aRect Rectangle that forms the mask of this stroking operation
- * aPattern Pattern that forms the source of this stroking operation
- * aOptions Options that are applied to this operation
+ * @param aRect Rectangle that forms the mask of this stroking operation
+ * @param aPattern Pattern that forms the source of this stroking operation
+ * @param aOptions Options that are applied to this operation
*/
virtual void StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
const DrawOptions &aOptions = DrawOptions()) = 0;
- /*
+ /**
* Stroke a line on the DrawTarget with a certain source pattern.
*
- * aStart Starting point of the line
- * aEnd End point of the line
- * aPattern Pattern that forms the source of this stroking operation
- * aOptions Options that are applied to this operation
+ * @param aStart Starting point of the line
+ * @param aEnd End point of the line
+ * @param aPattern Pattern that forms the source of this stroking operation
+ * @param aOptions Options that are applied to this operation
*/
virtual void StrokeLine(const Point &aStart,
const Point &aEnd,
@@ -660,88 +805,88 @@ public:
const StrokeOptions &aStrokeOptions = StrokeOptions(),
const DrawOptions &aOptions = DrawOptions()) = 0;
- /*
+ /**
* Stroke a path on the draw target with a certain source pattern.
*
- * aPath Path that is to be stroked
- * aPattern Pattern that should be used for the stroke
- * aStrokeOptions Stroke options used for this operation
- * aOptions Draw options used for this operation
+ * @param aPath Path that is to be stroked
+ * @param aPattern Pattern that should be used for the stroke
+ * @param aStrokeOptions Stroke options used for this operation
+ * @param aOptions Draw options used for this operation
*/
virtual void Stroke(const Path *aPath,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
const DrawOptions &aOptions = DrawOptions()) = 0;
- /*
+ /**
* Fill a path on the draw target with a certain source pattern.
*
- * aPath Path that is to be filled
- * aPattern Pattern that should be used for the fill
- * aOptions Draw options used for this operation
+ * @param aPath Path that is to be filled
+ * @param aPattern Pattern that should be used for the fill
+ * @param aOptions Draw options used for this operation
*/
virtual void Fill(const Path *aPath,
const Pattern &aPattern,
const DrawOptions &aOptions = DrawOptions()) = 0;
- /*
+ /**
* Fill a series of clyphs on the draw target with a certain source pattern.
*/
virtual void FillGlyphs(ScaledFont *aFont,
const GlyphBuffer &aBuffer,
const Pattern &aPattern,
const DrawOptions &aOptions = DrawOptions(),
- const GlyphRenderingOptions *aRenderingOptions = NULL) = 0;
+ const GlyphRenderingOptions *aRenderingOptions = nullptr) = 0;
- /*
+ /**
* This takes a source pattern and a mask, and composites the source pattern
* onto the destination surface using the alpha channel of the mask pattern
* as a mask for the operation.
*
- * aSource Source pattern
- * aMask Mask pattern
- * aOptions Drawing options
+ * @param aSource Source pattern
+ * @param aMask Mask pattern
+ * @param aOptions Drawing options
*/
virtual void Mask(const Pattern &aSource,
const Pattern &aMask,
const DrawOptions &aOptions = DrawOptions()) = 0;
- /*
+ /**
* This takes a source pattern and a mask, and composites the source pattern
* onto the destination surface using the alpha channel of the mask source.
* The operation is bound by the extents of the mask.
*
- * aSource Source pattern
- * aMask Mask surface
- * aOffset a transformed offset that the surface is masked at
- * aOptions Drawing options
+ * @param aSource Source pattern
+ * @param aMask Mask surface
+ * @param aOffset a transformed offset that the surface is masked at
+ * @param aOptions Drawing options
*/
virtual void MaskSurface(const Pattern &aSource,
SourceSurface *aMask,
Point aOffset,
const DrawOptions &aOptions = DrawOptions()) = 0;
- /*
+ /**
* Push a clip to the DrawTarget.
*
- * aPath The path to clip to
+ * @param aPath The path to clip to
*/
virtual void PushClip(const Path *aPath) = 0;
- /*
+ /**
* Push an axis-aligned rectangular clip to the DrawTarget. This rectangle
* is specified in user space.
*
- * aRect The rect to clip to
+ * @param aRect The rect to clip to
*/
virtual void PushClipRect(const Rect &aRect) = 0;
- /* Pop a clip from the DrawTarget. A pop without a corresponding push will
+ /** Pop a clip from the DrawTarget. A pop without a corresponding push will
* be ignored.
*/
virtual void PopClip() = 0;
- /*
+ /**
* Create a SourceSurface optimized for use with this DrawTarget from
* existing bitmap data in memory.
*
@@ -752,14 +897,14 @@ public:
int32_t aStride,
SurfaceFormat aFormat) const = 0;
- /*
- * Create a SourceSurface optimized for use with this DrawTarget from
- * an arbitrary other SourceSurface. This may return aSourceSurface or some
- * other existing surface.
+ /**
+ * Create a SourceSurface optimized for use with this DrawTarget from an
+ * arbitrary SourceSurface type supported by this backend. This may return
+ * aSourceSurface or some other existing surface.
*/
virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const = 0;
- /*
+ /**
* Create a SourceSurface for a type of NativeSurface. This may fail if the
* draw target does not know how to deal with the type of NativeSurface passed
* in.
@@ -767,13 +912,21 @@ public:
virtual TemporaryRef<SourceSurface>
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const = 0;
- /*
+ /**
* Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
*/
virtual TemporaryRef<DrawTarget>
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const = 0;
- /*
+ /**
+ * Create a DrawTarget that captures the drawing commands and can be replayed
+ * onto a compatible DrawTarget afterwards.
+ *
+ * @param aSize Size of the area this DT will capture.
+ */
+ virtual TemporaryRef<DrawTargetCapture> CreateCaptureDT(const IntSize& aSize);
+
+ /**
* Create a draw target optimized for drawing a shadow.
*
* Note that aSigma is the blur radius that must be used when we draw the
@@ -788,54 +941,85 @@ public:
return CreateSimilarDrawTarget(aSize, aFormat);
}
- /*
+ /**
* Create a path builder with the specified fillmode.
*
* We need the fill mode up front because of Direct2D.
* ID2D1SimplifiedGeometrySink requires the fill mode
* to be set before calling BeginFigure().
*/
- virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const = 0;
+ virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const = 0;
- /*
+ /**
* Create a GradientStops object that holds information about a set of
* gradient stops, this object is required for linear or radial gradient
* patterns to represent the color stops in the gradient.
*
- * aStops An array of gradient stops
- * aNumStops Number of stops in the array aStops
- * aExtendNone This describes how to extend the stop color outside of the
- * gradient area.
+ * @param aStops An array of gradient stops
+ * @param aNumStops Number of stops in the array aStops
+ * @param aExtendNone This describes how to extend the stop color outside of the
+ * gradient area.
*/
virtual TemporaryRef<GradientStops>
CreateGradientStops(GradientStop *aStops,
uint32_t aNumStops,
- ExtendMode aExtendMode = EXTEND_CLAMP) const = 0;
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const = 0;
- const Matrix &GetTransform() const { return mTransform; }
+ /**
+ * Create a FilterNode object that can be used to apply a filter to various
+ * inputs.
+ *
+ * @param aType Type of filter node to be created.
+ */
+ virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) = 0;
- /*
+ Matrix GetTransform() const { return mTransform; }
+
+ /**
* Set a transform on the surface, this transform is applied at drawing time
* to both the mask and source of the operation.
+ *
+ * Performance note: For some backends it is expensive to change the current
+ * transform (because transforms affect a lot of the parts of the pipeline,
+ * so new transform change can result in a pipeline flush). To get around
+ * this, DrawTarget implementations buffer transform changes and try to only
+ * set the current transform on the backend when required. That tracking has
+ * its own performance impact though, and ideally callers would be smart
+ * enough not to require it. At a future date this method may stop this
+ * doing transform buffering so, if you're a consumer, please try to be smart
+ * about calling this method as little as possible. For example, instead of
+ * concatenating a translation onto the current transform then calling
+ * FillRect, try to integrate the translation into FillRect's aRect
+ * argument's x/y offset.
*/
virtual void SetTransform(const Matrix &aTransform)
{ mTransform = aTransform; mTransformDirty = true; }
- SurfaceFormat GetFormat() { return mFormat; }
+ inline void ConcatTransform(const Matrix &aTransform)
+ { SetTransform(aTransform * Matrix(GetTransform())); }
- /* Tries to get a native surface for a DrawTarget, this may fail if the
+ SurfaceFormat GetFormat() const { return mFormat; }
+
+ /** Tries to get a native surface for a DrawTarget, this may fail if the
* draw target cannot convert to this surface type.
*/
- virtual void *GetNativeSurface(NativeSurfaceType aType) { return NULL; }
+ virtual void *GetNativeSurface(NativeSurfaceType aType) { return nullptr; }
+
+ virtual bool IsDualDrawTarget() const { return false; }
+ virtual bool IsTiledDrawTarget() const { return false; }
+ virtual bool SupportsRegionClipping() const { return true; }
void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
mUserData.Add(key, userData, destroy);
}
- void *GetUserData(UserDataKey *key) {
+ void *GetUserData(UserDataKey *key) const {
return mUserData.Get(key);
}
+ void *RemoveUserData(UserDataKey *key) {
+ return mUserData.Remove(key);
+ }
- /* Within this rectangle all pixels will be opaque by the time the result of
+ /** Within this rectangle all pixels will be opaque by the time the result of
* this DrawTarget is first used for drawing. Either by the underlying surface
* being used as an input to external drawing, or Snapshot() being called.
* This rectangle is specified in device space.
@@ -856,6 +1040,15 @@ public:
return mPermitSubpixelAA;
}
+#ifdef USE_SKIA_GPU
+ virtual bool InitWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat)
+ {
+ MOZ_CRASH();
+ }
+#endif
+
protected:
UserData mUserData;
Matrix mTransform;
@@ -866,18 +1059,65 @@ protected:
SurfaceFormat mFormat;
};
+class DrawTargetCapture : public DrawTarget
+{
+};
+
class DrawEventRecorder : public RefCounted<DrawEventRecorder>
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorder)
virtual ~DrawEventRecorder() { }
};
+struct Tile
+{
+ RefPtr<DrawTarget> mDrawTarget;
+ IntPoint mTileOrigin;
+};
+
+struct TileSet
+{
+ Tile* mTiles;
+ size_t mTileCount;
+};
+
+struct Config {
+ LogForwarder* mLogForwarder;
+ int32_t mMaxTextureSize;
+ int32_t mMaxAllocSize;
+
+ Config()
+ : mLogForwarder(nullptr)
+ , mMaxTextureSize(8192)
+ , mMaxAllocSize(52000000)
+ {}
+};
+
class GFX2D_API Factory
{
public:
+ static void Init(const Config& aConfig);
+ static void ShutDown();
+
static bool HasSSE2();
- static TemporaryRef<DrawTarget> CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize);
+ /** Make sure that the given dimensions don't overflow a 32-bit signed int
+ * using 4 bytes per pixel; optionally, make sure that either dimension
+ * doesn't exceed the given limit.
+ */
+ static bool CheckSurfaceSize(const IntSize &sz,
+ int32_t limit = 0,
+ int32_t allocLimit = 0);
+
+ /** Make sure the given dimension satisfies the CheckSurfaceSize and is
+ * within 8k limit. The 8k value is chosen a bit randomly.
+ */
+ static bool ReasonableSurfaceSize(const IntSize &aSize);
+
+ static bool AllowedSurfaceSize(const IntSize &aSize);
+
+ static TemporaryRef<DrawTarget> CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr);
static TemporaryRef<DrawTarget>
CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat);
@@ -894,16 +1134,16 @@ public:
/**
* This creates a ScaledFont from TrueType data.
*
- * aData - Pointer to the data
- * aSize - Size of the TrueType data
- * aFaceIndex - Index of the font face in the truetype data this ScaledFont needs to represent.
- * aGlyphSize - Size of the glyphs in this ScaledFont
- * aType - Type of ScaledFont that should be created.
+ * @param aData Pointer to the data
+ * @param aSize Size of the TrueType data
+ * @param aFaceIndex Index of the font face in the truetype data this ScaledFont needs to represent.
+ * @param aGlyphSize Size of the glyphs in this ScaledFont
+ * @param aType Type of ScaledFont that should be created.
*/
static TemporaryRef<ScaledFont>
CreateScaledFontForTrueTypeData(uint8_t *aData, uint32_t aSize, uint32_t aFaceIndex, Float aGlyphSize, FontType aType);
- /*
+ /**
* This creates a scaled font with an associated cairo_scaled_font_t, and
* must be used when using the Cairo backend. The NativeFont and
* cairo_scaled_font_t* parameters must correspond to the same font.
@@ -911,15 +1151,26 @@ public:
static TemporaryRef<ScaledFont>
CreateScaledFontWithCairo(const NativeFont &aNativeFont, Float aSize, cairo_scaled_font_t* aScaledFont);
- /*
+ /**
* This creates a simple data source surface for a certain size. It allocates
* new memory for the surface. This memory is freed when the surface is
- * destroyed.
+ * destroyed. The caller is responsible for handing the case where nullptr
+ * is returned. The surface is not zeroed unless requested.
*/
static TemporaryRef<DataSourceSurface>
- CreateDataSourceSurface(const IntSize &aSize, SurfaceFormat aFormat);
+ CreateDataSourceSurface(const IntSize &aSize, SurfaceFormat aFormat, bool aZero = false);
- /*
+ /**
+ * This creates a simple data source surface for a certain size with a
+ * specific stride, which must be large enough to fit all pixels.
+ * It allocates new memory for the surface. This memory is freed when
+ * the surface is destroyed. The caller is responsible for handling the case
+ * where nullptr is returned. The surface is not zeroed unless requested.
+ */
+ static TemporaryRef<DataSourceSurface>
+ CreateDataSourceSurfaceWithStride(const IntSize &aSize, SurfaceFormat aFormat, int32_t aStride, bool aZero = false);
+
+ /**
* This creates a simple data source surface for some existing data. It will
* wrap this data and the data for this source surface. The caller is
* responsible for deallocating the memory only after destruction of the
@@ -934,9 +1185,47 @@ public:
static void SetGlobalEventRecorder(DrawEventRecorder *aRecorder);
+ // This is a little hacky at the moment, but we want to have this data. Bug 1068613.
+ static void SetLogForwarder(LogForwarder* aLogFwd);
+
+ static uint32_t GetMaxSurfaceSize(BackendType aType);
+
+ static LogForwarder* GetLogForwarder() { return sConfig ? sConfig->mLogForwarder : nullptr; }
+
+private:
+ static Config* sConfig;
+public:
+
#ifdef USE_SKIA_GPU
static TemporaryRef<DrawTarget>
- CreateSkiaDrawTargetForFBO(unsigned int aFBOID, GrContext *aContext, const IntSize &aSize, SurfaceFormat aFormat);
+ CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat);
+#endif
+
+ static void PurgeAllCaches();
+
+#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
+ static TemporaryRef<GlyphRenderingOptions>
+ CreateCairoGlyphRenderingOptions(FontHinting aHinting, bool aAutoHinting);
+#endif
+ static TemporaryRef<DrawTarget>
+ CreateDualDrawTarget(DrawTarget *targetA, DrawTarget *targetB);
+
+ /*
+ * This creates a new tiled DrawTarget. When a tiled drawtarget is used the
+ * drawing is distributed over number of tiles which may each hold an
+ * individual offset. The tiles in the set must each have the same backend
+ * and format.
+ */
+ static TemporaryRef<DrawTarget> CreateTiledDrawTarget(const TileSet& aTileSet);
+
+ static bool DoesBackendSupportDataDrawtarget(BackendType aType);
+
+#ifdef XP_MACOSX
+ static TemporaryRef<DrawTarget> CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize);
+ static TemporaryRef<GlyphRenderingOptions>
+ CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor);
#endif
#ifdef WIN32
@@ -948,6 +1237,12 @@ public:
static void SetDirect3D10Device(ID3D10Device1 *aDevice);
static ID3D10Device1 *GetDirect3D10Device();
+ static TemporaryRef<DrawTarget> CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat);
+
+ static void SetDirect3D11Device(ID3D11Device *aDevice);
+ static ID3D11Device *GetDirect3D11Device();
+ static ID2D1Device *GetD2D1Device();
+ static bool SupportsD2D1();
static TemporaryRef<GlyphRenderingOptions>
CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams);
@@ -957,7 +1252,9 @@ public:
static void D2DCleanup();
private:
+ static ID2D1Device *mD2D1Device;
static ID3D10Device1 *mD3D10Device;
+ static ID3D11Device *mD3D11Device;
#endif
static DrawEventRecorder *mRecorder;
diff --git a/gfx/2d/BaseCoord.h b/gfx/2d/BaseCoord.h
new file mode 100644
index 000000000..bbccbc7ae
--- /dev/null
+++ b/gfx/2d/BaseCoord.h
@@ -0,0 +1,110 @@
+/* -*- 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 MOZILLA_GFX_BASECOORD_H_
+#define MOZILLA_GFX_BASECOORD_H_
+
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Do not use this class directly. Subclass it, pass that subclass as the
+ * Sub parameter, and only use that subclass. This allows methods to safely
+ * cast 'this' to 'Sub*'.
+ */
+template <class T, class Sub>
+struct BaseCoord {
+ T value;
+
+ // Constructors
+ MOZ_CONSTEXPR BaseCoord() : value(0) {}
+ explicit MOZ_CONSTEXPR BaseCoord(T aValue) : value(aValue) {}
+
+ // Note that '=' isn't defined so we'll get the
+ // compiler generated default assignment operator
+
+ operator T() const { return value; }
+
+ friend bool operator==(Sub aA, Sub aB) {
+ return aA.value == aB.value;
+ }
+ friend bool operator!=(Sub aA, Sub aB) {
+ return aA.value != aB.value;
+ }
+
+ friend Sub operator+(Sub aA, Sub aB) {
+ return Sub(aA.value + aB.value);
+ }
+ friend Sub operator-(Sub aA, Sub aB) {
+ return Sub(aA.value - aB.value);
+ }
+ friend Sub operator*(Sub aCoord, T aScale) {
+ return Sub(aCoord.value * aScale);
+ }
+ friend Sub operator*(T aScale, Sub aCoord) {
+ return Sub(aScale * aCoord.value);
+ }
+ friend Sub operator/(Sub aCoord, T aScale) {
+ return Sub(aCoord.value / aScale);
+ }
+ // 'scale / coord' is intentionally omitted because it doesn't make sense.
+
+ Sub& operator+=(Sub aCoord) {
+ value += aCoord.value;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(Sub aCoord) {
+ value -= aCoord.value;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator*=(T aScale) {
+ value *= aScale;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator/=(T aScale) {
+ value /= aScale;
+ return *static_cast<Sub*>(this);
+ }
+
+ // Since BaseCoord is implicitly convertible to its value type T, we need
+ // mixed-type operator overloads to avoid ambiguities at mixed-type call
+ // sites. As we transition more of our code to strongly-typed classes, we
+ // may be able to remove some or all of these overloads.
+ friend bool operator==(Sub aA, T aB) {
+ return aA.value == aB;
+ }
+ friend bool operator==(T aA, Sub aB) {
+ return aA == aB.value;
+ }
+ friend bool operator!=(Sub aA, T aB) {
+ return aA.value != aB;
+ }
+ friend bool operator!=(T aA, Sub aB) {
+ return aA != aB.value;
+ }
+ friend T operator+(Sub aA, T aB) {
+ return aA.value + aB;
+ }
+ friend T operator+(T aA, Sub aB) {
+ return aA + aB.value;
+ }
+ friend T operator-(Sub aA, T aB) {
+ return aA.value - aB;
+ }
+ friend T operator-(T aA, Sub aB) {
+ return aA - aB.value;
+ }
+
+ Sub operator-() const {
+ return Sub(-value);
+ }
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_BASECOORD_H_ */
diff --git a/gfx/2d/BaseMargin.h b/gfx/2d/BaseMargin.h
index e7203b0af..33afd4658 100644
--- a/gfx/2d/BaseMargin.h
+++ b/gfx/2d/BaseMargin.h
@@ -9,6 +9,57 @@
#include "Types.h"
namespace mozilla {
+
+/**
+ * Sides represents a set of physical sides.
+ */
+struct Sides final {
+ Sides() : mBits(0) {}
+ explicit Sides(SideBits aSideBits)
+ {
+ MOZ_ASSERT((aSideBits & ~eSideBitsAll) == 0, "illegal side bits");
+ mBits = aSideBits;
+ }
+ bool IsEmpty() const { return mBits == 0; }
+ bool Top() const { return mBits & eSideBitsTop; }
+ bool Right() const { return mBits & eSideBitsRight; }
+ bool Bottom() const { return mBits & eSideBitsBottom; }
+ bool Left() const { return mBits & eSideBitsLeft; }
+ bool Contains(SideBits aSideBits) const
+ {
+ MOZ_ASSERT((aSideBits & ~eSideBitsAll) == 0, "illegal side bits");
+ return (mBits & aSideBits) == aSideBits;
+ }
+ Sides operator|(Sides aOther) const
+ {
+ return Sides(SideBits(mBits | aOther.mBits));
+ }
+ Sides operator|(SideBits aSideBits) const
+ {
+ return *this | Sides(aSideBits);
+ }
+ Sides& operator|=(Sides aOther)
+ {
+ mBits |= aOther.mBits;
+ return *this;
+ }
+ Sides& operator|=(SideBits aSideBits)
+ {
+ return *this |= Sides(aSideBits);
+ }
+ bool operator==(Sides aOther) const
+ {
+ return mBits == aOther.mBits;
+ }
+ bool operator!=(Sides aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+private:
+ uint8_t mBits;
+};
+
namespace gfx {
/**
@@ -17,7 +68,7 @@ namespace gfx {
*/
template <class T, class Sub>
struct BaseMargin {
- typedef mozilla::css::Side SideT;
+ typedef mozilla::Side SideT; // because we have a method named Side
// Do not change the layout of these members; the Side() methods below
// depend on this order.
@@ -38,11 +89,27 @@ struct BaseMargin {
T& Side(SideT aSide) {
// This is ugly!
- return *(&top + aSide);
+ return *(&top + T(aSide));
}
T Side(SideT aSide) const {
// This is ugly!
- return *(&top + aSide);
+ return *(&top + T(aSide));
+ }
+
+ void ApplySkipSides(Sides aSkipSides)
+ {
+ if (aSkipSides.Top()) {
+ top = 0;
+ }
+ if (aSkipSides.Right()) {
+ right = 0;
+ }
+ if (aSkipSides.Bottom()) {
+ bottom = 0;
+ }
+ if (aSkipSides.Left()) {
+ left = 0;
+ }
}
// Overloaded operators. Note that '=' isn't defined so we'll get the
diff --git a/gfx/2d/BasePoint.h b/gfx/2d/BasePoint.h
index a7da0cc03..4bd60275f 100644
--- a/gfx/2d/BasePoint.h
+++ b/gfx/2d/BasePoint.h
@@ -6,6 +6,12 @@
#ifndef MOZILLA_GFX_BASEPOINT_H_
#define MOZILLA_GFX_BASEPOINT_H_
+#include <cmath>
+#include "mozilla/Attributes.h"
+#include "mozilla/ToString.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/TypeTraits.h"
+
namespace mozilla {
namespace gfx {
@@ -14,13 +20,13 @@ namespace gfx {
* Sub parameter, and only use that subclass. This allows methods to safely
* cast 'this' to 'Sub*'.
*/
-template <class T, class Sub>
+template <class T, class Sub, class Coord = T>
struct BasePoint {
T x, y;
// Constructors
- BasePoint() : x(0), y(0) {}
- BasePoint(T aX, T aY) : x(aX), y(aY) {}
+ MOZ_CONSTEXPR BasePoint() : x(0), y(0) {}
+ MOZ_CONSTEXPR BasePoint(Coord aX, Coord aY) : x(aX), y(aY) {}
void MoveTo(T aX, T aY) { x = aX; y = aY; }
void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
@@ -62,6 +68,32 @@ struct BasePoint {
Sub operator-() const {
return Sub(-x, -y);
}
+
+ T Length() const {
+ return hypot(x, y);
+ }
+
+ // Round() is *not* rounding to nearest integer if the values are negative.
+ // They are always rounding as floor(n + 0.5).
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
+ Sub& Round() {
+ x = Coord(floor(T(x) + T(0.5)));
+ y = Coord(floor(T(y) + T(0.5)));
+ return *static_cast<Sub*>(this);
+ }
+
+ // "Finite" means not inf and not NaN
+ bool IsFinite() const
+ {
+ typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value, float, double>::Type FloatType;
+ return (mozilla::IsFinite(FloatType(x)) && mozilla::IsFinite(FloatType(y)));
+ return true;
+ }
+
+ friend std::ostream& operator<<(std::ostream& stream, const BasePoint<T, Sub, Coord>& aPoint) {
+ return stream << '(' << aPoint.x << ',' << aPoint.y << ')';
+ }
+
};
}
diff --git a/gfx/2d/BasePoint3D.h b/gfx/2d/BasePoint3D.h
index a5fb266d5..c64ea247a 100644
--- a/gfx/2d/BasePoint3D.h
+++ b/gfx/2d/BasePoint3D.h
@@ -6,6 +6,8 @@
#ifndef MOZILLA_BASEPOINT3D_H_
#define MOZILLA_BASEPOINT3D_H_
+#include "mozilla/Assertions.h"
+
namespace mozilla {
namespace gfx {
@@ -29,12 +31,12 @@ struct BasePoint3D {
// compiler generated default assignment operator
T& operator[](int aIndex) {
- NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 2, "Invalid array index");
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 2);
return *((&x)+aIndex);
}
const T& operator[](int aIndex) const {
- NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 2, "Invalid array index");
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 2);
return *((&x)+aIndex);
}
diff --git a/gfx/2d/BasePoint4D.h b/gfx/2d/BasePoint4D.h
index ab9fa4523..e098876a8 100644
--- a/gfx/2d/BasePoint4D.h
+++ b/gfx/2d/BasePoint4D.h
@@ -6,6 +6,8 @@
#ifndef MOZILLA_BASEPOINT4D_H_
#define MOZILLA_BASEPOINT4D_H_
+#include "mozilla/Assertions.h"
+
namespace mozilla {
namespace gfx {
@@ -86,12 +88,12 @@ struct BasePoint4D {
}
T& operator[](int aIndex) {
- NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid array index");
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid array index");
return *((&x)+aIndex);
}
const T& operator[](int aIndex) const {
- NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid array index");
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid array index");
return *((&x)+aIndex);
}
@@ -114,6 +116,8 @@ struct BasePoint4D {
void Normalize() {
*this /= Length();
}
+
+ bool HasPositiveWCoord() { return w > 0; }
};
}
diff --git a/gfx/2d/BaseRect.h b/gfx/2d/BaseRect.h
index f0f5efec2..0b6810cfd 100644
--- a/gfx/2d/BaseRect.h
+++ b/gfx/2d/BaseRect.h
@@ -6,9 +6,14 @@
#ifndef MOZILLA_GFX_BASERECT_H_
#define MOZILLA_GFX_BASERECT_H_
-#include <cmath>
-#include <mozilla/Assertions.h>
#include <algorithm>
+#include <cmath>
+#include <ostream>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/TypeTraits.h"
+#include "Types.h"
namespace mozilla {
namespace gfx {
@@ -36,7 +41,7 @@ namespace gfx {
* Do not use this class directly. Subclass it, pass that subclass as the
* Sub parameter, and only use that subclass.
*/
-template <class T, class Sub, class Point, class SizeT, class Margin>
+template <class T, class Sub, class Point, class SizeT, class MarginT>
struct BaseRect {
T x, y, width, height;
@@ -59,10 +64,11 @@ struct BaseRect {
// "Finite" means not inf and not NaN
bool IsFinite() const
{
- return (std::isfinite(x) &&
- std::isfinite(y) &&
- std::isfinite(width) &&
- std::isfinite(height));
+ typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value, float, double>::Type FloatType;
+ return (mozilla::IsFinite(FloatType(x)) &&
+ mozilla::IsFinite(FloatType(y)) &&
+ mozilla::IsFinite(FloatType(width)) &&
+ mozilla::IsFinite(FloatType(height)));
}
// Returns true if this rectangle contains the interior of aRect. Always
@@ -74,13 +80,17 @@ struct BaseRect {
(x <= aRect.x && aRect.XMost() <= XMost() &&
y <= aRect.y && aRect.YMost() <= YMost());
}
- // Returns true if this rectangle contains the rectangle (aX,aY,1,1).
+ // Returns true if this rectangle contains the point. Points are considered
+ // in the rectangle if they are on the left or top edge, but outside if they
+ // are on the right or bottom edge.
bool Contains(T aX, T aY) const
{
- return x <= aX && aX + 1 <= XMost() &&
- y <= aY && aY + 1 <= YMost();
+ return x <= aX && aX < XMost() &&
+ y <= aY && aY < YMost();
}
- // Returns true if this rectangle contains the rectangle (aPoint.x,aPoint.y,1,1).
+ // Returns true if this rectangle contains the point. Points are considered
+ // in the rectangle if they are on the left or top edge, but outside if they
+ // are on the right or bottom edge.
bool Contains(const Point& aPoint) const { return Contains(aPoint.x, aPoint.y); }
// Intersection. Returns TRUE if the receiver's area has non-empty
@@ -88,7 +98,8 @@ struct BaseRect {
// Always returns false if aRect is empty or 'this' is empty.
bool Intersects(const Sub& aRect) const
{
- return x < aRect.XMost() && aRect.x < XMost() &&
+ return !IsEmpty() && !aRect.IsEmpty() &&
+ x < aRect.XMost() && aRect.x < XMost() &&
y < aRect.YMost() && aRect.y < YMost();
}
// Returns the rectangle containing the intersection of the points
@@ -166,6 +177,23 @@ struct BaseRect {
*static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2);
}
+ // Expands the rect to include the point
+ void ExpandToEnclose(const Point& aPoint)
+ {
+ if (aPoint.x < x) {
+ width = XMost() - aPoint.x;
+ x = aPoint.x;
+ } else if (aPoint.x > XMost()) {
+ width = aPoint.x - x;
+ }
+ if (aPoint.y < y) {
+ height = YMost() - aPoint.y;
+ y = aPoint.y;
+ } else if (aPoint.y > YMost()) {
+ height = aPoint.y - y;
+ }
+ }
+
void SetRect(T aX, T aY, T aWidth, T aHeight)
{
x = aX; y = aY; width = aWidth; height = aHeight;
@@ -189,7 +217,7 @@ struct BaseRect {
width += 2 * aDx;
height += 2 * aDy;
}
- void Inflate(const Margin& aMargin)
+ void Inflate(const MarginT& aMargin)
{
x -= aMargin.left;
y -= aMargin.top;
@@ -198,6 +226,20 @@ struct BaseRect {
}
void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); }
+ void InflateToMultiple(const SizeT& aMultiple)
+ {
+ T xMost = XMost();
+ T yMost = YMost();
+
+ x = static_cast<T>(floor(x / aMultiple.width)) * aMultiple.width;
+ y = static_cast<T>(floor(y / aMultiple.height)) * aMultiple.height;
+ xMost = static_cast<T>(ceil(x / aMultiple.width)) * aMultiple.width;
+ yMost = static_cast<T>(ceil(y / aMultiple.height)) * aMultiple.height;
+
+ width = xMost - x;
+ height = yMost - y;
+ }
+
void Deflate(T aD) { Deflate(aD, aD); }
void Deflate(T aDx, T aDy)
{
@@ -206,7 +248,7 @@ struct BaseRect {
width = std::max(T(0), width - 2 * aDx);
height = std::max(T(0), height - 2 * aDy);
}
- void Deflate(const Margin& aMargin)
+ void Deflate(const MarginT& aMargin)
{
x += aMargin.left;
y += aMargin.top;
@@ -231,13 +273,25 @@ struct BaseRect {
return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty());
}
- Sub operator+(const Point& aPoint) const
+ friend Sub operator+(Sub aSub, const Point& aPoint)
+ {
+ aSub += aPoint;
+ return aSub;
+ }
+ friend Sub operator-(Sub aSub, const Point& aPoint)
{
- return Sub(x + aPoint.x, y + aPoint.y, width, height);
+ aSub -= aPoint;
+ return aSub;
}
- Sub operator-(const Point& aPoint) const
+ friend Sub operator+(Sub aSub, const SizeT& aSize)
{
- return Sub(x - aPoint.x, y - aPoint.y, width, height);
+ aSub += aSize;
+ return aSub;
+ }
+ friend Sub operator-(Sub aSub, const SizeT& aSize)
+ {
+ aSub -= aSize;
+ return aSub;
}
Sub& operator+=(const Point& aPoint)
{
@@ -249,14 +303,25 @@ struct BaseRect {
MoveBy(-aPoint);
return *static_cast<Sub*>(this);
}
-
+ Sub& operator+=(const SizeT& aSize)
+ {
+ width += aSize.width;
+ height += aSize.height;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(const SizeT& aSize)
+ {
+ width -= aSize.width;
+ height -= aSize.height;
+ return *static_cast<Sub*>(this);
+ }
// Find difference as a Margin
- Margin operator-(const Sub& aRect) const
+ MarginT operator-(const Sub& aRect) const
{
- return Margin(aRect.y - y,
- XMost() - aRect.XMost(),
- YMost() - aRect.YMost(),
- aRect.x - x);
+ return MarginT(aRect.y - y,
+ XMost() - aRect.XMost(),
+ YMost() - aRect.YMost(),
+ aRect.x - x);
}
// Helpers for accessing the vertices
@@ -264,6 +329,33 @@ struct BaseRect {
Point TopRight() const { return Point(XMost(), y); }
Point BottomLeft() const { return Point(x, YMost()); }
Point BottomRight() const { return Point(XMost(), YMost()); }
+ Point AtCorner(int aCorner) const {
+ switch (aCorner) {
+ case RectCorner::TopLeft: return TopLeft();
+ case RectCorner::TopRight: return TopRight();
+ case RectCorner::BottomRight: return BottomRight();
+ case RectCorner::BottomLeft: return BottomLeft();
+ }
+ MOZ_CRASH("Incomplete switch");
+ }
+ Point CCWCorner(mozilla::Side side) const {
+ switch (side) {
+ case NS_SIDE_TOP: return TopLeft();
+ case NS_SIDE_RIGHT: return TopRight();
+ case NS_SIDE_BOTTOM: return BottomRight();
+ case NS_SIDE_LEFT: return BottomLeft();
+ }
+ MOZ_CRASH("Incomplete switch");
+ }
+ Point CWCorner(mozilla::Side side) const {
+ switch (side) {
+ case NS_SIDE_TOP: return TopRight();
+ case NS_SIDE_RIGHT: return BottomRight();
+ case NS_SIDE_BOTTOM: return BottomLeft();
+ case NS_SIDE_LEFT: return TopLeft();
+ }
+ MOZ_CRASH("Incomplete switch");
+ }
Point Center() const { return Point(x, y) + Point(width, height)/2; }
SizeT Size() const { return SizeT(width, height); }
@@ -440,21 +532,27 @@ struct BaseRect {
}
/**
- * Clamp aRect to this rectangle. This returns aRect after it is forced
- * inside the bounds of this rectangle. It will attempt to retain the size
- * but will shrink the dimensions that don't fit.
+ * Clamp this rectangle to be inside aRect. The function returns a copy of
+ * this rect after it is forced inside the bounds of aRect. It will attempt to
+ * retain the size but will shrink the dimensions that don't fit.
*/
- Sub ClampRect(const Sub& aRect) const
+ Sub ForceInside(const Sub& aRect) const
{
Sub rect(std::max(aRect.x, x),
std::max(aRect.y, y),
std::min(aRect.width, width),
std::min(aRect.height, height));
- rect.x = std::min(rect.XMost(), XMost()) - rect.width;
- rect.y = std::min(rect.YMost(), YMost()) - rect.height;
+ rect.x = std::min(rect.XMost(), aRect.XMost()) - rect.width;
+ rect.y = std::min(rect.YMost(), aRect.YMost()) - rect.height;
return rect;
}
+ friend std::ostream& operator<<(std::ostream& stream,
+ const BaseRect<T, Sub, Point, SizeT, MarginT>& aRect) {
+ return stream << '(' << aRect.x << ',' << aRect.y << ','
+ << aRect.width << ',' << aRect.height << ')';
+ }
+
private:
// Do not use the default operator== or operator!= !
// Use IsEqualEdges or IsEqualInterior explicitly.
diff --git a/gfx/2d/BaseSize.h b/gfx/2d/BaseSize.h
index 54c578d1d..bc057286e 100644
--- a/gfx/2d/BaseSize.h
+++ b/gfx/2d/BaseSize.h
@@ -6,6 +6,8 @@
#ifndef MOZILLA_GFX_BASESIZE_H_
#define MOZILLA_GFX_BASESIZE_H_
+#include "mozilla/Attributes.h"
+
namespace mozilla {
namespace gfx {
@@ -19,8 +21,8 @@ struct BaseSize {
T width, height;
// Constructors
- BaseSize() : width(0), height(0) {}
- BaseSize(T aWidth, T aHeight) : width(aWidth), height(aHeight) {}
+ MOZ_CONSTEXPR BaseSize() : width(0), height(0) {}
+ MOZ_CONSTEXPR BaseSize(T aWidth, T aHeight) : width(aWidth), height(aHeight) {}
void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
@@ -67,6 +69,10 @@ struct BaseSize {
Sub operator/(T aScale) const {
return Sub(width / aScale, height / aScale);
}
+ void Scale(T aXScale, T aYScale) {
+ width *= aXScale;
+ height *= aYScale;
+ }
Sub operator*(const Sub& aSize) const {
return Sub(width * aSize.width, height * aSize.height);
diff --git a/gfx/2d/Blur.cpp b/gfx/2d/Blur.cpp
index 1c7e0209d..c852c2c43 100644
--- a/gfx/2d/Blur.cpp
+++ b/gfx/2d/Blur.cpp
@@ -4,91 +4,28 @@
* 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/. */
-#include "mozilla/gfx/Blur.h"
+#include "Blur.h"
#include <algorithm>
#include <math.h>
#include <string.h>
-#ifdef WIN32
-#include <windows.h>
-#else
-#include "nspr.h"
-#endif
#include "mozilla/CheckedInt.h"
#include "mozilla/Constants.h"
-#include "mozilla/Util.h"
#include "2D.h"
+#include "DataSurfaceHelpers.h"
#include "Tools.h"
+#ifdef BUILD_ARM_NEON
+#include "mozilla/arm.h"
+#endif
+
using namespace std;
namespace mozilla {
namespace gfx {
-static uint32_t NumberOfProcessors = 0;
-
-
-#ifdef WIN32
-static void
-GetNumberOfLogicalProcessors(void)
-{
- SYSTEM_INFO SystemInfo;
-
- GetSystemInfo(&SystemInfo);
- NumberOfProcessors = SystemInfo.dwNumberOfProcessors;
-}
-#endif
-
-static void
-GetNumberOfProcessors(void)
-{
-#ifdef WIN32
- PSYSTEM_LOGICAL_PROCESSOR_INFORMATION SystemLogicalProcessorInformation = NULL;
- DWORD SizeSystemLogicalProcessorInformation = 0;
-
- while(!GetLogicalProcessorInformation(SystemLogicalProcessorInformation, &SizeSystemLogicalProcessorInformation)) {
- if(SystemLogicalProcessorInformation) free(SystemLogicalProcessorInformation);
-
- if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
- SystemLogicalProcessorInformation =
- static_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION>(malloc(SizeSystemLogicalProcessorInformation));
- } else {
- GetNumberOfLogicalProcessors();
- return;
- }
- }
-
- DWORD ProcessorCore = 0;
- PSYSTEM_LOGICAL_PROCESSOR_INFORMATION Ptr = SystemLogicalProcessorInformation;
-
- for(DWORD Offset = sizeof SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
- Offset <= SizeSystemLogicalProcessorInformation;
- Offset += sizeof SYSTEM_LOGICAL_PROCESSOR_INFORMATION) {
- if(Ptr++->Relationship == RelationProcessorCore) ProcessorCore++;
- }
-
- free(SystemLogicalProcessorInformation);
-
- if(ProcessorCore) {
- NumberOfProcessors = ProcessorCore;
- } else {
- GetNumberOfLogicalProcessors();
- }
-#else
- //Use fallback check on non-windows
- NumberOfProcessors = PR_GetNumberOfProcessors();
-
- if (NumberOfProcessors == 0 || !NumberOfProcessors)
- NumberOfProcessors = 1;
-
- if (NumberOfProcessors > 8)
- NumberOfProcessors = 8;
-
-#endif
-}
-
/**
* Box blur involves looking at one pixel, and setting its value to the average
* of its neighbouring pixels.
@@ -101,7 +38,6 @@ GetNumberOfProcessors(void)
* @param aSkipRect An area to skip blurring in.
* XXX shouldn't we pass stride in separately here?
*/
-
static void
BoxBlurHorizontal(unsigned char* aInput,
unsigned char* aOutput,
@@ -120,10 +56,6 @@ BoxBlurHorizontal(unsigned char* aInput,
memcpy(aOutput, aInput, aWidth*aRows);
return;
}
-
- // Pale Moon: Processor check.
- if(NumberOfProcessors == 0) GetNumberOfProcessors();
-
uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
for (int32_t y = 0; y < aRows; y++) {
@@ -136,9 +68,8 @@ BoxBlurHorizontal(unsigned char* aInput,
y = aSkipRect.YMost() - 1;
continue;
}
-
+
uint32_t alphaSum = 0;
-#pragma loop(hint_parallel(0))
for (int32_t i = 0; i < boxSize; i++) {
int32_t pos = i - aLeftLobe;
// See assertion above; if aWidth is zero, then we would have no
@@ -147,7 +78,6 @@ BoxBlurHorizontal(unsigned char* aInput,
pos = min(pos, aWidth - 1);
alphaSum += aInput[aWidth * y + pos];
}
-#pragma loop(hint_parallel(0))
for (int32_t x = 0; x < aWidth; x++) {
// Check whether we are within the skip rect. If so, go
// to the next point outside the skip rect.
@@ -215,7 +145,6 @@ BoxBlurVertical(unsigned char* aInput,
}
uint32_t alphaSum = 0;
-#pragma loop(hint_parallel(0))
for (int32_t i = 0; i < boxSize; i++) {
int32_t pos = i - aTopLobe;
// See assertion above; if aRows is zero, then we would have no
@@ -224,7 +153,6 @@ BoxBlurVertical(unsigned char* aInput,
pos = min(pos, aRows - 1);
alphaSum += aInput[aWidth * pos + x];
}
-#pragma loop(hint_parallel(0))
for (int32_t y = 0; y < aRows; y++) {
if (inSkipRectX && y >= aSkipRect.y &&
y < aSkipRect.YMost()) {
@@ -325,8 +253,7 @@ SpreadHorizontal(unsigned char* aInput,
y = aSkipRect.YMost() - 1;
continue;
}
-
-#pragma loop(hint_parallel(0))
+
for (int32_t x = 0; x < aWidth; x++) {
// Check whether we are within the skip rect. If so, go
// to the next point outside the skip rect.
@@ -372,7 +299,6 @@ SpreadVertical(unsigned char* aInput,
continue;
}
-#pragma loop(hint_parallel(0))
for (int32_t y = 0; y < aRows; y++) {
// Check whether we are within the skip rect. If so, go
// to the next point outside the skip rect.
@@ -413,7 +339,7 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
const Rect* aSkipRect)
: mSpreadRadius(aSpreadRadius),
mBlurRadius(aBlurRadius),
- mSurfaceAllocationSize(-1)
+ mSurfaceAllocationSize(0)
{
Rect rect(aRect);
rect.Inflate(Size(aBlurRadius + aSpreadRadius));
@@ -462,28 +388,29 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
// We need to leave room for an additional 3 bytes for a potential overrun
// in our blurring code.
- CheckedInt<int32_t> size = CheckedInt<int32_t>(mStride) * mRect.height + 3;
- if (size.isValid()) {
- mSurfaceAllocationSize = size.value();
+ size_t size = BufferSizeFromStrideAndHeight(mStride, mRect.height, 3);
+ if (size != 0) {
+ mSurfaceAllocationSize = size;
}
}
}
AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
int32_t aStride,
- float aSigma)
+ float aSigmaX,
+ float aSigmaY)
: mRect(int32_t(aRect.x), int32_t(aRect.y),
int32_t(aRect.width), int32_t(aRect.height)),
mSpreadRadius(),
- mBlurRadius(CalculateBlurRadius(Point(aSigma, aSigma))),
+ mBlurRadius(CalculateBlurRadius(Point(aSigmaX, aSigmaY))),
mStride(aStride),
- mSurfaceAllocationSize(-1)
+ mSurfaceAllocationSize(0)
{
IntRect intRect;
if (aRect.ToIntRect(&intRect)) {
- CheckedInt<int32_t> minDataSize = CheckedInt<int32_t>(intRect.width)*intRect.height;
- if (minDataSize.isValid()) {
- mSurfaceAllocationSize = minDataSize.value();
+ size_t minDataSize = BufferSizeFromStrideAndHeight(intRect.width, intRect.height);
+ if (minDataSize != 0) {
+ mSurfaceAllocationSize = minDataSize;
}
}
}
@@ -522,7 +449,7 @@ AlphaBoxBlur::GetDirtyRect()
return nullptr;
}
-int32_t
+size_t
AlphaBoxBlur::GetSurfaceAllocationSize() const
{
return mSurfaceAllocationSize;
@@ -575,7 +502,11 @@ AlphaBoxBlur::Blur(uint8_t* aData)
// No need to use CheckedInt here - we have validated it in the constructor.
size_t szB = stride * size.height;
- uint8_t* tmpData = new uint8_t[szB];
+ uint8_t* tmpData = new (std::nothrow) uint8_t[szB];
+ if (!tmpData) {
+ return;
+ }
+
memset(tmpData, 0, szB);
uint8_t* a = aData;
@@ -606,11 +537,18 @@ AlphaBoxBlur::Blur(uint8_t* aData)
// We need to leave room for an additional 12 bytes for a maximum overrun
// of 3 pixels in the blurring code.
- AlignedArray<uint32_t> integralImage((integralImageStride / 4) * integralImageSize.height + 12);
+ size_t bufLen = BufferSizeFromStrideAndHeight(integralImageStride, integralImageSize.height, 12);
+ if (bufLen == 0) {
+ return;
+ }
+ // bufLen is a byte count, but here we want a multiple of 32-bit ints, so
+ // we divide by 4.
+ AlignedArray<uint32_t> integralImage((bufLen / 4) + ((bufLen % 4) ? 1 : 0));
if (!integralImage) {
return;
}
+
#ifdef USE_SSE2
if (Factory::HasSSE2()) {
BoxBlur_SSE2(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
@@ -621,6 +559,16 @@ AlphaBoxBlur::Blur(uint8_t* aData)
verticalLobes[2][1], integralImage, integralImageStride);
} else
#endif
+#ifdef BUILD_ARM_NEON
+ if (mozilla::supports_neon()) {
+ BoxBlur_NEON(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
+ verticalLobes[0][1], integralImage, integralImageStride);
+ BoxBlur_NEON(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
+ verticalLobes[1][1], integralImage, integralImageStride);
+ BoxBlur_NEON(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
+ verticalLobes[2][1], integralImage, integralImageStride);
+ } else
+#endif
{
BoxBlur_C(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
verticalLobes[0][1], integralImage, integralImageStride);
@@ -755,7 +703,6 @@ AlphaBoxBlur::BoxBlur_C(uint8_t* aData,
IntRect skipRect = mSkipRect;
uint8_t *data = aData;
int32_t stride = mStride;
-#pragma loop(hint_parallel(0))
for (int32_t y = 0; y < size.height; y++) {
bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
@@ -780,7 +727,7 @@ AlphaBoxBlur::BoxBlur_C(uint8_t* aData,
uint32_t value = bottomRight - topRight - bottomLeft;
value += topLeft;
- data[stride * y + x] = (uint64_t(reciprocal) * value) >> 32;
+ data[stride * y + x] = (uint64_t(reciprocal) * value + (uint64_t(1) << 31)) >> 32;
}
}
}
@@ -802,8 +749,8 @@ static const Float GAUSSIAN_SCALE_FACTOR = Float((3 * sqrt(2 * M_PI) / 4) * 1.5)
IntSize
AlphaBoxBlur::CalculateBlurRadius(const Point& aStd)
{
- IntSize size(static_cast<int32_t>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5)),
- static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5)));
+ IntSize size(static_cast<int32_t>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5f)),
+ static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5f)));
return size;
}
diff --git a/gfx/2d/Blur.h b/gfx/2d/Blur.h
index 3de65a2bb..644b6a474 100644
--- a/gfx/2d/Blur.h
+++ b/gfx/2d/Blur.h
@@ -66,7 +66,8 @@ public:
AlphaBoxBlur(const Rect& aRect,
int32_t aStride,
- float aSigma);
+ float aSigmaX,
+ float aSigmaY);
~AlphaBoxBlur();
@@ -93,12 +94,12 @@ public:
/**
* Return the minimum buffer size that should be given to Blur() method. If
- * negative, the class is not properly setup for blurring. Note that this
+ * zero, the class is not properly setup for blurring. Note that this
* includes the extra three bytes on top of the stride*width, where something
* like gfxImageSurface::GetDataSize() would report without it, even if it
* happens to have the extra bytes.
*/
- int32_t GetSurfaceAllocationSize() const;
+ size_t GetSurfaceAllocationSize() const;
/**
* Perform the blur in-place on the surface backed by specified 8-bit
@@ -123,6 +124,11 @@ private:
void BoxBlur_SSE2(uint8_t* aData,
int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
+#ifdef BUILD_ARM_NEON
+ void BoxBlur_NEON(uint8_t* aData,
+ int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
+ int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
+#endif
static CheckedInt<int32_t> RoundUpToMultipleOf4(int32_t aVal);
@@ -161,7 +167,7 @@ private:
/**
* The minimum size of the buffer needed for the Blur() operation.
*/
- int32_t mSurfaceAllocationSize;
+ size_t mSurfaceAllocationSize;
/**
* Whether mDirtyRect contains valid data.
diff --git a/gfx/2d/BlurNEON.cpp b/gfx/2d/BlurNEON.cpp
new file mode 100644
index 000000000..978b3cdc0
--- /dev/null
+++ b/gfx/2d/BlurNEON.cpp
@@ -0,0 +1,288 @@
+/* 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/. */
+
+#include "Blur.h"
+#include <arm_neon.h>
+
+namespace mozilla {
+namespace gfx {
+
+MOZ_ALWAYS_INLINE
+uint16x4_t Divide(uint32x4_t aValues, uint32x2_t aDivisor)
+{
+ uint64x2_t roundingAddition = vdupq_n_u64(int64_t(1) << 31);
+ uint64x2_t multiplied21 = vmull_u32(vget_low_u32(aValues), aDivisor);
+ uint64x2_t multiplied43 = vmull_u32(vget_high_u32(aValues), aDivisor);
+ return vqmovn_u32(vcombine_u32(vshrn_n_u64(vaddq_u64(multiplied21, roundingAddition), 32),
+ vshrn_n_u64(vaddq_u64(multiplied43, roundingAddition), 32)));
+}
+
+MOZ_ALWAYS_INLINE
+uint16x4_t BlurFourPixels(const uint32x4_t& aTopLeft, const uint32x4_t& aTopRight,
+ const uint32x4_t& aBottomRight, const uint32x4_t& aBottomLeft,
+ const uint32x2_t& aDivisor)
+{
+ uint32x4_t values = vaddq_u32(vsubq_u32(vsubq_u32(aBottomRight, aTopRight), aBottomLeft), aTopLeft);
+ return Divide(values, aDivisor);
+}
+
+MOZ_ALWAYS_INLINE
+void LoadIntegralRowFromRow(uint32_t *aDest, const uint8_t *aSource,
+ int32_t aSourceWidth, int32_t aLeftInflation,
+ int32_t aRightInflation)
+{
+ int32_t currentRowSum = 0;
+
+ for (int x = 0; x < aLeftInflation; x++) {
+ currentRowSum += aSource[0];
+ aDest[x] = currentRowSum;
+ }
+ for (int x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x++) {
+ currentRowSum += aSource[(x - aLeftInflation)];
+ aDest[x] = currentRowSum;
+ }
+ for (int x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
+ currentRowSum += aSource[aSourceWidth - 1];
+ aDest[x] = currentRowSum;
+ }
+}
+
+MOZ_ALWAYS_INLINE void
+GenerateIntegralImage_NEON(int32_t aLeftInflation, int32_t aRightInflation,
+ int32_t aTopInflation, int32_t aBottomInflation,
+ uint32_t *aIntegralImage, size_t aIntegralImageStride,
+ uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize)
+{
+ MOZ_ASSERT(!(aLeftInflation & 3));
+
+ uint32_t stride32bit = aIntegralImageStride / 4;
+ IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
+ aSize.height + aTopInflation + aBottomInflation);
+
+ LoadIntegralRowFromRow(aIntegralImage, aSource, aSize.width, aLeftInflation, aRightInflation);
+
+ for (int y = 1; y < aTopInflation + 1; y++) {
+ uint32_t *intRow = aIntegralImage + (y * stride32bit);
+ uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
+ uint32_t *intFirstRow = aIntegralImage;
+
+ for (int x = 0; x < integralImageSize.width; x += 4) {
+ uint32x4_t firstRow = vld1q_u32(intFirstRow + x);
+ uint32x4_t previousRow = vld1q_u32(intPrevRow + x);
+ vst1q_u32(intRow + x, vaddq_u32(firstRow, previousRow));
+ }
+ }
+
+ for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
+ uint32x4_t currentRowSum = vdupq_n_u32(0);
+ uint32_t *intRow = aIntegralImage + (y * stride32bit);
+ uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
+ uint8_t *sourceRow = aSource + aSourceStride * (y - aTopInflation);
+
+ uint32_t pixel = sourceRow[0];
+ for (int x = 0; x < aLeftInflation; x += 4) {
+ uint32_t temp[4];
+ temp[0] = pixel;
+ temp[1] = temp[0] + pixel;
+ temp[2] = temp[1] + pixel;
+ temp[3] = temp[2] + pixel;
+ uint32x4_t sumPixels = vld1q_u32(temp);
+ sumPixels = vaddq_u32(sumPixels, currentRowSum);
+ currentRowSum = vdupq_n_u32(vgetq_lane_u32(sumPixels, 3));
+ vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x)));
+ }
+
+ for (int x = aLeftInflation; x < (aSize.width + aLeftInflation); x += 4) {
+ // It's important to shuffle here. When we exit this loop currentRowSum
+ // has to be set to sumPixels, so that the following loop can get the
+ // correct pixel for the currentRowSum. The highest order pixel in
+ // currentRowSum could've originated from accumulation in the stride.
+ currentRowSum = vdupq_n_u32(vgetq_lane_u32(currentRowSum, 3));
+
+ uint32_t temp[4];
+ temp[0] = *(sourceRow + (x - aLeftInflation));
+ temp[1] = temp[0] + *(sourceRow + (x - aLeftInflation) + 1);
+ temp[2] = temp[1] + *(sourceRow + (x - aLeftInflation) + 2);
+ temp[3] = temp[2] + *(sourceRow + (x - aLeftInflation) + 3);
+ uint32x4_t sumPixels = vld1q_u32(temp);
+ sumPixels = vaddq_u32(sumPixels, currentRowSum);
+ currentRowSum = sumPixels;
+ vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x)));
+ }
+
+ pixel = sourceRow[aSize.width - 1];
+ int x = (aSize.width + aLeftInflation);
+ if ((aSize.width & 3)) {
+ // Deal with unaligned portion. Get the correct pixel from currentRowSum,
+ // see explanation above.
+ uint32_t intCurrentRowSum = ((uint32_t*)&currentRowSum)[(aSize.width % 4) - 1];
+ for (; x < integralImageSize.width; x++) {
+ // We could be unaligned here!
+ if (!(x & 3)) {
+ // aligned!
+ currentRowSum = vdupq_n_u32(intCurrentRowSum);
+ break;
+ }
+ intCurrentRowSum += pixel;
+ intRow[x] = intPrevRow[x] + intCurrentRowSum;
+ }
+ } else {
+ currentRowSum = vdupq_n_u32(vgetq_lane_u32(currentRowSum, 3));
+ }
+
+ for (; x < integralImageSize.width; x += 4) {
+ uint32_t temp[4];
+ temp[0] = pixel;
+ temp[1] = temp[0] + pixel;
+ temp[2] = temp[1] + pixel;
+ temp[3] = temp[2] + pixel;
+ uint32x4_t sumPixels = vld1q_u32(temp);
+ sumPixels = vaddq_u32(sumPixels, currentRowSum);
+ currentRowSum = vdupq_n_u32(vgetq_lane_u32(sumPixels, 3));
+ vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x)));
+ }
+ }
+
+ if (aBottomInflation) {
+ // Store the last valid row of our source image in the last row of
+ // our integral image. This will be overwritten with the correct values
+ // in the upcoming loop.
+ LoadIntegralRowFromRow(aIntegralImage + (integralImageSize.height - 1) * stride32bit,
+ aSource + (aSize.height - 1) * aSourceStride, aSize.width, aLeftInflation, aRightInflation);
+
+ for (int y = aSize.height + aTopInflation; y < integralImageSize.height; y++) {
+ uint32_t *intRow = aIntegralImage + (y * stride32bit);
+ uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
+ uint32_t *intLastRow = aIntegralImage + (integralImageSize.height - 1) * stride32bit;
+ for (int x = 0; x < integralImageSize.width; x += 4) {
+ vst1q_u32(intRow + x,
+ vaddq_u32(vld1q_u32(intLastRow + x),
+ vld1q_u32(intPrevRow + x)));
+ }
+ }
+ }
+}
+
+/**
+ * Attempt to do an in-place box blur using an integral image.
+ */
+void
+AlphaBoxBlur::BoxBlur_NEON(uint8_t* aData,
+ int32_t aLeftLobe,
+ int32_t aRightLobe,
+ int32_t aTopLobe,
+ int32_t aBottomLobe,
+ uint32_t *aIntegralImage,
+ size_t aIntegralImageStride)
+{
+ IntSize size = GetSize();
+
+ MOZ_ASSERT(size.height > 0);
+
+ // Our 'left' or 'top' lobe will include the current pixel. i.e. when
+ // looking at an integral image the value of a pixel at 'x,y' is calculated
+ // using the value of the integral image values above/below that.
+ aLeftLobe++;
+ aTopLobe++;
+ int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe);
+
+ MOZ_ASSERT(boxSize > 0);
+
+ if (boxSize == 1) {
+ return;
+ }
+
+ uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
+ uint32_t stride32bit = aIntegralImageStride / 4;
+ int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
+
+ GenerateIntegralImage_NEON(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
+ aIntegralImage, aIntegralImageStride, aData,
+ mStride, size);
+
+ uint32x2_t divisor = vdup_n_u32(reciprocal);
+
+ // This points to the start of the rectangle within the IntegralImage that overlaps
+ // the surface being blurred.
+ uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
+ IntRect skipRect = mSkipRect;
+ int32_t stride = mStride;
+ uint8_t *data = aData;
+
+ for (int32_t y = 0; y < size.height; y++) {
+ bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
+ uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
+ uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) + aRightLobe);
+ uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) + aRightLobe);
+ uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
+
+ int32_t x = 0;
+ // Process 16 pixels at a time for as long as possible.
+ for (; x <= size.width - 16; x += 16) {
+ if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
+ x = skipRect.XMost() - 16;
+ // Trigger early jump on coming loop iterations, this will be reset
+ // next line anyway.
+ inSkipRectY = false;
+ continue;
+ }
+
+ uint32x4_t topLeft;
+ uint32x4_t topRight;
+ uint32x4_t bottomRight;
+ uint32x4_t bottomLeft;
+ topLeft = vld1q_u32(topLeftBase + x);
+ topRight = vld1q_u32(topRightBase + x);
+ bottomRight = vld1q_u32(bottomRightBase + x);
+ bottomLeft = vld1q_u32(bottomLeftBase + x);
+ uint16x4_t result1 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = vld1q_u32(topLeftBase + x + 4);
+ topRight = vld1q_u32(topRightBase + x + 4);
+ bottomRight = vld1q_u32(bottomRightBase + x + 4);
+ bottomLeft = vld1q_u32(bottomLeftBase + x + 4);
+ uint16x4_t result2 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = vld1q_u32(topLeftBase + x + 8);
+ topRight = vld1q_u32(topRightBase + x + 8);
+ bottomRight = vld1q_u32(bottomRightBase + x + 8);
+ bottomLeft = vld1q_u32(bottomLeftBase + x + 8);
+ uint16x4_t result3 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = vld1q_u32(topLeftBase + x + 12);
+ topRight = vld1q_u32(topRightBase + x + 12);
+ bottomRight = vld1q_u32(bottomRightBase + x + 12);
+ bottomLeft = vld1q_u32(bottomLeftBase + x + 12);
+ uint16x4_t result4 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ uint8x8_t combine1 = vqmovn_u16(vcombine_u16(result1, result2));
+ uint8x8_t combine2 = vqmovn_u16(vcombine_u16(result3, result4));
+ uint8x16_t final = vcombine_u8(combine1, combine2);
+ vst1q_u8(data + stride * y + x, final);
+ }
+
+ // Process the remaining pixels 4 bytes at a time.
+ for (; x < size.width; x += 4) {
+ if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
+ x = skipRect.XMost() - 4;
+ // Trigger early jump on coming loop iterations, this will be reset
+ // next line anyway.
+ inSkipRectY = false;
+ continue;
+ }
+
+ uint32x4_t topLeft = vld1q_u32(topLeftBase + x);
+ uint32x4_t topRight = vld1q_u32(topRightBase + x);
+ uint32x4_t bottomRight = vld1q_u32(bottomRightBase + x);
+ uint32x4_t bottomLeft = vld1q_u32(bottomLeftBase + x);
+ uint16x4_t result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+ uint32x2_t final = vreinterpret_u32_u8(vmovn_u16(vcombine_u16(result, vdup_n_u16(0))));
+ *(uint32_t*)(data + stride * y + x) = vget_lane_u32(final, 0);
+ }
+ }
+}
+
+}
+}
+
diff --git a/gfx/2d/BlurSSE2.cpp b/gfx/2d/BlurSSE2.cpp
index d67cf07bd..52ca189bb 100644
--- a/gfx/2d/BlurSSE2.cpp
+++ b/gfx/2d/BlurSSE2.cpp
@@ -12,14 +12,32 @@ namespace mozilla {
namespace gfx {
MOZ_ALWAYS_INLINE
-uint32_t DivideAndPack(__m128i aValues, __m128i aDivisor, __m128i aMask)
+__m128i Divide(__m128i aValues, __m128i aDivisor)
{
- __m128i multiplied = _mm_srli_epi64(_mm_mul_epu32(aValues, aDivisor), 32); // 00p300p1
- multiplied = _mm_or_si128(multiplied, _mm_and_si128(_mm_mul_epu32(_mm_srli_epi64(aValues, 32), aDivisor),
- aMask)); // p4p3p2p1
- __m128i final = _mm_packus_epi16(_mm_packs_epi32(multiplied, _mm_setzero_si128()), _mm_setzero_si128());
+ const __m128i mask = _mm_setr_epi32(0x0, 0xffffffff, 0x0, 0xffffffff);
+ static const union {
+ int64_t i64[2];
+ __m128i m;
+ } roundingAddition = { { int64_t(1) << 31, int64_t(1) << 31 } };
+
+ __m128i multiplied31 = _mm_mul_epu32(aValues, aDivisor);
+ __m128i multiplied42 = _mm_mul_epu32(_mm_srli_epi64(aValues, 32), aDivisor);
+
+ // Add 1 << 31 before shifting or masking the lower 32 bits away, so that the
+ // result is rounded.
+ __m128i p_3_1 = _mm_srli_epi64(_mm_add_epi64(multiplied31, roundingAddition.m), 32);
+ __m128i p4_2_ = _mm_and_si128(_mm_add_epi64(multiplied42, roundingAddition.m), mask);
+ __m128i p4321 = _mm_or_si128(p_3_1, p4_2_);
+ return p4321;
+}
- return _mm_cvtsi128_si32(final);
+MOZ_ALWAYS_INLINE
+__m128i BlurFourPixels(const __m128i& aTopLeft, const __m128i& aTopRight,
+ const __m128i& aBottomRight, const __m128i& aBottomLeft,
+ const __m128i& aDivisor)
+{
+ __m128i values = _mm_add_epi32(_mm_sub_epi32(_mm_sub_epi32(aBottomRight, aTopRight), aBottomLeft), aTopLeft);
+ return Divide(values, aDivisor);
}
MOZ_ALWAYS_INLINE
@@ -175,7 +193,7 @@ GenerateIntegralImage_SSE2(int32_t aLeftInflation, int32_t aRightInflation,
*/
void
AlphaBoxBlur::BoxBlur_SSE2(uint8_t* aData,
- int32_t aLeftLobe,
+ int32_t aLeftLobe,
int32_t aRightLobe,
int32_t aTopLobe,
int32_t aBottomLobe,
@@ -209,7 +227,6 @@ AlphaBoxBlur::BoxBlur_SSE2(uint8_t* aData,
mStride, size);
__m128i divisor = _mm_set1_epi32(reciprocal);
- __m128i mask = _mm_setr_epi32(0x0, 0xffffffff, 0x0, 0xffffffff);
// This points to the start of the rectangle within the IntegralImage that overlaps
// the surface being blurred.
@@ -226,7 +243,53 @@ AlphaBoxBlur::BoxBlur_SSE2(uint8_t* aData,
uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) + aRightLobe);
uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
- for (int32_t x = 0; x < size.width; x += 4) {
+ int32_t x = 0;
+ // Process 16 pixels at a time for as long as possible.
+ for (; x <= size.width - 16; x += 16) {
+ if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
+ x = skipRect.XMost() - 16;
+ // Trigger early jump on coming loop iterations, this will be reset
+ // next line anyway.
+ inSkipRectY = false;
+ continue;
+ }
+
+ __m128i topLeft;
+ __m128i topRight;
+ __m128i bottomRight;
+ __m128i bottomLeft;
+
+ topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
+ topRight = loadUnaligned128((__m128i*)(topRightBase + x));
+ bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
+ bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
+ __m128i result1 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 4));
+ topRight = loadUnaligned128((__m128i*)(topRightBase + x + 4));
+ bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 4));
+ bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 4));
+ __m128i result2 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 8));
+ topRight = loadUnaligned128((__m128i*)(topRightBase + x + 8));
+ bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 8));
+ bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 8));
+ __m128i result3 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 12));
+ topRight = loadUnaligned128((__m128i*)(topRightBase + x + 12));
+ bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 12));
+ bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 12));
+ __m128i result4 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ __m128i final = _mm_packus_epi16(_mm_packs_epi32(result1, result2), _mm_packs_epi32(result3, result4));
+
+ _mm_storeu_si128((__m128i*)(data + stride * y + x), final);
+ }
+
+ // Process the remaining pixels 4 bytes at a time.
+ for (; x < size.width; x += 4) {
if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
x = skipRect.XMost() - 4;
// Trigger early jump on coming loop iterations, this will be reset
@@ -239,9 +302,10 @@ AlphaBoxBlur::BoxBlur_SSE2(uint8_t* aData,
__m128i bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
__m128i bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
- __m128i values = _mm_add_epi32(_mm_sub_epi32(_mm_sub_epi32(bottomRight, topRight), bottomLeft), topLeft);
+ __m128i result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+ __m128i final = _mm_packus_epi16(_mm_packs_epi32(result, _mm_setzero_si128()), _mm_setzero_si128());
- *(uint32_t*)(data + stride * y + x) = DivideAndPack(values, divisor, mask);
+ *(uint32_t*)(data + stride * y + x) = _mm_cvtsi128_si32(final);
}
}
diff --git a/gfx/2d/BorrowedContext.h b/gfx/2d/BorrowedContext.h
new file mode 100644
index 000000000..b2d096bac
--- /dev/null
+++ b/gfx/2d/BorrowedContext.h
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 20; 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 _MOZILLA_GFX_BORROWED_CONTEXT_H
+#define _MOZILLA_GFX_BORROWED_CONTEXT_H
+
+#include "2D.h"
+
+struct _cairo;
+typedef struct _cairo cairo_t;
+
+namespace mozilla {
+
+namespace gfx {
+
+/* This is a helper class that let's you borrow a cairo_t from a
+ * DrawTargetCairo. This is used for drawing themed widgets.
+ *
+ * Callers should check the cr member after constructing the object
+ * to see if it succeeded. The DrawTarget should not be used while
+ * the context is borrowed. */
+class BorrowedCairoContext
+{
+public:
+ BorrowedCairoContext()
+ : mCairo(nullptr)
+ , mDT(nullptr)
+ { }
+
+ explicit BorrowedCairoContext(DrawTarget *aDT)
+ : mDT(aDT)
+ {
+ mCairo = BorrowCairoContextFromDrawTarget(aDT);
+ }
+
+ // We can optionally Init after construction in
+ // case we don't know what the DT will be at construction
+ // time.
+ cairo_t *Init(DrawTarget *aDT)
+ {
+ MOZ_ASSERT(!mDT, "Can't initialize twice!");
+ mDT = aDT;
+ return mCairo = BorrowCairoContextFromDrawTarget(aDT);
+ }
+
+ // The caller needs to call Finish if cr is non-null when
+ // they are done with the context. This is currently explicit
+ // instead of happening implicitly in the destructor to make
+ // what's happening in the caller more clear. It also
+ // let's you resume using the DrawTarget in the same scope.
+ void Finish()
+ {
+ if (mCairo) {
+ ReturnCairoContextToDrawTarget(mDT, mCairo);
+ mCairo = nullptr;
+ }
+ }
+
+ ~BorrowedCairoContext() {
+ MOZ_ASSERT(!mCairo);
+ }
+
+ cairo_t *mCairo;
+private:
+ static cairo_t* BorrowCairoContextFromDrawTarget(DrawTarget *aDT);
+ static void ReturnCairoContextToDrawTarget(DrawTarget *aDT, cairo_t *aCairo);
+ DrawTarget *mDT;
+};
+
+#ifdef XP_MACOSX
+/* This is a helper class that let's you borrow a CGContextRef from a
+ * DrawTargetCG. This is used for drawing themed widgets.
+ *
+ * Callers should check the cg member after constructing the object
+ * to see if it succeeded. The DrawTarget should not be used while
+ * the context is borrowed. */
+class BorrowedCGContext
+{
+public:
+ BorrowedCGContext()
+ : cg(nullptr)
+ , mDT(nullptr)
+ { }
+
+ explicit BorrowedCGContext(DrawTarget *aDT)
+ : mDT(aDT)
+ {
+ MOZ_ASSERT(aDT, "Caller should check for nullptr");
+ cg = BorrowCGContextFromDrawTarget(aDT);
+ }
+
+ // We can optionally Init after construction in
+ // case we don't know what the DT will be at construction
+ // time.
+ CGContextRef Init(DrawTarget *aDT)
+ {
+ MOZ_ASSERT(aDT, "Caller should check for nullptr");
+ MOZ_ASSERT(!mDT, "Can't initialize twice!");
+ mDT = aDT;
+ cg = BorrowCGContextFromDrawTarget(aDT);
+ return cg;
+ }
+
+ // The caller needs to call Finish if cg is non-null when
+ // they are done with the context. This is currently explicit
+ // instead of happening implicitly in the destructor to make
+ // what's happening in the caller more clear. It also
+ // let's you resume using the DrawTarget in the same scope.
+ void Finish()
+ {
+ if (cg) {
+ ReturnCGContextToDrawTarget(mDT, cg);
+ cg = nullptr;
+ }
+ }
+
+ ~BorrowedCGContext() {
+ MOZ_ASSERT(!cg);
+ }
+
+ CGContextRef cg;
+private:
+ static CGContextRef BorrowCGContextFromDrawTarget(DrawTarget *aDT);
+ static void ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg);
+ DrawTarget *mDT;
+};
+#endif
+
+}
+}
+
+#endif // _MOZILLA_GFX_BORROWED_CONTEXT_H
diff --git a/gfx/2d/Coord.h b/gfx/2d/Coord.h
new file mode 100644
index 000000000..2a981b0ef
--- /dev/null
+++ b/gfx/2d/Coord.h
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_COORD_H_
+#define MOZILLA_GFX_COORD_H_
+
+#include "mozilla/Attributes.h"
+#include "Types.h"
+#include "BaseCoord.h"
+
+#include <cmath>
+
+namespace mozilla {
+
+template <typename> struct IsPixel;
+
+namespace gfx {
+
+template <class units> struct IntCoordTyped;
+template <class units> struct CoordTyped;
+
+// CommonType<coord, primitive> is a metafunction that returns the type of the
+// result of an arithmetic operation on the underlying type of a strongly-typed
+// coordinate type 'coord', and a primitive type 'primitive'. C++ rules for
+// arithmetic conversions are designed to avoid losing information - for
+// example, the result of adding an int and a float is a float - and we want
+// the same behaviour when mixing our coordinate types with primitive types.
+// We get C++ to compute the desired result type using 'decltype'.
+
+template <class coord, class primitive>
+struct CommonType;
+
+template <class units, class primitive>
+struct CommonType<IntCoordTyped<units>, primitive> {
+ typedef decltype(int32_t() + primitive()) type;
+};
+
+template <class units, class primitive>
+struct CommonType<CoordTyped<units>, primitive> {
+ typedef decltype(Float() + primitive()) type;
+};
+
+// This is a base class that provides mixed-type operator overloads between
+// a strongly-typed Coord and a primitive value. It is needed to avoid
+// ambiguities at mixed-type call sites, because Coord classes are implicitly
+// convertible to their underlying value type. As we transition more of our code
+// to strongly-typed classes, we may be able to remove some or all of these
+// overloads.
+template <class coord, class primitive>
+struct CoordOperatorsHelper {
+ friend bool operator==(coord aA, primitive aB) {
+ return aA.value == aB;
+ }
+ friend bool operator==(primitive aA, coord aB) {
+ return aA == aB.value;
+ }
+ friend bool operator!=(coord aA, primitive aB) {
+ return aA.value != aB;
+ }
+ friend bool operator!=(primitive aA, coord aB) {
+ return aA != aB.value;
+ }
+
+ typedef typename CommonType<coord, primitive>::type result_type;
+
+ friend result_type operator+(coord aA, primitive aB) {
+ return aA.value + aB;
+ }
+ friend result_type operator+(primitive aA, coord aB) {
+ return aA + aB.value;
+ }
+ friend result_type operator-(coord aA, primitive aB) {
+ return aA.value - aB;
+ }
+ friend result_type operator-(primitive aA, coord aB) {
+ return aA - aB.value;
+ }
+ friend result_type operator*(coord aCoord, primitive aScale) {
+ return aCoord.value * aScale;
+ }
+ friend result_type operator*(primitive aScale, coord aCoord) {
+ return aScale * aCoord.value;
+ }
+ friend result_type operator/(coord aCoord, primitive aScale) {
+ return aCoord.value / aScale;
+ }
+ // 'scale / coord' is intentionally omitted because it doesn't make sense.
+};
+
+// Note: 'IntCoordTyped<units>' and 'CoordTyped<units>' do not derive from
+// 'units' to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61959.
+
+template<class units>
+struct IntCoordTyped :
+ public BaseCoord< int32_t, IntCoordTyped<units> >,
+ public CoordOperatorsHelper< IntCoordTyped<units>, float >,
+ public CoordOperatorsHelper< IntCoordTyped<units>, double > {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseCoord< int32_t, IntCoordTyped<units> > Super;
+
+ MOZ_CONSTEXPR IntCoordTyped() : Super() {}
+ MOZ_CONSTEXPR MOZ_IMPLICIT IntCoordTyped(int32_t aValue) : Super(aValue) {}
+};
+
+template<class units>
+struct CoordTyped :
+ public BaseCoord< Float, CoordTyped<units> >,
+ public CoordOperatorsHelper< CoordTyped<units>, int32_t >,
+ public CoordOperatorsHelper< CoordTyped<units>, uint32_t >,
+ public CoordOperatorsHelper< CoordTyped<units>, double > {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseCoord< Float, CoordTyped<units> > Super;
+
+ MOZ_CONSTEXPR CoordTyped() : Super() {}
+ MOZ_CONSTEXPR MOZ_IMPLICIT CoordTyped(Float aValue) : Super(aValue) {}
+ explicit MOZ_CONSTEXPR CoordTyped(const IntCoordTyped<units>& aCoord) : Super(float(aCoord.value)) {}
+
+ void Round() {
+ this->value = floor(this->value + 0.5);
+ }
+ void Truncate() {
+ this->value = int32_t(this->value);
+ }
+
+ IntCoordTyped<units> Rounded() const {
+ return IntCoordTyped<units>(int32_t(floor(this->value + 0.5)));
+ }
+ IntCoordTyped<units> Truncated() const {
+ return IntCoordTyped<units>(int32_t(this->value));
+ }
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_COORD_H_ */
diff --git a/gfx/2d/ScaledFontFreetype.h b/gfx/2d/DataSourceSurface.cpp
index bc492af12..573ae034a 100644
--- a/gfx/2d/ScaledFontFreetype.h
+++ b/gfx/2d/DataSourceSurface.cpp
@@ -3,22 +3,17 @@
* 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 MOZILLA_GFX_SCALEDFONTFREETYPE_H_
-#define MOZILLA_GFX_SCALEDFONTFREETYPE_H_
-
-#include "ScaledFontBase.h"
+#include "2D.h"
+#include "DataSourceSurfaceWrapper.h"
namespace mozilla {
namespace gfx {
-class ScaledFontFreetype : public ScaledFontBase
+TemporaryRef<DataSourceSurface>
+DataSourceSurface::GetDataSurface()
{
-public:
-
- ScaledFontFreetype(FontOptions* aFont, Float aSize);
-};
+ return (GetType() == SurfaceType::DATA) ? this : new DataSourceSurfaceWrapper(this);
+}
}
}
-
-#endif /* MOZILLA_GFX_SCALEDFONTFREETYPE_H_ */
diff --git a/gfx/2d/DataSourceSurfaceWrapper.h b/gfx/2d/DataSourceSurfaceWrapper.h
new file mode 100644
index 000000000..445a4adc2
--- /dev/null
+++ b/gfx/2d/DataSourceSurfaceWrapper.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_DATASOURCESURFACEWRAPPER_H_
+#define MOZILLA_GFX_DATASOURCESURFACEWRAPPER_H_
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+// Wraps a DataSourceSurface and forwards all methods except for GetType(),
+// from which it always returns SurfaceType::DATA.
+class DataSourceSurfaceWrapper : public DataSourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceWrapper, override)
+ explicit DataSourceSurfaceWrapper(DataSourceSurface *aSurface)
+ : mSurface(aSurface)
+ {}
+
+ virtual SurfaceType GetType() const override { return SurfaceType::DATA; }
+
+ virtual uint8_t *GetData() override { return mSurface->GetData(); }
+ virtual int32_t Stride() override { return mSurface->Stride(); }
+ virtual IntSize GetSize() const override { return mSurface->GetSize(); }
+ virtual SurfaceFormat GetFormat() const override { return mSurface->GetFormat(); }
+ virtual bool IsValid() const override { return mSurface->IsValid(); }
+
+private:
+ RefPtr<DataSourceSurface> mSurface;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_DATASOURCESURFACEWRAPPER_H_ */
diff --git a/gfx/2d/DataSurfaceHelpers.cpp b/gfx/2d/DataSurfaceHelpers.cpp
new file mode 100644
index 000000000..d0f08fa29
--- /dev/null
+++ b/gfx/2d/DataSurfaceHelpers.cpp
@@ -0,0 +1,285 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include <cstring>
+
+#include "2D.h"
+#include "DataSurfaceHelpers.h"
+#include "Logging.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/PodOperations.h"
+#include "Tools.h"
+
+namespace mozilla {
+namespace gfx {
+
+uint8_t*
+DataAtOffset(DataSourceSurface* aSurface, IntPoint aPoint)
+{
+ if (!SurfaceContainsPoint(aSurface, aPoint)) {
+ MOZ_CRASH("sample position needs to be inside surface!");
+ }
+
+ MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()),
+ "surface size overflows - this should have been prevented when the surface was created");
+
+ uint8_t* data = aSurface->GetData() + aPoint.y * aSurface->Stride() +
+ aPoint.x * BytesPerPixel(aSurface->GetFormat());
+
+ if (data < aSurface->GetData()) {
+ MOZ_CRASH("out-of-range data access");
+ }
+
+ return data;
+}
+
+// This check is safe against integer overflow.
+bool
+SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint)
+{
+ IntSize size = aSurface->GetSize();
+ return aPoint.x >= 0 && aPoint.x < size.width &&
+ aPoint.y >= 0 && aPoint.y < size.height;
+}
+
+void
+ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride)
+{
+ int height = aSize.height, width = aSize.width * 4;
+
+ for (int row = 0; row < height; ++row) {
+ for (int column = 0; column < width; column += 4) {
+#ifdef IS_BIG_ENDIAN
+ aData[column] = 0xFF;
+#else
+ aData[column + 3] = 0xFF;
+#endif
+ }
+ aData += aStride;
+ }
+}
+
+void
+CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize,
+ int32_t aSrcStride, int32_t aBytesPerPixel)
+{
+ MOZ_ASSERT(aBytesPerPixel > 0,
+ "Negative stride for aDst not currently supported");
+ MOZ_ASSERT(BufferSizeFromStrideAndHeight(aSrcStride, aSrcSize.height) > 0,
+ "How did we end up with a surface with such a big buffer?");
+
+ int packedStride = aSrcSize.width * aBytesPerPixel;
+
+ if (aSrcStride == packedStride) {
+ // aSrc is already packed, so we can copy with a single memcpy.
+ memcpy(aDst, aSrc, packedStride * aSrcSize.height);
+ } else {
+ // memcpy one row at a time.
+ for (int row = 0; row < aSrcSize.height; ++row) {
+ memcpy(aDst, aSrc, packedStride);
+ aSrc += aSrcStride;
+ aDst += packedStride;
+ }
+ }
+}
+
+void
+CopyBGRXSurfaceDataToPackedBGRArray(uint8_t* aSrc, uint8_t* aDst,
+ IntSize aSrcSize, int32_t aSrcStride)
+{
+ int packedStride = aSrcSize.width * 3;
+
+ uint8_t* srcPx = aSrc;
+ uint8_t* dstPx = aDst;
+
+ for (int row = 0; row < aSrcSize.height; ++row) {
+ for (int col = 0; col < aSrcSize.height; ++col) {
+ dstPx[0] = srcPx[0];
+ dstPx[1] = srcPx[1];
+ dstPx[2] = srcPx[2];
+ // srcPx[3] (unused or alpha component) dropped on floor
+ srcPx += 4;
+ dstPx += 3;
+ }
+ srcPx = aSrc += aSrcStride;
+ dstPx = aDst += packedStride;
+ }
+}
+
+uint8_t*
+SurfaceToPackedBGRA(DataSourceSurface *aSurface)
+{
+ SurfaceFormat format = aSurface->GetFormat();
+ if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) {
+ return nullptr;
+ }
+
+ IntSize size = aSurface->GetSize();
+
+ uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * sizeof(uint32_t)];
+ if (!imageBuffer) {
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+ delete [] imageBuffer;
+ return nullptr;
+ }
+
+ CopySurfaceDataToPackedArray(map.mData, imageBuffer, size,
+ map.mStride, 4 * sizeof(uint8_t));
+
+ aSurface->Unmap();
+
+ if (format == SurfaceFormat::B8G8R8X8) {
+ // Convert BGRX to BGRA by setting a to 255.
+ ConvertBGRXToBGRA(reinterpret_cast<uint8_t *>(imageBuffer), size, size.width * sizeof(uint32_t));
+ }
+
+ return imageBuffer;
+}
+
+uint8_t*
+SurfaceToPackedBGR(DataSourceSurface *aSurface)
+{
+ SurfaceFormat format = aSurface->GetFormat();
+ MOZ_ASSERT(format == SurfaceFormat::B8G8R8X8, "Format not supported");
+
+ if (format != SurfaceFormat::B8G8R8X8) {
+ // To support B8G8R8A8 we'd need to un-pre-multiply alpha
+ return nullptr;
+ }
+
+ IntSize size = aSurface->GetSize();
+
+ uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * 3 * sizeof(uint8_t)];
+ if (!imageBuffer) {
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+ delete [] imageBuffer;
+ return nullptr;
+ }
+
+ CopyBGRXSurfaceDataToPackedBGRArray(map.mData, imageBuffer, size,
+ map.mStride);
+
+ aSurface->Unmap();
+
+ return imageBuffer;
+}
+
+void
+ClearDataSourceSurface(DataSourceSurface *aSurface)
+{
+ DataSourceSurface::MappedSurface map;
+ if (!aSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
+ MOZ_ASSERT(false, "Failed to map DataSourceSurface");
+ return;
+ }
+
+ // We avoid writing into the gaps between the rows here since we can't be
+ // sure that some drivers don't use those bytes.
+
+ uint32_t width = aSurface->GetSize().width;
+ uint32_t bytesPerRow = width * BytesPerPixel(aSurface->GetFormat());
+ uint8_t* row = map.mData;
+ // converting to size_t here because otherwise the temporaries can overflow
+ // and we can end up with |end| being a bad address!
+ uint8_t* end = row + size_t(map.mStride) * size_t(aSurface->GetSize().height);
+
+ while (row != end) {
+ memset(row, 0, bytesPerRow);
+ row += map.mStride;
+ }
+
+ aSurface->Unmap();
+}
+
+size_t
+BufferSizeFromStrideAndHeight(int32_t aStride,
+ int32_t aHeight,
+ int32_t aExtraBytes)
+{
+ if (MOZ_UNLIKELY(aHeight <= 0)) {
+ return 0;
+ }
+
+ // We limit the length returned to values that can be represented by int32_t
+ // because we don't want to allocate buffers any bigger than that. This
+ // allows for a buffer size of over 2 GiB which is already rediculously
+ // large and will make the process janky. (Note the choice of the signed type
+ // is deliberate because we specifically don't want the returned value to
+ // overflow if someone stores the buffer length in an int32_t variable.)
+
+ CheckedInt32 requiredBytes =
+ CheckedInt32(aStride) * CheckedInt32(aHeight) + CheckedInt32(aExtraBytes);
+ if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
+ gfxWarning() << "Buffer size too big; returning zero";
+ return 0;
+ }
+ return requiredBytes.value();
+}
+
+/**
+ * aSrcRect: Rect relative to the aSrc surface
+ * aDestPoint: Point inside aDest surface
+ */
+void
+CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
+ IntRect aSrcRect, IntPoint aDestPoint)
+{
+ if (aSrcRect.Overflows() ||
+ IntRect(aDestPoint, aSrcRect.Size()).Overflows()) {
+ MOZ_CRASH("we should never be getting invalid rects at this point");
+ }
+
+ MOZ_RELEASE_ASSERT(aSrc->GetFormat() == aDest->GetFormat(),
+ "different surface formats");
+ MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect),
+ "source rect too big for source surface");
+ MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(IntRect(aDestPoint, aSrcRect.Size())),
+ "dest surface too small");
+
+ if (aSrcRect.IsEmpty()) {
+ return;
+ }
+
+ uint8_t* sourceData = DataAtOffset(aSrc, aSrcRect.TopLeft());
+ uint32_t sourceStride = aSrc->Stride();
+ uint8_t* destData = DataAtOffset(aDest, aDestPoint);
+ uint32_t destStride = aDest->Stride();
+
+ if (BytesPerPixel(aSrc->GetFormat()) == 4) {
+ for (int32_t y = 0; y < aSrcRect.height; y++) {
+ PodCopy((int32_t*)destData, (int32_t*)sourceData, aSrcRect.width);
+ sourceData += sourceStride;
+ destData += destStride;
+ }
+ } else if (BytesPerPixel(aSrc->GetFormat()) == 1) {
+ for (int32_t y = 0; y < aSrcRect.height; y++) {
+ PodCopy(destData, sourceData, aSrcRect.width);
+ sourceData += sourceStride;
+ destData += destStride;
+ }
+ }
+}
+
+TemporaryRef<DataSourceSurface>
+CreateDataSourceSurfaceByCloning(DataSourceSurface* aSource)
+{
+ RefPtr<DataSourceSurface> copy =
+ Factory::CreateDataSourceSurface(aSource->GetSize(), aSource->GetFormat(), true);
+ if (copy) {
+ CopyRect(aSource, copy, IntRect(IntPoint(), aSource->GetSize()), IntPoint());
+ }
+ return copy.forget();
+}
+
+}
+}
diff --git a/gfx/2d/DataSurfaceHelpers.h b/gfx/2d/DataSurfaceHelpers.h
new file mode 100644
index 000000000..e1afecee1
--- /dev/null
+++ b/gfx/2d/DataSurfaceHelpers.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 20; 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 _MOZILLA_GFX_DATASURFACEHELPERS_H
+#define _MOZILLA_GFX_DATASURFACEHELPERS_H
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+void
+ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride);
+
+/**
+ * Copy the pixel data from aSrc and pack it into aDst. aSrcSize, aSrcStride
+ * and aBytesPerPixel give the size, stride and bytes per pixel for aSrc's
+ * surface. Callers are responsible for making sure that aDst is big enough to
+ * contain |aSrcSize.width * aSrcSize.height * aBytesPerPixel| bytes.
+ */
+void
+CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize,
+ int32_t aSrcStride, int32_t aBytesPerPixel);
+
+/**
+ * Convert aSurface to a packed buffer in BGRA format. The pixel data is
+ * returned in a buffer allocated with new uint8_t[]. The caller then has
+ * ownership of the buffer and is responsible for delete[]'ing it.
+ */
+uint8_t*
+SurfaceToPackedBGRA(DataSourceSurface *aSurface);
+
+/**
+ * Convert aSurface to a packed buffer in BGR format. The pixel data is
+ * returned in a buffer allocated with new uint8_t[]. The caller then has
+ * ownership of the buffer and is responsible for delete[]'ing it.
+ *
+ * This function is currently only intended for use with surfaces of format
+ * SurfaceFormat::B8G8R8X8 since the X components of the pixel data (if any)
+ * are simply dropped (no attempt is made to un-pre-multiply alpha from the
+ * color components).
+ */
+uint8_t*
+SurfaceToPackedBGR(DataSourceSurface *aSurface);
+
+/**
+ * Clears all the bytes in a DataSourceSurface's data array to zero (so to
+ * transparent black for SurfaceFormat::B8G8R8A8, for example).
+ * Note that DataSourceSurfaces can be initialized to zero, which is
+ * more efficient than zeroing the surface after initialization.
+ */
+void
+ClearDataSourceSurface(DataSourceSurface *aSurface);
+
+/**
+ * Multiplies aStride and aHeight and makes sure the result is limited to
+ * something sane. To keep things consistent, this should always be used
+ * wherever we allocate a buffer based on surface stride and height.
+ *
+ * @param aExtra Optional argument to specify an additional number of trailing
+ * bytes (useful for creating intermediate surfaces for filters, for
+ * example).
+ *
+ * @return The result of the multiplication if it is acceptable, or else zero.
+ */
+size_t
+BufferSizeFromStrideAndHeight(int32_t aStride,
+ int32_t aHeight,
+ int32_t aExtraBytes = 0);
+
+/**
+ * Copy aSrcRect from aSrc to aDest starting at aDestPoint.
+ */
+void
+CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
+ IntRect aSrcRect, IntPoint aDestPoint);
+
+/**
+ * Create a non aliasing copy of aSource. This creates a new DataSourceSurface
+ * using the factory and copies the bits.
+ *
+ * @return a dss allocated by Factory that contains a copy a aSource.
+ */
+TemporaryRef<DataSourceSurface>
+CreateDataSourceSurfaceByCloning(DataSourceSurface* aSource);
+
+/**
+ * Return the byte at aPoint.
+ */
+uint8_t*
+DataAtOffset(DataSourceSurface* aSurface, IntPoint aPoint);
+
+/**
+ * Check if aPoint is contained by the surface.
+ *
+ * @returns true if and only if aPoint is inside the surface.
+ */
+bool
+SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint);
+
+}
+}
+
+#endif // _MOZILLA_GFX_DATASURFACEHELPERS_H
diff --git a/gfx/2d/DrawCommand.h b/gfx/2d/DrawCommand.h
new file mode 100644
index 000000000..6f1466d8e
--- /dev/null
+++ b/gfx/2d/DrawCommand.h
@@ -0,0 +1,504 @@
+/* -*- 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 MOZILLA_GFX_DRAWCOMMAND_H_
+#define MOZILLA_GFX_DRAWCOMMAND_H_
+
+#include "2D.h"
+#include "Filters.h"
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+enum class CommandType : int8_t {
+ DRAWSURFACE = 0,
+ DRAWFILTER,
+ DRAWSURFACEWITHSHADOW,
+ CLEARRECT,
+ COPYSURFACE,
+ COPYRECT,
+ FILLRECT,
+ STROKERECT,
+ STROKELINE,
+ STROKE,
+ FILL,
+ FILLGLYPHS,
+ MASK,
+ MASKSURFACE,
+ PUSHCLIP,
+ PUSHCLIPRECT,
+ POPCLIP,
+ SETTRANSFORM
+};
+
+class DrawingCommand
+{
+public:
+ virtual ~DrawingCommand() {}
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix& aTransform) = 0;
+
+protected:
+ explicit DrawingCommand(CommandType aType)
+ : mType(aType)
+ {
+ }
+
+ CommandType GetType() { return mType; }
+
+private:
+ CommandType mType;
+};
+
+class StoredPattern
+{
+public:
+ explicit StoredPattern(const Pattern& aPattern)
+ {
+ Assign(aPattern);
+ }
+
+ void Assign(const Pattern& aPattern)
+ {
+ switch (aPattern.GetType()) {
+ case PatternType::COLOR:
+ new (mColor)ColorPattern(*static_cast<const ColorPattern*>(&aPattern));
+ return;
+ case PatternType::SURFACE:
+ {
+ SurfacePattern* surfPat = new (mColor)SurfacePattern(*static_cast<const SurfacePattern*>(&aPattern));
+ surfPat->mSurface->GuaranteePersistance();
+ return;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ new (mColor)LinearGradientPattern(*static_cast<const LinearGradientPattern*>(&aPattern));
+ return;
+ case PatternType::RADIAL_GRADIENT:
+ new (mColor)RadialGradientPattern(*static_cast<const RadialGradientPattern*>(&aPattern));
+ return;
+ }
+ }
+
+ ~StoredPattern()
+ {
+ reinterpret_cast<Pattern*>(mColor)->~Pattern();
+ }
+
+ operator Pattern&()
+ {
+ return *reinterpret_cast<Pattern*>(mColor);
+ }
+
+ operator const Pattern&() const
+ {
+ return *reinterpret_cast<const Pattern*>(mColor);
+ }
+
+ StoredPattern(const StoredPattern& aPattern)
+ {
+ Assign(aPattern);
+ }
+
+private:
+ StoredPattern operator=(const StoredPattern& aOther)
+ {
+ // Block this so that we notice if someone's doing excessive assigning.
+ return *this;
+ }
+
+ union {
+ char mColor[sizeof(ColorPattern)];
+ char mLinear[sizeof(LinearGradientPattern)];
+ char mRadial[sizeof(RadialGradientPattern)];
+ char mSurface[sizeof(SurfacePattern)];
+ };
+};
+
+class DrawSurfaceCommand : public DrawingCommand
+{
+public:
+ DrawSurfaceCommand(SourceSurface *aSurface, const Rect& aDest,
+ const Rect& aSource, const DrawSurfaceOptions& aSurfOptions,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::DRAWSURFACE)
+ , mSurface(aSurface), mDest(aDest)
+ , mSource(aSource), mSurfOptions(aSurfOptions)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->DrawSurface(mSurface, mDest, mSource, mSurfOptions, mOptions);
+ }
+
+private:
+ RefPtr<SourceSurface> mSurface;
+ Rect mDest;
+ Rect mSource;
+ DrawSurfaceOptions mSurfOptions;
+ DrawOptions mOptions;
+};
+
+class DrawFilterCommand : public DrawingCommand
+{
+public:
+ DrawFilterCommand(FilterNode* aFilter, const Rect& aSourceRect,
+ const Point& aDestPoint, const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::DRAWSURFACE)
+ , mFilter(aFilter), mSourceRect(aSourceRect)
+ , mDestPoint(aDestPoint), mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->DrawFilter(mFilter, mSourceRect, mDestPoint, mOptions);
+ }
+
+private:
+ RefPtr<FilterNode> mFilter;
+ Rect mSourceRect;
+ Point mDestPoint;
+ DrawOptions mOptions;
+};
+
+class ClearRectCommand : public DrawingCommand
+{
+public:
+ explicit ClearRectCommand(const Rect& aRect)
+ : DrawingCommand(CommandType::CLEARRECT)
+ , mRect(aRect)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->ClearRect(mRect);
+ }
+
+private:
+ Rect mRect;
+};
+
+class CopySurfaceCommand : public DrawingCommand
+{
+public:
+ CopySurfaceCommand(SourceSurface* aSurface,
+ const IntRect& aSourceRect,
+ const IntPoint& aDestination)
+ : DrawingCommand(CommandType::COPYSURFACE)
+ , mSurface(aSurface)
+ , mSourceRect(aSourceRect)
+ , mDestination(aDestination)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix& aTransform)
+ {
+ MOZ_ASSERT(!aTransform.HasNonIntegerTranslation());
+ Point dest(Float(mDestination.x), Float(mDestination.y));
+ dest = aTransform * dest;
+ aDT->CopySurface(mSurface, mSourceRect, IntPoint(uint32_t(dest.x), uint32_t(dest.y)));
+ }
+
+private:
+ RefPtr<SourceSurface> mSurface;
+ IntRect mSourceRect;
+ IntPoint mDestination;
+};
+
+class FillRectCommand : public DrawingCommand
+{
+public:
+ FillRectCommand(const Rect& aRect,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::FILLRECT)
+ , mRect(aRect)
+ , mPattern(aPattern)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->FillRect(mRect, mPattern, mOptions);
+ }
+
+private:
+ Rect mRect;
+ StoredPattern mPattern;
+ DrawOptions mOptions;
+};
+
+class StrokeRectCommand : public DrawingCommand
+{
+public:
+ StrokeRectCommand(const Rect& aRect,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::STROKERECT)
+ , mRect(aRect)
+ , mPattern(aPattern)
+ , mStrokeOptions(aStrokeOptions)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->StrokeRect(mRect, mPattern, mStrokeOptions, mOptions);
+ }
+
+private:
+ Rect mRect;
+ StoredPattern mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+};
+
+class StrokeLineCommand : public DrawingCommand
+{
+public:
+ StrokeLineCommand(const Point& aStart,
+ const Point& aEnd,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::STROKELINE)
+ , mStart(aStart)
+ , mEnd(aEnd)
+ , mPattern(aPattern)
+ , mStrokeOptions(aStrokeOptions)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->StrokeLine(mStart, mEnd, mPattern, mStrokeOptions, mOptions);
+ }
+
+private:
+ Point mStart;
+ Point mEnd;
+ StoredPattern mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+};
+
+class FillCommand : public DrawingCommand
+{
+public:
+ FillCommand(const Path* aPath,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::FILL)
+ , mPath(const_cast<Path*>(aPath))
+ , mPattern(aPattern)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->Fill(mPath, mPattern, mOptions);
+ }
+
+private:
+ RefPtr<Path> mPath;
+ StoredPattern mPattern;
+ DrawOptions mOptions;
+};
+
+class StrokeCommand : public DrawingCommand
+{
+public:
+ StrokeCommand(const Path* aPath,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::STROKE)
+ , mPath(const_cast<Path*>(aPath))
+ , mPattern(aPattern)
+ , mStrokeOptions(aStrokeOptions)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->Stroke(mPath, mPattern, mStrokeOptions, mOptions);
+ }
+
+private:
+ RefPtr<Path> mPath;
+ StoredPattern mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+};
+
+class FillGlyphsCommand : public DrawingCommand
+{
+public:
+ FillGlyphsCommand(ScaledFont* aFont,
+ const GlyphBuffer& aBuffer,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions,
+ const GlyphRenderingOptions* aRenderingOptions)
+ : DrawingCommand(CommandType::FILLGLYPHS)
+ , mFont(aFont)
+ , mPattern(aPattern)
+ , mOptions(aOptions)
+ , mRenderingOptions(const_cast<GlyphRenderingOptions*>(aRenderingOptions))
+ {
+ mGlyphs.resize(aBuffer.mNumGlyphs);
+ memcpy(&mGlyphs.front(), aBuffer.mGlyphs, sizeof(Glyph) * aBuffer.mNumGlyphs);
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ GlyphBuffer buf;
+ buf.mNumGlyphs = mGlyphs.size();
+ buf.mGlyphs = &mGlyphs.front();
+ aDT->FillGlyphs(mFont, buf, mPattern, mOptions, mRenderingOptions);
+ }
+
+private:
+ RefPtr<ScaledFont> mFont;
+ std::vector<Glyph> mGlyphs;
+ StoredPattern mPattern;
+ DrawOptions mOptions;
+ RefPtr<GlyphRenderingOptions> mRenderingOptions;
+};
+
+class MaskCommand : public DrawingCommand
+{
+public:
+ MaskCommand(const Pattern& aSource,
+ const Pattern& aMask,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::MASK)
+ , mSource(aSource)
+ , mMask(aMask)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->Mask(mSource, mMask, mOptions);
+ }
+
+private:
+ StoredPattern mSource;
+ StoredPattern mMask;
+ DrawOptions mOptions;
+};
+
+class MaskSurfaceCommand : public DrawingCommand
+{
+public:
+ MaskSurfaceCommand(const Pattern& aSource,
+ const SourceSurface* aMask,
+ const Point& aOffset,
+ const DrawOptions& aOptions)
+ : DrawingCommand(CommandType::MASKSURFACE)
+ , mSource(aSource)
+ , mMask(const_cast<SourceSurface*>(aMask))
+ , mOffset(aOffset)
+ , mOptions(aOptions)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->MaskSurface(mSource, mMask, mOffset, mOptions);
+ }
+
+private:
+ StoredPattern mSource;
+ RefPtr<SourceSurface> mMask;
+ Point mOffset;
+ DrawOptions mOptions;
+};
+
+class PushClipCommand : public DrawingCommand
+{
+public:
+ explicit PushClipCommand(const Path* aPath)
+ : DrawingCommand(CommandType::PUSHCLIP)
+ , mPath(const_cast<Path*>(aPath))
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->PushClip(mPath);
+ }
+
+private:
+ RefPtr<Path> mPath;
+};
+
+class PushClipRectCommand : public DrawingCommand
+{
+public:
+ explicit PushClipRectCommand(const Rect& aRect)
+ : DrawingCommand(CommandType::PUSHCLIPRECT)
+ , mRect(aRect)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->PushClipRect(mRect);
+ }
+
+private:
+ Rect mRect;
+};
+
+class PopClipCommand : public DrawingCommand
+{
+public:
+ PopClipCommand()
+ : DrawingCommand(CommandType::POPCLIP)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
+ {
+ aDT->PopClip();
+ }
+};
+
+class SetTransformCommand : public DrawingCommand
+{
+public:
+ explicit SetTransformCommand(const Matrix& aTransform)
+ : DrawingCommand(CommandType::SETTRANSFORM)
+ , mTransform(aTransform)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix& aMatrix)
+ {
+ Matrix transform = mTransform;
+ transform *= aMatrix;
+ aDT->SetTransform(transform);
+ }
+
+private:
+ Matrix mTransform;
+};
+
+} /* namespace mozilla */
+} /* namespace gfx */
+
+#endif /* MOZILLA_GFX_DRAWCOMMAND_H_ */
diff --git a/gfx/2d/DrawEventRecorder.cpp b/gfx/2d/DrawEventRecorder.cpp
index 41d7680c6..3a663765c 100644
--- a/gfx/2d/DrawEventRecorder.cpp
+++ b/gfx/2d/DrawEventRecorder.cpp
@@ -29,7 +29,7 @@ DrawEventRecorderPrivate::RecordEvent(const RecordedEvent &aEvent)
}
DrawEventRecorderFile::DrawEventRecorderFile(const char *aFilename)
- : DrawEventRecorderPrivate(NULL)
+ : DrawEventRecorderPrivate(nullptr)
, mOutputFile(aFilename, ofstream::binary)
{
mOutputStream = &mOutputFile;
diff --git a/gfx/2d/DrawEventRecorder.h b/gfx/2d/DrawEventRecorder.h
index 3652996e7..e297a5871 100644
--- a/gfx/2d/DrawEventRecorder.h
+++ b/gfx/2d/DrawEventRecorder.h
@@ -12,7 +12,7 @@
#include <fstream>
#if defined(_MSC_VER)
-#include <hash_set>
+#include <unordered_set>
#else
#include <set>
#endif
@@ -25,7 +25,8 @@ class PathRecording;
class DrawEventRecorderPrivate : public DrawEventRecorder
{
public:
- DrawEventRecorderPrivate(std::ostream *aStream);
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPrivate)
+ explicit DrawEventRecorderPrivate(std::ostream *aStream);
virtual ~DrawEventRecorderPrivate() { }
void RecordEvent(const RecordedEvent &aEvent);
@@ -52,7 +53,7 @@ protected:
virtual void Flush() = 0;
#if defined(_MSC_VER)
- typedef stdext::hash_set<const void*> ObjectSet;
+ typedef std::unordered_set<const void*> ObjectSet;
#else
typedef std::set<const void*> ObjectSet;
#endif
@@ -64,7 +65,8 @@ protected:
class DrawEventRecorderFile : public DrawEventRecorderPrivate
{
public:
- DrawEventRecorderFile(const char *aFilename);
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderFile)
+ explicit DrawEventRecorderFile(const char *aFilename);
~DrawEventRecorderFile();
private:
diff --git a/gfx/2d/DrawTarget.cpp b/gfx/2d/DrawTarget.cpp
new file mode 100644
index 000000000..63f728993
--- /dev/null
+++ b/gfx/2d/DrawTarget.cpp
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "2D.h"
+#include "Logging.h"
+
+#include "DrawTargetCapture.h"
+
+namespace mozilla {
+namespace gfx {
+
+TemporaryRef<DrawTargetCapture>
+DrawTarget::CreateCaptureDT(const IntSize& aSize)
+{
+ RefPtr<DrawTargetCaptureImpl> dt = new DrawTargetCaptureImpl();
+
+ if (!dt->Init(aSize, this)) {
+ gfxWarning() << "Failed to initialize Capture DrawTarget!";
+ return nullptr;
+ }
+
+ return dt;
+}
+
+void
+DrawTarget::DrawCapturedDT(DrawTargetCapture *aCaptureDT,
+ const Matrix& aTransform)
+{
+ if (aTransform.HasNonIntegerTranslation()) {
+ gfxWarning() << "Non integer translations are not supported for DrawCaptureDT at this time!";
+ return;
+ }
+ static_cast<DrawTargetCaptureImpl*>(aCaptureDT)->ReplayToDrawTarget(this, aTransform);
+}
+
+}
+}
diff --git a/gfx/2d/DrawTargetCG.cpp b/gfx/2d/DrawTargetCG.cpp
index e24d2906a..1bd92f212 100644
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -2,13 +2,25 @@
* 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/. */
+
+#include "BorrowedContext.h"
+#include "DataSurfaceHelpers.h"
#include "DrawTargetCG.h"
+#include "Logging.h"
#include "SourceSurfaceCG.h"
#include "Rect.h"
#include "ScaledFontMac.h"
#include "Tools.h"
#include <vector>
-#include "QuartzSupport.h"
+#include <algorithm>
+#include "MacIOSurface.h"
+#include "FilterNodeSoftware.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/Types.h" // for decltype
+#include "mozilla/Vector.h"
+
+using namespace std;
//CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
@@ -18,12 +30,8 @@ CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
namespace mozilla {
namespace gfx {
-static CGRect RectToCGRect(Rect r)
-{
- return CGRectMake(r.x, r.y, r.width, r.height);
-}
-
-static CGRect IntRectToCGRect(IntRect r)
+template <typename T>
+static CGRect RectToCGRect(const T& r)
{
return CGRectMake(r.x, r.y, r.width, r.height);
}
@@ -32,82 +40,82 @@ CGBlendMode ToBlendMode(CompositionOp op)
{
CGBlendMode mode;
switch (op) {
- case OP_OVER:
+ case CompositionOp::OP_OVER:
mode = kCGBlendModeNormal;
break;
- case OP_ADD:
+ case CompositionOp::OP_ADD:
mode = kCGBlendModePlusLighter;
break;
- case OP_ATOP:
+ case CompositionOp::OP_ATOP:
mode = kCGBlendModeSourceAtop;
break;
- case OP_OUT:
+ case CompositionOp::OP_OUT:
mode = kCGBlendModeSourceOut;
break;
- case OP_IN:
+ case CompositionOp::OP_IN:
mode = kCGBlendModeSourceIn;
break;
- case OP_SOURCE:
+ case CompositionOp::OP_SOURCE:
mode = kCGBlendModeCopy;
break;
- case OP_DEST_IN:
+ case CompositionOp::OP_DEST_IN:
mode = kCGBlendModeDestinationIn;
break;
- case OP_DEST_OUT:
+ case CompositionOp::OP_DEST_OUT:
mode = kCGBlendModeDestinationOut;
break;
- case OP_DEST_OVER:
+ case CompositionOp::OP_DEST_OVER:
mode = kCGBlendModeDestinationOver;
break;
- case OP_DEST_ATOP:
+ case CompositionOp::OP_DEST_ATOP:
mode = kCGBlendModeDestinationAtop;
break;
- case OP_XOR:
+ case CompositionOp::OP_XOR:
mode = kCGBlendModeXOR;
break;
- case OP_MULTIPLY:
+ case CompositionOp::OP_MULTIPLY:
mode = kCGBlendModeMultiply;
break;
- case OP_SCREEN:
+ case CompositionOp::OP_SCREEN:
mode = kCGBlendModeScreen;
break;
- case OP_OVERLAY:
+ case CompositionOp::OP_OVERLAY:
mode = kCGBlendModeOverlay;
break;
- case OP_DARKEN:
+ case CompositionOp::OP_DARKEN:
mode = kCGBlendModeDarken;
break;
- case OP_LIGHTEN:
+ case CompositionOp::OP_LIGHTEN:
mode = kCGBlendModeLighten;
break;
- case OP_COLOR_DODGE:
+ case CompositionOp::OP_COLOR_DODGE:
mode = kCGBlendModeColorDodge;
break;
- case OP_COLOR_BURN:
+ case CompositionOp::OP_COLOR_BURN:
mode = kCGBlendModeColorBurn;
break;
- case OP_HARD_LIGHT:
+ case CompositionOp::OP_HARD_LIGHT:
mode = kCGBlendModeHardLight;
break;
- case OP_SOFT_LIGHT:
+ case CompositionOp::OP_SOFT_LIGHT:
mode = kCGBlendModeSoftLight;
break;
- case OP_DIFFERENCE:
+ case CompositionOp::OP_DIFFERENCE:
mode = kCGBlendModeDifference;
break;
- case OP_EXCLUSION:
+ case CompositionOp::OP_EXCLUSION:
mode = kCGBlendModeExclusion;
break;
- case OP_HUE:
+ case CompositionOp::OP_HUE:
mode = kCGBlendModeHue;
break;
- case OP_SATURATION:
+ case CompositionOp::OP_SATURATION:
mode = kCGBlendModeSaturation;
break;
- case OP_COLOR:
+ case CompositionOp::OP_COLOR:
mode = kCGBlendModeColor;
break;
- case OP_LUMINOSITY:
+ case CompositionOp::OP_LUMINOSITY:
mode = kCGBlendModeLuminosity;
break;
/*
@@ -120,33 +128,60 @@ CGBlendMode ToBlendMode(CompositionOp op)
return mode;
}
+static CGInterpolationQuality
+InterpolationQualityFromFilter(Filter aFilter)
+{
+ switch (aFilter) {
+ default:
+ case Filter::LINEAR:
+ return kCGInterpolationLow;
+ case Filter::POINT:
+ return kCGInterpolationNone;
+ case Filter::GOOD:
+ return kCGInterpolationLow;
+ }
+}
-DrawTargetCG::DrawTargetCG() : mCg(nullptr), mSnapshot(nullptr)
+DrawTargetCG::DrawTargetCG()
+ : mColorSpace(nullptr)
+ , mCg(nullptr)
+ , mMayContainInvalidPremultipliedData(false)
{
}
DrawTargetCG::~DrawTargetCG()
{
- MarkChanged();
+ if (mSnapshot) {
+ if (mSnapshot->refCount() > 1) {
+ // We only need to worry about snapshots that someone else knows about
+ mSnapshot->DrawTargetWillGoAway();
+ }
+ mSnapshot = nullptr;
+ }
- // We need to conditionally release these because Init can fail without initializing these.
- if (mColorSpace)
- CGColorSpaceRelease(mColorSpace);
- if (mCg)
- CGContextRelease(mCg);
- free(mData);
+ // Both of these are OK with nullptr arguments, so we do not
+ // need to check (these could be nullptr if Init fails)
+ CGColorSpaceRelease(mColorSpace);
+ CGContextRelease(mCg);
}
-BackendType
+DrawTargetType
DrawTargetCG::GetType() const
{
+ return GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED ?
+ DrawTargetType::HARDWARE_RASTER : DrawTargetType::SOFTWARE_RASTER;
+}
+
+BackendType
+DrawTargetCG::GetBackendType() const
+{
// It may be worth spliting Bitmap and IOSurface DrawTarget
// into seperate classes.
if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) {
- return BACKEND_COREGRAPHICS_ACCELERATED;
+ return BackendType::COREGRAPHICS_ACCELERATED;
} else {
- return BACKEND_COREGRAPHICS;
+ return BackendType::COREGRAPHICS;
}
}
@@ -156,9 +191,9 @@ DrawTargetCG::Snapshot()
if (!mSnapshot) {
if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) {
return new SourceSurfaceCGIOSurfaceContext(this);
- } else {
- mSnapshot = new SourceSurfaceCGBitmapContext(this);
}
+ Flush();
+ mSnapshot = new SourceSurfaceCGBitmapContext(this);
}
return mSnapshot;
@@ -170,11 +205,10 @@ DrawTargetCG::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aForma
// XXX: in thebes we use CGLayers to do this kind of thing. It probably makes sense
// to add that in somehow, but at a higher level
RefPtr<DrawTargetCG> newTarget = new DrawTargetCG();
- if (newTarget->Init(GetType(), aSize, aFormat)) {
- return newTarget;
- } else {
- return nullptr;
+ if (newTarget->Init(GetBackendType(), aSize, aFormat)) {
+ return newTarget.forget();
}
+ return nullptr;
}
TemporaryRef<SourceSurface>
@@ -185,58 +219,88 @@ DrawTargetCG::CreateSourceSurfaceFromData(unsigned char *aData,
{
RefPtr<SourceSurfaceCG> newSurf = new SourceSurfaceCG();
- if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
+ if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
return nullptr;
}
- return newSurf;
+ return newSurf.forget();
}
+static void releaseDataSurface(void* info, const void *data, size_t size)
+{
+ static_cast<DataSourceSurface*>(info)->Release();
+}
+
+// This function returns a retained CGImage that needs to be released after
+// use. The reason for this is that we want to either reuse an existing CGImage
+// or create a new one.
static CGImageRef
-GetImageFromSourceSurface(SourceSurface *aSurface)
+GetRetainedImageFromSourceSurface(SourceSurface *aSurface)
{
- if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE)
- return static_cast<SourceSurfaceCG*>(aSurface)->GetImage();
- else if (aSurface->GetType() == SURFACE_COREGRAPHICS_CGCONTEXT)
- return static_cast<SourceSurfaceCGContext*>(aSurface)->GetImage();
- else if (aSurface->GetType() == SURFACE_DATA)
- return static_cast<DataSourceSurfaceCG*>(aSurface)->GetImage();
- abort();
+ switch(aSurface->GetType()) {
+ case SurfaceType::COREGRAPHICS_IMAGE:
+ return CGImageRetain(static_cast<SourceSurfaceCG*>(aSurface)->GetImage());
+
+ case SurfaceType::COREGRAPHICS_CGCONTEXT:
+ return CGImageRetain(static_cast<SourceSurfaceCGContext*>(aSurface)->GetImage());
+
+ default:
+ {
+ RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
+ if (!data) {
+ MOZ_CRASH("unsupported source surface");
+ }
+ data->AddRef();
+ return CreateCGImage(releaseDataSurface, data.get(),
+ data->GetData(), data->GetSize(),
+ data->Stride(), data->GetFormat());
+ }
+ }
}
TemporaryRef<SourceSurface>
DrawTargetCG::OptimizeSourceSurface(SourceSurface *aSurface) const
{
- return nullptr;
+ return aSurface;
}
class UnboundnessFixer
{
CGRect mClipBounds;
CGLayerRef mLayer;
- CGContextRef mCg;
+ CGContextRef mLayerCg;
public:
- UnboundnessFixer() : mCg(nullptr) {}
+ UnboundnessFixer() : mLayerCg(nullptr) {}
- CGContextRef Check(CGContextRef baseCg, CompositionOp blend)
+ CGContextRef Check(CGContextRef baseCg, CompositionOp blend, const Rect* maskBounds = nullptr)
{
+ MOZ_ASSERT(baseCg);
if (!IsOperatorBoundByMask(blend)) {
mClipBounds = CGContextGetClipBoundingBox(baseCg);
+ // If we're entirely clipped out or if the drawing operation covers the entire clip then
+ // we don't need to create a temporary surface.
+ if (CGRectIsEmpty(mClipBounds) ||
+ (maskBounds && maskBounds->Contains(CGRectToRect(mClipBounds)))) {
+ return baseCg;
+ }
+
// TransparencyLayers aren't blended using the blend mode so
// we are forced to use CGLayers
//XXX: The size here is in default user space units, of the layer relative to the graphics context.
// is the clip bounds still correct if, for example, we have a scale applied to the context?
mLayer = CGLayerCreateWithContext(baseCg, mClipBounds.size, nullptr);
- //XXX: if the size is 0x0 we get a nullptr CGContext back from GetContext
- mCg = CGLayerGetContext(mLayer);
+ mLayerCg = CGLayerGetContext(mLayer);
// CGContext's default to have the origin at the bottom left
// so flip it to the top left and adjust for the origin
// of the layer
- CGContextTranslateCTM(mCg, -mClipBounds.origin.x, mClipBounds.origin.y + mClipBounds.size.height);
- CGContextScaleCTM(mCg, 1, -1);
+ if (MOZ2D_ERROR_IF(!mLayerCg)) {
+ return nullptr;
+ }
+ CGContextTranslateCTM(mLayerCg, -mClipBounds.origin.x, mClipBounds.origin.y + mClipBounds.size.height);
+ CGContextScaleCTM(mLayerCg, 1, -1);
- return mCg;
+ return mLayerCg;
} else {
return baseCg;
}
@@ -244,12 +308,14 @@ class UnboundnessFixer
void Fix(CGContextRef baseCg)
{
- if (mCg) {
+ if (mLayerCg) {
+ // we pushed a layer so draw it to baseCg
+ MOZ_ASSERT(baseCg);
CGContextTranslateCTM(baseCg, 0, mClipBounds.size.height);
CGContextScaleCTM(baseCg, 1, -1);
mClipBounds.origin.y *= -1;
CGContextDrawLayerAtPoint(baseCg, mClipBounds.origin, mLayer);
- CGContextRelease(mCg);
+ CGContextRelease(mLayerCg);
}
}
};
@@ -261,75 +327,76 @@ DrawTargetCG::DrawSurface(SourceSurface *aSurface,
const DrawSurfaceOptions &aSurfOptions,
const DrawOptions &aDrawOptions)
{
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
MarkChanged();
- CGImageRef image;
- CGImageRef subimage = nullptr;
CGContextSaveGState(mCg);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
UnboundnessFixer fixer;
- CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+ CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp, &aDest);
+ if (MOZ2D_ERROR_IF(!cg)) {
+ return;
+ }
CGContextSetAlpha(cg, aDrawOptions.mAlpha);
+ CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
- image = GetImageFromSourceSurface(aSurface);
- /* we have two options here:
- * - create a subimage -- this is slower
- * - fancy things with clip and different dest rects */
- {
- subimage = CGImageCreateWithImageInRect(image, RectToCGRect(aSource));
- image = subimage;
- }
- CGContextScaleCTM(cg, 1, -1);
+ CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(aSurfOptions.mFilter));
- CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + aDest.height),
- aDest.width, aDest.height);
+ CGImageRef image = GetRetainedImageFromSourceSurface(aSurface);
- //XXX: we should implement this for patterns too
- if (aSurfOptions.mFilter == FILTER_POINT)
- CGContextSetInterpolationQuality(cg, kCGInterpolationNone);
+ if (aSurfOptions.mFilter == Filter::POINT) {
+ CGImageRef subimage = CGImageCreateWithImageInRect(image, RectToCGRect(aSource));
+ CGImageRelease(image);
- CGContextDrawImage(cg, flippedRect, image);
+ CGContextScaleCTM(cg, 1, -1);
- fixer.Fix(mCg);
+ CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + aDest.height),
+ aDest.width, aDest.height);
- CGContextRestoreGState(mCg);
+ CGContextDrawImage(cg, flippedRect, subimage);
+ CGImageRelease(subimage);
+ } else {
+ CGRect destRect = CGRectMake(aDest.x, aDest.y, aDest.width, aDest.height);
+ CGContextClipToRect(cg, destRect);
- CGImageRelease(subimage);
-}
+ float xScale = aSource.width / aDest.width;
+ float yScale = aSource.height / aDest.height;
+ CGContextTranslateCTM(cg, aDest.x - aSource.x / xScale, aDest.y - aSource.y / yScale);
-void
-DrawTargetCG::MaskSurface(const Pattern &aSource,
- SourceSurface *aMask,
- Point aOffset,
- const DrawOptions &aDrawOptions)
-{
- MarkChanged();
+ CGRect adjustedDestRect = CGRectMake(0, 0, CGImageGetWidth(image) / xScale,
+ CGImageGetHeight(image) / yScale);
- CGImageRef image;
- CGContextSaveGState(mCg);
+ CGContextTranslateCTM(cg, 0, CGRectGetHeight(adjustedDestRect));
+ CGContextScaleCTM(cg, 1, -1);
- CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
- UnboundnessFixer fixer;
- CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
- CGContextSetAlpha(cg, aDrawOptions.mAlpha);
-
- CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
- image = GetImageFromSourceSurface(aMask);
-
- CGContextScaleCTM(cg, 1, -1);
+ CGContextDrawImage(cg, adjustedDestRect, image);
+ CGImageRelease(image);
+ }
- IntSize size = aMask->GetSize();
- CGContextClipToMask(cg, CGRectMake(aOffset.x, aOffset.y, size.width, size.height), image);
-
- FillRect(Rect(0, 0, size.width, size.height), aSource, aDrawOptions);
-
fixer.Fix(mCg);
CGContextRestoreGState(mCg);
+}
+TemporaryRef<FilterNode>
+DrawTargetCG::CreateFilter(FilterType aType)
+{
+ return FilterNodeSoftware::Create(aType);
+}
+
+void
+DrawTargetCG::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
+ filter->Draw(this, aSourceRect, aDestPoint, aOptions);
}
static CGColorRef ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColor)
@@ -341,78 +408,319 @@ static CGColorRef ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColo
class GradientStopsCG : public GradientStops
{
public:
- //XXX: The skia backend uses a vector and passes in aNumStops. It should do better
- GradientStopsCG(GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode)
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsCG)
+
+ GradientStopsCG(CGColorSpaceRef aColorSpace,
+ const std::vector<GradientStop>& aStops,
+ ExtendMode aExtendMode)
+ : mGradient(nullptr)
{
- //XXX: do the stops need to be in any particular order?
- // what should we do about the color space here? we certainly shouldn't be
- // recreating it all the time
- std::vector<CGFloat> colors;
- std::vector<CGFloat> offsets;
- colors.reserve(aNumStops*4);
- offsets.reserve(aNumStops);
-
- for (uint32_t i = 0; i < aNumStops; i++) {
- colors.push_back(aStops[i].color.r);
- colors.push_back(aStops[i].color.g);
- colors.push_back(aStops[i].color.b);
- colors.push_back(aStops[i].color.a);
-
- offsets.push_back(aStops[i].offset);
+ // This all works fine with empty aStops vector
+
+ mExtend = aExtendMode;
+ if (aExtendMode == ExtendMode::CLAMP) {
+ size_t numStops = aStops.size();
+
+ std::vector<CGFloat> colors;
+ std::vector<CGFloat> offsets;
+ colors.reserve(numStops*4);
+ offsets.reserve(numStops);
+
+ for (size_t i = 0; i < numStops; i++) {
+ colors.push_back(aStops[i].color.r);
+ colors.push_back(aStops[i].color.g);
+ colors.push_back(aStops[i].color.b);
+ colors.push_back(aStops[i].color.a);
+
+ offsets.push_back(aStops[i].offset);
+ }
+
+ mGradient = CGGradientCreateWithColorComponents(aColorSpace,
+ &colors.front(),
+ &offsets.front(),
+ offsets.size());
+ } else {
+ mStops = aStops;
}
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
- mGradient = CGGradientCreateWithColorComponents(colorSpace,
- &colors.front(),
- &offsets.front(),
- aNumStops);
- CGColorSpaceRelease(colorSpace);
}
+
virtual ~GradientStopsCG() {
+ // CGGradientRelease is OK with nullptr argument
CGGradientRelease(mGradient);
}
- // Will always report BACKEND_COREGRAPHICS, but it is compatible
- // with BACKEND_COREGRAPHICS_ACCELERATED
- BackendType GetBackendType() const { return BACKEND_COREGRAPHICS; }
+
+ // Will always report BackendType::COREGRAPHICS, but it is compatible
+ // with BackendType::COREGRAPHICS_ACCELERATED
+ BackendType GetBackendType() const { return BackendType::COREGRAPHICS; }
+ // XXX this should be a union
CGGradientRef mGradient;
+ std::vector<GradientStop> mStops;
+ ExtendMode mExtend;
};
TemporaryRef<GradientStops>
DrawTargetCG::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops,
ExtendMode aExtendMode) const
{
- return new GradientStopsCG(aStops, aNumStops, aExtendMode);
+ std::vector<GradientStop> stops(aStops, aStops+aNumStops);
+ return new GradientStopsCG(mColorSpace, stops, aExtendMode);
}
static void
-DrawGradient(CGContextRef cg, const Pattern &aPattern)
+UpdateLinearParametersToIncludePoint(double *min_t, double *max_t,
+ CGPoint *start,
+ double dx, double dy,
+ double x, double y)
{
- if (aPattern.GetType() == PATTERN_LINEAR_GRADIENT) {
- const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern);
- GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
- // XXX: we should take the m out of the properties of LinearGradientPatterns
- CGPoint startPoint = { pat.mBegin.x, pat.mBegin.y };
- CGPoint endPoint = { pat.mEnd.x, pat.mEnd.y };
+ MOZ_ASSERT(IsFinite(x) && IsFinite(y));
+
+ /**
+ * Compute a parameter t such that a line perpendicular to the (dx,dy)
+ * vector, passing through (start->x + dx*t, start->y + dy*t), also
+ * passes through (x,y).
+ *
+ * Let px = x - start->x, py = y - start->y.
+ * t is given by
+ * (px - dx*t)*dx + (py - dy*t)*dy = 0
+ *
+ * Solving for t we get
+ * numerator = dx*px + dy*py
+ * denominator = dx^2 + dy^2
+ * t = numerator/denominator
+ *
+ * In CalculateRepeatingGradientParams we know the length of (dx,dy)
+ * is not zero. (This is checked in DrawLinearRepeatingGradient.)
+ */
+ double px = x - start->x;
+ double py = y - start->y;
+ double numerator = dx * px + dy * py;
+ double denominator = dx * dx + dy * dy;
+ double t = numerator / denominator;
+
+ if (*min_t > t) {
+ *min_t = t;
+ }
+ if (*max_t < t) {
+ *max_t = t;
+ }
+}
- // Canvas spec states that we should avoid drawing degenerate gradients (XXX: should this be in common code?)
- //if (startPoint.x == endPoint.x && startPoint.y == endPoint.y)
- // return;
+/**
+ * Repeat the gradient line such that lines extended perpendicular to the
+ * gradient line at both start and end would completely enclose the drawing
+ * extents.
+ */
+static void
+CalculateRepeatingGradientParams(CGPoint *aStart, CGPoint *aEnd,
+ CGRect aExtents, int *aRepeatStartFactor,
+ int *aRepeatEndFactor)
+{
+ double t_min = INFINITY;
+ double t_max = -INFINITY;
+ double dx = aEnd->x - aStart->x;
+ double dy = aEnd->y - aStart->y;
+
+ double bounds_x1 = aExtents.origin.x;
+ double bounds_y1 = aExtents.origin.y;
+ double bounds_x2 = aExtents.origin.x + aExtents.size.width;
+ double bounds_y2 = aExtents.origin.y + aExtents.size.height;
+
+ UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy,
+ bounds_x1, bounds_y1);
+ UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy,
+ bounds_x2, bounds_y1);
+ UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy,
+ bounds_x2, bounds_y2);
+ UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy,
+ bounds_x1, bounds_y2);
+
+ MOZ_ASSERT(!isinf(t_min) && !isinf(t_max),
+ "The first call to UpdateLinearParametersToIncludePoint should have made t_min and t_max non-infinite.");
+
+ // Move t_min and t_max to the nearest usable integer to try to avoid
+ // subtle variations due to numerical instability, especially accidentally
+ // cutting off a pixel. Extending the gradient repetitions is always safe.
+ t_min = floor (t_min);
+ t_max = ceil (t_max);
+ aEnd->x = aStart->x + dx * t_max;
+ aEnd->y = aStart->y + dy * t_max;
+ aStart->x = aStart->x + dx * t_min;
+ aStart->y = aStart->y + dy * t_min;
+
+ *aRepeatStartFactor = t_min;
+ *aRepeatEndFactor = t_max;
+}
- CGContextDrawLinearGradient(cg, stops->mGradient, startPoint, endPoint,
- kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
- } else if (aPattern.GetType() == PATTERN_RADIAL_GRADIENT) {
- const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
- GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
+static CGGradientRef
+CreateRepeatingGradient(CGColorSpaceRef aColorSpace,
+ CGContextRef cg, GradientStopsCG* aStops,
+ int aRepeatStartFactor, int aRepeatEndFactor,
+ bool aReflect)
+{
+ int repeatCount = aRepeatEndFactor - aRepeatStartFactor;
+ uint32_t stopCount = aStops->mStops.size();
+ double scale = 1./repeatCount;
+
+ std::vector<CGFloat> colors;
+ std::vector<CGFloat> offsets;
+ colors.reserve(stopCount*repeatCount*4);
+ offsets.reserve(stopCount*repeatCount);
+
+ for (int j = aRepeatStartFactor; j < aRepeatEndFactor; j++) {
+ bool isReflected = aReflect && (j % 2) != 0;
+ for (uint32_t i = 0; i < stopCount; i++) {
+ uint32_t stopIndex = isReflected ? stopCount - i - 1 : i;
+ colors.push_back(aStops->mStops[stopIndex].color.r);
+ colors.push_back(aStops->mStops[stopIndex].color.g);
+ colors.push_back(aStops->mStops[stopIndex].color.b);
+ colors.push_back(aStops->mStops[stopIndex].color.a);
+
+ CGFloat offset = aStops->mStops[stopIndex].offset;
+ if (isReflected) {
+ offset = 1 - offset;
+ }
+ offsets.push_back((offset + (j - aRepeatStartFactor)) * scale);
+ }
+ }
+
+ CGGradientRef gradient = CGGradientCreateWithColorComponents(aColorSpace,
+ &colors.front(),
+ &offsets.front(),
+ repeatCount*stopCount);
+ return gradient;
+}
+
+static void
+DrawLinearRepeatingGradient(CGColorSpaceRef aColorSpace, CGContextRef cg,
+ const LinearGradientPattern &aPattern,
+ const CGRect &aExtents, bool aReflect)
+{
+ GradientStopsCG *stops = static_cast<GradientStopsCG*>(aPattern.mStops.get());
+ CGPoint startPoint = { aPattern.mBegin.x, aPattern.mBegin.y };
+ CGPoint endPoint = { aPattern.mEnd.x, aPattern.mEnd.y };
+
+ int repeatStartFactor = 0, repeatEndFactor = 1;
+ // if we don't have a line then we can't extend it
+ if (aPattern.mEnd.x != aPattern.mBegin.x ||
+ aPattern.mEnd.y != aPattern.mBegin.y) {
+ CalculateRepeatingGradientParams(&startPoint, &endPoint, aExtents,
+ &repeatStartFactor, &repeatEndFactor);
+ }
- // XXX: we should take the m out of the properties of RadialGradientPatterns
- CGPoint startCenter = { pat.mCenter1.x, pat.mCenter1.y };
- CGFloat startRadius = pat.mRadius1;
- CGPoint endCenter = { pat.mCenter2.x, pat.mCenter2.y };
- CGFloat endRadius = pat.mRadius2;
+ CGGradientRef gradient = CreateRepeatingGradient(aColorSpace, cg, stops, repeatStartFactor, repeatEndFactor, aReflect);
- //XXX: are there degenerate radial gradients that we should avoid drawing?
- CGContextDrawRadialGradient(cg, stops->mGradient, startCenter, startRadius, endCenter, endRadius,
- kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
+ CGContextDrawLinearGradient(cg, gradient, startPoint, endPoint,
+ kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
+ CGGradientRelease(gradient);
+}
+
+static CGPoint CGRectTopLeft(CGRect a)
+{ return a.origin; }
+static CGPoint CGRectBottomLeft(CGRect a)
+{ return CGPointMake(a.origin.x, a.origin.y + a.size.height); }
+static CGPoint CGRectTopRight(CGRect a)
+{ return CGPointMake(a.origin.x + a.size.width, a.origin.y); }
+static CGPoint CGRectBottomRight(CGRect a)
+{ return CGPointMake(a.origin.x + a.size.width, a.origin.y + a.size.height); }
+
+static CGFloat
+CGPointDistance(CGPoint a, CGPoint b)
+{
+ return hypot(a.x-b.x, a.y-b.y);
+}
+
+static void
+DrawRadialRepeatingGradient(CGColorSpaceRef aColorSpace, CGContextRef cg,
+ const RadialGradientPattern &aPattern,
+ const CGRect &aExtents, bool aReflect)
+{
+ GradientStopsCG *stops = static_cast<GradientStopsCG*>(aPattern.mStops.get());
+ CGPoint startCenter = { aPattern.mCenter1.x, aPattern.mCenter1.y };
+ CGFloat startRadius = aPattern.mRadius1;
+ CGPoint endCenter = { aPattern.mCenter2.x, aPattern.mCenter2.y };
+ CGFloat endRadius = aPattern.mRadius2;
+
+ // find the maximum distance from endCenter to a corner of aExtents
+ CGFloat minimumEndRadius = endRadius;
+ minimumEndRadius = max(minimumEndRadius, CGPointDistance(endCenter, CGRectTopLeft(aExtents)));
+ minimumEndRadius = max(minimumEndRadius, CGPointDistance(endCenter, CGRectBottomLeft(aExtents)));
+ minimumEndRadius = max(minimumEndRadius, CGPointDistance(endCenter, CGRectTopRight(aExtents)));
+ minimumEndRadius = max(minimumEndRadius, CGPointDistance(endCenter, CGRectBottomRight(aExtents)));
+
+ CGFloat length = endRadius - startRadius;
+ int repeatStartFactor = 0, repeatEndFactor = 1;
+ while (endRadius < minimumEndRadius) {
+ endRadius += length;
+ repeatEndFactor++;
+ }
+
+ while (startRadius-length >= 0) {
+ startRadius -= length;
+ repeatStartFactor--;
+ }
+
+ CGGradientRef gradient = CreateRepeatingGradient(aColorSpace, cg, stops, repeatStartFactor, repeatEndFactor, aReflect);
+
+ //XXX: are there degenerate radial gradients that we should avoid drawing?
+ CGContextDrawRadialGradient(cg, gradient, startCenter, startRadius, endCenter, endRadius,
+ kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
+ CGGradientRelease(gradient);
+}
+
+static void
+DrawGradient(CGColorSpaceRef aColorSpace,
+ CGContextRef cg, const Pattern &aPattern, const CGRect &aExtents)
+{
+ if (MOZ2D_ERROR_IF(!cg)) {
+ return;
+ }
+
+ if (CGRectIsEmpty(aExtents)) {
+ return;
+ }
+
+ if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
+ const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern);
+ GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
+ CGAffineTransform patternMatrix = GfxMatrixToCGAffineTransform(pat.mMatrix);
+ CGContextConcatCTM(cg, patternMatrix);
+ CGRect extents = CGRectApplyAffineTransform(aExtents, CGAffineTransformInvert(patternMatrix));
+ if (stops->mExtend == ExtendMode::CLAMP) {
+
+ // XXX: we should take the m out of the properties of LinearGradientPatterns
+ CGPoint startPoint = { pat.mBegin.x, pat.mBegin.y };
+ CGPoint endPoint = { pat.mEnd.x, pat.mEnd.y };
+
+ // Canvas spec states that we should avoid drawing degenerate gradients (XXX: should this be in common code?)
+ //if (startPoint.x == endPoint.x && startPoint.y == endPoint.y)
+ // return;
+
+ CGContextDrawLinearGradient(cg, stops->mGradient, startPoint, endPoint,
+ kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
+ } else if (stops->mExtend == ExtendMode::REPEAT || stops->mExtend == ExtendMode::REFLECT) {
+ DrawLinearRepeatingGradient(aColorSpace, cg, pat, extents, stops->mExtend == ExtendMode::REFLECT);
+ }
+ } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
+ const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
+ CGAffineTransform patternMatrix = GfxMatrixToCGAffineTransform(pat.mMatrix);
+ CGContextConcatCTM(cg, patternMatrix);
+ CGRect extents = CGRectApplyAffineTransform(aExtents, CGAffineTransformInvert(patternMatrix));
+ GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
+ if (stops->mExtend == ExtendMode::CLAMP) {
+
+ // XXX: we should take the m out of the properties of RadialGradientPatterns
+ CGPoint startCenter = { pat.mCenter1.x, pat.mCenter1.y };
+ CGFloat startRadius = pat.mRadius1;
+ CGPoint endCenter = { pat.mCenter2.x, pat.mCenter2.y };
+ CGFloat endRadius = pat.mRadius2;
+
+ //XXX: are there degenerate radial gradients that we should avoid drawing?
+ CGContextDrawRadialGradient(cg, stops->mGradient, startCenter, startRadius, endCenter, endRadius,
+ kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
+ } else if (stops->mExtend == ExtendMode::REPEAT || stops->mExtend == ExtendMode::REFLECT) {
+ DrawRadialRepeatingGradient(aColorSpace, cg, pat, extents, stops->mExtend == ExtendMode::REFLECT);
+ }
} else {
assert(0);
}
@@ -445,7 +753,14 @@ CGPatternCallbacks patternCallbacks = {
static bool
isGradient(const Pattern &aPattern)
{
- return aPattern.GetType() == PATTERN_LINEAR_GRADIENT || aPattern.GetType() == PATTERN_RADIAL_GRADIENT;
+ return aPattern.GetType() == PatternType::LINEAR_GRADIENT || aPattern.GetType() == PatternType::RADIAL_GRADIENT;
+}
+
+static bool
+isNonRepeatingSurface(const Pattern& aPattern)
+{
+ return aPattern.GetType() == PatternType::SURFACE &&
+ static_cast<const SurfacePattern&>(aPattern).mExtendMode != ExtendMode::REPEAT;
}
/* CoreGraphics patterns ignore the userspace transform so
@@ -455,17 +770,24 @@ CreateCGPattern(const Pattern &aPattern, CGAffineTransform aUserSpace)
{
const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
// XXX: is .get correct here?
- CGImageRef image = GetImageFromSourceSurface(pat.mSurface.get());
+ CGImageRef image = GetRetainedImageFromSourceSurface(pat.mSurface.get());
+ Matrix patTransform = pat.mMatrix;
+ if (!pat.mSamplingRect.IsEmpty()) {
+ CGImageRef temp = CGImageCreateWithImageInRect(image, RectToCGRect(pat.mSamplingRect));
+ CGImageRelease(image);
+ image = temp;
+ patTransform.PreTranslate(pat.mSamplingRect.x, pat.mSamplingRect.y);
+ }
CGFloat xStep, yStep;
switch (pat.mExtendMode) {
- case EXTEND_CLAMP:
+ case ExtendMode::CLAMP:
// The 1 << 22 comes from Webkit see Pattern::createPlatformPattern() in PatternCG.cpp for more info
xStep = static_cast<CGFloat>(1 << 22);
yStep = static_cast<CGFloat>(1 << 22);
break;
- case EXTEND_REFLECT:
+ case ExtendMode::REFLECT:
assert(0);
- case EXTEND_REPEAT:
+ case ExtendMode::REPEAT:
xStep = static_cast<CGFloat>(CGImageGetWidth(image));
yStep = static_cast<CGFloat>(CGImageGetHeight(image));
// webkit uses wkCGPatternCreateWithImageAndTransform a wrapper around CGPatternCreateWithImage2
@@ -489,25 +811,29 @@ CreateCGPattern(const Pattern &aPattern, CGAffineTransform aUserSpace)
CGAffineTransform transform =
CGAffineTransformConcat(CGAffineTransformConcat(CGAffineTransformMakeScale(1,
-1),
- GfxMatrixToCGAffineTransform(pat.mMatrix)),
+ GfxMatrixToCGAffineTransform(patTransform)),
aUserSpace);
transform = CGAffineTransformTranslate(transform, 0, -static_cast<float>(CGImageGetHeight(image)));
- return CGPatternCreate(CGImageRetain(image), bounds, transform, xStep, yStep, kCGPatternTilingConstantSpacing,
+ return CGPatternCreate(image, bounds, transform, xStep, yStep, kCGPatternTilingConstantSpacing,
true, &patternCallbacks);
}
static void
SetFillFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern &aPattern)
{
+ if (MOZ2D_ERROR_IF(!cg)) {
+ return;
+ }
+
assert(!isGradient(aPattern));
- if (aPattern.GetType() == PATTERN_COLOR) {
+ if (aPattern.GetType() == PatternType::COLOR) {
const Color& color = static_cast<const ColorPattern&>(aPattern).mColor;
//XXX: we should cache colors
CGColorRef cgcolor = ColorToCGColor(aColorSpace, color);
CGContextSetFillColorWithColor(cg, cgcolor);
CGColorRelease(cgcolor);
- } else if (aPattern.GetType() == PATTERN_SURFACE) {
+ } else if (aPattern.GetType() == PatternType::SURFACE) {
CGColorSpaceRef patternSpace;
patternSpace = CGColorSpaceCreatePattern (nullptr);
@@ -515,6 +841,8 @@ SetFillFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern &
CGColorSpaceRelease(patternSpace);
CGPatternRef pattern = CreateCGPattern(aPattern, CGContextGetCTM(cg));
+ const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
+ CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter));
CGFloat alpha = 1.;
CGContextSetFillPattern(cg, pattern, &alpha);
CGPatternRelease(pattern);
@@ -525,19 +853,21 @@ static void
SetStrokeFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern &aPattern)
{
assert(!isGradient(aPattern));
- if (aPattern.GetType() == PATTERN_COLOR) {
+ if (aPattern.GetType() == PatternType::COLOR) {
const Color& color = static_cast<const ColorPattern&>(aPattern).mColor;
//XXX: we should cache colors
CGColorRef cgcolor = ColorToCGColor(aColorSpace, color);
CGContextSetStrokeColorWithColor(cg, cgcolor);
CGColorRelease(cgcolor);
- } else if (aPattern.GetType() == PATTERN_SURFACE) {
+ } else if (aPattern.GetType() == PatternType::SURFACE) {
CGColorSpaceRef patternSpace;
patternSpace = CGColorSpaceCreatePattern (nullptr);
CGContextSetStrokeColorSpace(cg, patternSpace);
CGColorSpaceRelease(patternSpace);
CGPatternRef pattern = CreateCGPattern(aPattern, CGContextGetCTM(cg));
+ const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
+ CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter));
CGFloat alpha = 1.;
CGContextSetStrokePattern(cg, pattern, &alpha);
CGPatternRelease(pattern);
@@ -545,26 +875,112 @@ SetStrokeFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern
}
+void
+DrawTargetCG::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aDrawOptions)
+{
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
+
+ MarkChanged();
+
+ CGContextSaveGState(mCg);
+
+ CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
+ UnboundnessFixer fixer;
+ CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+ if (MOZ2D_ERROR_IF(!cg)) {
+ return;
+ }
+
+ CGContextSetAlpha(cg, aDrawOptions.mAlpha);
+ CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
+
+ CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
+ CGImageRef image = GetRetainedImageFromSourceSurface(aMask);
+
+ // use a negative-y so that the mask image draws right ways up
+ CGContextScaleCTM(cg, 1, -1);
+
+ IntSize size = aMask->GetSize();
+
+ CGContextClipToMask(cg, CGRectMake(aOffset.x, -(aOffset.y + size.height), size.width, size.height), image);
+
+ CGContextScaleCTM(cg, 1, -1);
+ if (isGradient(aSource)) {
+ // we shouldn't need to clip to an additional rectangle
+ // as the cliping to the mask should be sufficient.
+ DrawGradient(mColorSpace, cg, aSource, CGRectMake(aOffset.x, aOffset.y, size.width, size.height));
+ } else {
+ SetFillFromPattern(cg, mColorSpace, aSource);
+ CGContextFillRect(cg, CGRectMake(aOffset.x, aOffset.y, size.width, size.height));
+ }
+
+ CGImageRelease(image);
+
+ fixer.Fix(mCg);
+
+ CGContextRestoreGState(mCg);
+}
+
+
void
DrawTargetCG::FillRect(const Rect &aRect,
- const Pattern &aPattern,
- const DrawOptions &aDrawOptions)
+ const Pattern &aPattern,
+ const DrawOptions &aDrawOptions)
{
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
+
MarkChanged();
CGContextSaveGState(mCg);
UnboundnessFixer fixer;
- CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+ CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp, &aRect);
+ if (MOZ2D_ERROR_IF(!cg)) {
+ return;
+ }
+
CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
+ CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
if (isGradient(aPattern)) {
CGContextClipToRect(cg, RectToCGRect(aRect));
- DrawGradient(cg, aPattern);
+ CGRect clipBounds = CGContextGetClipBoundingBox(cg);
+ DrawGradient(mColorSpace, cg, aPattern, clipBounds);
+ } else if (isNonRepeatingSurface(aPattern)) {
+ // SetFillFromPattern can handle this case but using CGContextDrawImage
+ // should give us better performance, better output, smaller PDF and
+ // matches what cairo does.
+ const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
+ CGImageRef image = GetRetainedImageFromSourceSurface(pat.mSurface.get());
+ Matrix transform = pat.mMatrix;
+ if (!pat.mSamplingRect.IsEmpty()) {
+ CGImageRef temp = CGImageCreateWithImageInRect(image, RectToCGRect(pat.mSamplingRect));
+ CGImageRelease(image);
+ image = temp;
+ transform.PreTranslate(pat.mSamplingRect.x, pat.mSamplingRect.y);
+ }
+ CGContextClipToRect(cg, RectToCGRect(aRect));
+ CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(transform));
+ CGContextTranslateCTM(cg, 0, CGImageGetHeight(image));
+ CGContextScaleCTM(cg, 1, -1);
+
+ CGRect imageRect = CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image));
+
+ CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter));
+
+ CGContextDrawImage(cg, imageRect, image);
+ CGImageRelease(image);
} else {
SetFillFromPattern(cg, mColorSpace, aPattern);
CGContextFillRect(cg, RectToCGRect(aRect));
@@ -584,13 +1000,21 @@ DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPatte
return;
}
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
+
MarkChanged();
CGContextSaveGState(mCg);
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+ if (MOZ2D_ERROR_IF(!cg)) {
+ return;
+ }
CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
+ CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
@@ -603,9 +1027,10 @@ DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPatte
if (isGradient(aPattern)) {
CGContextReplacePathWithStrokedPath(cg);
+ CGRect extents = CGContextGetPathBoundingBox(cg);
//XXX: should we use EO clip here?
CGContextClip(cg);
- DrawGradient(cg, aPattern);
+ DrawGradient(mColorSpace, cg, aPattern, extents);
} else {
SetStrokeFromPattern(cg, mColorSpace, aPattern);
CGContextStrokePath(cg);
@@ -615,12 +1040,31 @@ DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPatte
CGContextRestoreGState(mCg);
}
+static bool
+IsInteger(Float aValue)
+{
+ return floorf(aValue) == aValue;
+}
+
+static bool
+IsPixelAlignedStroke(const Rect& aRect, Float aLineWidth)
+{
+ Float halfWidth = aLineWidth/2;
+ return IsInteger(aLineWidth) &&
+ IsInteger(aRect.x - halfWidth) && IsInteger(aRect.y - halfWidth) &&
+ IsInteger(aRect.XMost() - halfWidth) && IsInteger(aRect.YMost() - halfWidth);
+}
+
void
DrawTargetCG::StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions,
const DrawOptions &aDrawOptions)
{
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
+
if (!aRect.IsFinite()) {
return;
}
@@ -631,39 +1075,52 @@ DrawTargetCG::StrokeRect(const Rect &aRect,
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+ if (MOZ2D_ERROR_IF(!cg)) {
+ return;
+ }
CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
+ // Work around Quartz bug where antialiasing causes corner pixels to be off by
+ // 1 channel value (e.g. rgb(1,1,1) values appear at the corner of solid
+ // black stroke), by turning off antialiasing when the edges of the stroke
+ // are pixel-aligned. Note that when a transform's components are all
+ // integers, it maps integers coordinates to integer coordinates.
+ bool pixelAlignedStroke = mTransform.IsAllIntegers() &&
+ mTransform.PreservesAxisAlignedRectangles() &&
+ aPattern.GetType() == PatternType::COLOR &&
+ IsPixelAlignedStroke(aRect, aStrokeOptions.mLineWidth);
+ CGContextSetShouldAntialias(cg,
+ aDrawOptions.mAntialiasMode != AntialiasMode::NONE && !pixelAlignedStroke);
+
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
- // we don't need to set all of the stroke state because
- // it doesn't apply when stroking rects
- switch (aStrokeOptions.mLineJoin)
- {
- case JOIN_BEVEL:
- CGContextSetLineJoin(cg, kCGLineJoinBevel);
- break;
- case JOIN_ROUND:
- CGContextSetLineJoin(cg, kCGLineJoinRound);
- break;
- case JOIN_MITER:
- case JOIN_MITER_OR_BEVEL:
- CGContextSetLineJoin(cg, kCGLineJoinMiter);
- break;
- }
- CGContextSetLineWidth(cg, aStrokeOptions.mLineWidth);
+ SetStrokeOptions(cg, aStrokeOptions);
if (isGradient(aPattern)) {
// There's no CGContextClipStrokeRect so we do it by hand
CGContextBeginPath(cg);
CGContextAddRect(cg, RectToCGRect(aRect));
CGContextReplacePathWithStrokedPath(cg);
+ CGRect extents = CGContextGetPathBoundingBox(cg);
//XXX: should we use EO clip here?
CGContextClip(cg);
- DrawGradient(cg, aPattern);
+ DrawGradient(mColorSpace, cg, aPattern, extents);
} else {
SetStrokeFromPattern(cg, mColorSpace, aPattern);
- CGContextStrokeRect(cg, RectToCGRect(aRect));
+ // We'd like to use CGContextStrokeRect(cg, RectToCGRect(aRect));
+ // Unfortunately, newer versions of OS X no longer start at the top-left
+ // corner and stroke clockwise as older OS X versions and all the other
+ // Moz2D backends do. (Newer versions start at the top right-hand corner
+ // and stroke counter-clockwise.) For consistency we draw the rect by hand.
+ CGRect rect = RectToCGRect(aRect);
+ CGContextBeginPath(cg);
+ CGContextMoveToPoint(cg, CGRectGetMinX(rect), CGRectGetMinY(rect));
+ CGContextAddLineToPoint(cg, CGRectGetMaxX(rect), CGRectGetMinY(rect));
+ CGContextAddLineToPoint(cg, CGRectGetMaxX(rect), CGRectGetMaxY(rect));
+ CGContextAddLineToPoint(cg, CGRectGetMinX(rect), CGRectGetMaxY(rect));
+ CGContextClosePath(cg);
+ CGContextStrokePath(cg);
}
fixer.Fix(mCg);
@@ -674,6 +1131,10 @@ DrawTargetCG::StrokeRect(const Rect &aRect,
void
DrawTargetCG::ClearRect(const Rect &aRect)
{
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
+
MarkChanged();
CGContextSaveGState(mCg);
@@ -687,6 +1148,10 @@ DrawTargetCG::ClearRect(const Rect &aRect)
void
DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions)
{
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
+
if (!aPath->GetBounds().IsFinite()) {
return;
}
@@ -697,7 +1162,12 @@ DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOpt
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+ if (MOZ2D_ERROR_IF(!cg)) {
+ return;
+ }
+
CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
+ CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
@@ -705,7 +1175,7 @@ DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOpt
CGContextBeginPath(cg);
- assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS);
+ assert(aPath->GetBackendType() == BackendType::COREGRAPHICS);
const PathCG *cgPath = static_cast<const PathCG*>(aPath);
CGContextAddPath(cg, cgPath->GetPath());
@@ -713,9 +1183,10 @@ DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOpt
if (isGradient(aPattern)) {
CGContextReplacePathWithStrokedPath(cg);
+ CGRect extents = CGContextGetPathBoundingBox(cg);
//XXX: should we use EO clip here?
CGContextClip(cg);
- DrawGradient(cg, aPattern);
+ DrawGradient(mColorSpace, cg, aPattern, extents);
} else {
// XXX: we could put fill mode into the path fill rule if we wanted
@@ -730,16 +1201,25 @@ DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOpt
void
DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aDrawOptions)
{
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
+
MarkChanged();
- assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS);
+ assert(aPath->GetBackendType() == BackendType::COREGRAPHICS);
CGContextSaveGState(mCg);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+ if (MOZ2D_ERROR_IF(!cg)) {
+ return;
+ }
+
CGContextSetAlpha(cg, aDrawOptions.mAlpha);
+ CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
@@ -749,25 +1229,28 @@ DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions
if (isGradient(aPattern)) {
// setup a clip to draw the gradient through
+ CGRect extents;
if (CGPathIsEmpty(cgPath->GetPath())) {
// Adding an empty path will cause us not to clip
// so clip everything explicitly
CGContextClipToRect(mCg, CGRectZero);
+ extents = CGRectZero;
} else {
CGContextAddPath(cg, cgPath->GetPath());
- if (cgPath->GetFillRule() == FILL_EVEN_ODD)
+ extents = CGContextGetPathBoundingBox(cg);
+ if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD)
CGContextEOClip(mCg);
else
CGContextClip(mCg);
}
- DrawGradient(cg, aPattern);
+ DrawGradient(mColorSpace, cg, aPattern, extents);
} else {
CGContextAddPath(cg, cgPath->GetPath());
SetFillFromPattern(cg, mColorSpace, aPattern);
- if (cgPath->GetFillRule() == FILL_EVEN_ODD)
+ if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD)
CGContextEOFillPath(cg);
else
CGContextFillPath(cg);
@@ -777,34 +1260,116 @@ DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions
CGContextRestoreGState(mCg);
}
+CGRect ComputeGlyphsExtents(CGRect *bboxes, CGPoint *positions, CFIndex count, float scale)
+{
+ CGFloat x1, x2, y1, y2;
+ if (count < 1)
+ return CGRectZero;
+
+ x1 = bboxes[0].origin.x + positions[0].x;
+ x2 = bboxes[0].origin.x + positions[0].x + scale*bboxes[0].size.width;
+ y1 = bboxes[0].origin.y + positions[0].y;
+ y2 = bboxes[0].origin.y + positions[0].y + scale*bboxes[0].size.height;
+
+ // accumulate max and minimum coordinates
+ for (int i = 1; i < count; i++) {
+ x1 = min(x1, bboxes[i].origin.x + positions[i].x);
+ y1 = min(y1, bboxes[i].origin.y + positions[i].y);
+ x2 = max(x2, bboxes[i].origin.x + positions[i].x + scale*bboxes[i].size.width);
+ y2 = max(y2, bboxes[i].origin.y + positions[i].y + scale*bboxes[i].size.height);
+ }
+
+ CGRect extents = {{x1, y1}, {x2-x1, y2-y1}};
+ return extents;
+}
+
+typedef void (*CGContextSetFontSmoothingBackgroundColorFunc) (CGContextRef cgContext, CGColorRef color);
+
+static CGContextSetFontSmoothingBackgroundColorFunc
+GetCGContextSetFontSmoothingBackgroundColorFunc()
+{
+ static CGContextSetFontSmoothingBackgroundColorFunc func = nullptr;
+ static bool lookedUpFunc = false;
+ if (!lookedUpFunc) {
+ func = (CGContextSetFontSmoothingBackgroundColorFunc)dlsym(
+ RTLD_DEFAULT, "CGContextSetFontSmoothingBackgroundColor");
+ lookedUpFunc = true;
+ }
+ return func;
+}
void
DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aDrawOptions,
- const GlyphRenderingOptions*)
+ const GlyphRenderingOptions *aGlyphRenderingOptions)
{
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
+
MarkChanged();
assert(aBuffer.mNumGlyphs);
CGContextSaveGState(mCg);
+ if (aGlyphRenderingOptions && aGlyphRenderingOptions->GetType() == FontType::MAC) {
+ Color fontSmoothingBackgroundColor =
+ static_cast<const GlyphRenderingOptionsCG*>(aGlyphRenderingOptions)->FontSmoothingBackgroundColor();
+ if (fontSmoothingBackgroundColor.a > 0) {
+ CGContextSetFontSmoothingBackgroundColorFunc setFontSmoothingBGColorFunc =
+ GetCGContextSetFontSmoothingBackgroundColorFunc();
+ if (setFontSmoothingBGColorFunc) {
+ CGColorRef color = ColorToCGColor(mColorSpace, fontSmoothingBackgroundColor);
+ setFontSmoothingBGColorFunc(mCg, color);
+ CGColorRelease(color);
+
+ // Font rendering with a non-transparent font smoothing background color
+ // can leave pixels in our buffer where the rgb components exceed the alpha
+ // component. When this happens we need to clean up the data afterwards.
+ // The purpose of this is probably the following: Correct compositing of
+ // subpixel anti-aliased fonts on transparent backgrounds requires
+ // different alpha values per RGB component. Usually, premultiplied color
+ // values are derived by multiplying all components with the same per-pixel
+ // alpha value. However, if you multiply each component with a *different*
+ // alpha, and set the alpha component of the pixel to, say, the average
+ // of the alpha values that you used during the premultiplication of the
+ // RGB components, you can trick OVER compositing into doing a simplified
+ // form of component alpha compositing. (You just need to make sure to
+ // clamp the components of the result pixel to [0,255] afterwards.)
+ mMayContainInvalidPremultipliedData = true;
+ }
+ }
+ }
+
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+ if (MOZ2D_ERROR_IF(!cg)) {
+ return;
+ }
+
CGContextSetAlpha(cg, aDrawOptions.mAlpha);
+ CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
+ if (aDrawOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
+ CGContextSetShouldSmoothFonts(cg, aDrawOptions.mAntialiasMode == AntialiasMode::SUBPIXEL);
+ }
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
ScaledFontMac* macFont = static_cast<ScaledFontMac*>(aFont);
- //XXX: we should use a stack vector here when we have a class like that
- std::vector<CGGlyph> glyphs;
- std::vector<CGPoint> positions;
- glyphs.resize(aBuffer.mNumGlyphs);
- positions.resize(aBuffer.mNumGlyphs);
+ // This code can execute millions of times in short periods, so we want to
+ // avoid heap allocation whenever possible. So we use an inline vector
+ // capacity of 64 elements, which is enough to typically avoid heap
+ // allocation in ~99% of cases.
+ Vector<CGGlyph, 64> glyphs;
+ Vector<CGPoint, 64> positions;
+ if (!glyphs.resizeUninitialized(aBuffer.mNumGlyphs) ||
+ !positions.resizeUninitialized(aBuffer.mNumGlyphs)) {
+ MOZ_CRASH("glyphs/positions allocation failed");
+ }
// Handle the flip
- CGAffineTransform matrix = CGAffineTransformMakeScale(1, -1);
- CGContextConcatCTM(cg, matrix);
+ CGContextScaleCTM(cg, 1, -1);
// CGContextSetTextMatrix works differently with kCGTextClip && kCGTextFill
// It seems that it transforms the positions with TextFill and not with TextClip
// Therefore we'll avoid it. See also:
@@ -820,30 +1385,41 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pa
//XXX: CGContextShowGlyphsAtPositions is 10.5+ for older versions use CGContextShowGlyphsWithAdvances
if (isGradient(aPattern)) {
CGContextSetTextDrawingMode(cg, kCGTextClip);
+ CGRect extents;
if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) {
- ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, &glyphs.front(),
- &positions.front(),
- aBuffer.mNumGlyphs, cg);
+ CGRect *bboxes = new CGRect[aBuffer.mNumGlyphs];
+ CTFontGetBoundingRectsForGlyphs(macFont->mCTFont, kCTFontDefaultOrientation,
+ glyphs.begin(), bboxes, aBuffer.mNumGlyphs);
+ extents = ComputeGlyphsExtents(bboxes, positions.begin(), aBuffer.mNumGlyphs, 1.0f);
+ ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, glyphs.begin(),
+ positions.begin(), aBuffer.mNumGlyphs, cg);
+ delete[] bboxes;
} else {
+ CGRect *bboxes = new CGRect[aBuffer.mNumGlyphs];
+ CGFontGetGlyphBBoxes(macFont->mFont, glyphs.begin(), aBuffer.mNumGlyphs, bboxes);
+ extents = ComputeGlyphsExtents(bboxes, positions.begin(), aBuffer.mNumGlyphs, macFont->mSize);
+
CGContextSetFont(cg, macFont->mFont);
CGContextSetFontSize(cg, macFont->mSize);
- CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(),
+ CGContextShowGlyphsAtPositions(cg, glyphs.begin(), positions.begin(),
aBuffer.mNumGlyphs);
+ delete[] bboxes;
}
- DrawGradient(cg, aPattern);
+ CGContextScaleCTM(cg, 1, -1);
+ DrawGradient(mColorSpace, cg, aPattern, extents);
} else {
//XXX: with CoreGraphics we can stroke text directly instead of going
// through GetPath. It would be nice to add support for using that
CGContextSetTextDrawingMode(cg, kCGTextFill);
SetFillFromPattern(cg, mColorSpace, aPattern);
if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) {
- ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, &glyphs.front(),
- &positions.front(),
+ ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, glyphs.begin(),
+ positions.begin(),
aBuffer.mNumGlyphs, cg);
} else {
CGContextSetFont(cg, macFont->mFont);
CGContextSetFontSize(cg, macFont->mSize);
- CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(),
+ CGContextShowGlyphsAtPositions(cg, glyphs.begin(), positions.begin(),
aBuffer.mNumGlyphs);
}
}
@@ -862,47 +1438,56 @@ DrawTargetCG::CopySurface(SourceSurface *aSurface,
const IntRect& aSourceRect,
const IntPoint &aDestination)
{
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
+
MarkChanged();
- CGImageRef image;
- CGImageRef subimage = nullptr;
- if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) {
- image = GetImageFromSourceSurface(aSurface);
- /* we have two options here:
- * - create a subimage -- this is slower
- * - fancy things with clip and different dest rects */
- {
- subimage = CGImageCreateWithImageInRect(image, IntRectToCGRect(aSourceRect));
- image = subimage;
- }
+ if (aSurface->GetType() == SurfaceType::COREGRAPHICS_IMAGE ||
+ aSurface->GetType() == SurfaceType::COREGRAPHICS_CGCONTEXT ||
+ aSurface->GetType() == SurfaceType::DATA) {
+ CGImageRef image = GetRetainedImageFromSourceSurface(aSurface);
+
// XXX: it might be more efficient for us to do the copy directly if we have access to the bits
CGContextSaveGState(mCg);
// CopySurface ignores the clip, so we need to use private API to temporarily reset it
CGContextResetClip(mCg);
+ CGRect destRect = CGRectMake(aDestination.x, aDestination.y,
+ aSourceRect.width, aSourceRect.height);
+ CGContextClipToRect(mCg, destRect);
+
CGContextSetBlendMode(mCg, kCGBlendModeCopy);
CGContextScaleCTM(mCg, 1, -1);
- CGRect flippedRect = CGRectMake(aDestination.x, -(aDestination.y + aSourceRect.height),
- aSourceRect.width, aSourceRect.height);
+ CGRect flippedRect = CGRectMake(aDestination.x - aSourceRect.x, -(aDestination.y - aSourceRect.y + double(CGImageGetHeight(image))),
+ CGImageGetWidth(image), CGImageGetHeight(image));
+ // Quartz seems to copy A8 surfaces incorrectly if we don't initialize them
+ // to transparent first.
+ if (mFormat == SurfaceFormat::A8) {
+ CGContextClearRect(mCg, flippedRect);
+ }
CGContextDrawImage(mCg, flippedRect, image);
CGContextRestoreGState(mCg);
-
- CGImageRelease(subimage);
+ CGImageRelease(image);
}
}
void
DrawTargetCG::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, CompositionOp aOperator)
{
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
+
MarkChanged();
- CGImageRef image;
- image = GetImageFromSourceSurface(aSurface);
+ CGImageRef image = GetRetainedImageFromSourceSurface(aSurface);
IntSize size = aSurface->GetSize();
CGContextSaveGState(mCg);
@@ -922,6 +1507,7 @@ DrawTargetCG::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest,
CGContextDrawImage(mCg, flippedRect, image);
+ CGImageRelease(image);
CGContextRestoreGState(mCg);
}
@@ -935,13 +1521,14 @@ DrawTargetCG::Init(BackendType aType,
{
// XXX: we should come up with some consistent semantics for dealing
// with zero area drawtargets
- if (aSize.width <= 0 || aSize.height <= 0 ||
- // 32767 is the maximum size supported by cairo
- // we clamp to that to make it easier to interoperate
- aSize.width > 32767 || aSize.height > 32767) {
+ if (aSize.width <= 0 ||
+ aSize.height <= 0 ||
+ size_t(aSize.width) > GetMaxSurfaceSize() ||
+ size_t(aSize.height) > GetMaxSurfaceSize())
+ {
+ gfxWarning() << "Failed to Init() DrawTargetCG because of bad size.";
mColorSpace = nullptr;
mCg = nullptr;
- mData = nullptr;
return false;
}
@@ -950,32 +1537,50 @@ DrawTargetCG::Init(BackendType aType,
//XXX: we'd be better off reusing the Colorspace across draw targets
mColorSpace = CGColorSpaceCreateDeviceRGB();
- if (aData == nullptr && aType != BACKEND_COREGRAPHICS_ACCELERATED) {
+ if (aData == nullptr && aType != BackendType::COREGRAPHICS_ACCELERATED) {
// XXX: Currently, Init implicitly clears, that can often be a waste of time
- mData = calloc(aSize.height * aStride, 1);
- aData = static_cast<unsigned char*>(mData);
- } else {
- // mData == nullptr means DrawTargetCG doesn't own the image data and will not
- // delete it in the destructor
- mData = nullptr;
+ size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height);
+ if (bufLen == 0) {
+ mColorSpace = nullptr;
+ mCg = nullptr;
+ return false;
+ }
+ static_assert(sizeof(decltype(mData[0])) == 1,
+ "mData.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen");
+ mData.Realloc(/* actually an object count */ bufLen, true);
+ aData = static_cast<unsigned char*>(mData);
}
mSize = aSize;
- if (aType == BACKEND_COREGRAPHICS_ACCELERATED) {
+ if (aType == BackendType::COREGRAPHICS_ACCELERATED) {
RefPtr<MacIOSurface> ioSurface = MacIOSurface::CreateIOSurface(aSize.width, aSize.height);
mCg = ioSurface->CreateIOSurfaceContext();
// If we don't have the symbol for 'CreateIOSurfaceContext' mCg will be null
// and we will fallback to software below
- mData = nullptr;
}
- if (!mCg || aType == BACKEND_COREGRAPHICS) {
+ mFormat = SurfaceFormat::B8G8R8A8;
+
+ if (!mCg || aType == BackendType::COREGRAPHICS) {
int bitsPerComponent = 8;
CGBitmapInfo bitinfo;
- bitinfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
-
+ if (aFormat == SurfaceFormat::A8) {
+ if (mColorSpace)
+ CGColorSpaceRelease(mColorSpace);
+ mColorSpace = nullptr;
+ bitinfo = kCGImageAlphaOnly;
+ mFormat = SurfaceFormat::A8;
+ } else {
+ bitinfo = kCGBitmapByteOrder32Host;
+ if (aFormat == SurfaceFormat::B8G8R8X8) {
+ bitinfo |= kCGImageAlphaNoneSkipFirst;
+ mFormat = aFormat;
+ } else {
+ bitinfo |= kCGImageAlphaPremultipliedFirst;
+ }
+ }
// XXX: what should we do if this fails?
mCg = CGBitmapContextCreate (aData,
mSize.width,
@@ -987,6 +1592,11 @@ DrawTargetCG::Init(BackendType aType,
}
assert(mCg);
+ if (!mCg) {
+ gfxCriticalError() << "Failed to create CG context";
+ return false;
+ }
+
// CGContext's default to have the origin at the bottom left
// so flip it to the top left
CGContextTranslateCTM(mCg, 0, mSize.height);
@@ -1000,10 +1610,8 @@ DrawTargetCG::Init(BackendType aType,
// use the default for content.
CGContextSetInterpolationQuality(mCg, kCGInterpolationLow);
- // XXX: set correct format
- mFormat = FORMAT_B8G8R8A8;
- if (aType == BACKEND_COREGRAPHICS_ACCELERATED) {
+ if (aType == BackendType::COREGRAPHICS_ACCELERATED) {
// The bitmap backend uses callac to clear, we can't do that without
// reading back the surface. This should trigger something equivilent
// to glClear.
@@ -1013,10 +1621,55 @@ DrawTargetCG::Init(BackendType aType,
return true;
}
+static void
+EnsureValidPremultipliedData(CGContextRef aContext)
+{
+ if (CGBitmapContextGetBitsPerPixel(aContext) != 32 ||
+ CGBitmapContextGetAlphaInfo(aContext) != kCGImageAlphaPremultipliedFirst) {
+ return;
+ }
+
+ uint8_t* bitmapData = (uint8_t*)CGBitmapContextGetData(aContext);
+ int w = CGBitmapContextGetWidth(aContext);
+ int h = CGBitmapContextGetHeight(aContext);
+ int stride = CGBitmapContextGetBytesPerRow(aContext);
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ int i = y * stride + x * 4;
+ uint8_t a = bitmapData[i + 3];
+
+ // Clamp rgb components to the alpha component.
+ if (bitmapData[i + 0] > a) {
+ bitmapData[i + 0] = a;
+ }
+ if (bitmapData[i + 1] > a) {
+ bitmapData[i + 1] = a;
+ }
+ if (bitmapData[i + 2] > a) {
+ bitmapData[i + 2] = a;
+ }
+ }
+ }
+}
+
void
DrawTargetCG::Flush()
{
- CGContextFlush(mCg);
+ if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) {
+ CGContextFlush(mCg);
+ } else if (GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP &&
+ mMayContainInvalidPremultipliedData) {
+ // We can't guarantee that all our users can handle pixel data where an RGB
+ // component value exceeds the pixel's alpha value. In particular, the
+ // color conversion that CG does when we draw a CGImage snapshot of this
+ // context into a context that has a different color space throws up on
+ // invalid premultiplied data and creates completely wrong colors.
+ // Sanitizing the data means that we lose some of the fake component alpha
+ // behavior that font rendering tries to give us, but the result still
+ // looks good enough to prefer it over grayscale font anti-aliasing.
+ EnsureValidPremultipliedData(mCg);
+ mMayContainInvalidPremultipliedData = false;
+ }
}
bool
@@ -1027,7 +1680,6 @@ DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize)
if (aSize.width == 0 || aSize.height == 0) {
mColorSpace = nullptr;
mCg = nullptr;
- mData = nullptr;
return false;
}
@@ -1039,17 +1691,33 @@ DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize)
mSize = aSize;
mCg = cgContext;
-
- mData = nullptr;
+ CGContextRetain(mCg);
assert(mCg);
- // CGContext's default to have the origin at the bottom left
- // so flip it to the top left
- CGContextTranslateCTM(mCg, 0, mSize.height);
- CGContextScaleCTM(mCg, 1, -1);
+ if (!mCg) {
+ gfxCriticalError() << "Invalid CG context at Init";
+ return false;
+ }
- //XXX: set correct format
- mFormat = FORMAT_B8G8R8A8;
+ // CGContext's default to have the origin at the bottom left.
+ // However, currently the only use of this function is to construct a
+ // DrawTargetCG around a CGContextRef from a cairo quartz surface which
+ // already has it's origin adjusted.
+ //
+ // CGContextTranslateCTM(mCg, 0, mSize.height);
+ // CGContextScaleCTM(mCg, 1, -1);
+
+ mFormat = SurfaceFormat::B8G8R8A8;
+ if (GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP) {
+ CGColorSpaceRef colorspace;
+ CGBitmapInfo bitinfo = CGBitmapContextGetBitmapInfo(mCg);
+ colorspace = CGBitmapContextGetColorSpace (mCg);
+ if (CGColorSpaceGetNumberOfComponents(colorspace) == 1) {
+ mFormat = SurfaceFormat::A8;
+ } else if ((bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst) {
+ mFormat = SurfaceFormat::B8G8R8X8;
+ }
+ }
return true;
}
@@ -1057,7 +1725,7 @@ DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize)
bool
DrawTargetCG::Init(BackendType aType, const IntSize &aSize, SurfaceFormat &aFormat)
{
- int stride = aSize.width*4;
+ int32_t stride = GetAlignedStride<16>(aSize.width * BytesPerPixel(aFormat));
// Calling Init with aData == nullptr will allocate.
return Init(aType, nullptr, aSize, stride, aFormat);
@@ -1066,15 +1734,14 @@ DrawTargetCG::Init(BackendType aType, const IntSize &aSize, SurfaceFormat &aForm
TemporaryRef<PathBuilder>
DrawTargetCG::CreatePathBuilder(FillRule aFillRule) const
{
- RefPtr<PathBuilderCG> pb = new PathBuilderCG(aFillRule);
- return pb;
+ return new PathBuilderCG(aFillRule);
}
void*
DrawTargetCG::GetNativeSurface(NativeSurfaceType aType)
{
- if ((aType == NATIVE_SURFACE_CGCONTEXT && GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP) ||
- (aType == NATIVE_SURFACE_CGCONTEXT_ACCELERATED && GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE)) {
+ if ((aType == NativeSurfaceType::CGCONTEXT && GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP) ||
+ (aType == NativeSurfaceType::CGCONTEXT_ACCELERATED && GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE)) {
return mCg;
} else {
return nullptr;
@@ -1093,7 +1760,7 @@ DrawTargetCG::Mask(const Pattern &aSource,
if (isGradient(aMask)) {
assert(0);
} else {
- if (aMask.GetType() == PATTERN_COLOR) {
+ if (aMask.GetType() == PatternType::COLOR) {
DrawOptions drawOptions(aDrawOptions);
const Color& color = static_cast<const ColorPattern&>(aMask).mColor;
drawOptions.mAlpha *= color.a;
@@ -1101,13 +1768,15 @@ DrawTargetCG::Mask(const Pattern &aSource,
// XXX: we need to get a rect that when transformed covers the entire surface
//Rect
//FillRect(rect, aSource, drawOptions);
- } else if (aMask.GetType() == PATTERN_SURFACE) {
+ } else if (aMask.GetType() == PatternType::SURFACE) {
const SurfacePattern& pat = static_cast<const SurfacePattern&>(aMask);
- CGImageRef mask = GetImageFromSourceSurface(pat.mSurface.get());
+ CGImageRef mask = GetRetainedImageFromSourceSurface(pat.mSurface.get());
+ MOZ_ASSERT(pat.mSamplingRect.IsEmpty(), "Sampling rect not supported with masks!");
Rect rect(0,0, CGImageGetWidth(mask), CGImageGetHeight(mask));
// XXX: probably we need to do some flipping of the image or something
CGContextClipToMask(mCg, RectToCGRect(rect), mask);
FillRect(rect, aSource, aDrawOptions);
+ CGImageRelease(mask);
}
}
@@ -1117,6 +1786,10 @@ DrawTargetCG::Mask(const Pattern &aSource,
void
DrawTargetCG::PushClipRect(const Rect &aRect)
{
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
+
CGContextSaveGState(mCg);
/* We go through a bit of trouble to temporarilly set the transform
@@ -1131,10 +1804,14 @@ DrawTargetCG::PushClipRect(const Rect &aRect)
void
DrawTargetCG::PushClip(const Path *aPath)
{
+ if (MOZ2D_ERROR_IF(!mCg)) {
+ return;
+ }
+
CGContextSaveGState(mCg);
CGContextBeginPath(mCg);
- assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS);
+ assert(aPath->GetBackendType() == BackendType::COREGRAPHICS);
const PathCG *cgPath = static_cast<const PathCG*>(aPath);
@@ -1155,7 +1832,7 @@ DrawTargetCG::PushClip(const Path *aPath)
CGContextAddPath(mCg, cgPath->GetPath());
CGContextRestoreGState(mCg);
- if (cgPath->GetFillRule() == FILL_EVEN_ODD)
+ if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD)
CGContextEOClip(mCg);
else
CGContextClip(mCg);
@@ -1179,6 +1856,41 @@ DrawTargetCG::MarkChanged()
}
}
+CGContextRef
+BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget *aDT)
+{
+ if ((aDT->GetBackendType() == BackendType::COREGRAPHICS ||
+ aDT->GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED) &&
+ !aDT->IsTiledDrawTarget() && !aDT->IsDualDrawTarget()) {
+ DrawTargetCG* cgDT = static_cast<DrawTargetCG*>(aDT);
+ cgDT->Flush();
+ cgDT->MarkChanged();
+
+ // swap out the context
+ CGContextRef cg = cgDT->mCg;
+ if (MOZ2D_ERROR_IF(!cg)) {
+ return nullptr;
+ }
+ cgDT->mCg = nullptr;
+
+ // save the state to make it easier for callers to avoid mucking with things
+ CGContextSaveGState(cg);
+
+ CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(cgDT->mTransform));
+
+ return cg;
+ }
+ return nullptr;
+}
+
+void
+BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg)
+{
+ DrawTargetCG* cgDT = static_cast<DrawTargetCG*>(aDT);
+
+ CGContextRestoreGState(cg);
+ cgDT->mCg = cg;
+}
}
diff --git a/gfx/2d/DrawTargetCG.h b/gfx/2d/DrawTargetCG.h
index 9ddf29bbd..67651dabb 100644
--- a/gfx/2d/DrawTargetCG.h
+++ b/gfx/2d/DrawTargetCG.h
@@ -3,6 +3,9 @@
* 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 mozilla_gfx_DrawTargetCG_h
+#define mozilla_gfx_DrawTargetCG_h
+
#include <ApplicationServices/ApplicationServices.h>
#include "2D.h"
@@ -10,6 +13,7 @@
#include "PathCG.h"
#include "SourceSurfaceCG.h"
#include "GLDefs.h"
+#include "Tools.h"
namespace mozilla {
namespace gfx {
@@ -36,32 +40,38 @@ CGRectToRect(CGRect rect)
rect.size.height);
}
+static inline Point
+CGPointToPoint(CGPoint point)
+{
+ return Point(point.x, point.y);
+}
+
static inline void
SetStrokeOptions(CGContextRef cg, const StrokeOptions &aStrokeOptions)
{
switch (aStrokeOptions.mLineCap)
{
- case CAP_BUTT:
+ case CapStyle::BUTT:
CGContextSetLineCap(cg, kCGLineCapButt);
break;
- case CAP_ROUND:
+ case CapStyle::ROUND:
CGContextSetLineCap(cg, kCGLineCapRound);
break;
- case CAP_SQUARE:
+ case CapStyle::SQUARE:
CGContextSetLineCap(cg, kCGLineCapSquare);
break;
}
switch (aStrokeOptions.mLineJoin)
{
- case JOIN_BEVEL:
+ case JoinStyle::BEVEL:
CGContextSetLineJoin(cg, kCGLineJoinBevel);
break;
- case JOIN_ROUND:
+ case JoinStyle::ROUND:
CGContextSetLineJoin(cg, kCGLineJoinRound);
break;
- case JOIN_MITER:
- case JOIN_MITER_OR_BEVEL:
+ case JoinStyle::MITER:
+ case JoinStyle::MITER_OR_BEVEL:
CGContextSetLineJoin(cg, kCGLineJoinMiter);
break;
}
@@ -81,29 +91,53 @@ SetStrokeOptions(CGContextRef cg, const StrokeOptions &aStrokeOptions)
}
}
+class GlyphRenderingOptionsCG : public GlyphRenderingOptions
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptionsCG, override)
+
+ explicit GlyphRenderingOptionsCG(const Color &aFontSmoothingBackgroundColor)
+ : mFontSmoothingBackgroundColor(aFontSmoothingBackgroundColor)
+ {}
+
+ const Color &FontSmoothingBackgroundColor() const { return mFontSmoothingBackgroundColor; }
+
+ virtual FontType GetType() const override { return FontType::MAC; }
+
+private:
+ Color mFontSmoothingBackgroundColor;
+};
class DrawTargetCG : public DrawTarget
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetCG, override)
+ friend class BorrowedCGContext;
+ friend class SourceSurfaceCGBitmapContext;
DrawTargetCG();
virtual ~DrawTargetCG();
- virtual BackendType GetType() const;
- virtual TemporaryRef<SourceSurface> Snapshot();
+ virtual DrawTargetType GetType() const override;
+ virtual BackendType GetBackendType() const override;
+ virtual TemporaryRef<SourceSurface> Snapshot() override;
virtual void DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
const Rect &aSource,
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void MaskSurface(const Pattern &aSource,
SourceSurface *aMask,
Point aOffset,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void FillRect(const Rect &aRect,
const Pattern &aPattern,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
//XXX: why do we take a reference to SurfaceFormat?
@@ -112,42 +146,50 @@ public:
bool Init(CGContextRef cgContext, const IntSize &aSize);
// Flush if using IOSurface context
- virtual void Flush();
-
- virtual void DrawSurfaceWithShadow(SourceSurface *, const Point &, const Color &, const Point &, Float, CompositionOp);
- virtual void ClearRect(const Rect &);
- virtual void CopySurface(SourceSurface *, const IntRect&, const IntPoint&);
- virtual void StrokeRect(const Rect &, const Pattern &, const StrokeOptions&, const DrawOptions&);
- virtual void StrokeLine(const Point &, const Point &, const Pattern &, const StrokeOptions &, const DrawOptions &);
- virtual void Stroke(const Path *, const Pattern &, const StrokeOptions &, const DrawOptions &);
- virtual void Fill(const Path *, const Pattern &, const DrawOptions &);
- virtual void FillGlyphs(ScaledFont *, const GlyphBuffer&, const Pattern &, const DrawOptions &, const GlyphRenderingOptions *);
+ virtual void Flush() override;
+
+ virtual void DrawSurfaceWithShadow(SourceSurface *, const Point &, const Color &, const Point &, Float, CompositionOp) override;
+ virtual void ClearRect(const Rect &) override;
+ virtual void CopySurface(SourceSurface *, const IntRect&, const IntPoint&) override;
+ virtual void StrokeRect(const Rect &, const Pattern &, const StrokeOptions&, const DrawOptions&) override;
+ virtual void StrokeLine(const Point &, const Point &, const Pattern &, const StrokeOptions &, const DrawOptions &) override;
+ virtual void Stroke(const Path *, const Pattern &, const StrokeOptions &, const DrawOptions &) override;
+ virtual void Fill(const Path *, const Pattern &, const DrawOptions &) override;
+ virtual void FillGlyphs(ScaledFont *, const GlyphBuffer&, const Pattern &, const DrawOptions &, const GlyphRenderingOptions *) override;
virtual void Mask(const Pattern &aSource,
const Pattern &aMask,
- const DrawOptions &aOptions = DrawOptions());
- virtual void PushClip(const Path *);
- virtual void PushClipRect(const Rect &aRect);
- virtual void PopClip();
- virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromNativeSurface(const NativeSurface&) const { return nullptr;}
- virtual TemporaryRef<DrawTarget> CreateSimilarDrawTarget(const IntSize &, SurfaceFormat) const;
- virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule) const;
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void PushClip(const Path *) override;
+ virtual void PushClipRect(const Rect &aRect) override;
+ virtual void PopClip() override;
+ virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromNativeSurface(const NativeSurface&) const override { return nullptr;}
+ virtual TemporaryRef<DrawTarget> CreateSimilarDrawTarget(const IntSize &, SurfaceFormat) const override;
+ virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule) const override;
virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *, uint32_t,
- ExtendMode aExtendMode = EXTEND_CLAMP) const;
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
+ virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override;
- virtual void *GetNativeSurface(NativeSurfaceType);
+ virtual void *GetNativeSurface(NativeSurfaceType) override;
- virtual IntSize GetSize() { return mSize; }
+ virtual IntSize GetSize() override { return mSize; }
/* This is for creating good compatible surfaces */
virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
int32_t aStride,
- SurfaceFormat aFormat) const;
- virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const;
+ SurfaceFormat aFormat) const override;
+ virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
CGContextRef GetCGContext() {
return mCg;
}
+
+ // 32767 is the maximum size supported by cairo. We clamp to that to make it
+ // easier to interoperate.
+ static size_t GetMaxSurfaceSize() {
+ return 32767;
+ }
+
private:
void MarkChanged();
@@ -156,17 +198,19 @@ private:
CGContextRef mCg;
/**
- * A pointer to the image buffer if the buffer is owned by this class (set to
- * nullptr otherwise).
- * The data is not considered owned by DrawTargetCG if the DrawTarget was
- * created for a pre-existing buffer or if the buffer's lifetime is managed
- * by CoreGraphics.
- * Data owned by DrawTargetCG will be deallocated in the destructor.
+ * The image buffer, if the buffer is owned by this class.
+ * If the DrawTarget was created for a pre-existing buffer or if the buffer's
+ * lifetime is managed by CoreGraphics, mData will be null.
+ * Data owned by DrawTargetCG will be deallocated in the destructor.
*/
- void *mData;
+ AlignedArray<uint8_t> mData;
RefPtr<SourceSurfaceCGContext> mSnapshot;
+ bool mMayContainInvalidPremultipliedData;
};
}
}
+
+#endif
+
diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp
index 32d107256..441068a3d 100644
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -9,6 +9,10 @@
#include "PathCairo.h"
#include "HelpersCairo.h"
#include "ScaledFontBase.h"
+#include "BorrowedContext.h"
+#include "FilterNodeSoftware.h"
+#include "mozilla/Scoped.h"
+#include "mozilla/Vector.h"
#include "cairo.h"
#include "cairo-tee.h"
@@ -25,13 +29,26 @@
#ifdef CAIRO_HAS_XLIB_SURFACE
#include "cairo-xlib.h"
+#include "cairo-xlib-xrender.h"
+#endif
+
+#ifdef CAIRO_HAS_WIN32_SURFACE
+#include "cairo-win32.h"
#endif
#include <algorithm>
+// 2^23
+#define CAIRO_COORD_MAX (Float(0x7fffff))
+
namespace mozilla {
+
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCairoSurface, cairo_surface_t, cairo_surface_destroy);
+
namespace gfx {
+cairo_surface_t *DrawTargetCairo::mDummySurface;
+
namespace {
// An RAII class to prepare to draw a context and optional path. Saves and
@@ -55,7 +72,14 @@ public:
MOZ_ASSERT(cairo_status(mCtx) || dt->GetTransform() == GetTransform());
}
- ~AutoPrepareForDrawing() { cairo_restore(mCtx); }
+ ~AutoPrepareForDrawing()
+ {
+ cairo_restore(mCtx);
+ cairo_status_t status = cairo_status(mCtx);
+ if (status) {
+ gfxWarning() << "DrawTargetCairo context in error state: " << cairo_status_to_string(status) << "(" << status << ")";
+ }
+ }
private:
#ifdef DEBUG
@@ -70,42 +94,81 @@ private:
cairo_t* mCtx;
};
+/* Clamp r to (0,0) (2^23,2^23)
+ * these are to be device coordinates.
+ *
+ * Returns false if the rectangle is completely out of bounds,
+ * true otherwise.
+ *
+ * This function assumes that it will be called with a rectangle being
+ * drawn into a surface with an identity transformation matrix; that
+ * is, anything above or to the left of (0,0) will be offscreen.
+ *
+ * First it checks if the rectangle is entirely beyond
+ * CAIRO_COORD_MAX; if so, it can't ever appear on the screen --
+ * false is returned.
+ *
+ * Then it shifts any rectangles with x/y < 0 so that x and y are = 0,
+ * and adjusts the width and height appropriately. For example, a
+ * rectangle from (0,-5) with dimensions (5,10) will become a
+ * rectangle from (0,0) with dimensions (5,5).
+ *
+ * If after negative x/y adjustment to 0, either the width or height
+ * is negative, then the rectangle is completely offscreen, and
+ * nothing is drawn -- false is returned.
+ *
+ * Finally, if x+width or y+height are greater than CAIRO_COORD_MAX,
+ * the width and height are clamped such x+width or y+height are equal
+ * to CAIRO_COORD_MAX, and true is returned.
+ */
+static bool
+ConditionRect(Rect& r) {
+ // if either x or y is way out of bounds;
+ // note that we don't handle negative w/h here
+ if (r.X() > CAIRO_COORD_MAX || r.Y() > CAIRO_COORD_MAX)
+ return false;
+
+ if (r.X() < 0.f) {
+ r.width += r.X();
+ if (r.width < 0.f)
+ return false;
+ r.x = 0.f;
+ }
+
+ if (r.XMost() > CAIRO_COORD_MAX) {
+ r.width = CAIRO_COORD_MAX - r.X();
+ }
+
+ if (r.Y() < 0.f) {
+ r.height += r.Y();
+ if (r.Height() < 0.f)
+ return false;
+
+ r.y = 0.f;
+ }
+
+ if (r.YMost() > CAIRO_COORD_MAX) {
+ r.height = CAIRO_COORD_MAX - r.Y();
+ }
+ return true;
+}
+
} // end anonymous namespace
static bool
-GetCairoSurfaceSize(cairo_surface_t* surface, IntSize& size)
+SupportsSelfCopy(cairo_surface_t* surface)
{
switch (cairo_surface_get_type(surface))
{
- case CAIRO_SURFACE_TYPE_IMAGE:
- {
- size.width = cairo_image_surface_get_width(surface);
- size.height = cairo_image_surface_get_height(surface);
- return true;
- }
-
-#ifdef CAIRO_HAS_XLIB_SURFACE
- case CAIRO_SURFACE_TYPE_XLIB:
- {
- size.width = cairo_xlib_surface_get_width(surface);
- size.height = cairo_xlib_surface_get_height(surface);
- return true;
- }
-#endif
-
#ifdef CAIRO_HAS_QUARTZ_SURFACE
case CAIRO_SURFACE_TYPE_QUARTZ:
- {
- CGContextRef cgc = cairo_quartz_surface_get_cg_context(surface);
-
- // It's valid to call these CGBitmapContext functions on non-bitmap
- // contexts; they'll just return 0 in that case.
- size.width = CGBitmapContextGetWidth(cgc);
- size.height = CGBitmapContextGetWidth(cgc);
return true;
- }
#endif
-
+#ifdef CAIRO_HAS_WIN32_SURFACE
+ case CAIRO_SURFACE_TYPE_WIN32:
+ case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
+ return true;
+#endif
default:
return false;
}
@@ -116,15 +179,15 @@ PatternIsCompatible(const Pattern& aPattern)
{
switch (aPattern.GetType())
{
- case PATTERN_LINEAR_GRADIENT:
+ case PatternType::LINEAR_GRADIENT:
{
const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
- return pattern.mStops->GetBackendType() == BACKEND_CAIRO;
+ return pattern.mStops->GetBackendType() == BackendType::CAIRO;
}
- case PATTERN_RADIAL_GRADIENT:
+ case PatternType::RADIAL_GRADIENT:
{
const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
- return pattern.mStops->GetBackendType() == BACKEND_CAIRO;
+ return pattern.mStops->GetBackendType() == BackendType::CAIRO;
}
default:
return true;
@@ -136,7 +199,123 @@ static cairo_user_data_key_t surfaceDataKey;
void
ReleaseData(void* aData)
{
- static_cast<DataSourceSurface*>(aData)->Release();
+ DataSourceSurface *data = static_cast<DataSourceSurface*>(aData);
+ data->Unmap();
+ data->Release();
+}
+
+cairo_surface_t*
+CopyToImageSurface(unsigned char *aData,
+ const IntRect &aRect,
+ int32_t aStride,
+ SurfaceFormat aFormat)
+{
+ MOZ_ASSERT(aData);
+
+ cairo_surface_t* surf = cairo_image_surface_create(GfxFormatToCairoFormat(aFormat),
+ aRect.width,
+ aRect.height);
+ // In certain scenarios, requesting larger than 8k image fails. Bug 803568
+ // covers the details of how to run into it, but the full detailed
+ // investigation hasn't been done to determine the underlying cause. We
+ // will just handle the failure to allocate the surface to avoid a crash.
+ if (cairo_surface_status(surf)) {
+ return nullptr;
+ }
+
+ unsigned char* surfData = cairo_image_surface_get_data(surf);
+ int surfStride = cairo_image_surface_get_stride(surf);
+ int32_t pixelWidth = BytesPerPixel(aFormat);
+
+ unsigned char* source = aData +
+ aRect.y * aStride +
+ aRect.x * pixelWidth;
+
+ MOZ_ASSERT(aStride >= aRect.width * pixelWidth);
+ for (int32_t y = 0; y < aRect.height; ++y) {
+ memcpy(surfData + y * surfStride,
+ source + y * aStride,
+ aRect.width * pixelWidth);
+ }
+ cairo_surface_mark_dirty(surf);
+ return surf;
+}
+
+/**
+ * If aSurface can be represented as a surface of type
+ * CAIRO_SURFACE_TYPE_IMAGE then returns that surface. Does
+ * not add a reference.
+ */
+cairo_surface_t* GetAsImageSurface(cairo_surface_t* aSurface)
+{
+ if (cairo_surface_get_type(aSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
+ return aSurface;
+#ifdef CAIRO_HAS_WIN32_SURFACE
+ } else if (cairo_surface_get_type(aSurface) == CAIRO_SURFACE_TYPE_WIN32) {
+ return cairo_win32_surface_get_image(aSurface);
+#endif
+ }
+
+ return nullptr;
+}
+
+cairo_surface_t* CreateSubImageForData(unsigned char* aData,
+ const IntRect& aRect,
+ int aStride,
+ SurfaceFormat aFormat)
+{
+ if (!aData) {
+ gfxWarning() << "DrawTargetCairo.CreateSubImageForData null aData";
+ return nullptr;
+ }
+ unsigned char *data = aData +
+ aRect.y * aStride +
+ aRect.x * BytesPerPixel(aFormat);
+
+ cairo_surface_t *image =
+ cairo_image_surface_create_for_data(data,
+ GfxFormatToCairoFormat(aFormat),
+ aRect.width,
+ aRect.height,
+ aStride);
+ cairo_surface_set_device_offset(image, -aRect.x, -aRect.y);
+ return image;
+}
+
+/**
+ * Returns a referenced cairo_surface_t representing the
+ * sub-image specified by aSubImage.
+ */
+cairo_surface_t* ExtractSubImage(cairo_surface_t* aSurface,
+ const IntRect& aSubImage,
+ SurfaceFormat aFormat)
+{
+ // No need to worry about retaining a reference to the original
+ // surface since the only caller of this function guarantees
+ // that aSurface will stay alive as long as the result
+
+ cairo_surface_t* image = GetAsImageSurface(aSurface);
+ if (image) {
+ image = CreateSubImageForData(cairo_image_surface_get_data(image),
+ aSubImage,
+ cairo_image_surface_get_stride(image),
+ aFormat);
+ return image;
+ }
+
+ cairo_surface_t* similar =
+ cairo_surface_create_similar(aSurface,
+ cairo_surface_get_content(aSurface),
+ aSubImage.width, aSubImage.height);
+
+ cairo_t* ctx = cairo_create(similar);
+ cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_surface(ctx, aSurface, -aSubImage.x, -aSubImage.y);
+ cairo_paint(ctx);
+ cairo_destroy(ctx);
+
+ cairo_surface_set_device_offset(similar, -aSubImage.x, -aSubImage.y);
+ return similar;
}
/**
@@ -147,48 +326,135 @@ ReleaseData(void* aData)
* result when it is done with it.
*/
cairo_surface_t*
-GetCairoSurfaceForSourceSurface(SourceSurface *aSurface)
+GetCairoSurfaceForSourceSurface(SourceSurface *aSurface,
+ bool aExistingOnly = false,
+ const IntRect& aSubImage = IntRect())
{
- if (aSurface->GetType() == SURFACE_CAIRO) {
+ IntRect subimage = IntRect(IntPoint(), aSurface->GetSize());
+ if (!aSubImage.IsEmpty()) {
+ MOZ_ASSERT(!aExistingOnly);
+ MOZ_ASSERT(subimage.Contains(aSubImage));
+ subimage = aSubImage;
+ }
+
+ if (aSurface->GetType() == SurfaceType::CAIRO) {
cairo_surface_t* surf = static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface();
- cairo_surface_reference(surf);
+ if (aSubImage.IsEmpty()) {
+ cairo_surface_reference(surf);
+ } else {
+ surf = ExtractSubImage(surf, subimage, aSurface->GetFormat());
+ }
return surf;
}
- if (aSurface->GetType() == SURFACE_CAIRO_IMAGE) {
+ if (aSurface->GetType() == SurfaceType::CAIRO_IMAGE) {
cairo_surface_t* surf =
static_cast<const DataSourceSurfaceCairo*>(aSurface)->GetSurface();
- cairo_surface_reference(surf);
+ if (aSubImage.IsEmpty()) {
+ cairo_surface_reference(surf);
+ } else {
+ surf = ExtractSubImage(surf, subimage, aSurface->GetFormat());
+ }
return surf;
}
+ if (aExistingOnly) {
+ return nullptr;
+ }
+
RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
if (!data) {
return nullptr;
}
+ DataSourceSurface::MappedSurface map;
+ if (!data->Map(DataSourceSurface::READ, &map)) {
+ return nullptr;
+ }
+
cairo_surface_t* surf =
- cairo_image_surface_create_for_data(data->GetData(),
- GfxFormatToCairoFormat(data->GetFormat()),
- data->GetSize().width,
- data->GetSize().height,
- data->Stride());
+ CreateSubImageForData(map.mData, subimage,
+ map.mStride, data->GetFormat());
// In certain scenarios, requesting larger than 8k image fails. Bug 803568
// covers the details of how to run into it, but the full detailed
// investigation hasn't been done to determine the underlying cause. We
// will just handle the failure to allocate the surface to avoid a crash.
- if (cairo_surface_status(surf)) {
+ if (!surf || cairo_surface_status(surf)) {
+ if (surf && (cairo_surface_status(surf) == CAIRO_STATUS_INVALID_STRIDE)) {
+ // If we failed because of an invalid stride then copy into
+ // a new surface with a stride that cairo chooses. No need to
+ // set user data since we're not dependent on the original
+ // data.
+ cairo_surface_t* result =
+ CopyToImageSurface(map.mData,
+ subimage,
+ map.mStride,
+ data->GetFormat());
+ data->Unmap();
+ return result;
+ }
+ data->Unmap();
return nullptr;
}
cairo_surface_set_user_data(surf,
- &surfaceDataKey,
- data.forget().drop(),
- ReleaseData);
+ &surfaceDataKey,
+ data.forget().take(),
+ ReleaseData);
return surf;
}
+// An RAII class to temporarily clear any device offset set
+// on a surface. Note that this does not take a reference to the
+// surface.
+class AutoClearDeviceOffset
+{
+public:
+ explicit AutoClearDeviceOffset(SourceSurface* aSurface)
+ : mSurface(nullptr)
+ {
+ Init(aSurface);
+ }
+
+ explicit AutoClearDeviceOffset(const Pattern& aPattern)
+ : mSurface(nullptr)
+ {
+ if (aPattern.GetType() == PatternType::SURFACE) {
+ const SurfacePattern& pattern = static_cast<const SurfacePattern&>(aPattern);
+ Init(pattern.mSurface);
+ }
+ }
+
+ ~AutoClearDeviceOffset()
+ {
+ if (mSurface) {
+ cairo_surface_set_device_offset(mSurface, mX, mY);
+ }
+ }
+
+private:
+ void Init(SourceSurface* aSurface)
+ {
+ cairo_surface_t* surface = GetCairoSurfaceForSourceSurface(aSurface, true);
+ if (surface) {
+ Init(surface);
+ cairo_surface_destroy(surface);
+ }
+ }
+
+ void Init(cairo_surface_t *aSurface)
+ {
+ mSurface = aSurface;
+ cairo_surface_get_device_offset(mSurface, &mX, &mY);
+ cairo_surface_set_device_offset(mSurface, 0, 0);
+ }
+
+ cairo_surface_t* mSurface;
+ double mX;
+ double mY;
+};
+
// Never returns nullptr. As such, you must always pass in Cairo-compatible
// patterns, most notably gradients with a GradientStopCairo.
// The pattern returned must have cairo_pattern_destroy() called on it by the
@@ -200,50 +466,49 @@ static cairo_pattern_t*
GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha)
{
cairo_pattern_t* pat;
+ const Matrix* matrix = nullptr;
switch (aPattern.GetType())
{
- case PATTERN_COLOR:
+ case PatternType::COLOR:
{
Color color = static_cast<const ColorPattern&>(aPattern).mColor;
pat = cairo_pattern_create_rgba(color.r, color.g, color.b, color.a * aAlpha);
break;
}
- case PATTERN_SURFACE:
+ case PatternType::SURFACE:
{
const SurfacePattern& pattern = static_cast<const SurfacePattern&>(aPattern);
- cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(pattern.mSurface);
+ cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(pattern.mSurface,
+ false,
+ pattern.mSamplingRect);
+ if (!surf)
+ return nullptr;
pat = cairo_pattern_create_for_surface(surf);
- // The pattern matrix is a matrix that transforms the pattern into user
- // space. Cairo takes a matrix that converts from user space to pattern
- // space. Cairo therefore needs the inverse.
-
- cairo_matrix_t mat;
- GfxMatrixToCairoMatrix(pattern.mMatrix, mat);
- cairo_matrix_invert(&mat);
- cairo_pattern_set_matrix(pat, &mat);
+ matrix = &pattern.mMatrix;
cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(pattern.mFilter));
cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(pattern.mExtendMode));
cairo_surface_destroy(surf);
-
break;
}
- case PATTERN_LINEAR_GRADIENT:
+ case PatternType::LINEAR_GRADIENT:
{
const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
pat = cairo_pattern_create_linear(pattern.mBegin.x, pattern.mBegin.y,
pattern.mEnd.x, pattern.mEnd.y);
- MOZ_ASSERT(pattern.mStops->GetBackendType() == BACKEND_CAIRO);
+ MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO);
GradientStopsCairo* cairoStops = static_cast<GradientStopsCairo*>(pattern.mStops.get());
cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode()));
+ matrix = &pattern.mMatrix;
+
const std::vector<GradientStop>& stops = cairoStops->GetStops();
for (size_t i = 0; i < stops.size(); ++i) {
const GradientStop& stop = stops[i];
@@ -254,17 +519,19 @@ GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha)
break;
}
- case PATTERN_RADIAL_GRADIENT:
+ case PatternType::RADIAL_GRADIENT:
{
const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
pat = cairo_pattern_create_radial(pattern.mCenter1.x, pattern.mCenter1.y, pattern.mRadius1,
pattern.mCenter2.x, pattern.mCenter2.y, pattern.mRadius2);
- MOZ_ASSERT(pattern.mStops->GetBackendType() == BACKEND_CAIRO);
+ MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO);
GradientStopsCairo* cairoStops = static_cast<GradientStopsCairo*>(pattern.mStops.get());
cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode()));
+ matrix = &pattern.mMatrix;
+
const std::vector<GradientStop>& stops = cairoStops->GetStops();
for (size_t i = 0; i < stops.size(); ++i) {
const GradientStop& stop = stops[i];
@@ -282,6 +549,16 @@ GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha)
}
}
+ // The pattern matrix is a matrix that transforms the pattern into user
+ // space. Cairo takes a matrix that converts from user space to pattern
+ // space. Cairo therefore needs the inverse.
+ if (matrix) {
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(*matrix, mat);
+ cairo_matrix_invert(&mat);
+ cairo_pattern_set_matrix(pat, &mat);
+ }
+
return pat;
}
@@ -290,7 +567,7 @@ NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions)
{
// We pre-multiply colours' alpha by the global alpha, so we don't need to
// use an intermediate surface for them.
- if (aPattern.GetType() == PATTERN_COLOR)
+ if (aPattern.GetType() == PatternType::COLOR)
return false;
if (aOptions.mAlpha == 1.0)
@@ -302,20 +579,71 @@ NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions)
DrawTargetCairo::DrawTargetCairo()
: mContext(nullptr)
, mSurface(nullptr)
- , mPathObserver(nullptr)
+ , mLockedBits(nullptr)
{
}
DrawTargetCairo::~DrawTargetCairo()
{
- MarkSnapshotIndependent();
- if (mPathObserver) {
- mPathObserver->ForgetDrawTarget();
- }
cairo_destroy(mContext);
if (mSurface) {
cairo_surface_destroy(mSurface);
}
+ MOZ_ASSERT(!mLockedBits);
+}
+
+DrawTargetType
+DrawTargetCairo::GetType() const
+{
+ if (mContext) {
+ cairo_surface_type_t type = cairo_surface_get_type(mSurface);
+ if (type == CAIRO_SURFACE_TYPE_TEE) {
+ type = cairo_surface_get_type(cairo_tee_surface_index(mSurface, 0));
+ MOZ_ASSERT(type != CAIRO_SURFACE_TYPE_TEE, "C'mon!");
+ MOZ_ASSERT(type == cairo_surface_get_type(cairo_tee_surface_index(mSurface, 1)),
+ "What should we do here?");
+ }
+ switch (type) {
+ case CAIRO_SURFACE_TYPE_PDF:
+ case CAIRO_SURFACE_TYPE_PS:
+ case CAIRO_SURFACE_TYPE_SVG:
+ case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
+ case CAIRO_SURFACE_TYPE_XML:
+ return DrawTargetType::VECTOR;
+
+ case CAIRO_SURFACE_TYPE_VG:
+ case CAIRO_SURFACE_TYPE_GL:
+ case CAIRO_SURFACE_TYPE_GLITZ:
+ case CAIRO_SURFACE_TYPE_QUARTZ:
+ case CAIRO_SURFACE_TYPE_DIRECTFB:
+ return DrawTargetType::HARDWARE_RASTER;
+
+ case CAIRO_SURFACE_TYPE_SKIA:
+ case CAIRO_SURFACE_TYPE_QT:
+ MOZ_ASSERT(false, "Can't determine actual DrawTargetType for DrawTargetCairo - assuming SOFTWARE_RASTER");
+ // fallthrough
+ case CAIRO_SURFACE_TYPE_IMAGE:
+ case CAIRO_SURFACE_TYPE_XLIB:
+ case CAIRO_SURFACE_TYPE_XCB:
+ case CAIRO_SURFACE_TYPE_WIN32:
+ case CAIRO_SURFACE_TYPE_BEOS:
+ case CAIRO_SURFACE_TYPE_OS2:
+ case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE:
+ case CAIRO_SURFACE_TYPE_SCRIPT:
+ case CAIRO_SURFACE_TYPE_RECORDING:
+ case CAIRO_SURFACE_TYPE_DRM:
+ case CAIRO_SURFACE_TYPE_SUBSURFACE:
+#ifdef CAIRO_HAS_D2D_SURFACE
+ case CAIRO_SURFACE_TYPE_D2D:
+#endif
+ case CAIRO_SURFACE_TYPE_TEE: // included to silence warning about unhandled enum value
+ return DrawTargetType::SOFTWARE_RASTER;
+ default:
+ MOZ_CRASH("Unsupported cairo surface type");
+ }
+ }
+ MOZ_ASSERT(false, "Could not determine DrawTargetType for DrawTargetCairo");
+ return DrawTargetType::SOFTWARE_RASTER;
}
IntSize
@@ -333,14 +661,38 @@ DrawTargetCairo::Snapshot()
IntSize size = GetSize();
- cairo_content_t content = cairo_surface_get_content(mSurface);
mSnapshot = new SourceSurfaceCairo(mSurface,
size,
- CairoContentToGfxFormat(content),
+ GfxFormatForCairoSurface(mSurface),
this);
return mSnapshot;
}
+bool
+DrawTargetCairo::LockBits(uint8_t** aData, IntSize* aSize,
+ int32_t* aStride, SurfaceFormat* aFormat)
+{
+ if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
+ WillChange();
+
+ mLockedBits = cairo_image_surface_get_data(mSurface);
+ *aData = mLockedBits;
+ *aSize = GetSize();
+ *aStride = cairo_image_surface_get_stride(mSurface);
+ *aFormat = GetFormat();
+ return true;
+ }
+
+ return false;
+}
+
+void
+DrawTargetCairo::ReleaseBits(uint8_t* aData)
+{
+ MOZ_ASSERT(mLockedBits == aData);
+ mLockedBits = nullptr;
+}
+
void
DrawTargetCairo::Flush()
{
@@ -354,6 +706,18 @@ DrawTargetCairo::PrepareForDrawing(cairo_t* aContext, const Path* aPath /* = nul
WillChange(aPath);
}
+cairo_surface_t*
+DrawTargetCairo::GetDummySurface()
+{
+ if (mDummySurface) {
+ return mDummySurface;
+ }
+
+ mDummySurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
+
+ return mDummySurface;
+}
+
void
DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
@@ -362,6 +726,7 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
const DrawOptions &aOptions)
{
AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clear(aSurface);
float sx = aSource.Width() / aDest.Width();
float sy = aSource.Height() / aDest.Height();
@@ -378,20 +743,27 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(aSurfOptions.mFilter));
cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD);
+ cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
+
+ // If the destination rect covers the entire clipped area, then unbounded and bounded
+ // operations are identical, and we don't need to push a group.
+ bool needsGroup = !IsOperatorBoundByMask(aOptions.mCompositionOp) &&
+ !aDest.Contains(GetUserSpaceClip());
+
cairo_translate(mContext, aDest.X(), aDest.Y());
- if (IsOperatorBoundByMask(aOptions.mCompositionOp)) {
- cairo_new_path(mContext);
- cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
- cairo_clip(mContext);
- cairo_set_source(mContext, pat);
- } else {
+ if (needsGroup) {
cairo_push_group(mContext);
cairo_new_path(mContext);
cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
cairo_set_source(mContext, pat);
cairo_fill(mContext);
cairo_pop_group_to_source(mContext);
+ } else {
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
+ cairo_clip(mContext);
+ cairo_set_source(mContext, pat);
}
cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
@@ -402,6 +774,16 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
}
void
+DrawTargetCairo::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
+ filter->Draw(this, aSourceRect, aDestPoint, aOptions);
+}
+
+void
DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface,
const Point &aDest,
const Color &aColor,
@@ -409,10 +791,12 @@ DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface,
Float aSigma,
CompositionOp aOperator)
{
- if (aSurface->GetType() != SURFACE_CAIRO) {
+ if (aSurface->GetType() != SurfaceType::CAIRO) {
return;
}
+ AutoClearDeviceOffset clear(aSurface);
+
Float width = Float(aSurface->GetSize().width);
Float height = Float(aSurface->GetSize().height);
@@ -431,7 +815,7 @@ DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface,
Rect extents(0, 0, width, height);
AlphaBoxBlur blur(extents,
cairo_image_surface_get_stride(blursurf),
- aSigma);
+ aSigma, aSigma);
blur.Blur(cairo_image_surface_get_data(blursurf));
} else {
blursurf = sourcesurf;
@@ -476,21 +860,33 @@ void
DrawTargetCairo::DrawPattern(const Pattern& aPattern,
const StrokeOptions& aStrokeOptions,
const DrawOptions& aOptions,
- DrawPatternType aDrawType)
+ DrawPatternType aDrawType,
+ bool aPathBoundsClip)
{
if (!PatternIsCompatible(aPattern)) {
return;
}
+ AutoClearDeviceOffset clear(aPattern);
+
cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha);
+ if (!pat) {
+ return;
+ }
+ if (cairo_pattern_status(pat)) {
+ cairo_pattern_destroy(pat);
+ gfxWarning() << "Invalid pattern";
+ return;
+ }
+
cairo_set_source(mContext, pat);
+ cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
+
if (NeedIntermediateSurface(aPattern, aOptions) ||
- !IsOperatorBoundByMask(aOptions.mCompositionOp)) {
+ (!IsOperatorBoundByMask(aOptions.mCompositionOp) && !aPathBoundsClip)) {
cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
- ClearSurfaceForUnboundedSource(aOptions.mCompositionOp);
-
// Don't want operators to be applied twice
cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
@@ -507,7 +903,6 @@ DrawTargetCairo::DrawPattern(const Pattern& aPattern,
cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
cairo_paint_with_alpha(mContext, aOptions.mAlpha);
} else {
- ClearSurfaceForUnboundedSource(aOptions.mCompositionOp);
cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
if (aDrawType == DRAW_STROKE) {
@@ -528,10 +923,72 @@ DrawTargetCairo::FillRect(const Rect &aRect,
{
AutoPrepareForDrawing prep(this, mContext);
+ bool restoreTransform = false;
+ Matrix mat;
+ Rect r = aRect;
+
+ /* Clamp coordinates to work around a design bug in cairo */
+ if (r.width > CAIRO_COORD_MAX ||
+ r.height > CAIRO_COORD_MAX ||
+ r.x < -CAIRO_COORD_MAX ||
+ r.x > CAIRO_COORD_MAX ||
+ r.y < -CAIRO_COORD_MAX ||
+ r.y > CAIRO_COORD_MAX)
+ {
+ if (!mat.IsRectilinear()) {
+ gfxWarning() << "DrawTargetCairo::FillRect() misdrawing huge Rect "
+ "with non-rectilinear transform";
+ }
+
+ mat = GetTransform();
+ r = mat.TransformBounds(r);
+
+ if (!ConditionRect(r)) {
+ gfxWarning() << "Ignoring DrawTargetCairo::FillRect() call with "
+ "out-of-bounds Rect";
+ return;
+ }
+
+ restoreTransform = true;
+ SetTransform(Matrix());
+ }
+
cairo_new_path(mContext);
- cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height());
+ cairo_rectangle(mContext, r.x, r.y, r.Width(), r.Height());
- DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
+ bool pathBoundsClip = false;
+
+ if (r.Contains(GetUserSpaceClip())) {
+ pathBoundsClip = true;
+ }
+
+ DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL, pathBoundsClip);
+
+ if (restoreTransform) {
+ SetTransform(mat);
+ }
+}
+
+void
+DrawTargetCairo::CopySurfaceInternal(cairo_surface_t* aSurface,
+ const IntRect &aSource,
+ const IntPoint &aDest)
+{
+ if (cairo_surface_status(aSurface)) {
+ gfxWarning() << "Invalid surface";
+ return;
+ }
+
+ cairo_identity_matrix(mContext);
+
+ cairo_set_source_surface(mContext, aSurface, aDest.x - aSource.x, aDest.y - aSource.y);
+ cairo_set_operator(mContext, CAIRO_OPERATOR_SOURCE);
+ cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE);
+
+ cairo_reset_clip(mContext);
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, aDest.x, aDest.y, aSource.width, aSource.height);
+ cairo_fill(mContext);
}
void
@@ -540,23 +997,54 @@ DrawTargetCairo::CopySurface(SourceSurface *aSurface,
const IntPoint &aDest)
{
AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clear(aSurface);
- if (!aSurface || aSurface->GetType() != SURFACE_CAIRO) {
+ if (!aSurface) {
gfxWarning() << "Unsupported surface type specified";
return;
}
- cairo_surface_t* surf = static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface();
+ cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface);
+ if (!surf) {
+ gfxWarning() << "Unsupported surface type specified";
+ return;
+ }
- cairo_identity_matrix(mContext);
+ CopySurfaceInternal(surf, aSource, aDest);
+ cairo_surface_destroy(surf);
+}
- cairo_set_source_surface(mContext, surf, aDest.x - aSource.x, aDest.y - aSource.y);
- cairo_set_operator(mContext, CAIRO_OPERATOR_SOURCE);
+void
+DrawTargetCairo::CopyRect(const IntRect &aSource,
+ const IntPoint &aDest)
+{
+ AutoPrepareForDrawing prep(this, mContext);
- cairo_reset_clip(mContext);
- cairo_new_path(mContext);
- cairo_rectangle(mContext, aDest.x, aDest.y, aSource.width, aSource.height);
- cairo_fill(mContext);
+ IntRect source = aSource;
+ cairo_surface_t* surf = mSurface;
+
+ if (!SupportsSelfCopy(mSurface) &&
+ aDest.y >= aSource.y &&
+ aDest.y < aSource.YMost()) {
+ cairo_surface_t* similar = cairo_surface_create_similar(mSurface,
+ GfxFormatToCairoContent(GetFormat()),
+ aSource.width, aSource.height);
+ cairo_t* ctx = cairo_create(similar);
+ cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_surface(ctx, surf, -aSource.x, -aSource.y);
+ cairo_paint(ctx);
+ cairo_destroy(ctx);
+
+ source.x = 0;
+ source.y = 0;
+ surf = similar;
+ }
+
+ CopySurfaceInternal(surf, source, aDest);
+
+ if (surf != mSurface) {
+ cairo_surface_destroy(surf);
+ }
}
void
@@ -564,6 +1052,13 @@ DrawTargetCairo::ClearRect(const Rect& aRect)
{
AutoPrepareForDrawing prep(this, mContext);
+ if (!mContext || aRect.Width() <= 0 || aRect.Height() <= 0 ||
+ !IsFinite(aRect.X()) || !IsFinite(aRect.Width()) ||
+ !IsFinite(aRect.Y()) || !IsFinite(aRect.Height())) {
+ gfxCriticalError(CriticalLog::DefaultOptions(false)) << "ClearRect with invalid argument " << gfx::hexa(mContext) << " with " << aRect.Width() << "x" << aRect.Height() << " [" << aRect.X() << ", " << aRect.Y() << "]";
+ }
+
+ cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE);
cairo_new_path(mContext);
cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR);
cairo_rectangle(mContext, aRect.X(), aRect.Y(),
@@ -609,11 +1104,11 @@ DrawTargetCairo::Stroke(const Path *aPath,
{
AutoPrepareForDrawing prep(this, mContext, aPath);
- if (aPath->GetBackendType() != BACKEND_CAIRO)
+ if (aPath->GetBackendType() != BackendType::CAIRO)
return;
PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
- path->CopyPathTo(mContext, this);
+ path->SetPathOnContext(mContext);
DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
}
@@ -625,11 +1120,11 @@ DrawTargetCairo::Fill(const Path *aPath,
{
AutoPrepareForDrawing prep(this, mContext, aPath);
- if (aPath->GetBackendType() != BACKEND_CAIRO)
+ if (aPath->GetBackendType() != BackendType::CAIRO)
return;
PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
- path->CopyPathTo(mContext, this);
+ path->SetPathOnContext(mContext);
DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
}
@@ -652,16 +1147,30 @@ DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
const GlyphRenderingOptions*)
{
AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clear(aPattern);
ScaledFontBase* scaledFont = static_cast<ScaledFontBase*>(aFont);
cairo_set_scaled_font(mContext, scaledFont->GetCairoScaledFont());
cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha);
+ if (!pat)
+ return;
+
cairo_set_source(mContext, pat);
cairo_pattern_destroy(pat);
- // Convert our GlyphBuffer into an array of Cairo glyphs.
- std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
+ cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
+
+ // Convert our GlyphBuffer into a vector of Cairo glyphs. This code can
+ // execute millions of times in short periods, so we want to avoid heap
+ // allocation whenever possible. So we use an inline vector capacity of 1024
+ // bytes (the maximum allowed by mozilla::Vector), which gives an inline
+ // length of 1024 / 24 = 42 elements, which is enough to typically avoid heap
+ // allocation in ~99% of cases.
+ Vector<cairo_glyph_t, 1024 / sizeof(cairo_glyph_t)> glyphs;
+ if (!glyphs.resizeUninitialized(aBuffer.mNumGlyphs)) {
+ MOZ_CRASH("glyphs allocation failed");
+ }
for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
@@ -677,70 +1186,101 @@ DrawTargetCairo::Mask(const Pattern &aSource,
const DrawOptions &aOptions /* = DrawOptions() */)
{
AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clearSource(aSource);
+ AutoClearDeviceOffset clearMask(aMask);
+
+ cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
cairo_pattern_t* source = GfxPatternToCairoPattern(aSource, aOptions.mAlpha);
- cairo_set_source(mContext, source);
+ if (!source) {
+ return;
+ }
cairo_pattern_t* mask = GfxPatternToCairoPattern(aMask, aOptions.mAlpha);
+ if (!mask) {
+ cairo_pattern_destroy(source);
+ return;
+ }
+
+ if (cairo_pattern_status(source) || cairo_pattern_status(mask)) {
+ cairo_pattern_destroy(source);
+ cairo_pattern_destroy(mask);
+ gfxWarning() << "Invalid pattern";
+ return;
+ }
+
+ cairo_set_source(mContext, source);
cairo_mask(mContext, mask);
cairo_pattern_destroy(mask);
cairo_pattern_destroy(source);
}
-#if 0
-DrawTargetCairo::MaskSurface(SourceSurface *aSurface,
- const Rect &aDest,
- const Rect &aSource,
- const DrawSurfaceOptions &aSurfOptions,
+
+void
+DrawTargetCairo::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
const DrawOptions &aOptions)
{
AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clearSource(aSource);
+ AutoClearDeviceOffset clearMask(aMask);
- float sx = aSource.Width() / aDest.Width();
- float sy = aSource.Height() / aDest.Height();
+ if (!PatternIsCompatible(aSource)) {
+ return;
+ }
- cairo_matrix_t src_mat;
- cairo_matrix_init_translate(&src_mat, aSource.X(), aSource.Y());
- cairo_matrix_scale(&src_mat, sx, sy);
+ cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
- cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface);
- cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf);
- cairo_surface_destroy(surf);
+ cairo_pattern_t* pat = GfxPatternToCairoPattern(aSource, aOptions.mAlpha);
+ if (!pat) {
+ return;
+ }
- cairo_pattern_set_matrix(pat, &src_mat);
- cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(aSurfOptions.mFilter));
- cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD);
+ if (cairo_pattern_status(pat)) {
+ cairo_pattern_destroy(pat);
+ gfxWarning() << "Invalid pattern";
+ return;
+ }
- cairo_translate(mContext, aDest.X(), aDest.Y());
+ cairo_set_source(mContext, pat);
+
+ if (NeedIntermediateSurface(aSource, aOptions)) {
+ cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
+
+ // Don't want operators to be applied twice
+ cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
+
+ // Now draw the content using the desired operator
+ cairo_paint_with_alpha(mContext, aOptions.mAlpha);
- if (IsOperatorBoundByMask(aOptions.mCompositionOp)) {
- cairo_new_path(mContext);
- cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
- cairo_clip(mContext);
- cairo_set_source(mContext, pat);
- } else {
- cairo_push_group(mContext);
- cairo_new_path(mContext);
- cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
- cairo_set_source(mContext, pat);
- cairo_fill(mContext);
cairo_pop_group_to_source(mContext);
}
+ cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask);
+ if (!surf) {
+ cairo_pattern_destroy(pat);
+ return;
+ }
+ cairo_pattern_t* mask = cairo_pattern_create_for_surface(surf);
+ cairo_matrix_t matrix;
+
+ cairo_matrix_init_translate (&matrix, -aOffset.x, -aOffset.y);
+ cairo_pattern_set_matrix (mask, &matrix);
+
cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
- DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
- cairo_paint_with_alpha(mContext, aOptions.mAlpha);
+ cairo_mask(mContext, mask);
+ cairo_surface_destroy(surf);
+ cairo_pattern_destroy(mask);
cairo_pattern_destroy(pat);
}
-#endif
-
void
DrawTargetCairo::PushClip(const Path *aPath)
{
- if (aPath->GetBackendType() != BACKEND_CAIRO) {
+ if (aPath->GetBackendType() != BackendType::CAIRO) {
return;
}
@@ -748,7 +1288,7 @@ DrawTargetCairo::PushClip(const Path *aPath)
cairo_save(mContext);
PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
- path->CopyPathTo(mContext, this);
+ path->SetPathOnContext(mContext);
cairo_clip_preserve(mContext);
}
@@ -767,23 +1307,30 @@ void
DrawTargetCairo::PopClip()
{
// save/restore does not affect the path, so no need to call WillChange()
+
+ // cairo_restore will restore the transform too and we don't want to do that
+ // so we'll save it now and restore it after the cairo_restore
+ cairo_matrix_t mat;
+ cairo_get_matrix(mContext, &mat);
+
cairo_restore(mContext);
+
+ cairo_set_matrix(mContext, &mat);
+
+ MOZ_ASSERT(cairo_status(mContext) || GetTransform() == Matrix(mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0),
+ "Transforms are out of sync");
}
TemporaryRef<PathBuilder>
-DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FILL_WINDING */) const
+DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FillRule::FILL_WINDING */) const
{
- RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mContext,
- const_cast<DrawTargetCairo*>(this),
- aFillRule);
-
- return builder;
+ return new PathBuilderCairo(aFillRule);
}
void
DrawTargetCairo::ClearSurfaceForUnboundedSource(const CompositionOp &aOperator)
{
- if (aOperator != OP_SOURCE)
+ if (aOperator != CompositionOp::OP_SOURCE)
return;
cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR);
// It doesn't really matter what the source is here, since Paint
@@ -797,36 +1344,13 @@ TemporaryRef<GradientStops>
DrawTargetCairo::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops,
ExtendMode aExtendMode) const
{
- RefPtr<GradientStopsCairo> stops = new GradientStopsCairo(aStops, aNumStops,
- aExtendMode);
- return stops;
+ return new GradientStopsCairo(aStops, aNumStops, aExtendMode);
}
-/**
- * Copies pixel data from aData into aSurface; aData must have the dimensions
- * given in aSize, with a stride of aStride bytes and aPixelWidth bytes per pixel
- */
-static void
-CopyDataToCairoSurface(cairo_surface_t* aSurface,
- unsigned char *aData,
- const IntSize &aSize,
- int32_t aStride,
- int32_t aPixelWidth)
+TemporaryRef<FilterNode>
+DrawTargetCairo::CreateFilter(FilterType aType)
{
- unsigned char* surfData = cairo_image_surface_get_data(aSurface);
- // In certain scenarios, requesting larger than 8k image fails. Bug 803568
- // covers the details of how to run into it, but the full detailed
- // investigation hasn't been done to determine the underlying cause. We
- // will just handle the failure to allocate the surface to avoid a crash.
- if (!surfData) {
- return;
- }
- for (int32_t y = 0; y < aSize.height; ++y) {
- memcpy(surfData + y * aSize.width * aPixelWidth,
- aData + y * aStride,
- aSize.width * aPixelWidth);
- }
- cairo_surface_mark_dirty(aSurface);
+ return FilterNodeSoftware::Create(aType);
}
TemporaryRef<SourceSurface>
@@ -835,42 +1359,141 @@ DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData,
int32_t aStride,
SurfaceFormat aFormat) const
{
- cairo_surface_t* surf = cairo_image_surface_create(GfxFormatToCairoFormat(aFormat),
- aSize.width,
- aSize.height);
- // In certain scenarios, requesting larger than 8k image fails. Bug 803568
- // covers the details of how to run into it, but the full detailed
- // investigation hasn't been done to determine the underlying cause. We
- // will just handle the failure to allocate the surface to avoid a crash.
- if (cairo_surface_status(surf)) {
+ if (!aData) {
+ gfxWarning() << "DrawTargetCairo::CreateSourceSurfaceFromData null aData";
return nullptr;
}
- CopyDataToCairoSurface(surf, aData, aSize, aStride, BytesPerPixel(aFormat));
+ cairo_surface_t* surf = CopyToImageSurface(aData, IntRect(IntPoint(), aSize),
+ aStride, aFormat);
+ if (!surf) {
+ return nullptr;
+ }
RefPtr<SourceSurfaceCairo> source_surf = new SourceSurfaceCairo(surf, aSize, aFormat);
cairo_surface_destroy(surf);
- return source_surf;
+ return source_surf.forget();
}
+#ifdef CAIRO_HAS_XLIB_SURFACE
+static cairo_user_data_key_t gDestroyPixmapKey;
+
+struct DestroyPixmapClosure {
+ DestroyPixmapClosure(Drawable d, Screen *s) : mPixmap(d), mScreen(s) {}
+ ~DestroyPixmapClosure() {
+ XFreePixmap(DisplayOfScreen(mScreen), mPixmap);
+ }
+ Drawable mPixmap;
+ Screen *mScreen;
+};
+
+static void
+DestroyPixmap(void *data)
+{
+ delete static_cast<DestroyPixmapClosure*>(data);
+}
+#endif
+
TemporaryRef<SourceSurface>
DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
{
+#ifdef CAIRO_HAS_XLIB_SURFACE
+ cairo_surface_type_t ctype = cairo_surface_get_type(mSurface);
+ if (aSurface->GetType() == SurfaceType::CAIRO &&
+ cairo_surface_get_type(
+ static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface()) == ctype) {
+ return aSurface;
+ }
+
+ if (ctype != CAIRO_SURFACE_TYPE_XLIB) {
+ return aSurface;
+ }
+
+ IntSize size = aSurface->GetSize();
+ if (!size.width || !size.height) {
+ return aSurface;
+ }
+
+ // Although the dimension parameters in the xCreatePixmapReq wire protocol are
+ // 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
+ // either dimension cannot be represented by a 16-bit *signed* integer.
+ #define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
+
+ if (size.width > XLIB_IMAGE_SIDE_SIZE_LIMIT ||
+ size.height > XLIB_IMAGE_SIDE_SIZE_LIMIT) {
+ return aSurface;
+ }
+
+ SurfaceFormat format = aSurface->GetFormat();
+ Screen *screen = cairo_xlib_surface_get_screen(mSurface);
+ Display *dpy = DisplayOfScreen(screen);
+ XRenderPictFormat* xrenderFormat = nullptr;
+ switch (format) {
+ case SurfaceFormat::B8G8R8A8:
+ xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardARGB32);
+ break;
+ case SurfaceFormat::B8G8R8X8:
+ xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardRGB24);
+ break;
+ case SurfaceFormat::A8:
+ xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardA8);
+ break;
+ default:
+ return aSurface;
+ }
+ if (!xrenderFormat) {
+ return aSurface;
+ }
+
+ Drawable pixmap = XCreatePixmap(dpy, RootWindowOfScreen(screen),
+ size.width, size.height,
+ xrenderFormat->depth);
+ if (!pixmap) {
+ return aSurface;
+ }
+
+ ScopedDeletePtr<DestroyPixmapClosure> closure(
+ new DestroyPixmapClosure(pixmap, screen));
+
+ ScopedCairoSurface csurf(
+ cairo_xlib_surface_create_with_xrender_format(dpy, pixmap,
+ screen, xrenderFormat,
+ size.width, size.height));
+ if (!csurf || cairo_surface_status(csurf)) {
+ return aSurface;
+ }
+
+ cairo_surface_set_user_data(csurf, &gDestroyPixmapKey,
+ closure.forget(), DestroyPixmap);
+
+ RefPtr<DrawTargetCairo> dt = new DrawTargetCairo();
+ if (!dt->Init(csurf, size, &format)) {
+ return aSurface;
+ }
+
+ dt->CopySurface(aSurface,
+ IntRect(0, 0, size.width, size.height),
+ IntPoint(0, 0));
+ dt->Flush();
+
+ return new SourceSurfaceCairo(csurf, size, format);
+#endif
+
return aSurface;
}
TemporaryRef<SourceSurface>
DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
{
- if (aSurface.mType == NATIVE_SURFACE_CAIRO_SURFACE) {
- IntSize size;
- cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface);
- if (GetCairoSurfaceSize(surf, size)) {
- RefPtr<SourceSurfaceCairo> source =
- new SourceSurfaceCairo(surf, size, aSurface.mFormat);
- return source;
+ if (aSurface.mType == NativeSurfaceType::CAIRO_SURFACE) {
+ if (aSurface.mSize.width <= 0 ||
+ aSurface.mSize.height <= 0) {
+ gfxWarning() << "Can't create a SourceSurface without a valid size";
+ return nullptr;
}
+ cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface);
+ return new SourceSurfaceCairo(surf, aSurface.mSize, aSurface.mFormat);
}
return nullptr;
@@ -879,25 +1502,29 @@ DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurf
TemporaryRef<DrawTarget>
DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
{
- cairo_surface_t* similar = cairo_surface_create_similar(cairo_get_target(mContext),
+ cairo_surface_t* similar = cairo_surface_create_similar(mSurface,
GfxFormatToCairoContent(aFormat),
aSize.width, aSize.height);
if (!cairo_surface_status(similar)) {
RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
if (target->InitAlreadyReferenced(similar, aSize)) {
- return target;
+ return target.forget();
}
}
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "Failed to create similar cairo surface! Size: " << aSize << " Status: " << cairo_surface_status(similar);
+
return nullptr;
}
bool
-DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize)
+DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
{
- //check if aSurface is valid. Destroy and return early if not a valid surface.
if (cairo_surface_status(aSurface)) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize)))
+ << "Attempt to create DrawTarget for invalid surface. "
+ << aSize << " Cairo Status: " << cairo_surface_status(aSurface);
cairo_surface_destroy(aSurface);
return false;
}
@@ -905,10 +1532,17 @@ DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize&
mContext = cairo_create(aSurface);
mSurface = aSurface;
mSize = aSize;
- mFormat = CairoContentToGfxFormat(cairo_surface_get_content(aSurface));
+ mFormat = aFormat ? *aFormat : GfxFormatForCairoSurface(aSurface);
+
+ // Cairo image surface have a bug where they will allocate a mask surface (for clipping)
+ // the size of the clip extents, and don't take the surface extents into account.
+ // Add a manual clip to the surface extents to prevent this.
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, 0, 0, mSize.width, mSize.height);
+ cairo_clip(mContext);
- if (mFormat == FORMAT_B8G8R8A8 ||
- mFormat == FORMAT_R8G8B8A8) {
+ if (mFormat == SurfaceFormat::B8G8R8A8 ||
+ mFormat == SurfaceFormat::R8G8B8A8) {
SetPermitSubpixelAA(false);
} else {
SetPermitSubpixelAA(true);
@@ -934,9 +1568,9 @@ DrawTargetCairo::CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFor
if (aSigma == 0.0F) {
RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
if (target->InitAlreadyReferenced(similar, aSize)) {
- return target;
+ return target.forget();
} else {
- return nullptr;
+ return nullptr;
}
}
@@ -960,24 +1594,46 @@ DrawTargetCairo::CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFor
RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
if (target->InitAlreadyReferenced(tee, aSize)) {
- return target;
+ return target.forget();
}
return nullptr;
}
bool
-DrawTargetCairo::Init(cairo_surface_t* aSurface, const IntSize& aSize)
+DrawTargetCairo::Init(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
{
cairo_surface_reference(aSurface);
- return InitAlreadyReferenced(aSurface, aSize);
+ return InitAlreadyReferenced(aSurface, aSize, aFormat);
+}
+
+bool
+DrawTargetCairo::Init(const IntSize& aSize, SurfaceFormat aFormat)
+{
+ cairo_surface_t *surf = cairo_image_surface_create(GfxFormatToCairoFormat(aFormat), aSize.width, aSize.height);
+ return InitAlreadyReferenced(surf, aSize);
+}
+
+bool
+DrawTargetCairo::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat)
+{
+ cairo_surface_t* surf =
+ cairo_image_surface_create_for_data(aData,
+ GfxFormatToCairoFormat(aFormat),
+ aSize.width,
+ aSize.height,
+ aStride);
+ return InitAlreadyReferenced(surf, aSize);
}
void *
DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType)
{
- if (aType == NATIVE_SURFACE_CAIRO_SURFACE) {
+ if (aType == NativeSurfaceType::CAIRO_SURFACE) {
return cairo_get_target(mContext);
}
+ if (aType == NativeSurfaceType::CAIRO_CONTEXT) {
+ return mContext;
+ }
return nullptr;
}
@@ -998,21 +1654,7 @@ void
DrawTargetCairo::WillChange(const Path* aPath /* = nullptr */)
{
MarkSnapshotIndependent();
-
- if (mPathObserver &&
- (!aPath || !mPathObserver->ContainsPath(aPath))) {
- mPathObserver->PathWillChange();
- mPathObserver = nullptr;
- }
-}
-
-void
-DrawTargetCairo::SetPathObserver(CairoPathContext* aPathObserver)
-{
- if (mPathObserver && mPathObserver != aPathObserver) {
- mPathObserver->PathWillChange();
- }
- mPathObserver = aPathObserver;
+ MOZ_ASSERT(!mLockedBits);
}
void
@@ -1025,5 +1667,50 @@ DrawTargetCairo::SetTransform(const Matrix& aTransform)
cairo_set_matrix(mContext, &mat);
}
+Rect
+DrawTargetCairo::GetUserSpaceClip()
+{
+ double clipX1, clipY1, clipX2, clipY2;
+ cairo_clip_extents(mContext, &clipX1, &clipY1, &clipX2, &clipY2);
+ return Rect(clipX1, clipY1, clipX2 - clipX1, clipY2 - clipY1); // Narrowing of doubles to floats
+}
+
+cairo_t*
+BorrowedCairoContext::BorrowCairoContextFromDrawTarget(DrawTarget* aDT)
+{
+ if (aDT->GetBackendType() != BackendType::CAIRO ||
+ aDT->IsDualDrawTarget() ||
+ aDT->IsTiledDrawTarget()) {
+ return nullptr;
+ }
+ DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
+
+ cairoDT->WillChange();
+
+ // save the state to make it easier for callers to avoid mucking with things
+ cairo_save(cairoDT->mContext);
+
+ // Neuter the DrawTarget while the context is being borrowed
+ cairo_t* cairo = cairoDT->mContext;
+ cairoDT->mContext = nullptr;
+
+ return cairo;
+}
+
+void
+BorrowedCairoContext::ReturnCairoContextToDrawTarget(DrawTarget* aDT,
+ cairo_t* aCairo)
+{
+ if (aDT->GetBackendType() != BackendType::CAIRO ||
+ aDT->IsDualDrawTarget() ||
+ aDT->IsTiledDrawTarget()) {
+ return;
+ }
+ DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
+
+ cairo_restore(aCairo);
+ cairoDT->mContext = aCairo;
+}
+
}
}
diff --git a/gfx/2d/DrawTargetCairo.h b/gfx/2d/DrawTargetCairo.h
index d89de240b..db9e8b4a3 100644
--- a/gfx/2d/DrawTargetCairo.h
+++ b/gfx/2d/DrawTargetCairo.h
@@ -20,6 +20,7 @@ class SourceSurfaceCairo;
class GradientStopsCairo : public GradientStops
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsCairo)
GradientStopsCairo(GradientStop* aStops, uint32_t aNumStops,
ExtendMode aExtendMode)
: mExtendMode(aExtendMode)
@@ -41,7 +42,7 @@ class GradientStopsCairo : public GradientStops
return mExtendMode;
}
- virtual BackendType GetBackendType() const { return BACKEND_CAIRO; }
+ virtual BackendType GetBackendType() const { return BackendType::CAIRO; }
private:
std::vector<GradientStop> mStops;
@@ -51,115 +52,144 @@ class GradientStopsCairo : public GradientStops
class DrawTargetCairo : public DrawTarget
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetCairo, override)
+ friend class BorrowedCairoContext;
+
DrawTargetCairo();
virtual ~DrawTargetCairo();
- virtual BackendType GetType() const { return BACKEND_CAIRO; }
- virtual TemporaryRef<SourceSurface> Snapshot();
- virtual IntSize GetSize();
+ virtual DrawTargetType GetType() const override;
+ virtual BackendType GetBackendType() const override { return BackendType::CAIRO; }
+ virtual TemporaryRef<SourceSurface> Snapshot() override;
+ virtual IntSize GetSize() override;
+
+ virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) override;
+
+ virtual bool LockBits(uint8_t** aData, IntSize* aSize,
+ int32_t* aStride, SurfaceFormat* aFormat) override;
+ virtual void ReleaseBits(uint8_t* aData) override;
- virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA);
-
- virtual void Flush();
+ virtual void Flush() override;
virtual void DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
const Rect &aSource,
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
const Point &aDest,
const Color &aColor,
const Point &aOffset,
Float aSigma,
- CompositionOp aOperator);
+ CompositionOp aOperator) override;
- virtual void ClearRect(const Rect &aRect);
+ virtual void ClearRect(const Rect &aRect) override;
virtual void CopySurface(SourceSurface *aSurface,
const IntRect &aSourceRect,
- const IntPoint &aDestination);
+ const IntPoint &aDestination) override;
+ virtual void CopyRect(const IntRect &aSourceRect,
+ const IntPoint &aDestination) override;
virtual void FillRect(const Rect &aRect,
const Pattern &aPattern,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void StrokeLine(const Point &aStart,
const Point &aEnd,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void Stroke(const Path *aPath,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void Fill(const Path *aPath,
const Pattern &aPattern,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void FillGlyphs(ScaledFont *aFont,
const GlyphBuffer &aBuffer,
const Pattern &aPattern,
const DrawOptions &aOptions,
- const GlyphRenderingOptions *aRenderingOptions = nullptr);
+ const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
virtual void Mask(const Pattern &aSource,
const Pattern &aMask,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void MaskSurface(const Pattern &aSource,
SourceSurface *aMask,
Point aOffset,
- const DrawOptions &aOptions = DrawOptions()) { MOZ_ASSERT(0); };
+ const DrawOptions &aOptions = DrawOptions()) override;
- virtual void PushClip(const Path *aPath);
- virtual void PushClipRect(const Rect &aRect);
- virtual void PopClip();
+ virtual void PushClip(const Path *aPath) override;
+ virtual void PushClipRect(const Rect &aRect) override;
+ virtual void PopClip() override;
- virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const;
+ virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
int32_t aStride,
- SurfaceFormat aFormat) const;
- virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const;
+ SurfaceFormat aFormat) const override;
+ virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
virtual TemporaryRef<SourceSurface>
- CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const;
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
virtual TemporaryRef<DrawTarget>
- CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
virtual TemporaryRef<DrawTarget>
CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
- float aSigma) const;
+ float aSigma) const override;
virtual TemporaryRef<GradientStops>
CreateGradientStops(GradientStop *aStops,
uint32_t aNumStops,
- ExtendMode aExtendMode = EXTEND_CLAMP) const;
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
- virtual void *GetNativeSurface(NativeSurfaceType aType);
+ virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override;
- bool Init(cairo_surface_t* aSurface, const IntSize& aSize);
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override;
- void SetPathObserver(CairoPathContext* aPathObserver);
+ bool Init(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr);
+ bool Init(const IntSize& aSize, SurfaceFormat aFormat);
+ bool Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat);
- virtual void SetTransform(const Matrix& aTransform);
+ virtual void SetTransform(const Matrix& aTransform) override;
// Call to set up aContext for drawing (with the current transform, etc).
// Pass the path you're going to be using if you have one.
// Implicitly calls WillChange(aPath).
void PrepareForDrawing(cairo_t* aContext, const Path* aPath = nullptr);
+ static cairo_surface_t *GetDummySurface();
+
+ // Cairo hardcodes this as its maximum surface size.
+ static size_t GetMaxSurfaceSize() {
+ return 32767;
+ }
+
private: // methods
// Init cairo surface without doing a cairo_surface_reference() call.
- bool InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize);
-
+ bool InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr);
enum DrawPatternType { DRAW_FILL, DRAW_STROKE };
void DrawPattern(const Pattern& aPattern,
const StrokeOptions& aStrokeOptions,
const DrawOptions& aOptions,
- DrawPatternType aDrawType);
+ DrawPatternType aDrawType,
+ bool aPathBoundsClip = false);
+
+ void CopySurfaceInternal(cairo_surface_t* aSurface,
+ const IntRect& aSource,
+ const IntPoint& aDest);
+
+ Rect GetUserSpaceClip();
// Call before you make any changes to the backing surface with which this
// context is associated. Pass the path you're going to be using if you have
@@ -173,20 +203,18 @@ private: // methods
// If the current operator is "source" then clear the destination before we
// draw into it, to simulate the effect of an unbounded source operator.
void ClearSurfaceForUnboundedSource(const CompositionOp &aOperator);
+
private: // data
cairo_t* mContext;
cairo_surface_t* mSurface;
IntSize mSize;
+ uint8_t* mLockedBits;
+
// The latest snapshot of this surface. This needs to be told when this
// target is modified. We keep it alive as a cache.
RefPtr<SourceSurfaceCairo> mSnapshot;
-
- // It is safe to use a regular pointer here because the CairoPathContext will
- // deregister itself on destruction. Using a RefPtr would extend the life-
- // span of the CairoPathContext. This causes a problem when
- // PathBuilderCairo.Finish()
- mutable CairoPathContext* mPathObserver;
+ static cairo_surface_t *mDummySurface;
};
}
diff --git a/gfx/2d/DrawTargetCapture.cpp b/gfx/2d/DrawTargetCapture.cpp
new file mode 100644
index 000000000..d53a98ec7
--- /dev/null
+++ b/gfx/2d/DrawTargetCapture.cpp
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "DrawTargetCapture.h"
+#include "DrawCommand.h"
+
+namespace mozilla {
+namespace gfx {
+
+
+DrawTargetCaptureImpl::~DrawTargetCaptureImpl()
+{
+ uint8_t* start = &mDrawCommandStorage.front();
+
+ uint8_t* current = start;
+
+ while (current < start + mDrawCommandStorage.size()) {
+ reinterpret_cast<DrawingCommand*>(current + sizeof(uint32_t))->~DrawingCommand();
+ current += *(uint32_t*)current;
+ }
+}
+
+bool
+DrawTargetCaptureImpl::Init(const IntSize& aSize, DrawTarget* aRefDT)
+{
+ if (!aRefDT) {
+ return false;
+ }
+
+ mRefDT = aRefDT;
+
+ mSize = aSize;
+ return true;
+}
+
+TemporaryRef<SourceSurface>
+DrawTargetCaptureImpl::Snapshot()
+{
+ RefPtr<DrawTarget> dt = mRefDT->CreateSimilarDrawTarget(mSize, mRefDT->GetFormat());
+
+ ReplayToDrawTarget(dt, Matrix());
+
+ return dt->Snapshot();
+}
+
+#define AppendCommand(arg) new (AppendToCommandList<arg>()) arg
+
+void
+DrawTargetCaptureImpl::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions)
+{
+ aSurface->GuaranteePersistance();
+ AppendCommand(DrawSurfaceCommand)(aSurface, aDest, aSource, aSurfOptions, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ // @todo XXX - this won't work properly long term yet due to filternodes not
+ // being immutable.
+ AppendCommand(DrawFilterCommand)(aNode, aSourceRect, aDestPoint, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::ClearRect(const Rect &aRect)
+{
+ AppendCommand(ClearRectCommand)(aRect);
+}
+
+void
+DrawTargetCaptureImpl::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ aMask->GuaranteePersistance();
+ AppendCommand(MaskSurfaceCommand)(aSource, aMask, aOffset, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::CopySurface(SourceSurface* aSurface,
+ const IntRect& aSourceRect,
+ const IntPoint& aDestination)
+{
+ aSurface->GuaranteePersistance();
+ AppendCommand(CopySurfaceCommand)(aSurface, aSourceRect, aDestination);
+}
+
+void
+DrawTargetCaptureImpl::FillRect(const Rect& aRect,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions)
+{
+ AppendCommand(FillRectCommand)(aRect, aPattern, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::StrokeRect(const Rect& aRect,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions)
+{
+ AppendCommand(StrokeRectCommand)(aRect, aPattern, aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::StrokeLine(const Point& aStart,
+ const Point& aEnd,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions)
+{
+ AppendCommand(StrokeLineCommand)(aStart, aEnd, aPattern, aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::Stroke(const Path* aPath,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions)
+{
+ AppendCommand(StrokeCommand)(aPath, aPattern, aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::Fill(const Path* aPath,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions)
+{
+ AppendCommand(FillCommand)(aPath, aPattern, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::FillGlyphs(ScaledFont* aFont,
+ const GlyphBuffer& aBuffer,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions,
+ const GlyphRenderingOptions* aRenderingOptions)
+{
+ AppendCommand(FillGlyphsCommand)(aFont, aBuffer, aPattern, aOptions, aRenderingOptions);
+}
+
+void
+DrawTargetCaptureImpl::Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions)
+{
+ AppendCommand(MaskCommand)(aSource, aMask, aOptions);
+}
+
+void
+DrawTargetCaptureImpl::PushClip(const Path* aPath)
+{
+ AppendCommand(PushClipCommand)(aPath);
+}
+
+void
+DrawTargetCaptureImpl::PushClipRect(const Rect& aRect)
+{
+ AppendCommand(PushClipRectCommand)(aRect);
+}
+
+void
+DrawTargetCaptureImpl::PopClip()
+{
+ AppendCommand(PopClipCommand)();
+}
+
+void
+DrawTargetCaptureImpl::SetTransform(const Matrix& aTransform)
+{
+ AppendCommand(SetTransformCommand)(aTransform);
+}
+
+void
+DrawTargetCaptureImpl::ReplayToDrawTarget(DrawTarget* aDT, const Matrix& aTransform)
+{
+ uint8_t* start = &mDrawCommandStorage.front();
+
+ uint8_t* current = start;
+
+ while (current < start + mDrawCommandStorage.size()) {
+ reinterpret_cast<DrawingCommand*>(current + sizeof(uint32_t))->ExecuteOnDT(aDT, aTransform);
+ current += *(uint32_t*)current;
+ }
+}
+
+}
+}
diff --git a/gfx/2d/DrawTargetCapture.h b/gfx/2d/DrawTargetCapture.h
new file mode 100644
index 000000000..2ef8f0e62
--- /dev/null
+++ b/gfx/2d/DrawTargetCapture.h
@@ -0,0 +1,163 @@
+/* -*- 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 MOZILLA_GFX_DRAWTARGETCAPTURE_H_
+#define MOZILLA_GFX_DRAWTARGETCAPTURE_H_
+
+#include "2D.h"
+#include <vector>
+
+#include "Filters.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DrawingCommand;
+
+class DrawTargetCaptureImpl : public DrawTargetCapture
+{
+public:
+ DrawTargetCaptureImpl()
+ {}
+
+ bool Init(const IntSize& aSize, DrawTarget* aRefDT);
+
+ virtual BackendType GetBackendType() const { return mRefDT->GetBackendType(); }
+ virtual DrawTargetType GetType() const { return mRefDT->GetType(); }
+
+ virtual TemporaryRef<SourceSurface> Snapshot();
+ virtual IntSize GetSize() { return mSize; }
+
+ virtual void Flush() {}
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions);
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator) { /* Not implemented */ }
+
+ virtual void ClearRect(const Rect &aRect);
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions());
+
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination);
+
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions(),
+ const GlyphRenderingOptions *aRenderingOptions = nullptr);
+ virtual void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void PushClip(const Path *aPath);
+ virtual void PushClipRect(const Rect &aRect);
+ virtual void PopClip();
+
+ virtual void SetTransform(const Matrix &aTransform);
+
+ virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+ {
+ return mRefDT->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
+ }
+ virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const
+ {
+ return mRefDT->OptimizeSourceSurface(aSurface);
+ }
+
+ virtual TemporaryRef<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+ {
+ return mRefDT->CreateSourceSurfaceFromNativeSurface(aSurface);
+ }
+
+ virtual TemporaryRef<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+ {
+ return mRefDT->CreateSimilarDrawTarget(aSize, aFormat);
+ }
+
+ virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const
+ {
+ return mRefDT->CreatePathBuilder(aFillRule);
+ }
+
+ virtual TemporaryRef<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const
+ {
+ return mRefDT->CreateGradientStops(aStops, aNumStops, aExtendMode);
+ }
+ virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType)
+ {
+ return mRefDT->CreateFilter(aType);
+ }
+
+ void ReplayToDrawTarget(DrawTarget* aDT, const Matrix& aTransform);
+
+protected:
+ ~DrawTargetCaptureImpl();
+
+private:
+
+ // This storage system was used to minimize the amount of heap allocations
+ // that are required while recording. It should be noted there's no
+ // guarantees on the alignments of DrawingCommands allocated in this array.
+ template<typename T>
+ T* AppendToCommandList()
+ {
+ size_t oldSize = mDrawCommandStorage.size();
+ mDrawCommandStorage.resize(mDrawCommandStorage.size() + sizeof(T) + sizeof(uint32_t));
+ uint8_t* nextDrawLocation = &mDrawCommandStorage.front() + oldSize;
+ *(uint32_t*)(nextDrawLocation) = sizeof(T) + sizeof(uint32_t);
+ return reinterpret_cast<T*>(nextDrawLocation + sizeof(uint32_t));
+ }
+ RefPtr<DrawTarget> mRefDT;
+
+ IntSize mSize;
+
+ std::vector<uint8_t> mDrawCommandStorage;
+};
+
+} /* namespace mozilla */
+} /* namespace gfx */
+
+#endif /* MOZILLA_GFX_DRAWTARGETCAPTURE_H_ */
diff --git a/gfx/2d/DrawTargetD2D.cpp b/gfx/2d/DrawTargetD2D.cpp
index a6f6af2a2..2d80f2a7a 100644
--- a/gfx/2d/DrawTargetD2D.cpp
+++ b/gfx/2d/DrawTargetD2D.cpp
@@ -3,8 +3,10 @@
* 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/. */
+#include <initguid.h>
#include "DrawTargetD2D.h"
#include "SourceSurfaceD2D.h"
+#include "SourceSurfaceD2D1.h"
#include "SourceSurfaceD2DTarget.h"
#include "ShadersD2D.h"
#include "PathD2D.h"
@@ -15,9 +17,14 @@
#include "Tools.h"
#include <algorithm>
#include "mozilla/Constants.h"
+#include "FilterNodeSoftware.h"
+
+#include "FilterNodeD2D1.h"
+#include "ExtendInputEffectD2D1.h"
#include <dwrite.h>
+// decltype is not usable for overloaded functions.
typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
D2D1_FACTORY_TYPE factoryType,
REFIID iid,
@@ -25,21 +32,6 @@ typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
void **factory
);
-typedef HRESULT (WINAPI*D3D10CreateEffectFromMemoryFunc)(
- void *pData,
- SIZE_T DataLength,
- UINT FXFlags,
- ID3D10Device *pDevice,
- ID3D10EffectPool *pEffectPool,
- ID3D10Effect **ppEffect
-);
-
-typedef HRESULT (WINAPI*DWriteCreateFactoryFunc)(
- DWRITE_FACTORY_TYPE factoryType,
- REFIID iid,
- IUnknown **factory
-);
-
using namespace std;
namespace mozilla {
@@ -81,13 +73,12 @@ public:
HRESULT hr = mDT->mDevice->CreateTexture2D(&desc, nullptr, byRef(tmpTexture));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create temporary texture to hold surface data.";
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(size))) << "[D2D] 1 CreateTexture2D failure " << size << " Code: " << hexa(hr);
+ return;
}
mDT->mDevice->CopyResource(tmpTexture, mDT->mTexture);
- D2D1_BITMAP_PROPERTIES props =
- D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(format),
- AlphaMode(format)));
+ D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(format));
RefPtr<IDXGISurface> surf;
@@ -97,7 +88,8 @@ public:
&props, byRef(mOldSurfBitmap));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create shared bitmap for old surface.";
+ gfxCriticalError() << "[D2D] CreateSharedBitmap failure " << size << " Code: " << hexa(hr);
+ return;
}
IntRect clipBounds;
@@ -242,7 +234,7 @@ DrawTargetD2D::Flush()
HRESULT hr = mRT->Flush();
if (FAILED(hr)) {
- gfxWarning() << "Error reported when trying to flush D2D rendertarget. Code: " << hr;
+ gfxWarning() << "Error reported when trying to flush D2D rendertarget. Code: " << hexa(hr);
}
// We no longer depend on any target.
@@ -270,13 +262,13 @@ DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface,
switch (aSurface->GetType()) {
- case SURFACE_D2D1_BITMAP:
+ case SurfaceType::D2D1_BITMAP:
{
SourceSurfaceD2D *srcSurf = static_cast<SourceSurfaceD2D*>(aSurface);
bitmap = srcSurf->GetBitmap();
}
break;
- case SURFACE_D2D1_DRAWTARGET:
+ case SurfaceType::D2D1_DRAWTARGET:
{
SourceSurfaceD2DTarget *srcSurf = static_cast<SourceSurfaceD2DTarget*>(aSurface);
bitmap = srcSurf->GetBitmap(mRT);
@@ -292,8 +284,17 @@ DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface,
return nullptr;
}
- if (aSource.width > mRT->GetMaximumBitmapSize() ||
- aSource.height > mRT->GetMaximumBitmapSize()) {
+ // We need to include any pixels that are overlapped by aSource
+ Rect sourceRect(aSource);
+ sourceRect.RoundOut();
+
+ if (sourceRect.IsEmpty()) {
+ gfxDebug() << "Bitmap source is empty. DrawBitmap will silently fail.";
+ return nullptr;
+ }
+
+ if (sourceRect.width > mRT->GetMaximumBitmapSize() ||
+ sourceRect.height > mRT->GetMaximumBitmapSize()) {
gfxDebug() << "Bitmap source larger than texture size specified. DrawBitmap will silently fail.";
// Don't know how to deal with this yet.
return nullptr;
@@ -302,12 +303,12 @@ DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface,
int stride = srcSurf->Stride();
unsigned char *data = srcSurf->GetData() +
- (uint32_t)aSource.y * stride +
- (uint32_t)aSource.x * BytesPerPixel(srcSurf->GetFormat());
+ (uint32_t)sourceRect.y * stride +
+ (uint32_t)sourceRect.x * BytesPerPixel(srcSurf->GetFormat());
D2D1_BITMAP_PROPERTIES props =
- D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(srcSurf->GetFormat()), AlphaMode(srcSurf->GetFormat())));
- mRT->CreateBitmap(D2D1::SizeU(UINT32(aSource.width), UINT32(aSource.height)), data, stride, props, byRef(bitmap));
+ D2D1::BitmapProperties(D2DPixelFormat(srcSurf->GetFormat()));
+ mRT->CreateBitmap(D2D1::SizeU(UINT32(sourceRect.width), UINT32(sourceRect.height)), data, stride, props, byRef(bitmap));
// subtract the integer part leaving the fractional part
aSource.x -= (uint32_t)aSource.x;
@@ -319,6 +320,17 @@ DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface,
return bitmap;
}
+TemporaryRef<ID2D1Image>
+DrawTargetD2D::GetImageForSurface(SourceSurface *aSurface)
+{
+ RefPtr<ID2D1Image> image;
+
+ Rect r(Point(), Size(aSurface->GetSize()));
+ image = GetBitmapForSurface(aSurface, r);
+
+ return image;
+}
+
void
DrawTargetD2D::DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
@@ -347,6 +359,47 @@ DrawTargetD2D::DrawSurface(SourceSurface *aSurface,
}
void
+DrawTargetD2D::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ RefPtr<ID2D1DeviceContext> dc;
+ HRESULT hr;
+
+ hr = mRT->QueryInterface((ID2D1DeviceContext**)byRef(dc));
+
+ if (SUCCEEDED(hr) && aNode->GetBackendType() == FILTER_BACKEND_DIRECT2D1_1) {
+ ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()));
+
+ PrepareForDrawing(rt);
+
+ rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+ hr = rt->QueryInterface((ID2D1DeviceContext**)byRef(dc));
+
+ if (SUCCEEDED(hr)) {
+ FilterNodeD2D1* node = static_cast<FilterNodeD2D1*>(aNode);
+ node->WillDraw(this);
+
+ dc->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect));
+
+ Rect destRect = aSourceRect;
+ destRect.MoveBy(aDestPoint);
+ FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), destRect);
+ return;
+ }
+ }
+
+ if (aNode->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
+ gfxWarning() << "Invalid filter backend passed to DrawTargetD2D!";
+ return;
+ }
+
+ FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
+ filter->Draw(this, aSourceRect, aDestPoint, aOptions);
+}
+
+void
DrawTargetD2D::MaskSurface(const Pattern &aSource,
SourceSurface *aMask,
Point aOffset,
@@ -384,7 +437,7 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
CompositionOp aOperator)
{
RefPtr<ID3D10ShaderResourceView> srView = nullptr;
- if (aSurface->GetType() != SURFACE_D2D1_DRAWTARGET) {
+ if (aSurface->GetType() != SurfaceType::D2D1_DRAWTARGET) {
return;
}
@@ -410,7 +463,7 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
HRESULT hr = mDevice->CreateRenderTargetView(mTempTexture, nullptr, byRef(mTempRTView));
if (FAILED(hr)) {
- gfxWarning() << "Failure to create RenderTargetView. Code: " << hr;
+ gfxWarning() << "Failure to create RenderTargetView. Code: " << hexa(hr);
return;
}
}
@@ -471,8 +524,7 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mipTexture));
if (FAILED(hr)) {
- gfxWarning() << "Failure to create temporary texture. Size: " <<
- aSurface->GetSize() << " Code: " << hr;
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSurface->GetSize()))) << "[D2D] 2 CreateTexture2D failure " << aSurface->GetSize() << " Code: " << hexa(hr);
return;
}
@@ -499,7 +551,7 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(tmpDSTexture));
if (FAILED(hr)) {
- gfxWarning() << "Failure to create temporary texture. Size: " << dsSize << " Code: " << hr;
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(dsSize))) << "[D2D] 3 CreateTexture2D failure " << dsSize << " Code: " << hexa(hr);
return;
}
@@ -538,7 +590,7 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->
GetPassByIndex(0)->Apply(0);
- mDevice->OMSetBlendState(GetBlendStateForOperator(OP_OVER), nullptr, 0xffffffff);
+ mDevice->OMSetBlendState(GetBlendStateForOperator(CompositionOp::OP_OVER), nullptr, 0xffffffff);
mDevice->Draw(4, 0);
@@ -721,27 +773,37 @@ void
DrawTargetD2D::ClearRect(const Rect &aRect)
{
MarkChanged();
+ PushClipRect(aRect);
- FlushTransformToRT();
PopAllClips();
AutoSaveRestoreClippedOut restoreClippedOut(this);
- restoreClippedOut.Save();
-
- bool needsClip = false;
-
- needsClip = aRect.x > 0 || aRect.y > 0 ||
- aRect.XMost() < mSize.width ||
- aRect.YMost() < mSize.height;
+ D2D1_RECT_F clipRect;
+ bool isPixelAligned;
+ bool pushedClip = false;
+ if (mTransform.IsRectilinear() &&
+ GetDeviceSpaceClipRect(clipRect, isPixelAligned)) {
+ if (mTransformDirty ||
+ !mTransform.IsIdentity()) {
+ mRT->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+ }
- if (needsClip) {
- mRT->PushAxisAlignedClip(D2DRect(aRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ mRT->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ pushedClip = true;
+ } else {
+ FlushTransformToRT();
+ restoreClippedOut.Save();
}
+
mRT->Clear(D2D1::ColorF(0, 0.0f));
- if (needsClip) {
+
+ if (pushedClip) {
mRT->PopAxisAlignedClip();
}
+
+ PopClip();
return;
}
@@ -763,33 +825,22 @@ DrawTargetD2D::CopySurface(SourceSurface *aSurface,
mRT->Clear(D2D1::ColorF(0, 0.0f));
mRT->PopAxisAlignedClip();
- RefPtr<ID2D1Bitmap> bitmap;
-
- switch (aSurface->GetType()) {
- case SURFACE_D2D1_BITMAP:
- {
- SourceSurfaceD2D *srcSurf = static_cast<SourceSurfaceD2D*>(aSurface);
- bitmap = srcSurf->GetBitmap();
- }
- break;
- case SURFACE_D2D1_DRAWTARGET:
- {
- SourceSurfaceD2DTarget *srcSurf = static_cast<SourceSurfaceD2DTarget*>(aSurface);
- bitmap = srcSurf->GetBitmap(mRT);
- AddDependencyOnSource(srcSurf);
- }
- break;
- default:
- return;
- }
-
+ RefPtr<ID2D1Bitmap> bitmap = GetBitmapForSurface(aSurface, srcRect);
if (!bitmap) {
return;
}
- mRT->DrawBitmap(bitmap, D2DRect(dstRect), 1.0f,
- D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
- D2DRect(srcRect));
+ if (aSurface->GetFormat() == SurfaceFormat::A8) {
+ RefPtr<ID2D1SolidColorBrush> brush;
+ mRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White),
+ D2D1::BrushProperties(), byRef(brush));
+ mRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+ mRT->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS);
+ } else {
+ mRT->DrawBitmap(bitmap, D2DRect(dstRect), 1.0f,
+ D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
+ D2DRect(srcRect));
+ }
}
void
@@ -865,7 +916,7 @@ DrawTargetD2D::Stroke(const Path *aPath,
const StrokeOptions &aStrokeOptions,
const DrawOptions &aOptions)
{
- if (aPath->GetBackendType() != BACKEND_DIRECT2D) {
+ if (aPath->GetBackendType() != BackendType::DIRECT2D) {
gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
return;
}
@@ -894,7 +945,7 @@ DrawTargetD2D::Fill(const Path *aPath,
const Pattern &aPattern,
const DrawOptions &aOptions)
{
- if (aPath->GetBackendType() != BACKEND_DIRECT2D) {
+ if (aPath->GetBackendType() != BackendType::DIRECT2D) {
gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
return;
}
@@ -914,7 +965,7 @@ DrawTargetD2D::Fill(const Path *aPath,
}
Rect bounds;
- if (aOptions.mCompositionOp != OP_OVER) {
+ if (aOptions.mCompositionOp != CompositionOp::OP_OVER) {
D2D1_RECT_F d2dbounds;
d2dPath->mGeometry->GetBounds(D2D1::IdentityMatrix(), &d2dbounds);
bounds = ToRect(d2dbounds);
@@ -929,7 +980,7 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aFont,
const DrawOptions &aOptions,
const GlyphRenderingOptions* aRenderOptions)
{
- if (aFont->GetType() != FONT_DWRITE) {
+ if (aFont->GetType() != FontType::DWRITE) {
gfxDebug() << *this << ": Ignoring drawing call for incompatible font.";
return;
}
@@ -938,7 +989,7 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aFont,
IDWriteRenderingParams *params = nullptr;
if (aRenderOptions) {
- if (aRenderOptions->GetType() != FONT_DWRITE) {
+ if (aRenderOptions->GetType() != FontType::DWRITE) {
gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions.";
// This should never happen.
MOZ_ASSERT(false);
@@ -949,13 +1000,13 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aFont,
AntialiasMode aaMode = font->GetDefaultAAMode();
- if (aOptions.mAntialiasMode != AA_DEFAULT) {
+ if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
aaMode = aOptions.mAntialiasMode;
}
- if (mFormat == FORMAT_B8G8R8A8 && mPermitSubpixelAA &&
- aOptions.mCompositionOp == OP_OVER && aPattern.GetType() == PATTERN_COLOR &&
- aaMode == AA_SUBPIXEL) {
+ if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA &&
+ aOptions.mCompositionOp == CompositionOp::OP_OVER && aPattern.GetType() == PatternType::COLOR &&
+ aaMode == AntialiasMode::SUBPIXEL) {
if (FillGlyphsManual(font, aBuffer,
static_cast<const ColorPattern*>(&aPattern)->mColor,
params, aOptions)) {
@@ -966,31 +1017,25 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aFont,
ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
PrepareForDrawing(rt);
-
- bool forceClearType = false;
- if (mFormat == FORMAT_B8G8R8A8 && mPermitSubpixelAA &&
- aOptions.mCompositionOp == OP_OVER && aaMode == AA_SUBPIXEL) {
- forceClearType = true;
- }
D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
switch (aaMode) {
- case AA_NONE:
+ case AntialiasMode::NONE:
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
break;
- case AA_GRAY:
+ case AntialiasMode::GRAY:
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
break;
- case AA_SUBPIXEL:
+ case AntialiasMode::SUBPIXEL:
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
break;
default:
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
}
-
+
if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE &&
- mFormat != FORMAT_B8G8R8X8 && !forceClearType) {
+ mFormat != SurfaceFormat::B8G8R8X8) {
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
}
@@ -1050,7 +1095,7 @@ DrawTargetD2D::Mask(const Pattern &aSource,
void
DrawTargetD2D::PushClip(const Path *aPath)
{
- if (aPath->GetBackendType() != BACKEND_DIRECT2D) {
+ if (aPath->GetBackendType() != BackendType::DIRECT2D) {
gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
return;
}
@@ -1145,20 +1190,39 @@ DrawTargetD2D::CreateSourceSurfaceFromData(unsigned char *aData,
return nullptr;
}
- return newSurf;
+ return newSurf.forget();
}
TemporaryRef<SourceSurface>
DrawTargetD2D::OptimizeSourceSurface(SourceSurface *aSurface) const
{
- // Unsupported!
- return nullptr;
+ if (aSurface->GetType() == SurfaceType::D2D1_BITMAP ||
+ aSurface->GetType() == SurfaceType::D2D1_DRAWTARGET) {
+ return aSurface;
+ }
+
+ RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
+
+ DataSourceSurface::MappedSurface map;
+ if (!data->Map(DataSourceSurface::MapType::READ, &map)) {
+ return nullptr;
+ }
+
+ RefPtr<SourceSurfaceD2D> newSurf = new SourceSurfaceD2D();
+ bool success = newSurf->InitFromData(map.mData, data->GetSize(), map.mStride, data->GetFormat(), mRT);
+
+ data->Unmap();
+
+ if (!success) {
+ return data.forget();
+ }
+ return newSurf.forget();
}
TemporaryRef<SourceSurface>
DrawTargetD2D::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
{
- if (aSurface.mType != NATIVE_SURFACE_D3D10_TEXTURE) {
+ if (aSurface.mType != NativeSurfaceType::D3D10_TEXTURE) {
gfxDebug() << *this << ": Failure to create source surface from non-D3D10 texture native surface.";
return nullptr;
}
@@ -1172,7 +1236,7 @@ DrawTargetD2D::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurfac
return nullptr;
}
- return newSurf;
+ return newSurf.forget();
}
TemporaryRef<DrawTarget>
@@ -1186,7 +1250,7 @@ DrawTargetD2D::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aForm
return nullptr;
}
- return newTarget;
+ return newTarget.forget();
}
TemporaryRef<PathBuilder>
@@ -1196,22 +1260,22 @@ DrawTargetD2D::CreatePathBuilder(FillRule aFillRule) const
HRESULT hr = factory()->CreatePathGeometry(byRef(path));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create Direct2D Path Geometry. Code: " << hr;
+ gfxWarning() << "Failed to create Direct2D Path Geometry. Code: " << hexa(hr);
return nullptr;
}
RefPtr<ID2D1GeometrySink> sink;
hr = path->Open(byRef(sink));
if (FAILED(hr)) {
- gfxWarning() << "Failed to access Direct2D Path Geometry. Code: " << hr;
+ gfxWarning() << "Failed to access Direct2D Path Geometry. Code: " << hexa(hr);
return nullptr;
}
- if (aFillRule == FILL_WINDING) {
+ if (aFillRule == FillRule::FILL_WINDING) {
sink->SetFillMode(D2D1_FILL_MODE_WINDING);
}
- return new PathBuilderD2D(sink, path, aFillRule);
+ return new PathBuilderD2D(sink, path, aFillRule, BackendType::DIRECT2D);
}
TemporaryRef<GradientStops>
@@ -1233,17 +1297,29 @@ DrawTargetD2D::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, E
delete [] stops;
if (FAILED(hr)) {
- gfxWarning() << "Failed to create GradientStopCollection. Code: " << hr;
+ gfxWarning() << "Failed to create GradientStopCollection. Code: " << hexa(hr);
return nullptr;
}
- return new GradientStopsD2D(stopCollection);
+ return new GradientStopsD2D(stopCollection, Factory::GetDirect3D11Device());
+}
+
+TemporaryRef<FilterNode>
+DrawTargetD2D::CreateFilter(FilterType aType)
+{
+ RefPtr<ID2D1DeviceContext> dc;
+ HRESULT hr = mRT->QueryInterface((ID2D1DeviceContext**)byRef(dc));
+
+ if (SUCCEEDED(hr)) {
+ return FilterNodeD2D1::Create(dc, aType);
+ }
+ return FilterNodeSoftware::Create(aType);
}
void*
DrawTargetD2D::GetNativeSurface(NativeSurfaceType aType)
{
- if (aType != NATIVE_SURFACE_D3D10_TEXTURE) {
+ if (aType != NativeSurfaceType::D3D10_TEXTURE) {
return nullptr;
}
@@ -1262,7 +1338,7 @@ DrawTargetD2D::Init(const IntSize &aSize, SurfaceFormat aFormat)
mFormat = aFormat;
if (!Factory::GetDirect3D10Device()) {
- gfxDebug() << "Failed to Init Direct2D DrawTarget (No D3D10 Device set.)";
+ gfxCriticalError() << "Failed to Init Direct2D DrawTarget (No D3D10 Device set.)";
return false;
}
mDevice = Factory::GetDirect3D10Device();
@@ -1276,7 +1352,7 @@ DrawTargetD2D::Init(const IntSize &aSize, SurfaceFormat aFormat)
hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mTexture));
if (FAILED(hr)) {
- gfxDebug() << "Failed to init Direct2D DrawTarget. Size: " << mSize << " Code: " << hr;
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "Failed to init Direct2D DrawTarget. Size: " << mSize << " Code: " << hexa(hr);
return false;
}
@@ -1297,7 +1373,7 @@ DrawTargetD2D::Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat)
mFormat = aFormat;
if (!mTexture) {
- gfxDebug() << "No valid texture for Direct2D draw target initialization.";
+ gfxCriticalError() << "No valid texture for Direct2D draw target initialization.";
return false;
}
@@ -1307,7 +1383,7 @@ DrawTargetD2D::Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat)
hr = device->QueryInterface((ID3D10Device1**)byRef(mDevice));
if (FAILED(hr)) {
- gfxWarning() << "Failed to get D3D10 device from texture.";
+ gfxCriticalError() << "Failed to get D3D10 device from texture.";
return false;
}
@@ -1338,15 +1414,15 @@ DrawTargetD2D::InitD3D10Data()
mPrivateData = new PrivateD3D10DataD2D;
- D3D10CreateEffectFromMemoryFunc createD3DEffect;
+ decltype(D3D10CreateEffectFromMemory)* createD3DEffect;
HMODULE d3dModule = LoadLibraryW(L"d3d10_1.dll");
- createD3DEffect = (D3D10CreateEffectFromMemoryFunc)
+ createD3DEffect = (decltype(D3D10CreateEffectFromMemory)*)
GetProcAddress(d3dModule, "D3D10CreateEffectFromMemory");
hr = createD3DEffect((void*)d2deffect, sizeof(d2deffect), 0, mDevice, nullptr, byRef(mPrivateData->mEffect));
if (FAILED(hr)) {
- gfxWarning() << "Failed to initialize Direct2D required effects. Code: " << hr;
+ gfxWarning() << "Failed to initialize Direct2D required effects. Code: " << hexa(hr);
return false;
}
@@ -1368,7 +1444,7 @@ DrawTargetD2D::InitD3D10Data()
byRef(mPrivateData->mInputLayout));
if (FAILED(hr)) {
- gfxWarning() << "Failed to initialize Direct2D required InputLayout. Code: " << hr;
+ gfxWarning() << "Failed to initialize Direct2D required InputLayout. Code: " << hexa(hr);
return false;
}
@@ -1380,7 +1456,7 @@ DrawTargetD2D::InitD3D10Data()
hr = mDevice->CreateBuffer(&bufferDesc, &data, byRef(mPrivateData->mVB));
if (FAILED(hr)) {
- gfxWarning() << "Failed to initialize Direct2D required VertexBuffer. Code: " << hr;
+ gfxWarning() << "Failed to initialize Direct2D required VertexBuffer. Code: " << hexa(hr);
return false;
}
@@ -1412,7 +1488,7 @@ DrawTargetD2D::GetCachedLayer()
}
mCurrentCachedLayer++;
- return layer;
+ return layer.forget();
}
void
@@ -1426,6 +1502,7 @@ bool
DrawTargetD2D::InitD2DRenderTarget()
{
if (!factory()) {
+ gfxCriticalError() << "No valid D2D factory available.";
return false;
}
@@ -1437,7 +1514,7 @@ DrawTargetD2D::InitD2DRenderTarget()
mRT->BeginDraw();
- if (mFormat == FORMAT_B8G8R8X8) {
+ if (mFormat == SurfaceFormat::B8G8R8X8) {
mRT->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
}
@@ -1497,8 +1574,9 @@ DrawTargetD2D::MarkChanged()
ID3D10BlendState*
DrawTargetD2D::GetBlendStateForOperator(CompositionOp aOperator)
{
- if (mPrivateData->mBlendStates[aOperator]) {
- return mPrivateData->mBlendStates[aOperator];
+ size_t operatorIndex = static_cast<size_t>(aOperator);
+ if (mPrivateData->mBlendStates[operatorIndex]) {
+ return mPrivateData->mBlendStates[operatorIndex];
}
D3D10_BLEND_DESC desc;
@@ -1511,43 +1589,43 @@ DrawTargetD2D::GetBlendStateForOperator(CompositionOp aOperator)
desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
switch (aOperator) {
- case OP_ADD:
+ case CompositionOp::OP_ADD:
desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE;
desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE;
break;
- case OP_IN:
+ case CompositionOp::OP_IN:
desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA;
desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO;
break;
- case OP_OUT:
+ case CompositionOp::OP_OUT:
desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA;
desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO;
break;
- case OP_ATOP:
+ case CompositionOp::OP_ATOP:
desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA;
desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
break;
- case OP_DEST_IN:
+ case CompositionOp::OP_DEST_IN:
desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO;
desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA;
break;
- case OP_DEST_OUT:
+ case CompositionOp::OP_DEST_OUT:
desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO;
desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
break;
- case OP_DEST_ATOP:
+ case CompositionOp::OP_DEST_ATOP:
desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA;
desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA;
break;
- case OP_DEST_OVER:
+ case CompositionOp::OP_DEST_OVER:
desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA;
desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE;
break;
- case OP_XOR:
+ case CompositionOp::OP_XOR:
desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA;
desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
break;
- case OP_SOURCE:
+ case CompositionOp::OP_SOURCE:
desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE;
desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO;
break;
@@ -1556,9 +1634,9 @@ DrawTargetD2D::GetBlendStateForOperator(CompositionOp aOperator)
desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
}
- mDevice->CreateBlendState(&desc, byRef(mPrivateData->mBlendStates[aOperator]));
+ mDevice->CreateBlendState(&desc, byRef(mPrivateData->mBlendStates[operatorIndex]));
- return mPrivateData->mBlendStates[aOperator];
+ return mPrivateData->mBlendStates[operatorIndex];
}
/* This function prepares the temporary RT for drawing and returns it when a
@@ -1567,13 +1645,13 @@ DrawTargetD2D::GetBlendStateForOperator(CompositionOp aOperator)
ID2D1RenderTarget*
DrawTargetD2D::GetRTForOperation(CompositionOp aOperator, const Pattern &aPattern)
{
- if (aOperator == OP_OVER && IsPatternSupportedByD2D(aPattern)) {
+ if (aOperator == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) {
return mRT;
}
PopAllClips();
- if (aOperator > OP_XOR) {
+ if (aOperator > CompositionOp::OP_XOR) {
mRT->Flush();
}
@@ -1585,11 +1663,11 @@ DrawTargetD2D::GetRTForOperation(CompositionOp aOperator, const Pattern &aPatter
EnsureViews();
if (!mRTView || !mSRView) {
- gfxDebug() << *this << ": Failed to get required views. Defaulting to OP_OVER.";
+ gfxDebug() << *this << ": Failed to get required views. Defaulting to CompositionOp::OP_OVER.";
return mRT;
}
- mTempRT = CreateRTForTexture(mTempTexture, FORMAT_B8G8R8A8);
+ mTempRT = CreateRTForTexture(mTempTexture, SurfaceFormat::B8G8R8A8);
if (!mTempRT) {
return mRT;
@@ -1614,7 +1692,7 @@ DrawTargetD2D::GetRTForOperation(CompositionOp aOperator, const Pattern &aPatter
void
DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aPattern, const Rect &aBounds)
{
- if (aOperator == OP_OVER && IsPatternSupportedByD2D(aPattern)) {
+ if (aOperator == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) {
return;
}
@@ -1668,7 +1746,7 @@ DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aP
mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(mSRView);
// Handle the case where we blend with the backdrop
- if (aOperator > OP_XOR) {
+ if (aOperator > CompositionOp::OP_XOR) {
IntSize size = mSize;
SurfaceFormat format = mFormat;
@@ -1683,7 +1761,7 @@ DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aP
mDevice->CopyResource(tmpTexture, mTexture);
if (FAILED(hr)) {
- gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hr;
+ gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hexa(hr);
return;
}
@@ -1692,18 +1770,18 @@ DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aP
hr = mDevice->CreateShaderResourceView(tmpTexture, nullptr, byRef(mBckSRView));
if (FAILED(hr)) {
- gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hr;
+ gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hexa(hr);
return;
}
- unsigned int compop = (unsigned int)aOperator - (unsigned int)OP_XOR;
+ unsigned int compop = (unsigned int)aOperator - (unsigned int)CompositionOp::OP_XOR;
mPrivateData->mEffect->GetVariableByName("bcktex")->AsShaderResource()->SetResource(mBckSRView);
mPrivateData->mEffect->GetVariableByName("blendop")->AsScalar()->SetInt(compop);
- if (aOperator > OP_EXCLUSION)
+ if (aOperator > CompositionOp::OP_EXCLUSION)
mPrivateData->mEffect->GetTechniqueByName("SampleTextureForNonSeparableBlending")->
GetPassByIndex(0)->Apply(0);
- else if (aOperator > OP_COLOR_DODGE)
+ else if (aOperator > CompositionOp::OP_COLOR_DODGE)
mPrivateData->mEffect->GetTechniqueByName("SampleTextureForSeparableBlending_2")->
GetPassByIndex(0)->Apply(0);
else
@@ -1714,7 +1792,7 @@ DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aP
mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->GetPassByIndex(0)->Apply(0);
}
- } else if (aPattern.GetType() == PATTERN_RADIAL_GRADIENT) {
+ } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern);
if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) {
@@ -1741,9 +1819,40 @@ IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2)
result.top = max(aRect1.top, aRect2.top);
result.right = min(aRect1.right, aRect2.right);
result.bottom = min(aRect1.bottom, aRect2.bottom);
+
+ result.right = max(result.right, result.left);
+ result.bottom = max(result.bottom, result.top);
+
return result;
}
+bool
+DrawTargetD2D::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned)
+{
+ if (!mPushedClips.size()) {
+ return false;
+ }
+
+ std::vector<DrawTargetD2D::PushedClip>::iterator iter = mPushedClips.begin();
+ if (iter->mPath) {
+ return false;
+ }
+ aClipRect = iter->mBounds;
+ aIsPixelAligned = iter->mIsPixelAligned;
+
+ iter++;
+ for (;iter != mPushedClips.end(); iter++) {
+ if (iter->mPath) {
+ return false;
+ }
+ aClipRect = IntersectRect(aClipRect, iter->mBounds);
+ if (!iter->mIsPixelAligned) {
+ aIsPixelAligned = false;
+ }
+ }
+ return true;
+}
+
TemporaryRef<ID2D1Geometry>
DrawTargetD2D::GetClippedGeometry(IntRect *aClipBounds)
{
@@ -1819,10 +1928,10 @@ DrawTargetD2D::GetClippedGeometry(IntRect *aClipBounds)
pathGeom = newGeom.forget();
}
- // For now we need mCurrentClippedGeometry to always be non-NULL. This method
- // might seem a little strange but it is just fine, if pathGeom is NULL
- // pathRect will always still contain 1 clip unaccounted for regardless of
- // mCurrentClipBounds.
+ // For now we need mCurrentClippedGeometry to always be non-nullptr. This
+ // method might seem a little strange but it is just fine, if pathGeom is
+ // nullptr pathRect will always still contain 1 clip unaccounted for
+ // regardless of mCurrentClipBounds.
if (!pathGeom) {
pathGeom = ConvertRectToGeometry(pathRect);
}
@@ -1842,7 +1951,7 @@ DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aForm
hr = aTexture->QueryInterface((IDXGISurface**)byRef(surface));
if (FAILED(hr)) {
- gfxWarning() << "Failed to QI texture to surface.";
+ gfxCriticalError() << "Failed to QI texture to surface. Code: " << hr;
return nullptr;
}
@@ -1851,7 +1960,7 @@ DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aForm
D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
- if (aFormat == FORMAT_B8G8R8X8 && aTexture == mTexture) {
+ if (aFormat == SurfaceFormat::B8G8R8X8 && aTexture == mTexture) {
alphaMode = D2D1_ALPHA_MODE_IGNORE;
}
@@ -1860,11 +1969,11 @@ DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aForm
hr = factory()->CreateDxgiSurfaceRenderTarget(surface, props, byRef(rt));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create D2D render target for texture.";
+ gfxCriticalError() << "Failed to create D2D render target for texture. Code:" << hr << " " << mSize << " Format: " << uint32_t(aFormat);
return nullptr;
}
- return rt;
+ return rt.forget();
}
void
@@ -1886,21 +1995,21 @@ DrawTargetD2D::EnsureViews()
if (FAILED(hr)) {
gfxWarning() << *this << "Failed to create temporary texture for rendertarget. Size: "
- << mSize << " Code: " << hr;
+ << mSize << " Code: " << hexa(hr);
return;
}
hr = mDevice->CreateShaderResourceView(mTempTexture, nullptr, byRef(mSRView));
if (FAILED(hr)) {
- gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hr;
+ gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hexa(hr);
return;
}
hr = mDevice->CreateRenderTargetView(mTexture, nullptr, byRef(mRTView));
if (FAILED(hr)) {
- gfxWarning() << *this << "Failed to create rendertarget view for temp texture. Code: " << hr;
+ gfxWarning() << *this << "Failed to create rendertarget view for temp texture. Code: " << hexa(hr);
}
}
@@ -1962,7 +2071,7 @@ DrawTargetD2D::EnsureClipMaskTexture(IntRect *aBounds)
return;
}
- RefPtr<ID2D1RenderTarget> rt = CreateRTForTexture(mCurrentClipMaskTexture, FORMAT_A8);
+ RefPtr<ID2D1RenderTarget> rt = CreateRTForTexture(mCurrentClipMaskTexture, SurfaceFormat::A8);
if (!rt) {
gfxWarning() << "Failed to create RT for ClipMask!";
@@ -2164,18 +2273,19 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
if (!IsPatternSupportedByD2D(aPattern)) {
RefPtr<ID2D1SolidColorBrush> colBrush;
mRT->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), byRef(colBrush));
- return colBrush;
+ return colBrush.forget();
}
- if (aPattern.GetType() == PATTERN_COLOR) {
+ if (aPattern.GetType() == PatternType::COLOR) {
RefPtr<ID2D1SolidColorBrush> colBrush;
Color color = static_cast<const ColorPattern*>(&aPattern)->mColor;
mRT->CreateSolidColorBrush(D2D1::ColorF(color.r, color.g,
color.b, color.a),
D2D1::BrushProperties(aAlpha),
byRef(colBrush));
- return colBrush;
- } else if (aPattern.GetType() == PATTERN_LINEAR_GRADIENT) {
+ return colBrush.forget();
+ }
+ if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
RefPtr<ID2D1LinearGradientBrush> gradBrush;
const LinearGradientPattern *pat =
static_cast<const LinearGradientPattern*>(&aPattern);
@@ -2195,7 +2305,7 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
mRT->CreateSolidColorBrush(d2dStops.back().color,
D2D1::BrushProperties(aAlpha),
byRef(colBrush));
- return colBrush;
+ return colBrush.forget();
}
mRT->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin),
@@ -2203,8 +2313,9 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
stops->mStopCollection,
byRef(gradBrush));
- return gradBrush;
- } else if (aPattern.GetType() == PATTERN_RADIAL_GRADIENT) {
+ return gradBrush.forget();
+ }
+ if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
RefPtr<ID2D1RadialGradientBrush> gradBrush;
const RadialGradientPattern *pat =
static_cast<const RadialGradientPattern*>(&aPattern);
@@ -2225,8 +2336,9 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
stops->mStopCollection,
byRef(gradBrush));
- return gradBrush;
- } else if (aPattern.GetType() == PATTERN_SURFACE) {
+ return gradBrush.forget();
+ }
+ if (aPattern.GetType() == PatternType::SURFACE) {
RefPtr<ID2D1BitmapBrush> bmBrush;
const SurfacePattern *pat =
static_cast<const SurfacePattern*>(&aPattern);
@@ -2239,11 +2351,32 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
RefPtr<ID2D1Bitmap> bitmap;
Matrix mat = pat->mMatrix;
+
+ RefPtr<SourceSurface> source = pat->mSurface;
+
+ if (!pat->mSamplingRect.IsEmpty() &&
+ (source->GetType() == SurfaceType::D2D1_BITMAP ||
+ source->GetType() == SurfaceType::D2D1_DRAWTARGET)) {
+ IntRect samplingRect = pat->mSamplingRect;
+
+ RefPtr<DrawTargetD2D> dt = new DrawTargetD2D();
+ if (!dt->Init(samplingRect.Size(),
+ source->GetFormat())) {
+ // FIXME: Uncomment assertion, bug 1068195
+ // MOZ_ASSERT(false, "Invalid sampling rect size!");
+ return nullptr;
+ }
+
+ dt->CopySurface(source, samplingRect, IntPoint());
+ source = dt->Snapshot();
+
+ mat.PreTranslate(samplingRect.x, samplingRect.y);
+ }
- switch (pat->mSurface->GetType()) {
- case SURFACE_D2D1_BITMAP:
+ switch (source->GetType()) {
+ case SurfaceType::D2D1_BITMAP:
{
- SourceSurfaceD2D *surf = static_cast<SourceSurfaceD2D*>(pat->mSurface.get());
+ SourceSurfaceD2D *surf = static_cast<SourceSurfaceD2D*>(source.get());
bitmap = surf->mBitmap;
@@ -2252,25 +2385,32 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
}
}
break;
- case SURFACE_D2D1_DRAWTARGET:
+ case SurfaceType::D2D1_DRAWTARGET:
{
SourceSurfaceD2DTarget *surf =
- static_cast<SourceSurfaceD2DTarget*>(pat->mSurface.get());
+ static_cast<SourceSurfaceD2DTarget*>(source.get());
bitmap = surf->GetBitmap(mRT);
AddDependencyOnSource(surf);
}
break;
default:
{
- RefPtr<DataSourceSurface> dataSurf = pat->mSurface->GetDataSurface();
+ RefPtr<DataSourceSurface> dataSurf = source->GetDataSurface();
if (!dataSurf) {
gfxWarning() << "Invalid surface type.";
return nullptr;
}
- bitmap = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, pat->mExtendMode, mat, mRT);
+ IntRect sourceRect = pat->mSamplingRect;
+ if (sourceRect.IsEmpty()) {
+ sourceRect = IntRect(0, 0, source->GetSize().width, source->GetSize().height);
+ }
+
+ bitmap = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, pat->mExtendMode, mat, mRT, &sourceRect);
if (!bitmap) {
- return nullptr;
+ RefPtr<ID2D1SolidColorBrush> colBrush;
+ mRT->CreateSolidColorBrush(D2D1::ColorF(0, 0), byRef(colBrush));
+ return colBrush.forget();
}
}
break;
@@ -2283,7 +2423,7 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
byRef(bmBrush));
- return bmBrush;
+ return bmBrush.forget();
}
gfxWarning() << "Invalid pattern type detected.";
@@ -2359,7 +2499,7 @@ DrawTargetD2D::CreateGradientTexture(const GradientStopsD2D *aStops)
RefPtr<ID3D10Texture2D> tex;
mDevice->CreateTexture2D(&desc, &data, byRef(tex));
- return tex;
+ return tex.forget();
}
TemporaryRef<ID3D10Texture2D>
@@ -2424,7 +2564,7 @@ DrawTargetD2D::CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, cons
return nullptr;
}
- return tex;
+ return tex.forget();
}
void
@@ -2547,6 +2687,7 @@ DrawTargetD2D::factory()
#else
options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
#endif
+ //options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
__uuidof(ID2D1Factory),
@@ -2557,6 +2698,12 @@ DrawTargetD2D::factory()
gfxWarning() << "Failed to create Direct2D factory.";
}
+ RefPtr<ID2D1Factory1> factoryD2D1;
+ hr = mFactory->QueryInterface((ID2D1Factory1**)byRef(factoryD2D1));
+ if (SUCCEEDED(hr)) {
+ ExtendInputEffectD2D1::Register(factoryD2D1);
+ }
+
return mFactory;
}
@@ -2564,6 +2711,12 @@ void
DrawTargetD2D::CleanupD2D()
{
if (mFactory) {
+ RefPtr<ID2D1Factory1> factoryD2D1;
+ HRESULT hr = mFactory->QueryInterface((ID2D1Factory1**)byRef(factoryD2D1));
+ if (SUCCEEDED(hr)) {
+ ExtendInputEffectD2D1::Unregister(factoryD2D1);
+ }
+
mFactory->Release();
mFactory = nullptr;
}
@@ -2576,9 +2729,9 @@ DrawTargetD2D::GetDWriteFactory()
return mDWriteFactory;
}
- DWriteCreateFactoryFunc createDWriteFactory;
+ decltype(DWriteCreateFactory)* createDWriteFactory;
HMODULE dwriteModule = LoadLibraryW(L"dwrite.dll");
- createDWriteFactory = (DWriteCreateFactoryFunc)
+ createDWriteFactory = (decltype(DWriteCreateFactory)*)
GetProcAddress(dwriteModule, "DWriteCreateFactory");
if (!createDWriteFactory) {
diff --git a/gfx/2d/DrawTargetD2D.h b/gfx/2d/DrawTargetD2D.h
index 0df48621d..5eea0a132 100644
--- a/gfx/2d/DrawTargetD2D.h
+++ b/gfx/2d/DrawTargetD2D.h
@@ -14,11 +14,7 @@
#include <vector>
#include <sstream>
-#ifdef _MSC_VER
-#include <hash_set>
-#else
#include <unordered_set>
-#endif
struct IDWriteFactory;
@@ -37,16 +33,18 @@ struct PrivateD3D10DataD2D
RefPtr<ID3D10Effect> mEffect;
RefPtr<ID3D10InputLayout> mInputLayout;
RefPtr<ID3D10Buffer> mVB;
- RefPtr<ID3D10BlendState> mBlendStates[OP_COUNT];
+ RefPtr<ID3D10BlendState> mBlendStates[size_t(CompositionOp::OP_COUNT)];
};
class DrawTargetD2D : public DrawTarget
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetD2D)
DrawTargetD2D();
virtual ~DrawTargetD2D();
- virtual BackendType GetType() const { return BACKEND_DIRECT2D; }
+ virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; }
+ virtual BackendType GetBackendType() const { return BackendType::DIRECT2D; }
virtual TemporaryRef<SourceSurface> Snapshot();
virtual IntSize GetSize() { return mSize; }
@@ -56,6 +54,10 @@ public:
const Rect &aSource,
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
const DrawOptions &aOptions = DrawOptions());
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions());
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
const Point &aDest,
const Color &aColor,
@@ -116,12 +118,16 @@ public:
virtual TemporaryRef<DrawTarget>
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
- virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const;
+ virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const;
virtual TemporaryRef<GradientStops>
CreateGradientStops(GradientStop *aStops,
uint32_t aNumStops,
- ExtendMode aExtendMode = EXTEND_CLAMP) const;
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const;
+
+ virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType);
+
+ virtual bool SupportsRegionClipping() const { return false; }
virtual void *GetNativeSurface(NativeSurfaceType aType);
@@ -132,9 +138,16 @@ public:
TemporaryRef<ID2D1Layer> GetCachedLayer();
void PopCachedLayer(ID2D1RenderTarget *aRT);
+ TemporaryRef<ID2D1Image> GetImageForSurface(SourceSurface *aSurface);
+
static ID2D1Factory *factory();
static void CleanupD2D();
static IDWriteFactory *GetDWriteFactory();
+ ID2D1RenderTarget *GetRT() { return mRT; }
+
+ static uint32_t GetMaxSurfaceSize() {
+ return D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ }
operator std::string() const {
std::stringstream stream;
@@ -147,16 +160,12 @@ public:
private:
TemporaryRef<ID2D1Bitmap>
- DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface,
- Rect &aSource);
+ GetBitmapForSurface(SourceSurface *aSurface,
+ Rect &aSource);
friend class AutoSaveRestoreClippedOut;
friend class SourceSurfaceD2DTarget;
-#ifdef _MSC_VER
- typedef stdext::hash_set<DrawTargetD2D*> TargetSet;
-#else
typedef std::unordered_set<DrawTargetD2D*> TargetSet;
-#endif
bool InitD2DRenderTarget();
void PrepareForDrawing(ID2D1RenderTarget *aRT);
@@ -198,6 +207,8 @@ private:
// bounds to correctly reflect the total clip. This is in device space.
TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
+ bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
+
TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
TemporaryRef<ID3D10Texture2D> CreateGradientTexture(const GradientStopsD2D *aStops);
@@ -207,7 +218,7 @@ private:
void SetupStateForRendering();
// Set the scissor rect to a certain IntRects, resets the scissor rect to
- // surface bounds when NULL is specified.
+ // surface bounds when nullptr is specified.
void SetScissorToRect(IntRect *aRect);
void PushD2DLayer(ID2D1RenderTarget *aRT, ID2D1Geometry *aGeometry, ID2D1Layer *aLayer, const D2D1_MATRIX_3X2_F &aTransform);
@@ -242,7 +253,7 @@ private:
RefPtr<ID2D1Layer> mLayer;
D2D1_RECT_F mBounds;
union {
- // If mPath is non-NULL, the mTransform member will be used, otherwise
+ // If mPath is non-nullptr, the mTransform member will be used, otherwise
// the mIsPixelAligned member is valid.
D2D1_MATRIX_3X2_F mTransform;
bool mIsPixelAligned;
diff --git a/gfx/2d/DrawTargetD2D1.cpp b/gfx/2d/DrawTargetD2D1.cpp
new file mode 100644
index 000000000..c949716f0
--- /dev/null
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -0,0 +1,1462 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include <initguid.h>
+#include "DrawTargetD2D1.h"
+#include "DrawTargetD2D.h"
+#include "FilterNodeSoftware.h"
+#include "GradientStopsD2D.h"
+#include "SourceSurfaceD2D1.h"
+#include "SourceSurfaceD2D.h"
+#include "RadialGradientEffectD2D1.h"
+
+#include "HelpersD2D.h"
+#include "FilterNodeD2D1.h"
+#include "Tools.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+uint64_t DrawTargetD2D1::mVRAMUsageDT;
+uint64_t DrawTargetD2D1::mVRAMUsageSS;
+ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr;
+
+ID2D1Factory1 *D2DFactory1()
+{
+ return DrawTargetD2D1::factory();
+}
+
+DrawTargetD2D1::DrawTargetD2D1()
+ : mClipsArePushed(false)
+{
+}
+
+DrawTargetD2D1::~DrawTargetD2D1()
+{
+ PopAllClips();
+
+ if (mSnapshot) {
+ // We may hold the only reference. MarkIndependent will clear mSnapshot;
+ // keep the snapshot object alive so it doesn't get destroyed while
+ // MarkIndependent is running.
+ RefPtr<SourceSurfaceD2D1> deathGrip = mSnapshot;
+ // mSnapshot can be treated as independent of this DrawTarget since we know
+ // this DrawTarget won't change again.
+ deathGrip->MarkIndependent();
+ // mSnapshot will be cleared now.
+ }
+
+ mDC->EndDraw();
+
+ // Targets depending on us can break that dependency, since we're obviously not going to
+ // be modified in the future.
+ for (auto iter = mDependentTargets.begin();
+ iter != mDependentTargets.end(); iter++) {
+ (*iter)->mDependingOnTargets.erase(this);
+ }
+ // Our dependencies on other targets no longer matter.
+ for (TargetSet::iterator iter = mDependingOnTargets.begin();
+ iter != mDependingOnTargets.end(); iter++) {
+ (*iter)->mDependentTargets.erase(this);
+ }
+}
+
+TemporaryRef<SourceSurface>
+DrawTargetD2D1::Snapshot()
+{
+ if (mSnapshot) {
+ return mSnapshot;
+ }
+ PopAllClips();
+
+ mDC->Flush();
+
+ mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this);
+
+ return mSnapshot;
+}
+
+void
+DrawTargetD2D1::Flush()
+{
+ mDC->Flush();
+
+ // We no longer depend on any target.
+ for (TargetSet::iterator iter = mDependingOnTargets.begin();
+ iter != mDependingOnTargets.end(); iter++) {
+ (*iter)->mDependentTargets.erase(this);
+ }
+ mDependingOnTargets.clear();
+}
+
+void
+DrawTargetD2D1::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions)
+{
+ PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
+
+ D2D1_RECT_F samplingBounds;
+
+ if (aSurfOptions.mSamplingBounds == SamplingBounds::BOUNDED) {
+ samplingBounds = D2DRect(aSource);
+ } else {
+ samplingBounds = D2D1::RectF(0, 0, Float(aSurface->GetSize().width), Float(aSurface->GetSize().height));
+ }
+
+ Float xScale = aDest.width / aSource.width;
+ Float yScale = aDest.height / aSource.height;
+
+ RefPtr<ID2D1ImageBrush> brush;
+
+ // Here we scale the source pattern up to the size and position where we want
+ // it to be.
+ Matrix transform;
+ transform.PreTranslate(aDest.x - aSource.x * xScale, aDest.y - aSource.y * yScale);
+ transform.PreScale(xScale, yScale);
+
+ RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, transform, ExtendMode::CLAMP);
+
+ if (!image) {
+ gfxWarning() << *this << ": Unable to get D2D image for surface.";
+ return;
+ }
+
+ RefPtr<ID2D1Bitmap> bitmap;
+ if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
+ // If this is called with a DataSourceSurface it might do a partial upload
+ // that our DrawBitmap call doesn't support.
+ image->QueryInterface((ID2D1Bitmap**)byRef(bitmap));
+ }
+
+ if (bitmap && aSurfOptions.mSamplingBounds == SamplingBounds::UNBOUNDED) {
+ mDC->DrawBitmap(bitmap, D2DRect(aDest), aOptions.mAlpha, D2DFilter(aSurfOptions.mFilter), D2DRect(aSource));
+ } else {
+ // This has issues ignoring the alpha channel on windows 7 with images marked opaque.
+ MOZ_ASSERT(aSurface->GetFormat() != SurfaceFormat::B8G8R8X8);
+ mDC->CreateImageBrush(image,
+ D2D1::ImageBrushProperties(samplingBounds,
+ D2D1_EXTEND_MODE_CLAMP,
+ D2D1_EXTEND_MODE_CLAMP,
+ D2DInterpolationMode(aSurfOptions.mFilter)),
+ D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)),
+ byRef(brush));
+ mDC->FillRectangle(D2DRect(aDest), brush);
+ }
+
+ FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
+}
+
+void
+DrawTargetD2D1::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ if (aNode->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) {
+ gfxWarning() << *this << ": Incompatible filter passed to DrawFilter.";
+ return;
+ }
+
+ PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
+
+ mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
+ FilterNodeD2D1* node = static_cast<FilterNodeD2D1*>(aNode);
+ node->WillDraw(this);
+
+ mDC->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect));
+
+ FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
+}
+
+void
+DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator)
+{
+ MarkChanged();
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ Matrix mat;
+ RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP);
+
+ if (!image) {
+ gfxWarning() << "Couldn't get image for surface.";
+ return;
+ }
+
+ if (!mat.IsIdentity()) {
+ gfxDebug() << *this << ": At this point complex partial uploads are not supported for Shadow surfaces.";
+ return;
+ }
+
+ // Step 1, create the shadow effect.
+ RefPtr<ID2D1Effect> shadowEffect;
+ mDC->CreateEffect(CLSID_D2D1Shadow, byRef(shadowEffect));
+ shadowEffect->SetInput(0, image);
+ shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, aSigma);
+ D2D1_VECTOR_4F color = { aColor.r, aColor.g, aColor.b, aColor.a };
+ shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color);
+
+ D2D1_POINT_2F shadowPoint = D2DPoint(aDest + aOffset);
+ mDC->DrawImage(shadowEffect, &shadowPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
+
+ D2D1_POINT_2F imgPoint = D2DPoint(aDest);
+ mDC->DrawImage(image, &imgPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
+}
+
+void
+DrawTargetD2D1::ClearRect(const Rect &aRect)
+{
+ MarkChanged();
+
+ PopAllClips();
+
+ PushClipRect(aRect);
+
+ if (mTransformDirty ||
+ !mTransform.IsIdentity()) {
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+ }
+
+ D2D1_RECT_F clipRect;
+ bool isPixelAligned;
+ if (mTransform.IsRectilinear() &&
+ GetDeviceSpaceClipRect(clipRect, isPixelAligned)) {
+ mDC->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ mDC->Clear();
+ mDC->PopAxisAlignedClip();
+
+ PopClip();
+ return;
+ }
+
+ mDC->SetTarget(mTempBitmap);
+ mDC->Clear();
+
+ IntRect addClipRect;
+ RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&addClipRect);
+
+ RefPtr<ID2D1SolidColorBrush> brush;
+ mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), byRef(brush));
+ mDC->PushAxisAlignedClip(D2D1::RectF(addClipRect.x, addClipRect.y, addClipRect.XMost(), addClipRect.YMost()), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ mDC->FillGeometry(geom, brush);
+ mDC->PopAxisAlignedClip();
+
+ mDC->SetTarget(mBitmap);
+ mDC->DrawImage(mTempBitmap, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_DESTINATION_OUT);
+
+ PopClip();
+
+ return;
+}
+
+void
+DrawTargetD2D1::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+
+ RefPtr<ID2D1Bitmap> bitmap;
+
+ RefPtr<ID2D1Image> image = GetImageForSurface(aMask, ExtendMode::CLAMP);
+
+ if (!image) {
+ gfxWarning() << "Failed to get image for surface.";
+ return;
+ }
+
+ PrepareForDrawing(aOptions.mCompositionOp, aSource);
+
+ // FillOpacityMask only works if the antialias mode is MODE_ALIASED
+ mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+
+ IntSize size = aMask->GetSize();
+ Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height));
+ image->QueryInterface((ID2D1Bitmap**)&bitmap);
+ if (!bitmap) {
+ gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces.";
+ return;
+ }
+
+ Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height));
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
+ mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect));
+
+ mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+
+ FinalizeDrawing(aOptions.mCompositionOp, aSource);
+}
+
+void
+DrawTargetD2D1::CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination)
+{
+ MarkChanged();
+
+ PopAllClips();
+
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ Matrix mat;
+ RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP);
+
+ if (!image) {
+ gfxWarning() << "Couldn't get image for surface.";
+ return;
+ }
+
+ if (!mat.IsIdentity()) {
+ gfxDebug() << *this << ": At this point complex partial uploads are not supported for CopySurface.";
+ return;
+ }
+
+ if (mFormat == SurfaceFormat::A8) {
+ RefPtr<ID2D1Bitmap> bitmap;
+ image->QueryInterface((ID2D1Bitmap**)byRef(bitmap));
+
+ mDC->PushAxisAlignedClip(D2D1::RectF(aDestination.x, aDestination.y,
+ aDestination.x + aSourceRect.width,
+ aDestination.y + aSourceRect.height),
+ D2D1_ANTIALIAS_MODE_ALIASED);
+ mDC->Clear();
+ mDC->PopAxisAlignedClip();
+
+ RefPtr<ID2D1SolidColorBrush> brush;
+ mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White),
+ D2D1::BrushProperties(), byRef(brush));
+ mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+ mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS);
+ mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ return;
+ }
+
+ mDC->DrawImage(image, D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)),
+ D2D1::RectF(Float(aSourceRect.x), Float(aSourceRect.y),
+ Float(aSourceRect.XMost()), Float(aSourceRect.YMost())),
+ D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
+}
+
+void
+DrawTargetD2D1::FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ PrepareForDrawing(aOptions.mCompositionOp, aPattern);
+
+ mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
+ mDC->FillRectangle(D2DRect(aRect), brush);
+
+ FinalizeDrawing(aOptions.mCompositionOp, aPattern);
+}
+
+void
+DrawTargetD2D1::StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ PrepareForDrawing(aOptions.mCompositionOp, aPattern);
+
+ mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
+ RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
+
+ mDC->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle);
+
+ FinalizeDrawing(aOptions.mCompositionOp, aPattern);
+}
+
+void
+DrawTargetD2D1::StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ PrepareForDrawing(aOptions.mCompositionOp, aPattern);
+
+ mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
+ RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
+
+ mDC->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle);
+
+ FinalizeDrawing(aOptions.mCompositionOp, aPattern);
+}
+
+void
+DrawTargetD2D1::Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
+ gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
+ return;
+ }
+ const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
+
+ PrepareForDrawing(aOptions.mCompositionOp, aPattern);
+
+ mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
+ RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
+
+ mDC->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle);
+
+ FinalizeDrawing(aOptions.mCompositionOp, aPattern);
+}
+
+void
+DrawTargetD2D1::Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
+ gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
+ return;
+ }
+ const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
+
+ PrepareForDrawing(aOptions.mCompositionOp, aPattern);
+
+ mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
+
+ mDC->FillGeometry(d2dPath->mGeometry, brush);
+
+ FinalizeDrawing(aOptions.mCompositionOp, aPattern);
+}
+
+void
+DrawTargetD2D1::FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions,
+ const GlyphRenderingOptions *aRenderingOptions)
+{
+ if (aFont->GetType() != FontType::DWRITE) {
+ gfxDebug() << *this << ": Ignoring drawing call for incompatible font.";
+ return;
+ }
+
+ ScaledFontDWrite *font = static_cast<ScaledFontDWrite*>(aFont);
+
+ IDWriteRenderingParams *params = nullptr;
+ if (aRenderingOptions) {
+ if (aRenderingOptions->GetType() != FontType::DWRITE) {
+ gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions.";
+ // This should never happen.
+ MOZ_ASSERT(false);
+ } else {
+ params = static_cast<const GlyphRenderingOptionsDWrite*>(aRenderingOptions)->mParams;
+ }
+ }
+
+ AntialiasMode aaMode = font->GetDefaultAAMode();
+
+ if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
+ aaMode = aOptions.mAntialiasMode;
+ }
+
+ PrepareForDrawing(aOptions.mCompositionOp, aPattern);
+
+ bool forceClearType = false;
+ if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA &&
+ aOptions.mCompositionOp == CompositionOp::OP_OVER && aaMode == AntialiasMode::SUBPIXEL) {
+ forceClearType = true;
+ }
+
+
+ D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
+
+ switch (aaMode) {
+ case AntialiasMode::NONE:
+ d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
+ break;
+ case AntialiasMode::GRAY:
+ d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
+ break;
+ case AntialiasMode::SUBPIXEL:
+ d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
+ break;
+ default:
+ d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
+ }
+
+ if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE &&
+ mFormat != SurfaceFormat::B8G8R8X8 && !forceClearType) {
+ d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
+ }
+
+ mDC->SetTextAntialiasMode(d2dAAMode);
+
+ if (params != mTextRenderingParams) {
+ mDC->SetTextRenderingParams(params);
+ mTextRenderingParams = params;
+ }
+
+ RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
+
+ AutoDWriteGlyphRun autoRun;
+ DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun);
+
+ if (brush) {
+ mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
+ }
+
+ FinalizeDrawing(aOptions.mCompositionOp, aPattern);
+}
+
+void
+DrawTargetD2D1::Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions)
+{
+ PrepareForDrawing(aOptions.mCompositionOp, aSource);
+
+ RefPtr<ID2D1Brush> source = CreateBrushForPattern(aSource, aOptions.mAlpha);
+ RefPtr<ID2D1Brush> mask = CreateBrushForPattern(aMask, 1.0f);
+ mDC->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr,
+ D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+ D2D1::IdentityMatrix(),
+ 1.0f, mask),
+ nullptr);
+
+ Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height);
+ Matrix mat = mTransform;
+ mat.Invert();
+
+ mDC->FillRectangle(D2DRect(mat.TransformBounds(rect)), source);
+
+ mDC->PopLayer();
+
+ FinalizeDrawing(aOptions.mCompositionOp, aSource);
+}
+
+void
+DrawTargetD2D1::PushClip(const Path *aPath)
+{
+ if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
+ gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
+ return;
+ }
+
+ mCurrentClippedGeometry = nullptr;
+
+ RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
+
+ PushedClip clip;
+ clip.mTransform = D2DMatrix(mTransform);
+ clip.mPath = pathD2D;
+
+ pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds);
+
+ mPushedClips.push_back(clip);
+
+ // The transform of clips is relative to the world matrix, since we use the total
+ // transform for the clips, make the world matrix identity.
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ if (mClipsArePushed) {
+ PushD2DLayer(mDC, pathD2D->mGeometry, clip.mTransform);
+ }
+}
+
+void
+DrawTargetD2D1::PushClipRect(const Rect &aRect)
+{
+ if (!mTransform.IsRectilinear()) {
+ // Whoops, this isn't a rectangle in device space, Direct2D will not deal
+ // with this transform the way we want it to.
+ // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx
+
+ RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
+ pathBuilder->MoveTo(aRect.TopLeft());
+ pathBuilder->LineTo(aRect.TopRight());
+ pathBuilder->LineTo(aRect.BottomRight());
+ pathBuilder->LineTo(aRect.BottomLeft());
+ pathBuilder->Close();
+ RefPtr<Path> path = pathBuilder->Finish();
+ return PushClip(path);
+ }
+
+ mCurrentClippedGeometry = nullptr;
+
+ PushedClip clip;
+ Rect rect = mTransform.TransformBounds(aRect);
+ IntRect intRect;
+ clip.mIsPixelAligned = rect.ToIntRect(&intRect);
+
+ // Do not store the transform, just store the device space rectangle directly.
+ clip.mBounds = D2DRect(rect);
+
+ mPushedClips.push_back(clip);
+
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ if (mClipsArePushed) {
+ mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ }
+}
+
+void
+DrawTargetD2D1::PopClip()
+{
+ mCurrentClippedGeometry = nullptr;
+
+ if (mClipsArePushed) {
+ if (mPushedClips.back().mPath) {
+ mDC->PopLayer();
+ } else {
+ mDC->PopAxisAlignedClip();
+ }
+ }
+ mPushedClips.pop_back();
+}
+
+TemporaryRef<SourceSurface>
+DrawTargetD2D1::CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+{
+ RefPtr<ID2D1Bitmap1> bitmap;
+
+ HRESULT hr = mDC->CreateBitmap(D2DIntSize(aSize), aData, aStride,
+ D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(aFormat)),
+ byRef(bitmap));
+
+ if (FAILED(hr) || !bitmap) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D2D1.1] 1CreateBitmap failure " << aSize << " Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ return new SourceSurfaceD2D1(bitmap.get(), mDC, aFormat, aSize);
+}
+
+TemporaryRef<DrawTarget>
+DrawTargetD2D1::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+ RefPtr<DrawTargetD2D1> dt = new DrawTargetD2D1();
+
+ if (!dt->Init(aSize, aFormat)) {
+ return nullptr;
+ }
+
+ return dt.forget();
+}
+
+TemporaryRef<PathBuilder>
+DrawTargetD2D1::CreatePathBuilder(FillRule aFillRule) const
+{
+ RefPtr<ID2D1PathGeometry> path;
+ HRESULT hr = factory()->CreatePathGeometry(byRef(path));
+
+ if (FAILED(hr)) {
+ gfxWarning() << *this << ": Failed to create Direct2D Path Geometry. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ RefPtr<ID2D1GeometrySink> sink;
+ hr = path->Open(byRef(sink));
+ if (FAILED(hr)) {
+ gfxWarning() << *this << ": Failed to access Direct2D Path Geometry. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ if (aFillRule == FillRule::FILL_WINDING) {
+ sink->SetFillMode(D2D1_FILL_MODE_WINDING);
+ }
+
+ return new PathBuilderD2D(sink, path, aFillRule, BackendType::DIRECT2D1_1);
+}
+
+TemporaryRef<GradientStops>
+DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const
+{
+ if (aNumStops == 0) {
+ gfxWarning() << *this << ": Failed to create GradientStopCollection with no stops.";
+ return nullptr;
+ }
+
+ D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops];
+
+ for (uint32_t i = 0; i < aNumStops; i++) {
+ stops[i].position = rawStops[i].offset;
+ stops[i].color = D2DColor(rawStops[i].color);
+ }
+
+ RefPtr<ID2D1GradientStopCollection> stopCollection;
+
+ HRESULT hr =
+ mDC->CreateGradientStopCollection(stops, aNumStops,
+ D2D1_GAMMA_2_2, D2DExtend(aExtendMode),
+ byRef(stopCollection));
+ delete [] stops;
+
+ if (FAILED(hr)) {
+ gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ return new GradientStopsD2D(stopCollection, Factory::GetDirect3D11Device());
+}
+
+TemporaryRef<FilterNode>
+DrawTargetD2D1::CreateFilter(FilterType aType)
+{
+ return FilterNodeD2D1::Create(mDC, aType);
+}
+
+bool
+DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat)
+{
+ HRESULT hr;
+
+ hr = Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, byRef(mDC));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() <<"[D2D1.1] 1Failed to create a DeviceContext, code: " << hexa(hr);
+ return false;
+ }
+
+ RefPtr<IDXGISurface> dxgiSurface;
+ aTexture->QueryInterface(__uuidof(IDXGISurface),
+ (void**)((IDXGISurface**)byRef(dxgiSurface)));
+ if (!dxgiSurface) {
+ gfxCriticalError() <<"[D2D1.1] Failed to obtain a DXGI surface.";
+ return false;
+ }
+
+ D2D1_BITMAP_PROPERTIES1 props;
+ props.dpiX = 96;
+ props.dpiY = 96;
+ props.pixelFormat = D2DPixelFormat(aFormat);
+ props.colorContext = nullptr;
+ props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
+ hr = mDC->CreateBitmapFromDxgiSurface(dxgiSurface, props, (ID2D1Bitmap1**)byRef(mBitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1.1] CreateBitmapFromDxgiSurface failure Code: " << hexa(hr);
+ return false;
+ }
+
+ mFormat = aFormat;
+ D3D11_TEXTURE2D_DESC desc;
+ aTexture->GetDesc(&desc);
+ desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ mSize.width = desc.Width;
+ mSize.height = desc.Height;
+ props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
+ props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
+
+ hr = mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mTempBitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 2CreateBitmap failure " << mSize << " Code: " << hexa(hr);
+ return false;
+ }
+
+ mDC->SetTarget(mBitmap);
+
+ mDC->BeginDraw();
+ return true;
+}
+
+bool
+DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
+{
+ HRESULT hr;
+
+ hr = Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, byRef(mDC));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() <<"[D2D1.1] 2Failed to create a DeviceContext, code: " << hexa(hr);
+ return false;
+ }
+
+ if (mDC->GetMaximumBitmapSize() < UINT32(aSize.width) ||
+ mDC->GetMaximumBitmapSize() < UINT32(aSize.height)) {
+ // This is 'ok', so don't assert
+ gfxCriticalError(CriticalLog::DefaultOptions(false)) << "[D2D1.1] Attempt to use unsupported surface size " << aSize;
+ return false;
+ }
+
+ D2D1_BITMAP_PROPERTIES1 props;
+ props.dpiX = 96;
+ props.dpiY = 96;
+ props.pixelFormat = D2DPixelFormat(aFormat);
+ props.colorContext = nullptr;
+ props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
+ hr = mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mBitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1.1] 3CreateBitmap failure " << aSize << " Code: " << hexa(hr);
+ return false;
+ }
+
+ props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
+ props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
+
+ hr = mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mTempBitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1.1] failed to create new TempBitmap " << aSize << " Code: " << hexa(hr);
+ return false;
+ }
+
+ mDC->SetTarget(mBitmap);
+
+ mDC->BeginDraw();
+
+ mDC->Clear();
+
+ mFormat = aFormat;
+ mSize = aSize;
+
+ return true;
+}
+
+/**
+ * Private helpers.
+ */
+uint32_t
+DrawTargetD2D1::GetByteSize() const
+{
+ return mSize.width * mSize.height * BytesPerPixel(mFormat);
+}
+
+ID2D1Factory1*
+DrawTargetD2D1::factory()
+{
+ if (mFactory) {
+ return mFactory;
+ }
+
+ ID2D1Factory* d2dFactory = D2DFactory();
+ if (!d2dFactory) {
+ return nullptr;
+ }
+
+ HRESULT hr = d2dFactory->QueryInterface((ID2D1Factory1**)&mFactory);
+
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ RadialGradientEffectD2D1::Register(mFactory);
+
+ return mFactory;
+}
+
+void
+DrawTargetD2D1::CleanupD2D()
+{
+ if (mFactory) {
+ RadialGradientEffectD2D1::Unregister(mFactory);
+ mFactory->Release();
+ mFactory = nullptr;
+ }
+}
+
+void
+DrawTargetD2D1::MarkChanged()
+{
+ if (mSnapshot) {
+ if (mSnapshot->hasOneRef()) {
+ // Just destroy it, since no-one else knows about it.
+ mSnapshot = nullptr;
+ } else {
+ mSnapshot->DrawTargetWillChange();
+ // The snapshot will no longer depend on this target.
+ MOZ_ASSERT(!mSnapshot);
+ }
+ }
+ if (mDependentTargets.size()) {
+ // Copy mDependentTargets since the Flush()es below will modify it.
+ TargetSet tmpTargets = mDependentTargets;
+ for (TargetSet::iterator iter = tmpTargets.begin();
+ iter != tmpTargets.end(); iter++) {
+ (*iter)->Flush();
+ }
+ // The Flush() should have broken all dependencies on this target.
+ MOZ_ASSERT(!mDependentTargets.size());
+ }
+}
+
+void
+DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern)
+{
+ MarkChanged();
+
+ if (aOp == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) {
+ // It's important to do this before FlushTransformToDC! As this will cause
+ // the transform to become dirty.
+ PushAllClips();
+
+ FlushTransformToDC();
+ return;
+ }
+
+ PopAllClips();
+
+ mDC->SetTarget(mTempBitmap);
+ mDC->Clear(D2D1::ColorF(0, 0));
+
+ PushAllClips();
+ FlushTransformToDC();
+}
+
+void
+DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
+{
+ bool patternSupported = IsPatternSupportedByD2D(aPattern);
+
+ if (aOp == CompositionOp::OP_OVER && patternSupported) {
+ return;
+ }
+
+ PopAllClips();
+
+ RefPtr<ID2D1Image> image;
+ mDC->GetTarget(byRef(image));
+
+ mDC->SetTarget(mBitmap);
+
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ if (patternSupported) {
+ if (D2DSupportsCompositeMode(aOp)) {
+ D2D1_RECT_F rect;
+ bool isAligned;
+ RefPtr<ID2D1Bitmap> tmpBitmap;
+ bool clipIsComplex = mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+
+ if (clipIsComplex) {
+ if (!IsOperatorBoundByMask(aOp)) {
+ HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), byRef(tmpBitmap));
+ if (FAILED(hr)) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr);
+ // For now, crash in this scenario; this should happen because tmpBitmap is
+ // null and CopyFromBitmap call below dereferences it.
+ // return;
+ }
+ mDC->Flush();
+
+ tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
+ }
+ } else {
+ PushAllClips();
+ }
+ mDC->DrawImage(image, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
+
+ if (tmpBitmap) {
+ RefPtr<ID2D1BitmapBrush> brush;
+ RefPtr<ID2D1Geometry> inverseGeom = GetInverseClippedGeometry();
+ mDC->CreateBitmapBrush(tmpBitmap, byRef(brush));
+
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
+ mDC->FillGeometry(inverseGeom, brush);
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
+ }
+ return;
+ }
+
+ if (!mBlendEffect) {
+ mDC->CreateEffect(CLSID_D2D1Blend, byRef(mBlendEffect));
+
+ if (!mBlendEffect) {
+ gfxWarning() << "Failed to create blend effect!";
+ return;
+ }
+ }
+
+ RefPtr<ID2D1Bitmap> tmpBitmap;
+ mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), byRef(tmpBitmap));
+
+ // This flush is important since the copy method will not know about the context drawing to the surface.
+ // We also need to pop all the clips to make sure any drawn content will have made it to the final bitmap.
+ mDC->Flush();
+
+ // We need to use a copy here because affects don't accept a surface on
+ // both their in- and outputs.
+ tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
+
+ mBlendEffect->SetInput(0, tmpBitmap);
+ mBlendEffect->SetInput(1, mTempBitmap);
+ mBlendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
+
+ PushAllClips();
+
+ mDC->DrawImage(mBlendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
+ return;
+ }
+
+ const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern);
+ if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) {
+ // Draw nothing!
+ return;
+ }
+
+ PushAllClips();
+
+ RefPtr<ID2D1Effect> radialGradientEffect;
+
+ mDC->CreateEffect(CLSID_RadialGradientEffect, byRef(radialGradientEffect));
+
+ radialGradientEffect->SetValue(RADIAL_PROP_STOP_COLLECTION,
+ static_cast<const GradientStopsD2D*>(pat->mStops.get())->mStopCollection);
+ radialGradientEffect->SetValue(RADIAL_PROP_CENTER_1, D2D1::Vector2F(pat->mCenter1.x, pat->mCenter1.y));
+ radialGradientEffect->SetValue(RADIAL_PROP_CENTER_2, D2D1::Vector2F(pat->mCenter2.x, pat->mCenter2.y));
+ radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_1, pat->mRadius1);
+ radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2);
+ radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2);
+ radialGradientEffect->SetValue(RADIAL_PROP_TRANSFORM, D2DMatrix(pat->mMatrix * mTransform));
+ radialGradientEffect->SetInput(0, image);
+
+ mDC->DrawImage(radialGradientEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
+}
+
+void
+DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* aSource)
+{
+ if (aSource->mDrawTarget && !mDependingOnTargets.count(aSource->mDrawTarget)) {
+ aSource->mDrawTarget->mDependentTargets.insert(this);
+ mDependingOnTargets.insert(aSource->mDrawTarget);
+ }
+}
+
+static D2D1_RECT_F
+IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2)
+{
+ D2D1_RECT_F result;
+ result.left = max(aRect1.left, aRect2.left);
+ result.top = max(aRect1.top, aRect2.top);
+ result.right = min(aRect1.right, aRect2.right);
+ result.bottom = min(aRect1.bottom, aRect2.bottom);
+
+ result.right = max(result.right, result.left);
+ result.bottom = max(result.bottom, result.top);
+
+ return result;
+}
+
+bool
+DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned)
+{
+ if (!mPushedClips.size()) {
+ return false;
+ }
+
+ aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height);
+ for (auto iter = mPushedClips.begin();iter != mPushedClips.end(); iter++) {
+ if (iter->mPath) {
+ return false;
+ }
+ aClipRect = IntersectRect(aClipRect, iter->mBounds);
+ if (!iter->mIsPixelAligned) {
+ aIsPixelAligned = false;
+ }
+ }
+ return true;
+}
+
+TemporaryRef<ID2D1Geometry>
+DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
+{
+ if (mCurrentClippedGeometry) {
+ *aClipBounds = mCurrentClipBounds;
+ return mCurrentClippedGeometry;
+ }
+
+ MOZ_ASSERT(mPushedClips.size());
+
+ mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize);
+
+ // if pathGeom is null then pathRect represents the path.
+ RefPtr<ID2D1Geometry> pathGeom;
+ D2D1_RECT_F pathRect;
+ bool pathRectIsAxisAligned = false;
+ auto iter = mPushedClips.begin();
+
+ if (iter->mPath) {
+ pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
+ } else {
+ pathRect = iter->mBounds;
+ pathRectIsAxisAligned = iter->mIsPixelAligned;
+ }
+
+ iter++;
+ for (;iter != mPushedClips.end(); iter++) {
+ // Do nothing but add it to the current clip bounds.
+ if (!iter->mPath && iter->mIsPixelAligned) {
+ mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
+ IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top),
+ int32_t(iter->mBounds.right - iter->mBounds.left),
+ int32_t(iter->mBounds.bottom - iter->mBounds.top)));
+ continue;
+ }
+
+ if (!pathGeom) {
+ if (pathRectIsAxisAligned) {
+ mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
+ IntRect(int32_t(pathRect.left), int32_t(pathRect.top),
+ int32_t(pathRect.right - pathRect.left),
+ int32_t(pathRect.bottom - pathRect.top)));
+ }
+ if (iter->mPath) {
+ // See if pathRect needs to go into the path geometry.
+ if (!pathRectIsAxisAligned) {
+ pathGeom = ConvertRectToGeometry(pathRect);
+ } else {
+ pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
+ }
+ } else {
+ pathRect = IntersectRect(pathRect, iter->mBounds);
+ pathRectIsAxisAligned = false;
+ continue;
+ }
+ }
+
+ RefPtr<ID2D1PathGeometry> newGeom;
+ factory()->CreatePathGeometry(byRef(newGeom));
+
+ RefPtr<ID2D1GeometrySink> currentSink;
+ newGeom->Open(byRef(currentSink));
+
+ if (iter->mPath) {
+ pathGeom->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT,
+ iter->mTransform, currentSink);
+ } else {
+ RefPtr<ID2D1Geometry> rectGeom = ConvertRectToGeometry(iter->mBounds);
+ pathGeom->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT,
+ D2D1::IdentityMatrix(), currentSink);
+ }
+
+ currentSink->Close();
+
+ pathGeom = newGeom.forget();
+ }
+
+ // For now we need mCurrentClippedGeometry to always be non-nullptr. This
+ // method might seem a little strange but it is just fine, if pathGeom is
+ // nullptr pathRect will always still contain 1 clip unaccounted for
+ // regardless of mCurrentClipBounds.
+ if (!pathGeom) {
+ pathGeom = ConvertRectToGeometry(pathRect);
+ }
+ mCurrentClippedGeometry = pathGeom.forget();
+ *aClipBounds = mCurrentClipBounds;
+ return mCurrentClippedGeometry;
+}
+
+TemporaryRef<ID2D1Geometry>
+DrawTargetD2D1::GetInverseClippedGeometry()
+{
+ IntRect bounds;
+ RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&bounds);
+ RefPtr<ID2D1RectangleGeometry> rectGeom;
+ RefPtr<ID2D1PathGeometry> inverseGeom;
+
+ factory()->CreateRectangleGeometry(D2D1::RectF(0, 0, mSize.width, mSize.height), byRef(rectGeom));
+ factory()->CreatePathGeometry(byRef(inverseGeom));
+ RefPtr<ID2D1GeometrySink> sink;
+ inverseGeom->Open(byRef(sink));
+ rectGeom->CombineWithGeometry(geom, D2D1_COMBINE_MODE_EXCLUDE, D2D1::IdentityMatrix(), sink);
+ sink->Close();
+
+ return inverseGeom;
+}
+
+void
+DrawTargetD2D1::PopAllClips()
+{
+ if (mClipsArePushed) {
+ PopClipsFromDC(mDC);
+
+ mClipsArePushed = false;
+ }
+}
+
+void
+DrawTargetD2D1::PushAllClips()
+{
+ if (!mClipsArePushed) {
+ PushClipsToDC(mDC);
+
+ mClipsArePushed = true;
+ }
+}
+
+void
+DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC)
+{
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ for (std::vector<PushedClip>::iterator iter = mPushedClips.begin();
+ iter != mPushedClips.end(); iter++) {
+ if (iter->mPath) {
+ PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform);
+ } else {
+ mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ }
+ }
+}
+
+void
+DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC)
+{
+ for (int i = mPushedClips.size() - 1; i >= 0; i--) {
+ if (mPushedClips[i].mPath) {
+ aDC->PopLayer();
+ } else {
+ aDC->PopAxisAlignedClip();
+ }
+ }
+}
+
+TemporaryRef<ID2D1Brush>
+DrawTargetD2D1::CreateTransparentBlackBrush()
+{
+ RefPtr<ID2D1SolidColorBrush> brush;
+ mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0), byRef(brush));
+ return brush;
+}
+
+TemporaryRef<ID2D1Brush>
+DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
+{
+ if (!IsPatternSupportedByD2D(aPattern)) {
+ RefPtr<ID2D1SolidColorBrush> colBrush;
+ mDC->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), byRef(colBrush));
+ return colBrush.forget();
+ }
+
+ if (aPattern.GetType() == PatternType::COLOR) {
+ RefPtr<ID2D1SolidColorBrush> colBrush;
+ Color color = static_cast<const ColorPattern*>(&aPattern)->mColor;
+ mDC->CreateSolidColorBrush(D2D1::ColorF(color.r, color.g,
+ color.b, color.a),
+ D2D1::BrushProperties(aAlpha),
+ byRef(colBrush));
+ return colBrush.forget();
+ }
+ if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
+ RefPtr<ID2D1LinearGradientBrush> gradBrush;
+ const LinearGradientPattern *pat =
+ static_cast<const LinearGradientPattern*>(&aPattern);
+
+ GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
+
+ if (!stops) {
+ gfxDebug() << "No stops specified for gradient pattern.";
+ return CreateTransparentBlackBrush();
+ }
+
+ if (pat->mBegin == pat->mEnd) {
+ RefPtr<ID2D1SolidColorBrush> colBrush;
+ uint32_t stopCount = stops->mStopCollection->GetGradientStopCount();
+ vector<D2D1_GRADIENT_STOP> d2dStops(stopCount);
+ stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount);
+ mDC->CreateSolidColorBrush(d2dStops.back().color,
+ D2D1::BrushProperties(aAlpha),
+ byRef(colBrush));
+ return colBrush.forget();
+ }
+
+ mDC->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin),
+ D2DPoint(pat->mEnd)),
+ D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
+ stops->mStopCollection,
+ byRef(gradBrush));
+
+ if (!gradBrush) {
+ gfxWarning() << "Couldn't create gradient brush.";
+ return CreateTransparentBlackBrush();
+ }
+
+ return gradBrush.forget();
+ }
+ if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
+ RefPtr<ID2D1RadialGradientBrush> gradBrush;
+ const RadialGradientPattern *pat =
+ static_cast<const RadialGradientPattern*>(&aPattern);
+
+ GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
+
+ if (!stops) {
+ gfxDebug() << "No stops specified for gradient pattern.";
+ return CreateTransparentBlackBrush();
+ }
+
+ // This will not be a complex radial gradient brush.
+ mDC->CreateRadialGradientBrush(
+ D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter2),
+ D2DPoint(pat->mCenter1 - pat->mCenter2),
+ pat->mRadius2, pat->mRadius2),
+ D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
+ stops->mStopCollection,
+ byRef(gradBrush));
+
+ if (!gradBrush) {
+ gfxWarning() << "Couldn't create gradient brush.";
+ return CreateTransparentBlackBrush();
+ }
+
+ return gradBrush.forget();
+ }
+ if (aPattern.GetType() == PatternType::SURFACE) {
+ const SurfacePattern *pat =
+ static_cast<const SurfacePattern*>(&aPattern);
+
+ if (!pat->mSurface) {
+ gfxDebug() << "No source surface specified for surface pattern";
+ return CreateTransparentBlackBrush();
+ }
+
+ D2D1_RECT_F samplingBounds;
+ Matrix mat = pat->mMatrix;
+
+ bool useSamplingRect = false;
+ if (!pat->mSamplingRect.IsEmpty() &&
+ (pat->mSurface->GetType() == SurfaceType::D2D1_1_IMAGE)) {
+ samplingBounds = D2DRect(pat->mSamplingRect);
+ mat.PreTranslate(pat->mSamplingRect.x, pat->mSamplingRect.y);
+ } else if (!pat->mSamplingRect.IsEmpty()) {
+ // We will do a partial upload of the sampling restricted area from GetImageForSurface.
+ samplingBounds = D2D1::RectF(0, 0, pat->mSamplingRect.width, pat->mSamplingRect.height);
+ } else {
+ samplingBounds = D2D1::RectF(0, 0,
+ Float(pat->mSurface->GetSize().width),
+ Float(pat->mSurface->GetSize().height));
+ }
+
+ MOZ_ASSERT(pat->mSurface->IsValid());
+
+ RefPtr<ID2D1ImageBrush> imageBrush;
+ RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode, !pat->mSamplingRect.IsEmpty() ? &pat->mSamplingRect : nullptr);
+
+ if (!image) {
+ return CreateTransparentBlackBrush();
+ }
+
+ mDC->CreateImageBrush(image,
+ D2D1::ImageBrushProperties(samplingBounds,
+ D2DExtend(pat->mExtendMode),
+ D2DExtend(pat->mExtendMode),
+ D2DInterpolationMode(pat->mFilter)),
+ D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
+ byRef(imageBrush));
+ return imageBrush.forget();
+ }
+
+ gfxWarning() << "Invalid pattern type detected.";
+ return CreateTransparentBlackBrush();
+}
+
+TemporaryRef<ID2D1Image>
+DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
+ ExtendMode aExtendMode, const IntRect* aSourceRect)
+{
+ RefPtr<ID2D1Image> image;
+
+ switch (aSurface->GetType()) {
+ case SurfaceType::D2D1_1_IMAGE:
+ {
+ SourceSurfaceD2D1 *surf = static_cast<SourceSurfaceD2D1*>(aSurface);
+ image = surf->GetImage();
+ AddDependencyOnSource(surf);
+ }
+ break;
+ default:
+ {
+ RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
+ if (!dataSurf) {
+ gfxWarning() << "Invalid surface type.";
+ return nullptr;
+ }
+ return CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, aExtendMode,
+ aSourceTransform, mDC, aSourceRect);
+ }
+ break;
+ }
+
+ return image.forget();
+}
+
+TemporaryRef<SourceSurface>
+DrawTargetD2D1::OptimizeSourceSurface(SourceSurface* aSurface) const
+{
+ if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
+ return aSurface;
+ }
+
+ RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
+
+ DataSourceSurface::MappedSurface map;
+ if (!data->Map(DataSourceSurface::MapType::READ, &map)) {
+ return nullptr;
+ }
+
+ RefPtr<ID2D1Bitmap1> bitmap;
+ HRESULT hr = mDC->CreateBitmap(D2DIntSize(data->GetSize()), map.mData, map.mStride,
+ D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(data->GetFormat())),
+ byRef(bitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(data->GetSize()))) << "[D2D1.1] 4CreateBitmap failure " << data->GetSize() << " Code: " << hexa(hr);
+ }
+
+ data->Unmap();
+
+ if (!bitmap) {
+ return data.forget();
+ }
+
+ return new SourceSurfaceD2D1(bitmap.get(), mDC, data->GetFormat(), data->GetSize());
+}
+
+void
+DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform)
+{
+ D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
+
+ if (aDC->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) {
+ options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
+ }
+
+ mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry,
+ D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform,
+ 1.0, nullptr, options), nullptr);
+}
+
+}
+}
diff --git a/gfx/2d/DrawTargetD2D1.h b/gfx/2d/DrawTargetD2D1.h
new file mode 100644
index 000000000..f769e0a2b
--- /dev/null
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_DRAWTARGETD2D1_H_
+#define MOZILLA_GFX_DRAWTARGETD2D1_H_
+
+#include "2D.h"
+#include <d3d11.h>
+#include <d2d1_1.h>
+#include "PathD2D.h"
+#include "HelpersD2D.h"
+
+#include <vector>
+#include <sstream>
+
+#include <unordered_set>
+
+struct IDWriteFactory;
+
+namespace mozilla {
+namespace gfx {
+
+class SourceSurfaceD2D1;
+class GradientStopsD2D;
+class ScaledFontDWrite;
+
+const int32_t kLayerCacheSize1 = 5;
+
+class DrawTargetD2D1 : public DrawTarget
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetD2D1)
+ DrawTargetD2D1();
+ virtual ~DrawTargetD2D1();
+
+ virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; }
+ virtual BackendType GetBackendType() const { return BackendType::DIRECT2D1_1; }
+ virtual TemporaryRef<SourceSurface> Snapshot();
+ virtual IntSize GetSize() { return mSize; }
+
+ virtual void Flush();
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions);
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator);
+ virtual void ClearRect(const Rect &aRect);
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions());
+
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination);
+
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions(),
+ const GlyphRenderingOptions *aRenderingOptions = nullptr);
+ virtual void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void PushClip(const Path *aPath);
+ virtual void PushClipRect(const Rect &aRect);
+ virtual void PopClip();
+
+ virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const;
+ virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const;
+
+ virtual TemporaryRef<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const { return nullptr; }
+
+ virtual TemporaryRef<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
+
+ virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const;
+
+ virtual TemporaryRef<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const;
+
+ virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType);
+
+ virtual bool SupportsRegionClipping() const { return false; }
+
+ virtual void *GetNativeSurface(NativeSurfaceType aType) { return nullptr; }
+
+ bool Init(const IntSize &aSize, SurfaceFormat aFormat);
+ bool Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat);
+ uint32_t GetByteSize() const;
+
+ TemporaryRef<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
+ ExtendMode aExtendMode, const IntRect* aSourceRect = nullptr);
+
+ TemporaryRef<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, ExtendMode aExtendMode) {
+ Matrix mat;
+ return GetImageForSurface(aSurface, mat, aExtendMode, nullptr);
+ }
+
+ static ID2D1Factory1 *factory();
+ static void CleanupD2D();
+ static IDWriteFactory *GetDWriteFactory();
+
+ operator std::string() const {
+ std::stringstream stream;
+ stream << "DrawTargetD2D 1.1 (" << this << ")";
+ return stream.str();
+ }
+
+ static uint32_t GetMaxSurfaceSize() {
+ return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ }
+
+ static uint64_t mVRAMUsageDT;
+ static uint64_t mVRAMUsageSS;
+
+private:
+ friend class SourceSurfaceD2D1;
+
+ typedef std::unordered_set<DrawTargetD2D1*> TargetSet;
+
+ // This function will mark the surface as changing, and make sure any
+ // copy-on-write snapshots are notified.
+ void MarkChanged();
+ void PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern);
+ void FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern);
+ void FlushTransformToDC() {
+ if (mTransformDirty) {
+ mDC->SetTransform(D2DMatrix(mTransform));
+ mTransformDirty = false;
+ }
+ }
+ void AddDependencyOnSource(SourceSurfaceD2D1* aSource);
+
+ // This returns the clipped geometry, in addition it returns aClipBounds which
+ // represents the intersection of all pixel-aligned rectangular clips that
+ // are currently set. The returned clipped geometry must be clipped by these
+ // bounds to correctly reflect the total clip. This is in device space.
+ TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
+
+ TemporaryRef<ID2D1Geometry> GetInverseClippedGeometry();
+
+ bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
+
+ void PopAllClips();
+ void PushAllClips();
+ void PushClipsToDC(ID2D1DeviceContext *aDC);
+ void PopClipsFromDC(ID2D1DeviceContext *aDC);
+
+ TemporaryRef<ID2D1Brush> CreateTransparentBlackBrush();
+ TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
+
+ void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform);
+
+ IntSize mSize;
+
+ RefPtr<ID3D11Device> mDevice;
+ RefPtr<ID3D11Texture2D> mTexture;
+ RefPtr<ID2D1Geometry> mCurrentClippedGeometry;
+ // This is only valid if mCurrentClippedGeometry is non-null. And will
+ // only be the intersection of all pixel-aligned retangular clips. This is in
+ // device space.
+ IntRect mCurrentClipBounds;
+ mutable RefPtr<ID2D1DeviceContext> mDC;
+ RefPtr<ID2D1Bitmap1> mBitmap;
+ RefPtr<ID2D1Bitmap1> mTempBitmap;
+ RefPtr<ID2D1Effect> mBlendEffect;
+
+ // We store this to prevent excessive SetTextRenderingParams calls.
+ RefPtr<IDWriteRenderingParams> mTextRenderingParams;
+
+ // List of pushed clips.
+ struct PushedClip
+ {
+ D2D1_RECT_F mBounds;
+ union {
+ // If mPath is non-null, the mTransform member will be used, otherwise
+ // the mIsPixelAligned member is valid.
+ D2D1_MATRIX_3X2_F mTransform;
+ bool mIsPixelAligned;
+ };
+ RefPtr<PathD2D> mPath;
+ };
+ std::vector<PushedClip> mPushedClips;
+
+ // The latest snapshot of this surface. This needs to be told when this
+ // target is modified. We keep it alive as a cache.
+ RefPtr<SourceSurfaceD2D1> mSnapshot;
+ // A list of targets we need to flush when we're modified.
+ TargetSet mDependentTargets;
+ // A list of targets which have this object in their mDependentTargets set
+ TargetSet mDependingOnTargets;
+
+ // True of the current clip stack is pushed to the main RT.
+ bool mClipsArePushed;
+ static ID2D1Factory1 *mFactory;
+ static IDWriteFactory *mDWriteFactory;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */
diff --git a/gfx/2d/DrawTargetDual.cpp b/gfx/2d/DrawTargetDual.cpp
index 3af57e23f..e971337b2 100644
--- a/gfx/2d/DrawTargetDual.cpp
+++ b/gfx/2d/DrawTargetDual.cpp
@@ -5,6 +5,7 @@
#include "DrawTargetDual.h"
#include "Tools.h"
+#include "Logging.h"
namespace mozilla {
namespace gfx {
@@ -12,9 +13,9 @@ namespace gfx {
class DualSurface
{
public:
- inline DualSurface(SourceSurface *aSurface)
+ inline explicit DualSurface(SourceSurface *aSurface)
{
- if (aSurface->GetType() != SURFACE_DUAL_DT) {
+ if (aSurface->GetType() != SurfaceType::DUAL_DT) {
mA = mB = aSurface;
return;
}
@@ -36,10 +37,10 @@ public:
class DualPattern
{
public:
- inline DualPattern(const Pattern &aPattern)
+ inline explicit DualPattern(const Pattern &aPattern)
: mPatternsInitialized(false)
{
- if (aPattern.GetType() != PATTERN_SURFACE) {
+ if (aPattern.GetType() != PatternType::SURFACE) {
mA = mB = &aPattern;
return;
}
@@ -47,7 +48,7 @@ public:
const SurfacePattern *surfPat =
static_cast<const SurfacePattern*>(&aPattern);
- if (surfPat->mSurface->GetType() != SURFACE_DUAL_DT) {
+ if (surfPat->mSurface->GetType() != SurfaceType::DUAL_DT) {
mA = mB = &aPattern;
return;
}
@@ -186,6 +187,11 @@ DrawTargetDual::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFor
RefPtr<DrawTarget> dtA = mA->CreateSimilarDrawTarget(aSize, aFormat);
RefPtr<DrawTarget> dtB = mB->CreateSimilarDrawTarget(aSize, aFormat);
+ if (!dtA || !dtB) {
+ gfxWarning() << "Failure to allocate a similar DrawTargetDual. Size: " << aSize;
+ return nullptr;
+ }
+
return new DrawTargetDual(dtA, dtB);
}
diff --git a/gfx/2d/DrawTargetDual.h b/gfx/2d/DrawTargetDual.h
index 8f519603a..0cb0fe420 100644
--- a/gfx/2d/DrawTargetDual.h
+++ b/gfx/2d/DrawTargetDual.h
@@ -12,14 +12,15 @@
#include "SourceSurfaceDual.h"
#include "2D.h"
+#include "Filters.h"
namespace mozilla {
namespace gfx {
#define FORWARD_FUNCTION(funcName) \
- virtual void funcName() { mA->funcName(); mB->funcName(); }
+ virtual void funcName() override { mA->funcName(); mB->funcName(); }
#define FORWARD_FUNCTION1(funcName, var1Type, var1Name) \
- virtual void funcName(var1Type var1Name) { mA->funcName(var1Name); mB->funcName(var1Name); }
+ virtual void funcName(var1Type var1Name) override { mA->funcName(var1Name); mB->funcName(var1Name); }
/* This is a special type of DrawTarget. It duplicates all drawing calls
* accross two drawtargets. An exception to this is when a snapshot of another
@@ -34,6 +35,7 @@ namespace gfx {
class DrawTargetDual : public DrawTarget
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetDual, override)
DrawTargetDual(DrawTarget *aA, DrawTarget *aB)
: mA(aA)
, mB(aB)
@@ -41,9 +43,10 @@ public:
mFormat = aA->GetFormat();
}
- virtual BackendType GetType() const { return mA->GetType(); }
- virtual TemporaryRef<SourceSurface> Snapshot() { return new SourceSurfaceDual(mA, mB); }
- virtual IntSize GetSize() { return mA->GetSize(); }
+ virtual DrawTargetType GetType() const override { return mA->GetType(); }
+ virtual BackendType GetBackendType() const override { return mA->GetBackendType(); }
+ virtual TemporaryRef<SourceSurface> Snapshot() override { return new SourceSurfaceDual(mA, mB); }
+ virtual IntSize GetSize() override { return mA->GetSize(); }
FORWARD_FUNCTION(Flush)
FORWARD_FUNCTION1(PushClip, const Path *, aPath)
@@ -51,70 +54,79 @@ public:
FORWARD_FUNCTION(PopClip)
FORWARD_FUNCTION1(ClearRect, const Rect &, aRect)
- virtual void SetTransform(const Matrix &aTransform) {
+ virtual void SetTransform(const Matrix &aTransform) override {
mTransform = aTransform;
mA->SetTransform(aTransform);
mB->SetTransform(aTransform);
}
virtual void DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect & aSource,
- const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions);
+ const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions) override;
+
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) override
+ {
+ mA->DrawFilter(aNode, aSourceRect, aDestPoint, aOptions);
+ mB->DrawFilter(aNode, aSourceRect, aDestPoint, aOptions);
+ }
virtual void MaskSurface(const Pattern &aSource,
SourceSurface *aMask,
Point aOffset,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest,
const Color &aColor, const Point &aOffset,
- Float aSigma, CompositionOp aOp);
+ Float aSigma, CompositionOp aOp) override;
virtual void CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect,
- const IntPoint &aDestination);
+ const IntPoint &aDestination) override;
- virtual void FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions);
+ virtual void FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions) override;
virtual void StrokeRect(const Rect &aRect, const Pattern &aPattern,
- const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions);
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override;
virtual void StrokeLine(const Point &aStart, const Point &aEnd, const Pattern &aPattern,
- const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions);
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override;
virtual void Stroke(const Path *aPath, const Pattern &aPattern,
- const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions);
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override;
- virtual void Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions);
+ virtual void Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions) override;
virtual void FillGlyphs(ScaledFont *aScaledFont, const GlyphBuffer &aBuffer,
const Pattern &aPattern, const DrawOptions &aOptions,
- const GlyphRenderingOptions *aRenderingOptions);
+ const GlyphRenderingOptions *aRenderingOptions) override;
- virtual void Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions);
+ virtual void Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions) override;
virtual TemporaryRef<SourceSurface>
CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
int32_t aStride,
- SurfaceFormat aFormat) const
+ SurfaceFormat aFormat) const override
{
return mA->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
}
- virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const
+ virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override
{
return mA->OptimizeSourceSurface(aSurface);
}
virtual TemporaryRef<SourceSurface>
- CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override
{
return mA->CreateSourceSurfaceFromNativeSurface(aSurface);
}
virtual TemporaryRef<DrawTarget>
- CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
- virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const
+ virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override
{
return mA->CreatePathBuilder(aFillRule);
}
@@ -122,15 +134,25 @@ public:
virtual TemporaryRef<GradientStops>
CreateGradientStops(GradientStop *aStops,
uint32_t aNumStops,
- ExtendMode aExtendMode = EXTEND_CLAMP) const
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override
{
return mA->CreateGradientStops(aStops, aNumStops, aExtendMode);
}
-
- virtual void *GetNativeSurface(NativeSurfaceType aType)
+
+ virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override
+ {
+ return mA->CreateFilter(aType);
+ }
+
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override
{
return nullptr;
}
+
+ virtual bool IsDualDrawTarget() const override
+ {
+ return true;
+ }
private:
RefPtr<DrawTarget> mA;
diff --git a/gfx/2d/DrawTargetRecording.cpp b/gfx/2d/DrawTargetRecording.cpp
index 37b386bac..a39f97844 100644
--- a/gfx/2d/DrawTargetRecording.cpp
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -9,6 +9,7 @@
#include "Logging.h"
#include "Tools.h"
+#include "Filters.h"
namespace mozilla {
namespace gfx {
@@ -16,6 +17,7 @@ namespace gfx {
class SourceSurfaceRecording : public SourceSurface
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceRecording)
SourceSurfaceRecording(SourceSurface *aFinalSurface, DrawEventRecorderPrivate *aRecorder)
: mFinalSurface(aFinalSurface), mRecorder(aRecorder)
{
@@ -26,7 +28,7 @@ public:
mRecorder->RecordEvent(RecordedSourceSurfaceDestruction(this));
}
- virtual SurfaceType GetType() const { return SURFACE_RECORDING; }
+ virtual SurfaceType GetType() const { return SurfaceType::RECORDING; }
virtual IntSize GetSize() const { return mFinalSurface->GetSize(); }
virtual SurfaceFormat GetFormat() const { return mFinalSurface->GetFormat(); }
virtual TemporaryRef<DataSourceSurface> GetDataSurface() { return mFinalSurface->GetDataSurface(); }
@@ -38,6 +40,7 @@ public:
class GradientStopsRecording : public GradientStops
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsRecording)
GradientStopsRecording(GradientStops *aFinalGradientStops, DrawEventRecorderPrivate *aRecorder)
: mFinalGradientStops(aFinalGradientStops), mRecorder(aRecorder)
{
@@ -48,7 +51,7 @@ public:
mRecorder->RecordEvent(RecordedGradientStopsDestruction(this));
}
- virtual BackendType GetBackendType() const { return BACKEND_RECORDING; }
+ virtual BackendType GetBackendType() const { return BackendType::RECORDING; }
RefPtr<GradientStops> mFinalGradientStops;
RefPtr<DrawEventRecorderPrivate> mRecorder;
@@ -57,7 +60,7 @@ public:
static SourceSurface *
GetSourceSurface(SourceSurface *aSurface)
{
- if (aSurface->GetType() != SURFACE_RECORDING) {
+ if (aSurface->GetType() != SurfaceType::RECORDING) {
return aSurface;
}
@@ -67,17 +70,95 @@ GetSourceSurface(SourceSurface *aSurface)
static GradientStops *
GetGradientStops(GradientStops *aStops)
{
- if (aStops->GetBackendType() != BACKEND_RECORDING) {
+ if (aStops->GetBackendType() != BackendType::RECORDING) {
return aStops;
}
return static_cast<GradientStopsRecording*>(aStops)->mFinalGradientStops;
}
+class FilterNodeRecording : public FilterNode
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeRecording, override)
+ using FilterNode::SetAttribute;
+
+ FilterNodeRecording(FilterNode *aFinalFilterNode, DrawEventRecorderPrivate *aRecorder)
+ : mFinalFilterNode(aFinalFilterNode), mRecorder(aRecorder)
+ {
+ }
+
+ ~FilterNodeRecording()
+ {
+ mRecorder->RecordEvent(RecordedFilterNodeDestruction(this));
+ }
+
+ virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override
+ {
+ mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aSurface));
+ mFinalFilterNode->SetInput(aIndex, GetSourceSurface(aSurface));
+ }
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override
+ {
+ FilterNode *finalNode = aFilter;
+ if (aFilter->GetBackendType() != FILTER_BACKEND_RECORDING) {
+ gfxWarning() << "Non recording filter node used with recording DrawTarget!";
+ } else {
+ finalNode = static_cast<FilterNodeRecording*>(aFilter)->mFinalFilterNode;
+ }
+
+ mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aFilter));
+ mFinalFilterNode->SetInput(aIndex, finalNode);
+ }
+
+
+#define FORWARD_SET_ATTRIBUTE(type, argtype) \
+ virtual void SetAttribute(uint32_t aIndex, type aValue) override { \
+ mRecorder->RecordEvent(RecordedFilterNodeSetAttribute(this, aIndex, aValue, RecordedFilterNodeSetAttribute::ARGTYPE_##argtype)); \
+ mFinalFilterNode->SetAttribute(aIndex, aValue); \
+ }
+
+ FORWARD_SET_ATTRIBUTE(bool, BOOL);
+ FORWARD_SET_ATTRIBUTE(uint32_t, UINT32);
+ FORWARD_SET_ATTRIBUTE(Float, FLOAT);
+ FORWARD_SET_ATTRIBUTE(const Size&, SIZE);
+ FORWARD_SET_ATTRIBUTE(const IntSize&, INTSIZE);
+ FORWARD_SET_ATTRIBUTE(const IntPoint&, INTPOINT);
+ FORWARD_SET_ATTRIBUTE(const Rect&, RECT);
+ FORWARD_SET_ATTRIBUTE(const IntRect&, INTRECT);
+ FORWARD_SET_ATTRIBUTE(const Point&, POINT);
+ FORWARD_SET_ATTRIBUTE(const Matrix5x4&, MATRIX5X4);
+ FORWARD_SET_ATTRIBUTE(const Point3D&, POINT3D);
+ FORWARD_SET_ATTRIBUTE(const Color&, COLOR);
+
+#undef FORWARD_SET_ATTRIBUTE
+
+ virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override {
+ mRecorder->RecordEvent(RecordedFilterNodeSetAttribute(this, aIndex, aFloat, aSize));
+ mFinalFilterNode->SetAttribute(aIndex, aFloat, aSize);
+ }
+
+ virtual FilterBackend GetBackendType() override { return FILTER_BACKEND_RECORDING; }
+
+ RefPtr<FilterNode> mFinalFilterNode;
+ RefPtr<DrawEventRecorderPrivate> mRecorder;
+};
+
+static FilterNode*
+GetFilterNode(FilterNode* aNode)
+{
+ if (aNode->GetBackendType() != FILTER_BACKEND_RECORDING) {
+ gfxWarning() << "Non recording filter node used with recording DrawTarget!";
+ return aNode;
+ }
+
+ return static_cast<FilterNodeRecording*>(aNode)->mFinalFilterNode;
+}
+
struct AdjustedPattern
{
- AdjustedPattern(const Pattern &aPattern)
- : mPattern(NULL)
+ explicit AdjustedPattern(const Pattern &aPattern)
+ : mPattern(nullptr)
{
mOrigPattern = const_cast<Pattern*>(&aPattern);
}
@@ -91,9 +172,9 @@ struct AdjustedPattern
operator Pattern*()
{
switch(mOrigPattern->GetType()) {
- case PATTERN_COLOR:
+ case PatternType::COLOR:
return mOrigPattern;
- case PATTERN_SURFACE:
+ case PatternType::SURFACE:
{
SurfacePattern *surfPat = static_cast<SurfacePattern*>(mOrigPattern);
mPattern =
@@ -102,7 +183,7 @@ struct AdjustedPattern
surfPat->mFilter);
return mPattern;
}
- case PATTERN_LINEAR_GRADIENT:
+ case PatternType::LINEAR_GRADIENT:
{
LinearGradientPattern *linGradPat = static_cast<LinearGradientPattern*>(mOrigPattern);
mPattern =
@@ -111,7 +192,7 @@ struct AdjustedPattern
linGradPat->mMatrix);
return mPattern;
}
- case PATTERN_RADIAL_GRADIENT:
+ case PatternType::RADIAL_GRADIENT:
{
RadialGradientPattern *radGradPat = static_cast<RadialGradientPattern*>(mOrigPattern);
mPattern =
@@ -139,11 +220,16 @@ struct AdjustedPattern
Pattern *mPattern;
};
-DrawTargetRecording::DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT)
+DrawTargetRecording::DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT, bool aHasData)
: mRecorder(static_cast<DrawEventRecorderPrivate*>(aRecorder))
, mFinalDT(aDT)
{
- mRecorder->RecordEvent(RecordedDrawTargetCreation(this, mFinalDT->GetType(), mFinalDT->GetSize(), mFinalDT->GetFormat()));
+ RefPtr<SourceSurface> snapshot = aHasData ? mFinalDT->Snapshot() : nullptr;
+ mRecorder->RecordEvent(RecordedDrawTargetCreation(this,
+ mFinalDT->GetBackendType(),
+ mFinalDT->GetSize(),
+ mFinalDT->GetFormat(),
+ aHasData, snapshot));
mFormat = mFinalDT->GetFormat();
}
@@ -185,8 +271,8 @@ DrawTargetRecording::StrokeLine(const Point &aBegin,
Path*
DrawTargetRecording::GetPathForPathRecording(const Path *aPath) const
{
- if (aPath->GetBackendType() != BACKEND_RECORDING) {
- return NULL;
+ if (aPath->GetBackendType() != BackendType::RECORDING) {
+ return nullptr;
}
return static_cast<const PathRecording*>(aPath)->mPath;
@@ -214,7 +300,10 @@ void RecordingFontUserDataDestroyFunc(void *aUserData)
RecordingFontUserData *userData =
static_cast<RecordingFontUserData*>(aUserData);
+ // TODO support font in b2g recordings
+#ifndef MOZ_WIDGET_GONK
userData->recorder->RecordEvent(RecordedScaledFontDestruction(userData->refPtr));
+#endif
delete userData;
}
@@ -227,7 +316,10 @@ DrawTargetRecording::FillGlyphs(ScaledFont *aFont,
const GlyphRenderingOptions *aRenderingOptions)
{
if (!aFont->GetUserData(reinterpret_cast<UserDataKey*>(mRecorder.get()))) {
+ // TODO support font in b2g recordings
+#ifndef MOZ_WIDGET_GONK
mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, aFont));
+#endif
RecordingFontUserData *userData = new RecordingFontUserData;
userData->refPtr = aFont;
userData->recorder = mRecorder;
@@ -235,7 +327,10 @@ DrawTargetRecording::FillGlyphs(ScaledFont *aFont,
&RecordingFontUserDataDestroyFunc);
}
+ // TODO support font in b2g recordings
+#ifndef MOZ_WIDGET_GONK
mRecorder->RecordEvent(RecordedFillGlyphs(this, aFont, aPattern, aOptions, aBuffer.mGlyphs, aBuffer.mNumGlyphs));
+#endif
mFinalDT->FillGlyphs(aFont, aBuffer, aPattern, aOptions, aRenderingOptions);
}
@@ -279,7 +374,7 @@ DrawTargetRecording::Snapshot()
mRecorder->RecordEvent(RecordedSnapshot(retSurf, this));
- return retSurf;
+ return retSurf.forget();
}
void
@@ -306,6 +401,28 @@ DrawTargetRecording::DrawSurfaceWithShadow(SourceSurface *aSurface,
}
void
+DrawTargetRecording::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ mRecorder->RecordEvent(RecordedDrawFilter(this, aNode, aSourceRect, aDestPoint, aOptions));
+ mFinalDT->DrawFilter(GetFilterNode(aNode), aSourceRect, aDestPoint, aOptions);
+}
+
+TemporaryRef<FilterNode>
+DrawTargetRecording::CreateFilter(FilterType aType)
+{
+ RefPtr<FilterNode> node = mFinalDT->CreateFilter(aType);
+
+ RefPtr<FilterNode> retNode = new FilterNodeRecording(node, mRecorder);
+
+ mRecorder->RecordEvent(RecordedFilterNodeCreation(retNode, aType));
+
+ return retNode.forget();
+}
+
+void
DrawTargetRecording::ClearRect(const Rect &aRect)
{
mRecorder->RecordEvent(RecordedClearRect(this, aRect));
@@ -356,7 +473,7 @@ DrawTargetRecording::CreateSourceSurfaceFromData(unsigned char *aData,
mRecorder->RecordEvent(RecordedSourceSurfaceCreation(retSurf, aData, aStride, aSize, aFormat));
- return retSurf;
+ return retSurf.forget();
}
TemporaryRef<SourceSurface>
@@ -389,7 +506,7 @@ DrawTargetRecording::OptimizeSourceSurface(SourceSurface *aSurface) const
dataSurf->GetSize(), dataSurf->GetFormat()));
}
- return retSurf;
+ return retSurf.forget();
}
TemporaryRef<SourceSurface>
@@ -417,17 +534,14 @@ DrawTargetRecording::CreateSourceSurfaceFromNativeSurface(const NativeSurface &a
dataSurf->GetSize(), dataSurf->GetFormat()));
}
- return retSurf;
+ return retSurf.forget();
}
TemporaryRef<DrawTarget>
DrawTargetRecording::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
{
RefPtr<DrawTarget> dt = mFinalDT->CreateSimilarDrawTarget(aSize, aFormat);
-
- RefPtr<DrawTarget> retDT = new DrawTargetRecording(mRecorder.get(), dt);
-
- return retDT;
+ return new DrawTargetRecording(mRecorder.get(), dt);
}
TemporaryRef<PathBuilder>
@@ -448,7 +562,7 @@ DrawTargetRecording::CreateGradientStops(GradientStop *aStops,
mRecorder->RecordEvent(RecordedGradientStopsCreation(retStops, aStops, aNumStops, aExtendMode));
- return retStops;
+ return retStops.forget();
}
void
@@ -463,7 +577,7 @@ void
DrawTargetRecording::EnsureStored(const Path *aPath)
{
if (!mRecorder->HasStoredPath(aPath)) {
- if (aPath->GetBackendType() != BACKEND_RECORDING) {
+ if (aPath->GetBackendType() != BackendType::RECORDING) {
gfxWarning() << "Cannot record this fill path properly!";
} else {
PathRecording *recPath = const_cast<PathRecording*>(static_cast<const PathRecording*>(aPath));
diff --git a/gfx/2d/DrawTargetRecording.h b/gfx/2d/DrawTargetRecording.h
index f1e7da428..ba89769c2 100644
--- a/gfx/2d/DrawTargetRecording.h
+++ b/gfx/2d/DrawTargetRecording.h
@@ -15,20 +15,22 @@ namespace gfx {
class DrawTargetRecording : public DrawTarget
{
public:
- DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT);
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetRecording, override)
+ DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT, bool aHasData = false);
~DrawTargetRecording();
- virtual BackendType GetType() const { return mFinalDT->GetType(); }
+ virtual DrawTargetType GetType() const override { return mFinalDT->GetType(); }
+ virtual BackendType GetBackendType() const override { return mFinalDT->GetBackendType(); }
- virtual TemporaryRef<SourceSurface> Snapshot();
+ virtual TemporaryRef<SourceSurface> Snapshot() override;
- virtual IntSize GetSize() { return mFinalDT->GetSize(); }
+ virtual IntSize GetSize() override { return mFinalDT->GetSize(); }
/* Ensure that the DrawTarget backend has flushed all drawing operations to
* this draw target. This must be called before using the backing surface of
* this draw target outside of GFX 2D code.
*/
- virtual void Flush() { mFinalDT->Flush(); }
+ virtual void Flush() override { mFinalDT->Flush(); }
/*
* Draw a surface to the draw target. Possibly doing partial drawing or
@@ -45,7 +47,12 @@ public:
const Rect &aDest,
const Rect &aSource,
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) override;
/*
* Blend a surface to the draw target with a shadow. The shadow is drawn as a
@@ -66,7 +73,7 @@ public:
const Color &aColor,
const Point &aOffset,
Float aSigma,
- CompositionOp aOperator);
+ CompositionOp aOperator) override;
/*
* Clear a rectangle on the draw target to transparent black. This will
@@ -74,7 +81,7 @@ public:
*
* aRect Rectangle to clear
*/
- virtual void ClearRect(const Rect &aRect);
+ virtual void ClearRect(const Rect &aRect) override;
/*
* This is essentially a 'memcpy' between two surfaces. It moves a pixel
@@ -87,7 +94,7 @@ public:
*/
virtual void CopySurface(SourceSurface *aSurface,
const IntRect &aSourceRect,
- const IntPoint &aDestination);
+ const IntPoint &aDestination) override;
/*
* Fill a rectangle on the DrawTarget with a certain source pattern.
@@ -98,7 +105,7 @@ public:
*/
virtual void FillRect(const Rect &aRect,
const Pattern &aPattern,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
/*
* Stroke a rectangle on the DrawTarget with a certain source pattern.
@@ -110,7 +117,7 @@ public:
virtual void StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
/*
* Stroke a line on the DrawTarget with a certain source pattern.
@@ -124,7 +131,7 @@ public:
const Point &aEnd,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
/*
* Stroke a path on the draw target with a certain source pattern.
@@ -137,7 +144,7 @@ public:
virtual void Stroke(const Path *aPath,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
/*
* Fill a path on the draw target with a certain source pattern.
@@ -148,7 +155,7 @@ public:
*/
virtual void Fill(const Path *aPath,
const Pattern &aPattern,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
/*
* Fill a series of clyphs on the draw target with a certain source pattern.
@@ -157,7 +164,7 @@ public:
const GlyphBuffer &aBuffer,
const Pattern &aPattern,
const DrawOptions &aOptions = DrawOptions(),
- const GlyphRenderingOptions *aRenderingOptions = NULL);
+ const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
/*
* This takes a source pattern and a mask, and composites the source pattern
@@ -170,19 +177,19 @@ public:
*/
virtual void Mask(const Pattern &aSource,
const Pattern &aMask,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void MaskSurface(const Pattern &aSource,
SourceSurface *aMask,
Point aOffset,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
/*
* Push a clip to the DrawTarget.
*
* aPath The path to clip to
*/
- virtual void PushClip(const Path *aPath);
+ virtual void PushClip(const Path *aPath) override;
/*
* Push an axis-aligned rectangular clip to the DrawTarget. This rectangle
@@ -190,12 +197,12 @@ public:
*
* aRect The rect to clip to
*/
- virtual void PushClipRect(const Rect &aRect);
+ virtual void PushClipRect(const Rect &aRect) override;
/* Pop a clip from the DrawTarget. A pop without a corresponding push will
* be ignored.
*/
- virtual void PopClip();
+ virtual void PopClip() override;
/*
* Create a SourceSurface optimized for use with this DrawTarget from
@@ -206,14 +213,14 @@ public:
virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
int32_t aStride,
- SurfaceFormat aFormat) const;
+ SurfaceFormat aFormat) const override;
/*
* Create a SourceSurface optimized for use with this DrawTarget from
* an arbitrary other SourceSurface. This may return aSourceSurface or some
* other existing surface.
*/
- virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const;
+ virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
/*
* Create a SourceSurface for a type of NativeSurface. This may fail if the
@@ -221,13 +228,13 @@ public:
* in.
*/
virtual TemporaryRef<SourceSurface>
- CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const;
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
/*
* Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
*/
virtual TemporaryRef<DrawTarget>
- CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
/*
* Create a path builder with the specified fillmode.
@@ -236,7 +243,7 @@ public:
* ID2D1SimplifiedGeometrySink requires the fill mode
* to be set before calling BeginFigure().
*/
- virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const;
+ virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
/*
* Create a GradientStops object that holds information about a set of
@@ -251,18 +258,20 @@ public:
virtual TemporaryRef<GradientStops>
CreateGradientStops(GradientStop *aStops,
uint32_t aNumStops,
- ExtendMode aExtendMode = EXTEND_CLAMP) const;
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
+
+ virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override;
/*
* Set a transform on the surface, this transform is applied at drawing time
* to both the mask and source of the operation.
*/
- virtual void SetTransform(const Matrix &aTransform);
+ virtual void SetTransform(const Matrix &aTransform) override;
/* Tries to get a native surface for a DrawTarget, this may fail if the
* draw target cannot convert to this surface type.
*/
- virtual void *GetNativeSurface(NativeSurfaceType aType) { return mFinalDT->GetNativeSurface(aType); }
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override { return mFinalDT->GetNativeSurface(aType); }
private:
Path *GetPathForPathRecording(const Path *aPath) const;
diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp
index 005ba2b6d..837cf9922 100644
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -4,12 +4,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DrawTargetSkia.h"
+#include "SourceSurfaceCairo.h"
#include "SourceSurfaceSkia.h"
#include "ScaledFontBase.h"
-#include "skia/SkDevice.h"
+#include "ScaledFontCairo.h"
+#include "skia/SkGpuDevice.h"
+#include "skia/SkBitmapDevice.h"
+#include "FilterNodeSoftware.h"
#ifdef USE_SKIA_GPU
#include "skia/SkGpuDevice.h"
+#include "skia/GrGLInterface.h"
#endif
#include "skia/SkTypeface.h"
@@ -17,26 +22,22 @@
#include "skia/SkBlurDrawLooper.h"
#include "skia/SkBlurMaskFilter.h"
#include "skia/SkColorFilter.h"
+#include "skia/SkDropShadowImageFilter.h"
#include "skia/SkLayerRasterizer.h"
#include "skia/SkLayerDrawLooper.h"
#include "skia/SkDashPathEffect.h"
#include "Logging.h"
-#include "HelpersSkia.h"
#include "Tools.h"
+#include "DataSurfaceHelpers.h"
#include <algorithm>
-#ifdef ANDROID
-# define USE_SOFT_CLIPPING false
-#else
-# define USE_SOFT_CLIPPING true
-#endif
-
namespace mozilla {
namespace gfx {
class GradientStopsSkia : public GradientStops
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsSkia)
GradientStopsSkia(const std::vector<GradientStop>& aStops, uint32_t aNumStops, ExtendMode aExtendMode)
: mCount(aNumStops)
, mExtendMode(aExtendMode)
@@ -71,7 +72,7 @@ public:
}
}
- BackendType GetBackendType() const { return BACKEND_SKIA; }
+ BackendType GetBackendType() const { return BackendType::SKIA; }
std::vector<SkColor> mColors;
std::vector<SkScalar> mPositions;
@@ -79,43 +80,85 @@ public:
ExtendMode mExtendMode;
};
+/**
+ * When constructing a temporary SkBitmap via GetBitmapForSurface, we may also
+ * have to construct a temporary DataSourceSurface, which must live as long as
+ * the SkBitmap. So we return a pair of the SkBitmap and the (optional)
+ * temporary surface.
+ */
+struct TempBitmap
+{
+ SkBitmap mBitmap;
+ RefPtr<SourceSurface> mTmpSurface;
+};
+
+static TempBitmap
+GetBitmapForSurface(SourceSurface* aSurface)
+{
+ TempBitmap result;
+
+ if (aSurface->GetType() == SurfaceType::SKIA) {
+ result.mBitmap = static_cast<SourceSurfaceSkia*>(aSurface)->GetBitmap();
+ return result;
+ }
+
+ RefPtr<DataSourceSurface> surf = aSurface->GetDataSurface();
+ if (!surf) {
+ MOZ_CRASH("Non-skia SourceSurfaces need to be DataSourceSurfaces");
+ }
+
+ SkAlphaType alphaType = (surf->GetFormat() == SurfaceFormat::B8G8R8X8) ?
+ kOpaque_SkAlphaType : kPremul_SkAlphaType;
+
+ SkImageInfo info = SkImageInfo::Make(surf->GetSize().width,
+ surf->GetSize().height,
+ GfxFormatToSkiaColorType(surf->GetFormat()),
+ alphaType);
+ result.mBitmap.setInfo(info, surf->Stride());
+
+ result.mBitmap.setPixels(surf->GetData());
+ result.mTmpSurface = surf.forget();
+ return result;
+}
+
DrawTargetSkia::DrawTargetSkia()
+ :
+#ifdef USE_SKIA_GPU
+ mTexture(0),
+#endif
+ mSnapshot(nullptr)
{
}
DrawTargetSkia::~DrawTargetSkia()
{
- if (mSnapshots.size()) {
- for (std::vector<SourceSurfaceSkia*>::iterator iter = mSnapshots.begin();
- iter != mSnapshots.end(); iter++) {
- (*iter)->DrawTargetDestroyed();
- }
- // All snapshots will now have copied data.
- mSnapshots.clear();
- }
}
TemporaryRef<SourceSurface>
DrawTargetSkia::Snapshot()
{
- RefPtr<SourceSurfaceSkia> source = new SourceSurfaceSkia();
-
- if (!source->InitFromCanvas(mCanvas.get(), mFormat, this))
- return nullptr;
+ RefPtr<SourceSurfaceSkia> snapshot = mSnapshot;
+ if (!snapshot) {
+ snapshot = new SourceSurfaceSkia();
+ mSnapshot = snapshot;
+ if (!snapshot->InitFromCanvas(mCanvas.get(), mFormat, this))
+ return nullptr;
+ }
- AppendSnapshot(source);
- return source;
+ return snapshot.forget();
}
-void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0)
+static void
+SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap,
+ Float aAlpha = 1.0)
{
switch (aPattern.GetType()) {
- case PATTERN_COLOR: {
+ case PatternType::COLOR: {
Color color = static_cast<const ColorPattern&>(aPattern).mColor;
aPaint.setColor(ColorToSkColor(color, aAlpha));
break;
}
- case PATTERN_LINEAR_GRADIENT: {
+ case PatternType::LINEAR_GRADIENT: {
const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern);
GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get());
SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode);
@@ -125,17 +168,18 @@ void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.
points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x), SkFloatToScalar(pat.mBegin.y));
points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x), SkFloatToScalar(pat.mEnd.y));
- SkShader* shader = SkGradientShader::CreateLinear(points,
- &stops->mColors.front(),
- &stops->mPositions.front(),
- stops->mCount,
+ SkShader* shader = SkGradientShader::CreateLinear(points,
+ &stops->mColors.front(),
+ &stops->mPositions.front(),
+ stops->mCount,
mode);
if (shader) {
SkMatrix mat;
GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
- shader->setLocalMatrix(mat);
- SkSafeUnref(aPaint.setShader(shader));
+ SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat);
+ SkSafeUnref(shader);
+ SkSafeUnref(aPaint.setShader(matrixShader));
}
} else {
@@ -143,7 +187,7 @@ void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.
}
break;
}
- case PATTERN_RADIAL_GRADIENT: {
+ case PatternType::RADIAL_GRADIENT: {
const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get());
SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode);
@@ -153,19 +197,20 @@ void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.
points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x), SkFloatToScalar(pat.mCenter1.y));
points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x), SkFloatToScalar(pat.mCenter2.y));
- SkShader* shader = SkGradientShader::CreateTwoPointConical(points[0],
+ SkShader* shader = SkGradientShader::CreateTwoPointConical(points[0],
SkFloatToScalar(pat.mRadius1),
- points[1],
+ points[1],
SkFloatToScalar(pat.mRadius2),
- &stops->mColors.front(),
- &stops->mPositions.front(),
- stops->mCount,
+ &stops->mColors.front(),
+ &stops->mPositions.front(),
+ stops->mCount,
mode);
if (shader) {
SkMatrix mat;
GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
- shader->setLocalMatrix(mat);
- SkSafeUnref(aPaint.setShader(shader));
+ SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat);
+ SkSafeUnref(shader);
+ SkSafeUnref(aPaint.setShader(matrixShader));
}
} else {
@@ -173,36 +218,53 @@ void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.
}
break;
}
- case PATTERN_SURFACE: {
+ case PatternType::SURFACE: {
const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
- const SkBitmap& bitmap = static_cast<SourceSurfaceSkia*>(pat.mSurface.get())->GetBitmap();
+ aTmpBitmap = GetBitmapForSurface(pat.mSurface);
+ SkBitmap& bitmap = aTmpBitmap.mBitmap;
- SkShader::TileMode mode = ExtendModeToTileMode(pat.mExtendMode);
- SkShader* shader = SkShader::CreateBitmapShader(bitmap, mode, mode);
SkMatrix mat;
GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
- shader->setLocalMatrix(mat);
- SkSafeUnref(aPaint.setShader(shader));
- if (pat.mFilter == FILTER_POINT) {
- aPaint.setFilterBitmap(false);
+
+ if (!pat.mSamplingRect.IsEmpty()) {
+ SkIRect rect = IntRectToSkIRect(pat.mSamplingRect);
+ bitmap.extractSubset(&bitmap, rect);
+ mat.preTranslate(rect.x(), rect.y());
+ }
+
+ SkShader::TileMode mode = ExtendModeToTileMode(pat.mExtendMode);
+ SkShader* shader = SkShader::CreateBitmapShader(bitmap, mode, mode);
+ SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat);
+ SkSafeUnref(shader);
+ SkSafeUnref(aPaint.setShader(matrixShader));
+ if (pat.mFilter == Filter::POINT) {
+ aPaint.setFilterLevel(SkPaint::kNone_FilterLevel);
}
break;
}
}
}
+static inline Rect
+GetClipBounds(SkCanvas *aCanvas)
+{
+ SkRect clipBounds;
+ aCanvas->getClipBounds(&clipBounds);
+ return SkRectToRect(clipBounds);
+}
+
struct AutoPaintSetup {
- AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Pattern& aPattern)
+ AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Pattern& aPattern, const Rect* aMaskBounds = nullptr)
: mNeedsRestore(false), mAlpha(1.0)
{
- Init(aCanvas, aOptions);
- SetPaintPattern(mPaint, aPattern, mAlpha);
+ Init(aCanvas, aOptions, aMaskBounds);
+ SetPaintPattern(mPaint, aPattern, mTmpBitmap, mAlpha);
}
- AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions)
+ AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr)
: mNeedsRestore(false), mAlpha(1.0)
{
- Init(aCanvas, aOptions);
+ Init(aCanvas, aOptions, aMaskBounds);
}
~AutoPaintSetup()
@@ -212,40 +274,43 @@ struct AutoPaintSetup {
}
}
- void Init(SkCanvas *aCanvas, const DrawOptions& aOptions)
+ void Init(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds)
{
mPaint.setXfermodeMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
mCanvas = aCanvas;
//TODO: Can we set greyscale somehow?
- if (aOptions.mAntialiasMode != AA_NONE) {
+ if (aOptions.mAntialiasMode != AntialiasMode::NONE) {
mPaint.setAntiAlias(true);
} else {
mPaint.setAntiAlias(false);
}
- MOZ_ASSERT(aOptions.mSnapping == SNAP_NONE, "Pixel snapping not supported yet!");
-
+ Rect clipBounds = GetClipBounds(aCanvas);
+ bool needsGroup = !IsOperatorBoundByMask(aOptions.mCompositionOp) &&
+ (!aMaskBounds || !aMaskBounds->Contains(clipBounds));
+
// TODO: We could skip the temporary for operator_source and just
// clear the clip rect. The other operators would be harder
// but could be worth it to skip pushing a group.
- if (!IsOperatorBoundByMask(aOptions.mCompositionOp)) {
+ if (needsGroup) {
mPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
SkPaint temp;
temp.setXfermodeMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
- temp.setAlpha(U8CPU(aOptions.mAlpha*255));
+ temp.setAlpha(ColorFloatToByte(aOptions.mAlpha));
//TODO: Get a rect here
mCanvas->saveLayer(nullptr, &temp);
mNeedsRestore = true;
} else {
- mPaint.setAlpha(U8CPU(aOptions.mAlpha*255.0));
+ mPaint.setAlpha(ColorFloatToByte(aOptions.mAlpha));
mAlpha = aOptions.mAlpha;
}
- mPaint.setFilterBitmap(true);
+ mPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
}
// TODO: Maybe add an operator overload to access this easier?
SkPaint mPaint;
+ TempBitmap mTmpBitmap;
bool mNeedsRestore;
SkCanvas* mCanvas;
Float mAlpha;
@@ -264,8 +329,15 @@ DrawTargetSkia::DrawSurface(SourceSurface *aSurface,
const DrawSurfaceOptions &aSurfOptions,
const DrawOptions &aOptions)
{
- if (aSurface->GetType() != SURFACE_SKIA) {
- return;
+ RefPtr<SourceSurface> dataSurface;
+
+ if (!(aSurface->GetType() == SurfaceType::SKIA || aSurface->GetType() == SurfaceType::DATA)) {
+ dataSurface = aSurface->GetDataSurface();
+ if (!dataSurface) {
+ gfxDebug() << *this << ": DrawSurface() can't draw surface";
+ return;
+ }
+ aSurface = dataSurface.get();
}
if (aSource.IsEmpty()) {
@@ -277,19 +349,35 @@ DrawTargetSkia::DrawSurface(SourceSurface *aSurface,
SkRect destRect = RectToSkRect(aDest);
SkRect sourceRect = RectToSkRect(aSource);
- SkMatrix matrix;
- matrix.setRectToRect(sourceRect, destRect, SkMatrix::kFill_ScaleToFit);
-
- const SkBitmap& bitmap = static_cast<SourceSurfaceSkia*>(aSurface)->GetBitmap();
-
- AutoPaintSetup paint(mCanvas.get(), aOptions);
- SkShader *shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
- shader->setLocalMatrix(matrix);
- SkSafeUnref(paint.mPaint.setShader(shader));
- if (aSurfOptions.mFilter != FILTER_LINEAR) {
- paint.mPaint.setFilterBitmap(false);
+ TempBitmap bitmap = GetBitmapForSurface(aSurface);
+
+ AutoPaintSetup paint(mCanvas.get(), aOptions, &aDest);
+ if (aSurfOptions.mFilter == Filter::POINT) {
+ paint.mPaint.setFilterLevel(SkPaint::kNone_FilterLevel);
+ }
+
+ mCanvas->drawBitmapRectToRect(bitmap.mBitmap, &sourceRect, destRect, &paint.mPaint);
+}
+
+DrawTargetType
+DrawTargetSkia::GetType() const
+{
+#ifdef USE_SKIA_GPU
+ if (mGrContext) {
+ return DrawTargetType::HARDWARE_RASTER;
}
- mCanvas->drawRect(destRect, paint.mPaint);
+#endif
+ return DrawTargetType::SOFTWARE_RASTER;
+}
+
+void
+DrawTargetSkia::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
+ filter->Draw(this, aSourceRect, aDestPoint, aOptions);
}
void
@@ -300,59 +388,27 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface,
Float aSigma,
CompositionOp aOperator)
{
+ if (!(aSurface->GetType() == SurfaceType::SKIA || aSurface->GetType() == SurfaceType::DATA)) {
+ return;
+ }
+
MarkChanged();
- mCanvas->save(SkCanvas::kMatrix_SaveFlag);
+
+ mCanvas->save();
mCanvas->resetMatrix();
- uint32_t blurFlags = SkBlurMaskFilter::kHighQuality_BlurFlag |
- SkBlurMaskFilter::kIgnoreTransform_BlurFlag;
- const SkBitmap& bitmap = static_cast<SourceSurfaceSkia*>(aSurface)->GetBitmap();
- SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
- SkMatrix matrix;
- matrix.reset();
- matrix.setTranslateX(SkFloatToScalar(aDest.x));
- matrix.setTranslateY(SkFloatToScalar(aDest.y));
- shader->setLocalMatrix(matrix);
- SkLayerDrawLooper* dl = new SkLayerDrawLooper;
- SkLayerDrawLooper::LayerInfo info;
- info.fPaintBits |= SkLayerDrawLooper::kShader_Bit;
- SkPaint *layerPaint = dl->addLayer(info);
- layerPaint->setShader(shader);
-
- info.fPaintBits = 0;
- info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit;
- info.fPaintBits |= SkLayerDrawLooper::kColorFilter_Bit;
- info.fColorMode = SkXfermode::kDst_Mode;
- info.fOffset.set(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
- info.fPostTranslate = true;
-
- SkMaskFilter* mf = SkBlurMaskFilter::Create(aSigma, SkBlurMaskFilter::kNormal_BlurStyle, blurFlags);
- SkColor color = ColorToSkColor(aColor, 1);
- SkColorFilter* cf = SkColorFilter::CreateModeFilter(color, SkXfermode::kSrcIn_Mode);
-
-
- layerPaint = dl->addLayer(info);
- SkSafeUnref(layerPaint->setMaskFilter(mf));
- SkSafeUnref(layerPaint->setColorFilter(cf));
- layerPaint->setColor(color);
-
- // TODO: This is using the rasterizer to calculate an alpha mask
- // on both the shadow and normal layers. We should fix this
- // properly so it only happens for the shadow layer
- SkLayerRasterizer *raster = new SkLayerRasterizer();
- SkPaint maskPaint;
- SkSafeUnref(maskPaint.setShader(shader));
- raster->addLayer(maskPaint, 0, 0);
-
+ TempBitmap bitmap = GetBitmapForSurface(aSurface);
+
SkPaint paint;
- paint.setAntiAlias(true);
- SkSafeUnref(paint.setRasterizer(raster));
+
+ SkImageFilter* filter = SkDropShadowImageFilter::Create(aOffset.x, aOffset.y,
+ aSigma, aSigma,
+ ColorToSkColor(aColor, 1.0));
+
+ paint.setImageFilter(filter);
paint.setXfermodeMode(GfxOpToSkiaOp(aOperator));
- SkSafeUnref(paint.setLooper(dl));
- SkRect rect = RectToSkRect(Rect(Float(aDest.x), Float(aDest.y),
- Float(bitmap.width()), Float(bitmap.height())));
- mCanvas->drawRect(rect, paint);
+ mCanvas->drawBitmap(bitmap.mBitmap, aDest.x, aDest.y, &paint);
mCanvas->restore();
}
@@ -363,7 +419,7 @@ DrawTargetSkia::FillRect(const Rect &aRect,
{
MarkChanged();
SkRect rect = RectToSkRect(aRect);
- AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern, &aRect);
mCanvas->drawRect(rect, paint.mPaint);
}
@@ -376,7 +432,7 @@ DrawTargetSkia::Stroke(const Path *aPath,
{
MarkChanged();
MOZ_ASSERT(aPath, "Null path");
- if (aPath->GetBackendType() != BACKEND_SKIA) {
+ if (aPath->GetBackendType() != BackendType::SKIA) {
return;
}
@@ -406,7 +462,7 @@ DrawTargetSkia::StrokeRect(const Rect &aRect,
mCanvas->drawRect(RectToSkRect(aRect), paint.mPaint);
}
-void
+void
DrawTargetSkia::StrokeLine(const Point &aStart,
const Point &aEnd,
const Pattern &aPattern,
@@ -419,8 +475,8 @@ DrawTargetSkia::StrokeLine(const Point &aStart,
return;
}
- mCanvas->drawLine(SkFloatToScalar(aStart.x), SkFloatToScalar(aStart.y),
- SkFloatToScalar(aEnd.x), SkFloatToScalar(aEnd.y),
+ mCanvas->drawLine(SkFloatToScalar(aStart.x), SkFloatToScalar(aStart.y),
+ SkFloatToScalar(aEnd.x), SkFloatToScalar(aEnd.y),
paint.mPaint);
}
@@ -430,7 +486,7 @@ DrawTargetSkia::Fill(const Path *aPath,
const DrawOptions &aOptions)
{
MarkChanged();
- if (aPath->GetBackendType() != BACKEND_SKIA) {
+ if (aPath->GetBackendType() != BackendType::SKIA) {
return;
}
@@ -446,11 +502,11 @@ DrawTargetSkia::FillGlyphs(ScaledFont *aFont,
const GlyphBuffer &aBuffer,
const Pattern &aPattern,
const DrawOptions &aOptions,
- const GlyphRenderingOptions*)
+ const GlyphRenderingOptions *aRenderingOptions)
{
- if (aFont->GetType() != FONT_MAC &&
- aFont->GetType() != FONT_SKIA &&
- aFont->GetType() != FONT_GDI) {
+ if (aFont->GetType() != FontType::MAC &&
+ aFont->GetType() != FontType::SKIA &&
+ aFont->GetType() != FontType::GDI) {
return;
}
@@ -462,7 +518,30 @@ DrawTargetSkia::FillGlyphs(ScaledFont *aFont,
paint.mPaint.setTypeface(skiaFont->GetSkTypeface());
paint.mPaint.setTextSize(SkFloatToScalar(skiaFont->mSize));
paint.mPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
+
+ if (aRenderingOptions && aRenderingOptions->GetType() == FontType::CAIRO) {
+ switch (static_cast<const GlyphRenderingOptionsCairo*>(aRenderingOptions)->GetHinting()) {
+ case FontHinting::NONE:
+ paint.mPaint.setHinting(SkPaint::kNo_Hinting);
+ break;
+ case FontHinting::LIGHT:
+ paint.mPaint.setHinting(SkPaint::kSlight_Hinting);
+ break;
+ case FontHinting::NORMAL:
+ paint.mPaint.setHinting(SkPaint::kNormal_Hinting);
+ break;
+ case FontHinting::FULL:
+ paint.mPaint.setHinting(SkPaint::kFull_Hinting);
+ break;
+ }
+
+ if (static_cast<const GlyphRenderingOptionsCairo*>(aRenderingOptions)->GetAutoHinting()) {
+ paint.mPaint.setAutohinted(true);
+ }
+ } else {
+ paint.mPaint.setHinting(SkPaint::kNormal_Hinting);
+ }
+
std::vector<uint16_t> indices;
std::vector<SkPoint> offsets;
indices.resize(aBuffer.mNumGlyphs);
@@ -486,32 +565,55 @@ DrawTargetSkia::Mask(const Pattern &aSource,
AutoPaintSetup paint(mCanvas.get(), aOptions, aSource);
SkPaint maskPaint;
- SetPaintPattern(maskPaint, aMask);
-
- SkLayerRasterizer *raster = new SkLayerRasterizer();
- raster->addLayer(maskPaint);
- SkSafeUnref(paint.mPaint.setRasterizer(raster));
-
- // Skia only uses the mask rasterizer when we are drawing a path/rect.
- // Take our destination bounds and convert them into user space to use
- // as the path to draw.
- SkPath path;
- path.addRect(SkRect::MakeWH(SkScalar(mSize.width), SkScalar(mSize.height)));
-
- Matrix temp = mTransform;
- temp.Invert();
- SkMatrix mat;
- GfxMatrixToSkiaMatrix(temp, mat);
- path.transform(mat);
+ TempBitmap tmpBitmap;
+ SetPaintPattern(maskPaint, aMask, tmpBitmap);
+
+ SkLayerRasterizer::Builder builder;
+ builder.addLayer(maskPaint);
+ SkAutoTUnref<SkRasterizer> raster(builder.detachRasterizer());
+ paint.mPaint.setRasterizer(raster.get());
+
+ mCanvas->drawRect(SkRectCoveringWholeSurface(), paint.mPaint);
+}
- mCanvas->drawPath(path, paint.mPaint);
+void
+DrawTargetSkia::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aSource);
+
+ TempBitmap bitmap = GetBitmapForSurface(aMask);
+ if (bitmap.mBitmap.colorType() == kAlpha_8_SkColorType) {
+ mCanvas->drawBitmap(bitmap.mBitmap, aOffset.x, aOffset.y, &paint.mPaint);
+ } else {
+ SkPaint maskPaint;
+ TempBitmap tmpBitmap;
+ SetPaintPattern(maskPaint, SurfacePattern(aMask, ExtendMode::CLAMP), tmpBitmap);
+
+ SkMatrix transform = maskPaint.getShader()->getLocalMatrix();
+ transform.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
+ SkShader* matrixShader = SkShader::CreateLocalMatrixShader(maskPaint.getShader(), transform);
+ SkSafeUnref(maskPaint.setShader(matrixShader));
+
+ SkLayerRasterizer::Builder builder;
+ builder.addLayer(maskPaint);
+ SkAutoTUnref<SkRasterizer> raster(builder.detachRasterizer());
+ paint.mPaint.setRasterizer(raster.get());
+
+ IntSize size = aMask->GetSize();
+ Rect rect = Rect(aOffset.x, aOffset.y, size.width, size.height);
+ mCanvas->drawRect(RectToSkRect(rect), paint.mPaint);
+ }
}
TemporaryRef<SourceSurface>
DrawTargetSkia::CreateSourceSurfaceFromData(unsigned char *aData,
- const IntSize &aSize,
- int32_t aStride,
- SurfaceFormat aFormat) const
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
{
RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
@@ -519,8 +621,8 @@ DrawTargetSkia::CreateSourceSurfaceFromData(unsigned char *aData,
gfxDebug() << *this << ": Failure to create source surface from data. Size: " << aSize;
return nullptr;
}
-
- return newSurf;
+
+ return newSurf.forget();
}
TemporaryRef<DrawTarget>
@@ -530,18 +632,72 @@ DrawTargetSkia::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFor
if (!target->Init(aSize, aFormat)) {
return nullptr;
}
- return target;
+ return target.forget();
+}
+
+bool
+DrawTargetSkia::UsingSkiaGPU() const
+{
+#ifdef USE_SKIA_GPU
+ return !!mTexture;
+#else
+ return false;
+#endif
}
TemporaryRef<SourceSurface>
DrawTargetSkia::OptimizeSourceSurface(SourceSurface *aSurface) const
{
- return nullptr;
+ if (aSurface->GetType() == SurfaceType::SKIA) {
+ return aSurface;
+ }
+
+ if (!UsingSkiaGPU()) {
+ // If we're not using skia-gl then drawing doesn't require any
+ // uploading, so any data surface is fine. Call GetDataSurface
+ // to trigger any required readback so that it only happens
+ // once.
+ return aSurface->GetDataSurface();
+ }
+
+ // If we are using skia-gl then we want to copy into a surface that
+ // will cache the uploaded gl texture.
+ RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
+ DataSourceSurface::MappedSurface map;
+ if (!dataSurf->Map(DataSourceSurface::READ, &map)) {
+ return nullptr;
+ }
+
+ RefPtr<SourceSurface> result = CreateSourceSurfaceFromData(map.mData,
+ dataSurf->GetSize(),
+ map.mStride,
+ dataSurf->GetFormat());
+ dataSurf->Unmap();
+ return result.forget();
}
TemporaryRef<SourceSurface>
DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
{
+ if (aSurface.mType == NativeSurfaceType::CAIRO_SURFACE) {
+ if (aSurface.mSize.width <= 0 ||
+ aSurface.mSize.height <= 0) {
+ gfxWarning() << "Can't create a SourceSurface without a valid size";
+ return nullptr;
+ }
+ cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface);
+ return new SourceSurfaceCairo(surf, aSurface.mSize, aSurface.mFormat);
+#if USE_SKIA_GPU
+ } else if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE) {
+ RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
+ unsigned int texture = (unsigned int)((uintptr_t)aSurface.mSurface);
+ if (UsingSkiaGPU() && newSurf->InitFromTexture((DrawTargetSkia*)this, texture, aSurface.mSize, aSurface.mFormat)) {
+ return newSurf;
+ }
+ return nullptr;
+#endif
+ }
+
return nullptr;
}
@@ -551,23 +707,39 @@ DrawTargetSkia::CopySurface(SourceSurface *aSurface,
const IntPoint &aDestination)
{
//TODO: We could just use writePixels() here if the sourceRect is the entire source
-
- if (aSurface->GetType() != SURFACE_SKIA) {
+
+ if (aSurface->GetType() != SurfaceType::SKIA && aSurface->GetType() != SurfaceType::DATA) {
return;
}
MarkChanged();
-
- const SkBitmap& bitmap = static_cast<SourceSurfaceSkia*>(aSurface)->GetBitmap();
+
+ TempBitmap bitmap = GetBitmapForSurface(aSurface);
+
+ // This is a fast path that is disabled for now to mimimize risk
+ if (false && !bitmap.mBitmap.getTexture() && mCanvas->imageInfo() == bitmap.mBitmap.info()) {
+ SkBitmap bm(bitmap.mBitmap);
+ bm.lockPixels();
+ if (bm.getPixels()) {
+ SkImageInfo info = bm.info();
+ info.fWidth = aSourceRect.width;
+ info.fHeight = aSourceRect.height;
+ uint8_t* pixels = static_cast<uint8_t*>(bm.getPixels());
+ // adjust pixels for the source offset
+ pixels += aSourceRect.x + aSourceRect.y*bm.rowBytes();
+ mCanvas->writePixels(info, pixels, bm.rowBytes(), aDestination.x, aDestination.y);
+ return;
+ }
+ }
mCanvas->save();
mCanvas->resetMatrix();
- SkRect dest = IntRectToSkRect(IntRect(aDestination.x, aDestination.y, aSourceRect.width, aSourceRect.height));
+ SkRect dest = IntRectToSkRect(IntRect(aDestination.x, aDestination.y, aSourceRect.width, aSourceRect.height));
SkIRect source = IntRectToSkIRect(aSourceRect);
mCanvas->clipRect(dest, SkRegion::kReplace_Op);
SkPaint paint;
- if (mCanvas->getDevice()->config() == SkBitmap::kRGB_565_Config) {
+ if (mCanvas->imageInfo().colorType() == kRGB_565_SkColorType) {
// Set the xfermode to SOURCE_OVER to workaround
// http://code.google.com/p/skia/issues/detail?id=628
// RGB565 is opaque so they're equivalent anyway
@@ -575,15 +747,33 @@ DrawTargetSkia::CopySurface(SourceSurface *aSurface,
} else {
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
}
-
- mCanvas->drawBitmapRect(bitmap, &source, dest, &paint);
+ // drawBitmapRect with A8 bitmaps ends up doing a mask operation
+ // so we need to clear before
+ if (bitmap.mBitmap.colorType() == kAlpha_8_SkColorType) {
+ SkPaint clearPaint;
+ clearPaint.setColor(SkColorSetARGB(0, 0, 0, 0));
+ clearPaint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ mCanvas->drawPaint(clearPaint);
+ }
+ mCanvas->drawBitmapRect(bitmap.mBitmap, &source, dest, &paint);
mCanvas->restore();
}
bool
DrawTargetSkia::Init(const IntSize &aSize, SurfaceFormat aFormat)
{
- SkAutoTUnref<SkDevice> device(new SkDevice(GfxFormatToSkiaConfig(aFormat), aSize.width, aSize.height));
+ SkAlphaType alphaType = (aFormat == SurfaceFormat::B8G8R8X8) ?
+ kOpaque_SkAlphaType : kPremul_SkAlphaType;
+
+ SkImageInfo skiInfo = SkImageInfo::Make(
+ aSize.width, aSize.height,
+ GfxFormatToSkiaColorType(aFormat),
+ alphaType);
+
+ SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(skiInfo));
+ if (!device) {
+ return false;
+ }
SkBitmap bitmap = device->accessBitmap(true);
if (!bitmap.allocPixels()) {
@@ -592,60 +782,70 @@ DrawTargetSkia::Init(const IntSize &aSize, SurfaceFormat aFormat)
bitmap.eraseARGB(0, 0, 0, 0);
- SkAutoTUnref<SkCanvas> canvas(new SkCanvas(device.get()));
+ mCanvas.adopt(new SkCanvas(device.get()));
mSize = aSize;
- mCanvas = canvas.get();
mFormat = aFormat;
return true;
}
#ifdef USE_SKIA_GPU
-void
-DrawTargetSkia::InitWithFBO(unsigned int aFBOID, GrContext* aGrContext, const IntSize &aSize, SurfaceFormat aFormat)
+bool
+DrawTargetSkia::InitWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat)
{
- GrPlatformRenderTargetDesc targetDescriptor;
+ MOZ_ASSERT(aGrContext, "null GrContext");
- targetDescriptor.fWidth = aSize.width;
- targetDescriptor.fHeight = aSize.height;
- targetDescriptor.fConfig = GfxFormatToGrConfig(aFormat);
+ mGrContext = aGrContext;
+ mSize = aSize;
+ mFormat = aFormat;
+
+ GrTextureDesc targetDescriptor;
+
+ targetDescriptor.fFlags = kRenderTarget_GrTextureFlagBit;
+ targetDescriptor.fWidth = mSize.width;
+ targetDescriptor.fHeight = mSize.height;
+ targetDescriptor.fConfig = GfxFormatToGrConfig(mFormat);
+ targetDescriptor.fOrigin = kBottomLeft_GrSurfaceOrigin;
targetDescriptor.fSampleCnt = 0;
- targetDescriptor.fRenderTargetHandle = aFBOID;
- SkAutoTUnref<GrRenderTarget> target(aGrContext->createPlatformRenderTarget(targetDescriptor));
+ SkAutoTUnref<GrTexture> skiaTexture(mGrContext->createUncachedTexture(targetDescriptor, NULL, 0));
+ if (!skiaTexture) {
+ return false;
+ }
- SkAutoTUnref<SkDevice> device(new SkGpuDevice(aGrContext, target.get()));
- SkAutoTUnref<SkCanvas> canvas(new SkCanvas(device.get()));
- mSize = aSize;
+ mTexture = (uint32_t)skiaTexture->getTextureHandle();
- mCanvas = canvas.get();
- mFormat = aFormat;
+ SkAutoTUnref<SkBaseDevice> device(new SkGpuDevice(mGrContext.get(), skiaTexture->asRenderTarget()));
+ mCanvas.adopt(new SkCanvas(device.get()));
+
+ return true;
}
+
#endif
void
DrawTargetSkia::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat)
{
- bool isOpaque = false;
- if (aFormat == FORMAT_B8G8R8X8) {
+ SkAlphaType alphaType = kPremul_SkAlphaType;
+ if (aFormat == SurfaceFormat::B8G8R8X8) {
// We have to manually set the A channel to be 255 as Skia doesn't understand BGRX
ConvertBGRXToBGRA(aData, aSize, aStride);
- isOpaque = true;
+ alphaType = kOpaque_SkAlphaType;
}
-
- SkAutoTUnref<SkDevice> device(new SkDevice(GfxFormatToSkiaConfig(aFormat), aSize.width, aSize.height, isOpaque));
- SkBitmap bitmap = (SkBitmap)device->accessBitmap(true);
- bitmap.lockPixels();
+ SkBitmap bitmap;
+
+ SkImageInfo info = SkImageInfo::Make(aSize.width,
+ aSize.height,
+ GfxFormatToSkiaColorType(aFormat),
+ alphaType);
+ bitmap.setInfo(info, aStride);
bitmap.setPixels(aData);
- bitmap.setConfig(GfxFormatToSkiaConfig(aFormat), aSize.width, aSize.height, aStride);
- bitmap.unlockPixels();
- bitmap.notifyPixelsChanged();
+ mCanvas.adopt(new SkCanvas(bitmap));
- SkAutoTUnref<SkCanvas> canvas(new SkCanvas(device.get()));
mSize = aSize;
-
- mCanvas = canvas.get();
mFormat = aFormat;
}
@@ -658,11 +858,22 @@ DrawTargetSkia::SetTransform(const Matrix& aTransform)
mTransform = aTransform;
}
-TemporaryRef<PathBuilder>
+void*
+DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType)
+{
+#ifdef USE_SKIA_GPU
+ if (aType == NativeSurfaceType::OPENGL_TEXTURE) {
+ return (void*)((uintptr_t)mTexture);
+ }
+#endif
+ return nullptr;
+}
+
+
+TemporaryRef<PathBuilder>
DrawTargetSkia::CreatePathBuilder(FillRule aFillRule) const
{
- RefPtr<PathBuilderSkia> pb = new PathBuilderSkia(aFillRule);
- return pb;
+ return new PathBuilderSkia(aFillRule);
}
void
@@ -671,7 +882,7 @@ DrawTargetSkia::ClearRect(const Rect &aRect)
MarkChanged();
SkPaint paint;
mCanvas->save();
- mCanvas->clipRect(RectToSkRect(aRect), SkRegion::kIntersect_Op, USE_SOFT_CLIPPING);
+ mCanvas->clipRect(RectToSkRect(aRect), SkRegion::kIntersect_Op, true);
paint.setColor(SkColorSetARGB(0, 0, 0, 0));
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
mCanvas->drawPaint(paint);
@@ -681,13 +892,13 @@ DrawTargetSkia::ClearRect(const Rect &aRect)
void
DrawTargetSkia::PushClip(const Path *aPath)
{
- if (aPath->GetBackendType() != BACKEND_SKIA) {
+ if (aPath->GetBackendType() != BackendType::SKIA) {
return;
}
const PathSkia *skiaPath = static_cast<const PathSkia*>(aPath);
- mCanvas->save(SkCanvas::kClip_SaveFlag);
- mCanvas->clipPath(skiaPath->GetPath(), SkRegion::kIntersect_Op, USE_SOFT_CLIPPING);
+ mCanvas->save();
+ mCanvas->clipPath(skiaPath->GetPath(), SkRegion::kIntersect_Op, true);
}
void
@@ -695,8 +906,8 @@ DrawTargetSkia::PushClipRect(const Rect& aRect)
{
SkRect rect = RectToSkRect(aRect);
- mCanvas->save(SkCanvas::kClip_SaveFlag);
- mCanvas->clipRect(rect, SkRegion::kIntersect_Op, USE_SOFT_CLIPPING);
+ mCanvas->save();
+ mCanvas->clipRect(rect, SkRegion::kIntersect_Op, true);
}
void
@@ -714,36 +925,37 @@ DrawTargetSkia::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, Ex
stops[i] = aStops[i];
}
std::stable_sort(stops.begin(), stops.end());
-
+
return new GradientStopsSkia(stops, aNumStops, aExtendMode);
}
-void
-DrawTargetSkia::AppendSnapshot(SourceSurfaceSkia* aSnapshot)
+TemporaryRef<FilterNode>
+DrawTargetSkia::CreateFilter(FilterType aType)
{
- mSnapshots.push_back(aSnapshot);
+ return FilterNodeSoftware::Create(aType);
}
void
-DrawTargetSkia::RemoveSnapshot(SourceSurfaceSkia* aSnapshot)
+DrawTargetSkia::MarkChanged()
{
- std::vector<SourceSurfaceSkia*>::iterator iter = std::find(mSnapshots.begin(), mSnapshots.end(), aSnapshot);
- if (iter != mSnapshots.end()) {
- mSnapshots.erase(iter);
+ if (mSnapshot) {
+ mSnapshot->DrawTargetWillChange();
+ mSnapshot = nullptr;
}
}
+// Return a rect (in user space) that covers the entire surface by applying
+// the inverse of GetTransform() to (0, 0, mSize.width, mSize.height).
+SkRect
+DrawTargetSkia::SkRectCoveringWholeSurface() const
+{
+ return RectToSkRect(mTransform.TransformBounds(Rect(0, 0, mSize.width, mSize.height)));
+}
+
void
-DrawTargetSkia::MarkChanged()
+DrawTargetSkia::SnapshotDestroyed()
{
- if (mSnapshots.size()) {
- for (std::vector<SourceSurfaceSkia*>::iterator iter = mSnapshots.begin();
- iter != mSnapshots.end(); iter++) {
- (*iter)->DrawTargetWillChange();
- }
- // All snapshots will now have copied data.
- mSnapshots.clear();
- }
+ mSnapshot = nullptr;
}
}
diff --git a/gfx/2d/DrawTargetSkia.h b/gfx/2d/DrawTargetSkia.h
index 3f8c05e32..27a72748a 100644
--- a/gfx/2d/DrawTargetSkia.h
+++ b/gfx/2d/DrawTargetSkia.h
@@ -3,7 +3,8 @@
* 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/. */
-#pragma once
+#ifndef _MOZILLA_GFX_SOURCESURFACESKIA_H
+#define _MOZILLA_GFX_SOURCESURFACESKIA_H
#ifdef USE_SKIA_GPU
#include "skia/GrContext.h"
@@ -13,6 +14,7 @@
#include "skia/SkCanvas.h"
#include "2D.h"
+#include "HelpersSkia.h"
#include "Rect.h"
#include "PathSkia.h"
#include <sstream>
@@ -26,81 +28,92 @@ class SourceSurfaceSkia;
class DrawTargetSkia : public DrawTarget
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetSkia, override)
DrawTargetSkia();
virtual ~DrawTargetSkia();
- virtual BackendType GetType() const { return BACKEND_SKIA; }
- virtual TemporaryRef<SourceSurface> Snapshot();
- virtual IntSize GetSize() { return mSize; }
- virtual void Flush();
+ virtual DrawTargetType GetType() const override;
+ virtual BackendType GetBackendType() const override { return BackendType::SKIA; }
+ virtual TemporaryRef<SourceSurface> Snapshot() override;
+ virtual IntSize GetSize() override { return mSize; }
+ virtual void Flush() override;
virtual void DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
const Rect &aSource,
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
const Point &aDest,
const Color &aColor,
const Point &aOffset,
Float aSigma,
- CompositionOp aOperator);
- virtual void ClearRect(const Rect &aRect);
+ CompositionOp aOperator) override;
+ virtual void ClearRect(const Rect &aRect) override;
virtual void CopySurface(SourceSurface *aSurface,
const IntRect &aSourceRect,
- const IntPoint &aDestination);
+ const IntPoint &aDestination) override;
virtual void FillRect(const Rect &aRect,
const Pattern &aPattern,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void StrokeLine(const Point &aStart,
const Point &aEnd,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void Stroke(const Path *aPath,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void Fill(const Path *aPath,
const Pattern &aPattern,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void FillGlyphs(ScaledFont *aFont,
const GlyphBuffer &aBuffer,
const Pattern &aPattern,
const DrawOptions &aOptions = DrawOptions(),
- const GlyphRenderingOptions *aRenderingOptions = nullptr);
+ const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
virtual void Mask(const Pattern &aSource,
const Pattern &aMask,
- const DrawOptions &aOptions = DrawOptions());
+ const DrawOptions &aOptions = DrawOptions()) override;
virtual void MaskSurface(const Pattern &aSource,
SourceSurface *aMask,
Point aOffset,
- const DrawOptions &aOptions = DrawOptions()) { MOZ_ASSERT(0); };
- virtual void PushClip(const Path *aPath);
- virtual void PushClipRect(const Rect& aRect);
- virtual void PopClip();
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void PushClip(const Path *aPath) override;
+ virtual void PushClipRect(const Rect& aRect) override;
+ virtual void PopClip() override;
virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
int32_t aStride,
- SurfaceFormat aFormat) const;
- virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const;
+ SurfaceFormat aFormat) const override;
+ virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
virtual TemporaryRef<SourceSurface>
- CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const;
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
virtual TemporaryRef<DrawTarget>
- CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
- virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const;
- virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = EXTEND_CLAMP) const;
- virtual void SetTransform(const Matrix &aTransform);
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+ virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
+ virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
+ virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override;
+ virtual void SetTransform(const Matrix &aTransform) override;
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override;
bool Init(const IntSize &aSize, SurfaceFormat aFormat);
void Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat);
+
#ifdef USE_SKIA_GPU
- void InitWithFBO(unsigned int aFBOID, GrContext* aGrContext, const IntSize &aSize, SurfaceFormat aFormat);
+ bool InitWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat) override;
#endif
-
+
operator std::string() const {
std::stringstream stream;
stream << "DrawTargetSkia(" << this << ")";
@@ -109,15 +122,25 @@ public:
private:
friend class SourceSurfaceSkia;
- void AppendSnapshot(SourceSurfaceSkia* aSnapshot);
- void RemoveSnapshot(SourceSurfaceSkia* aSnapshot);
+ void SnapshotDestroyed();
void MarkChanged();
+ SkRect SkRectCoveringWholeSurface() const;
+
+ bool UsingSkiaGPU() const;
+
+#ifdef USE_SKIA_GPU
+ RefPtrSkia<GrContext> mGrContext;
+ uint32_t mTexture;
+#endif
+
IntSize mSize;
- SkRefPtr<SkCanvas> mCanvas;
- std::vector<SourceSurfaceSkia*> mSnapshots;
+ RefPtrSkia<SkCanvas> mCanvas;
+ SourceSurfaceSkia* mSnapshot;
};
}
}
+
+#endif // _MOZILLA_GFX_SOURCESURFACESKIA_H
diff --git a/gfx/2d/DrawTargetTiled.cpp b/gfx/2d/DrawTargetTiled.cpp
new file mode 100644
index 000000000..641b3cef7
--- /dev/null
+++ b/gfx/2d/DrawTargetTiled.cpp
@@ -0,0 +1,296 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#define _USE_MATH_DEFINES
+#include <cmath>
+
+#include "DrawTargetTiled.h"
+#include "Logging.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+DrawTargetTiled::DrawTargetTiled()
+{
+}
+
+bool
+DrawTargetTiled::Init(const TileSet& aTiles)
+{
+ if (!aTiles.mTileCount) {
+ return false;
+ }
+
+ mTiles.reserve(aTiles.mTileCount);
+ for (size_t i = 0; i < aTiles.mTileCount; ++i) {
+ mTiles.push_back(TileInternal(aTiles.mTiles[i]));
+ if (!aTiles.mTiles[i].mDrawTarget) {
+ return false;
+ }
+ if (mTiles[0].mDrawTarget->GetFormat() != mTiles.back().mDrawTarget->GetFormat() ||
+ mTiles[0].mDrawTarget->GetBackendType() != mTiles.back().mDrawTarget->GetBackendType()) {
+ return false;
+ }
+ uint32_t newXMost = max(mRect.XMost(),
+ mTiles[i].mTileOrigin.x + mTiles[i].mDrawTarget->GetSize().width);
+ uint32_t newYMost = max(mRect.YMost(),
+ mTiles[i].mTileOrigin.y + mTiles[i].mDrawTarget->GetSize().height);
+ mRect.x = min(mRect.x, mTiles[i].mTileOrigin.x);
+ mRect.y = min(mRect.y, mTiles[i].mTileOrigin.y);
+ mRect.width = newXMost - mRect.x;
+ mRect.height = newYMost - mRect.y;
+ mTiles[i].mDrawTarget->SetTransform(Matrix::Translation(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y));
+ }
+ mFormat = mTiles[0].mDrawTarget->GetFormat();
+ return true;
+}
+
+TemporaryRef<SourceSurface>
+DrawTargetTiled::Snapshot()
+{
+ return new SnapshotTiled(mTiles, mRect);
+}
+
+// Skip the mClippedOut check since this is only used for Flush() which
+// should happen even if we're clipped.
+#define TILED_COMMAND(command) \
+ void \
+ DrawTargetTiled::command() \
+ { \
+ for (size_t i = 0; i < mTiles.size(); i++) { \
+ mTiles[i].mDrawTarget->command(); \
+ } \
+ }
+#define TILED_COMMAND1(command, type1) \
+ void \
+ DrawTargetTiled::command(type1 arg1) \
+ { \
+ for (size_t i = 0; i < mTiles.size(); i++) { \
+ if (!mTiles[i].mClippedOut) \
+ mTiles[i].mDrawTarget->command(arg1); \
+ } \
+ }
+#define TILED_COMMAND3(command, type1, type2, type3) \
+ void \
+ DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3) \
+ { \
+ for (size_t i = 0; i < mTiles.size(); i++) { \
+ if (!mTiles[i].mClippedOut) \
+ mTiles[i].mDrawTarget->command(arg1, arg2, arg3); \
+ } \
+ }
+#define TILED_COMMAND4(command, type1, type2, type3, type4) \
+ void \
+ DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
+ { \
+ for (size_t i = 0; i < mTiles.size(); i++) { \
+ if (!mTiles[i].mClippedOut) \
+ mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4); \
+ } \
+ }
+#define TILED_COMMAND5(command, type1, type2, type3, type4, type5) \
+ void \
+ DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \
+ { \
+ for (size_t i = 0; i < mTiles.size(); i++) { \
+ if (!mTiles[i].mClippedOut) \
+ mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4, arg5); \
+ } \
+ }
+
+TILED_COMMAND(Flush)
+TILED_COMMAND4(DrawFilter, FilterNode*, const Rect&, const Point&, const DrawOptions&)
+TILED_COMMAND1(ClearRect, const Rect&)
+TILED_COMMAND4(MaskSurface, const Pattern&, SourceSurface*, Point, const DrawOptions&)
+TILED_COMMAND4(StrokeRect, const Rect&, const Pattern&, const StrokeOptions&, const DrawOptions&)
+TILED_COMMAND5(StrokeLine, const Point&, const Point&, const Pattern&, const StrokeOptions&, const DrawOptions&)
+TILED_COMMAND5(FillGlyphs, ScaledFont*, const GlyphBuffer&, const Pattern&, const DrawOptions&, const GlyphRenderingOptions*)
+TILED_COMMAND3(Mask, const Pattern&, const Pattern&, const DrawOptions&)
+
+void
+DrawTargetTiled::PushClip(const Path* aPath)
+{
+ mClippedOutTilesStack.push_back(std::vector<uint32_t>());
+ std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back();
+
+ Rect deviceRect = aPath->GetBounds(mTransform);
+
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut) {
+ if (deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->PushClip(aPath);
+ } else {
+ mTiles[i].mClippedOut = true;
+ clippedTiles.push_back(i);
+ }
+ }
+ }
+}
+
+void
+DrawTargetTiled::PushClipRect(const Rect& aRect)
+{
+ mClippedOutTilesStack.push_back(std::vector<uint32_t>());
+ std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back();
+
+ Rect deviceRect = mTransform.TransformBounds(aRect);
+
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut) {
+ if (deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->PushClipRect(aRect);
+ } else {
+ mTiles[i].mClippedOut = true;
+ clippedTiles.push_back(i);
+ }
+ }
+ }
+}
+
+void
+DrawTargetTiled::PopClip()
+{
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut) {
+ mTiles[i].mDrawTarget->PopClip();
+ }
+ }
+
+ std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back();
+ for (size_t i = 0; i < clippedTiles.size(); i++) {
+ mTiles[clippedTiles[i]].mClippedOut = false;
+ }
+
+ mClippedOutTilesStack.pop_back();
+}
+
+void
+DrawTargetTiled::CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination)
+{
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ IntPoint tileOrigin = mTiles[i].mTileOrigin;
+ IntSize tileSize = mTiles[i].mDrawTarget->GetSize();
+ if (!IntRect(aDestination, aSourceRect.Size()).Intersects(IntRect(tileOrigin, tileSize))) {
+ continue;
+ }
+ // CopySurface ignores the transform, account for that here.
+ mTiles[i].mDrawTarget->CopySurface(aSurface, aSourceRect, aDestination - tileOrigin);
+ }
+}
+
+void
+DrawTargetTiled::SetTransform(const Matrix& aTransform)
+{
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ Matrix mat = aTransform;
+ mat.PostTranslate(Float(-mTiles[i].mTileOrigin.x), Float(-mTiles[i].mTileOrigin.y));
+ mTiles[i].mDrawTarget->SetTransform(mat);
+ }
+ DrawTarget::SetTransform(aTransform);
+}
+
+void
+DrawTargetTiled::DrawSurface(SourceSurface* aSurface, const Rect& aDest, const Rect& aSource, const DrawSurfaceOptions& aSurfaceOptions, const DrawOptions& aDrawOptions)
+{
+ Rect deviceRect = mTransform.TransformBounds(aDest);
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut &&
+ deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->DrawSurface(aSurface, aDest, aSource, aSurfaceOptions, aDrawOptions);
+ }
+ }
+}
+
+void
+DrawTargetTiled::FillRect(const Rect& aRect, const Pattern& aPattern, const DrawOptions& aDrawOptions)
+{
+ Rect deviceRect = mTransform.TransformBounds(aRect);
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut &&
+ deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->FillRect(aRect, aPattern, aDrawOptions);
+ }
+ }
+}
+
+// The logic for this comes from _cairo_stroke_style_max_distance_from_path
+static Rect
+PathExtentsToMaxStrokeExtents(const StrokeOptions &aStrokeOptions,
+ const Rect &aRect,
+ const Matrix &aTransform)
+{
+ double styleExpansionFactor = 0.5f;
+
+ if (aStrokeOptions.mLineCap == CapStyle::SQUARE) {
+ styleExpansionFactor = M_SQRT1_2;
+ }
+
+ if (aStrokeOptions.mLineJoin == JoinStyle::MITER &&
+ styleExpansionFactor < M_SQRT2 * aStrokeOptions.mMiterLimit) {
+ styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit;
+ }
+
+ styleExpansionFactor *= aStrokeOptions.mLineWidth;
+
+ double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21);
+ double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12);
+
+ Rect result = aRect;
+ result.Inflate(dx, dy);
+ return result;
+}
+
+void
+DrawTargetTiled::Stroke(const Path* aPath, const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aDrawOptions)
+{
+ // Approximate the stroke extents, since Path::GetStrokeExtents can be slow
+ Rect deviceRect = PathExtentsToMaxStrokeExtents(aStrokeOptions,
+ aPath->GetBounds(mTransform),
+ mTransform);
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut &&
+ deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->Stroke(aPath, aPattern, aStrokeOptions, aDrawOptions);
+ }
+ }
+}
+
+void
+DrawTargetTiled::Fill(const Path* aPath, const Pattern& aPattern, const DrawOptions& aDrawOptions)
+{
+ Rect deviceRect = aPath->GetBounds(mTransform);
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (!mTiles[i].mClippedOut &&
+ deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height))) {
+ mTiles[i].mDrawTarget->Fill(aPath, aPattern, aDrawOptions);
+ }
+ }
+}
+
+}
+}
diff --git a/gfx/2d/DrawTargetTiled.h b/gfx/2d/DrawTargetTiled.h
new file mode 100644
index 000000000..75fa6a8bc
--- /dev/null
+++ b/gfx/2d/DrawTargetTiled.h
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_DRAWTARGETTILED_H_
+#define MOZILLA_GFX_DRAWTARGETTILED_H_
+
+#include "2D.h"
+#include "Filters.h"
+#include "Logging.h"
+
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+struct TileInternal : public Tile {
+ TileInternal()
+ : mClippedOut(false)
+ {}
+
+ explicit TileInternal(const Tile& aOther)
+ : Tile(aOther)
+ , mClippedOut(false)
+ {}
+
+ bool mClippedOut;
+};
+
+
+class DrawTargetTiled : public DrawTarget
+{
+public:
+ DrawTargetTiled();
+
+ bool Init(const TileSet& mTiles);
+
+ virtual bool IsTiledDrawTarget() const override { return true; }
+
+ virtual DrawTargetType GetType() const override { return mTiles[0].mDrawTarget->GetType(); }
+ virtual BackendType GetBackendType() const override { return mTiles[0].mDrawTarget->GetBackendType(); }
+ virtual TemporaryRef<SourceSurface> Snapshot() override;
+ virtual IntSize GetSize() override {
+ MOZ_ASSERT(mRect.width > 0 && mRect.height > 0);
+ return IntSize(mRect.XMost(), mRect.YMost());
+ }
+
+ virtual void Flush() override;
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions) override;
+ virtual void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator) override { /* Not implemented */ MOZ_CRASH(); }
+
+ virtual void ClearRect(const Rect &aRect) override;
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination) override;
+
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions(),
+ const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
+ virtual void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void PushClip(const Path *aPath) override;
+ virtual void PushClipRect(const Rect &aRect) override;
+ virtual void PopClip() override;
+
+ virtual void SetTransform(const Matrix &aTransform) override;
+
+ virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const override
+ {
+ return mTiles[0].mDrawTarget->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
+ }
+ virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override
+ {
+ return mTiles[0].mDrawTarget->OptimizeSourceSurface(aSurface);
+ }
+
+ virtual TemporaryRef<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override
+ {
+ return mTiles[0].mDrawTarget->CreateSourceSurfaceFromNativeSurface(aSurface);
+ }
+
+ virtual TemporaryRef<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override
+ {
+ return mTiles[0].mDrawTarget->CreateSimilarDrawTarget(aSize, aFormat);
+ }
+
+ virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override
+ {
+ return mTiles[0].mDrawTarget->CreatePathBuilder(aFillRule);
+ }
+
+ virtual TemporaryRef<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override
+ {
+ return mTiles[0].mDrawTarget->CreateGradientStops(aStops, aNumStops, aExtendMode);
+ }
+ virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override
+ {
+ return mTiles[0].mDrawTarget->CreateFilter(aType);
+ }
+
+private:
+ std::vector<TileInternal> mTiles;
+ std::vector<std::vector<uint32_t> > mClippedOutTilesStack;
+ IntRect mRect;
+};
+
+class SnapshotTiled : public SourceSurface
+{
+public:
+ SnapshotTiled(const std::vector<TileInternal>& aTiles, const IntRect& aRect)
+ : mRect(aRect)
+ {
+ for (size_t i = 0; i < aTiles.size(); i++) {
+ mSnapshots.push_back(aTiles[i].mDrawTarget->Snapshot());
+ mOrigins.push_back(aTiles[i].mTileOrigin);
+ }
+ }
+
+ virtual SurfaceType GetType() const { return SurfaceType::TILED; }
+ virtual IntSize GetSize() const {
+ MOZ_ASSERT(mRect.width > 0 && mRect.height > 0);
+ return IntSize(mRect.XMost(), mRect.YMost());
+ }
+ virtual SurfaceFormat GetFormat() const { return mSnapshots[0]->GetFormat(); }
+
+ virtual TemporaryRef<DataSourceSurface> GetDataSurface()
+ {
+ RefPtr<DataSourceSurface> surf = Factory::CreateDataSourceSurface(GetSize(), GetFormat());
+
+ DataSourceSurface::MappedSurface mappedSurf;
+ if (!surf->Map(DataSourceSurface::MapType::WRITE, &mappedSurf)) {
+ gfxCriticalError() << "DrawTargetTiled::GetDataSurface failed to map surface";
+ return nullptr;
+ }
+
+ {
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForData(BackendType::CAIRO, mappedSurf.mData,
+ GetSize(), mappedSurf.mStride, GetFormat());
+
+ for (size_t i = 0; i < mSnapshots.size(); i++) {
+ RefPtr<DataSourceSurface> dataSurf = mSnapshots[i]->GetDataSurface();
+ dt->CopySurface(dataSurf, IntRect(IntPoint(0, 0), mSnapshots[i]->GetSize()), mOrigins[i]);
+ }
+ }
+ surf->Unmap();
+
+ return surf.forget();
+ }
+
+ std::vector<RefPtr<SourceSurface>> mSnapshots;
+ std::vector<IntPoint> mOrigins;
+ IntRect mRect;
+};
+
+}
+}
+
+#endif
diff --git a/gfx/2d/ExtendInputEffectD2D1.cpp b/gfx/2d/ExtendInputEffectD2D1.cpp
new file mode 100644
index 000000000..b11c36eca
--- /dev/null
+++ b/gfx/2d/ExtendInputEffectD2D1.cpp
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "ExtendInputEffectD2D1.h"
+
+#include "Logging.h"
+
+#include "ShadersD2D1.h"
+#include "HelpersD2D.h"
+
+#include <vector>
+
+#define TEXTW(x) L##x
+#define XML(X) TEXTW(#X) // This macro creates a single string from multiple lines of text.
+
+static const PCWSTR kXmlDescription =
+ XML(
+ <?xml version='1.0'?>
+ <Effect>
+ <!-- System Properties -->
+ <Property name='DisplayName' type='string' value='ExtendInputEffect'/>
+ <Property name='Author' type='string' value='Mozilla'/>
+ <Property name='Category' type='string' value='Utility Effects'/>
+ <Property name='Description' type='string' value='This effect is used to extend the output rect of any input effect to a specified rect.'/>
+ <Inputs>
+ <Input name='InputEffect'/>
+ </Inputs>
+ <Property name='OutputRect' type='vector4'>
+ <Property name='DisplayName' type='string' value='Output Rect'/>
+ </Property>
+ </Effect>
+ );
+
+// {FB947CDA-718E-40CC-AE7B-D255830D7D14}
+static const GUID GUID_SampleExtendInputPS =
+ {0xfb947cda, 0x718e, 0x40cc, {0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14}};
+// {2C468128-6546-453C-8E25-F2DF0DE10A0F}
+static const GUID GUID_SampleExtendInputA0PS =
+ {0x2c468128, 0x6546, 0x453c, {0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf}};
+
+namespace mozilla {
+namespace gfx {
+
+ExtendInputEffectD2D1::ExtendInputEffectD2D1()
+ : mRefCount(0)
+ , mOutputRect(D2D1::Vector4F(-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX))
+{
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph)
+{
+ HRESULT hr;
+ hr = pTransformGraph->SetSingleTransformNode(this);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
+{
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph)
+{
+ return E_NOTIMPL;
+}
+
+IFACEMETHODIMP_(ULONG)
+ExtendInputEffectD2D1::AddRef()
+{
+ return ++mRefCount;
+}
+
+IFACEMETHODIMP_(ULONG)
+ExtendInputEffectD2D1::Release()
+{
+ if (!--mRefCount) {
+ delete this;
+ return 0;
+ }
+ return mRefCount;
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::QueryInterface(const IID &aIID, void **aPtr)
+{
+ if (!aPtr) {
+ return E_POINTER;
+ }
+
+ if (aIID == IID_IUnknown) {
+ *aPtr = static_cast<IUnknown*>(static_cast<ID2D1EffectImpl*>(this));
+ } else if (aIID == IID_ID2D1EffectImpl) {
+ *aPtr = static_cast<ID2D1EffectImpl*>(this);
+ } else if (aIID == IID_ID2D1DrawTransform) {
+ *aPtr = static_cast<ID2D1DrawTransform*>(this);
+ } else if (aIID == IID_ID2D1Transform) {
+ *aPtr = static_cast<ID2D1Transform*>(this);
+ } else if (aIID == IID_ID2D1TransformNode) {
+ *aPtr = static_cast<ID2D1TransformNode*>(this);
+ } else {
+ return E_NOINTERFACE;
+ }
+
+ static_cast<IUnknown*>(*aPtr)->AddRef();
+ return S_OK;
+}
+
+static D2D1_RECT_L
+ConvertFloatToLongRect(const D2D1_VECTOR_4F& aRect)
+{
+ // Clamp values to LONG range. We can't use std::min/max here because we want
+ // the comparison to operate on a type that's different from the type of the
+ // result.
+ return D2D1::RectL(aRect.x <= LONG_MIN ? LONG_MIN : LONG(aRect.x),
+ aRect.y <= LONG_MIN ? LONG_MIN : LONG(aRect.y),
+ aRect.z >= LONG_MAX ? LONG_MAX : LONG(aRect.z),
+ aRect.w >= LONG_MAX ? LONG_MAX : LONG(aRect.w));
+}
+
+static D2D1_RECT_L
+IntersectRect(const D2D1_RECT_L& aRect1, const D2D1_RECT_L& aRect2)
+{
+ return D2D1::RectL(std::max(aRect1.left, aRect2.left),
+ std::max(aRect1.top, aRect2.top),
+ std::min(aRect1.right, aRect2.right),
+ std::min(aRect1.bottom, aRect2.bottom));
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects,
+ const D2D1_RECT_L* pInputOpaqueSubRects,
+ UINT32 inputRectCount,
+ D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pOutputOpaqueSubRect)
+{
+ // This transform only accepts one input, so there will only be one input rect.
+ if (inputRectCount != 1) {
+ return E_INVALIDARG;
+ }
+
+ // Set the output rect to the specified rect. This is the whole purpose of this effect.
+ *pOutputRect = ConvertFloatToLongRect(mOutputRect);
+ *pOutputOpaqueSubRect = IntersectRect(*pOutputRect, pInputOpaqueSubRects[0]);
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pInputRects,
+ UINT32 inputRectCount) const
+{
+ if (inputRectCount != 1) {
+ return E_INVALIDARG;
+ }
+
+ *pInputRects = *pOutputRect;
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ExtendInputEffectD2D1::MapInvalidRect(UINT32 inputIndex,
+ D2D1_RECT_L invalidInputRect,
+ D2D1_RECT_L* pInvalidOutputRect) const
+{
+ MOZ_ASSERT(inputIndex = 0);
+
+ *pInvalidOutputRect = invalidInputRect;
+ return S_OK;
+}
+
+HRESULT
+ExtendInputEffectD2D1::Register(ID2D1Factory1 *aFactory)
+{
+ D2D1_PROPERTY_BINDING bindings[] = {
+ D2D1_VALUE_TYPE_BINDING(L"OutputRect", &ExtendInputEffectD2D1::SetOutputRect, &ExtendInputEffectD2D1::GetOutputRect),
+ };
+ HRESULT hr = aFactory->RegisterEffectFromString(CLSID_ExtendInputEffect, kXmlDescription, bindings, 1, CreateEffect);
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to register extend input effect.";
+ }
+ return hr;
+}
+
+void
+ExtendInputEffectD2D1::Unregister(ID2D1Factory1 *aFactory)
+{
+ aFactory->UnregisterEffect(CLSID_ExtendInputEffect);
+}
+
+HRESULT __stdcall
+ExtendInputEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
+{
+ *aEffectImpl = static_cast<ID2D1EffectImpl*>(new ExtendInputEffectD2D1());
+ (*aEffectImpl)->AddRef();
+
+ return S_OK;
+}
+
+}
+}
diff --git a/gfx/2d/ExtendInputEffectD2D1.h b/gfx/2d/ExtendInputEffectD2D1.h
new file mode 100644
index 000000000..093063897
--- /dev/null
+++ b/gfx/2d/ExtendInputEffectD2D1.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_EXTENDINPUTEFFECTD2D1_H_
+#define MOZILLA_GFX_EXTENDINPUTEFFECTD2D1_H_
+
+#include <d2d1_1.h>
+#include <d2d1effectauthor.h>
+#include <d2d1effecthelpers.h>
+
+#include "2D.h"
+#include "mozilla/Attributes.h"
+
+// {97143DC6-CBC4-4DD4-A8BA-13342B0BA46D}
+DEFINE_GUID(CLSID_ExtendInputEffect,
+0x5fb55c7c, 0xd795, 0x4ba3, 0xa9, 0x5c, 0x22, 0x82, 0x5d, 0x0c, 0x4d, 0xf7);
+
+namespace mozilla {
+namespace gfx {
+
+enum {
+ EXTENDINPUT_PROP_OUTPUT_RECT = 0
+};
+
+// An effect type that passes through its input unchanged but sets the effect's
+// output rect to a specified rect. Unlike the built-in Crop effect, the
+// ExtendInput effect can extend the input rect, and not just make it smaller.
+// The added margins are filled with transparent black.
+// Some effects have different output depending on their input effect's output
+// rect, for example the Border effect (which repeats the edges of its input
+// effect's output rect) or the component transfer and color matrix effects
+// (which can transform transparent pixels into non-transparent ones, but only
+// inside their input effect's output rect).
+class ExtendInputEffectD2D1 final : public ID2D1EffectImpl
+ , public ID2D1DrawTransform
+{
+public:
+ // ID2D1EffectImpl
+ IFACEMETHODIMP Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph);
+ IFACEMETHODIMP PrepareForRender(D2D1_CHANGE_TYPE changeType);
+ IFACEMETHODIMP SetGraph(ID2D1TransformGraph* pGraph);
+
+ // IUnknown
+ IFACEMETHODIMP_(ULONG) AddRef();
+ IFACEMETHODIMP_(ULONG) Release();
+ IFACEMETHODIMP QueryInterface(REFIID riid, void** ppOutput);
+
+ // ID2D1Transform
+ IFACEMETHODIMP MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects,
+ const D2D1_RECT_L* pInputOpaqueSubRects,
+ UINT32 inputRectCount,
+ D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pOutputOpaqueSubRect);
+ IFACEMETHODIMP MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pInputRects,
+ UINT32 inputRectCount) const;
+ IFACEMETHODIMP MapInvalidRect(UINT32 inputIndex,
+ D2D1_RECT_L invalidInputRect,
+ D2D1_RECT_L* pInvalidOutputRect) const;
+
+ // ID2D1TransformNode
+ IFACEMETHODIMP_(UINT32) GetInputCount() const { return 1; }
+
+ // ID2D1DrawTransform
+ IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo *pDrawInfo) { return S_OK; }
+
+ static HRESULT Register(ID2D1Factory1* aFactory);
+ static void Unregister(ID2D1Factory1* aFactory);
+ static HRESULT __stdcall CreateEffect(IUnknown** aEffectImpl);
+
+ HRESULT SetOutputRect(D2D1_VECTOR_4F aOutputRect)
+ { mOutputRect = aOutputRect; return S_OK; }
+ D2D1_VECTOR_4F GetOutputRect() const { return mOutputRect; }
+
+private:
+ ExtendInputEffectD2D1();
+
+ uint32_t mRefCount;
+ D2D1_VECTOR_4F mOutputRect;
+};
+
+}
+}
+#undef SIMPLE_PROP
+
+#endif
diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp
index e98df9d74..6750c28f8 100644
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -7,18 +7,19 @@
#ifdef USE_CAIRO
#include "DrawTargetCairo.h"
-#include "ScaledFontBase.h"
+#include "ScaledFontCairo.h"
#endif
#ifdef USE_SKIA
#include "DrawTargetSkia.h"
#include "ScaledFontBase.h"
#ifdef MOZ_ENABLE_FREETYPE
-#include "ScaledFontFreetype.h"
+#define USE_SKIA_FREETYPE
+#include "ScaledFontCairo.h"
#endif
#endif
-#if defined(WIN32) && defined(USE_SKIA)
+#if defined(WIN32)
#include "ScaledFontWin.h"
#endif
@@ -33,11 +34,14 @@
#ifdef WIN32
#include "DrawTargetD2D.h"
+#include "DrawTargetD2D1.h"
#include "ScaledFontDWrite.h"
#include <d3d10_1.h>
+#include "HelpersD2D.h"
#endif
#include "DrawTargetDual.h"
+#include "DrawTargetTiled.h"
#include "DrawTargetRecording.h"
#include "SourceSurfaceRawData.h"
@@ -46,8 +50,10 @@
#include "Logging.h"
-#ifdef PR_LOGGING
-PRLogModuleInfo *
+#include "mozilla/CheckedInt.h"
+
+#if defined(PR_LOGGING)
+GFX2D_API PRLogModuleInfo *
GetGFX2DLog()
{
static PRLogModuleInfo *sLog;
@@ -63,6 +69,9 @@ enum CPUIDRegister { eax = 0, ebx = 1, ecx = 2, edx = 3 };
#ifdef HAVE_CPUID_H
+#if !(defined(__SSE2__) || defined(_M_X64) || \
+ (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
+// cpuid.h is available on gcc 4.3 and higher on i386 and x86_64
#include <cpuid.h>
static inline bool
@@ -72,6 +81,7 @@ HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit)
return __get_cpuid(level, &regs[0], &regs[1], &regs[2], &regs[3]) &&
(regs[reg] & bit);
}
+#endif
#define HAVE_CPU_DETECTION
#else
@@ -144,15 +154,69 @@ HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit)
namespace mozilla {
namespace gfx {
-// XXX - Need to define an API to set this.
-int sGfxLogLevel = LOG_DEBUG;
+// These values we initialize with should match those in
+// PreferenceAccess::RegisterAll method.
+int32_t PreferenceAccess::sGfxLogLevel = LOG_DEFAULT;
+
+PreferenceAccess* PreferenceAccess::sAccess = nullptr;
+PreferenceAccess::~PreferenceAccess()
+{
+}
+
+// Just a placeholder, the derived class will set the variable to default
+// if the preference doesn't exist.
+void PreferenceAccess::LivePref(const char* aName, int32_t* aVar, int32_t aDef)
+{
+ *aVar = aDef;
+}
+
+// This will be called with the derived class, so we will want to register
+// the callbacks with it.
+void PreferenceAccess::SetAccess(PreferenceAccess* aAccess) {
+ sAccess = aAccess;
+ if (sAccess) {
+ RegisterAll();
+ }
+}
+
#ifdef WIN32
ID3D10Device1 *Factory::mD3D10Device;
+ID3D11Device *Factory::mD3D11Device;
+ID2D1Device *Factory::mD2D1Device;
#endif
DrawEventRecorder *Factory::mRecorder;
+mozilla::gfx::Config* Factory::sConfig = nullptr;
+
+void
+Factory::Init(const Config& aConfig)
+{
+ MOZ_ASSERT(!sConfig);
+ sConfig = new Config(aConfig);
+
+ // Make sure we don't completely break rendering because of a typo in the
+ // pref or whatnot.
+ const int32_t kMinAllocPref = 10000000;
+ const int32_t kMinSizePref = 2048;
+ if (sConfig->mMaxAllocSize < kMinAllocPref) {
+ sConfig->mMaxAllocSize = kMinAllocPref;
+ }
+ if (sConfig->mMaxTextureSize < kMinSizePref) {
+ sConfig->mMaxTextureSize = kMinSizePref;
+ }
+}
+
+void
+Factory::ShutDown()
+{
+ if (sConfig) {
+ delete sConfig;
+ sConfig = nullptr;
+ }
+}
+
bool
Factory::HasSSE2()
{
@@ -162,19 +226,110 @@ Factory::HasSSE2()
// cl.exe with -arch:SSE2 (default on x64 compiler)
return true;
#elif defined(HAVE_CPU_DETECTION)
- return HasCPUIDBit(1u, edx, (1u<<26));
+ static enum {
+ UNINITIALIZED,
+ NO_SSE2,
+ HAS_SSE2
+ } sDetectionState = UNINITIALIZED;
+
+ if (sDetectionState == UNINITIALIZED) {
+ sDetectionState = HasCPUIDBit(1u, edx, (1u<<26)) ? HAS_SSE2 : NO_SSE2;
+ }
+ return sDetectionState == HAS_SSE2;
#else
return false;
#endif
}
+// If the size is "reasonable", we want gfxCriticalError to assert, so
+// this is the option set up for it.
+inline int LoggerOptionsBasedOnSize(const IntSize& aSize)
+{
+ return CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize));
+}
+
+bool
+Factory::ReasonableSurfaceSize(const IntSize &aSize)
+{
+ return Factory::CheckSurfaceSize(aSize, 8192);
+}
+
+bool
+Factory::AllowedSurfaceSize(const IntSize &aSize)
+{
+ if (sConfig) {
+ return Factory::CheckSurfaceSize(aSize,
+ sConfig->mMaxTextureSize,
+ sConfig->mMaxAllocSize);
+ }
+
+ return CheckSurfaceSize(aSize);
+}
+
+bool
+Factory::CheckSurfaceSize(const IntSize &sz,
+ int32_t extentLimit,
+ int32_t allocLimit)
+{
+ if (sz.width <= 0 || sz.height <= 0) {
+ gfxDebug() << "Surface width or height <= 0!";
+ return false;
+ }
+
+ // reject images with sides bigger than limit
+ if (extentLimit && (sz.width > extentLimit || sz.height > extentLimit)) {
+ gfxDebug() << "Surface size too large (exceeds extent limit)!";
+ return false;
+ }
+
+ // make sure the surface area doesn't overflow a int32_t
+ CheckedInt<int32_t> tmp = sz.width;
+ tmp *= sz.height;
+ if (!tmp.isValid()) {
+ gfxDebug() << "Surface size too large (would overflow)!";
+ return false;
+ }
+
+ // assuming 4 bytes per pixel, make sure the allocation size
+ // doesn't overflow a int32_t either
+ CheckedInt<int32_t> stride = sz.width;
+ stride *= 4;
+
+ // When aligning the stride to 16 bytes, it can grow by up to 15 bytes.
+ stride += 16 - 1;
+
+ if (!stride.isValid()) {
+ gfxDebug() << "Surface size too large (stride overflows int32_t)!";
+ return false;
+ }
+
+ CheckedInt<int32_t> numBytes = GetAlignedStride<16>(sz.width * 4);
+ numBytes *= sz.height;
+ if (!numBytes.isValid()) {
+ gfxDebug() << "Surface size too large (allocation size would overflow int32_t)!";
+ return false;
+ }
+
+ if (allocLimit && allocLimit < numBytes.value()) {
+ gfxDebug() << "Surface size too large (exceeds allocation limit)!";
+ return false;
+ }
+
+ return true;
+}
+
TemporaryRef<DrawTarget>
Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat)
{
+ if (!AllowedSurfaceSize(aSize)) {
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size " << aSize;
+ return nullptr;
+ }
+
RefPtr<DrawTarget> retVal;
switch (aBackend) {
#ifdef WIN32
- case BACKEND_DIRECT2D:
+ case BackendType::DIRECT2D:
{
RefPtr<DrawTargetD2D> newTarget;
newTarget = new DrawTargetD2D();
@@ -183,9 +338,18 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor
}
break;
}
+ case BackendType::DIRECT2D1_1:
+ {
+ RefPtr<DrawTargetD2D1> newTarget;
+ newTarget = new DrawTargetD2D1();
+ if (newTarget->Init(aSize, aFormat)) {
+ retVal = newTarget;
+ }
+ break;
+ }
#elif defined XP_MACOSX
- case BACKEND_COREGRAPHICS:
- case BACKEND_COREGRAPHICS_ACCELERATED:
+ case BackendType::COREGRAPHICS:
+ case BackendType::COREGRAPHICS_ACCELERATED:
{
RefPtr<DrawTargetCG> newTarget;
newTarget = new DrawTargetCG();
@@ -196,7 +360,7 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor
}
#endif
#ifdef USE_SKIA
- case BACKEND_SKIA:
+ case BackendType::SKIA:
{
RefPtr<DrawTargetSkia> newTarget;
newTarget = new DrawTargetSkia();
@@ -206,23 +370,32 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor
break;
}
#endif
+#ifdef USE_CAIRO
+ case BackendType::CAIRO:
+ {
+ RefPtr<DrawTargetCairo> newTarget;
+ newTarget = new DrawTargetCairo();
+ if (newTarget->Init(aSize, aFormat)) {
+ retVal = newTarget;
+ }
+ break;
+ }
+#endif
default:
gfxDebug() << "Invalid draw target type specified.";
return nullptr;
}
if (mRecorder && retVal) {
- RefPtr<DrawTarget> recordDT;
- recordDT = new DrawTargetRecording(mRecorder, retVal);
- return recordDT;
+ return new DrawTargetRecording(mRecorder, retVal);
}
if (!retVal) {
// Failed
- gfxDebug() << "Failed to create DrawTarget, Type: " << aBackend << " Size: " << aSize;
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to create DrawTarget, Type: " << int(aBackend) << " Size: " << aSize;
}
-
- return retVal;
+
+ return retVal.forget();
}
TemporaryRef<DrawTarget>
@@ -232,30 +405,48 @@ Factory::CreateRecordingDrawTarget(DrawEventRecorder *aRecorder, DrawTarget *aDT
}
TemporaryRef<DrawTarget>
-Factory::CreateDrawTargetForData(BackendType aBackend,
- unsigned char *aData,
- const IntSize &aSize,
- int32_t aStride,
+Factory::CreateDrawTargetForData(BackendType aBackend,
+ unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
SurfaceFormat aFormat)
{
+ MOZ_ASSERT(aData);
+ if (!AllowedSurfaceSize(aSize)) {
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size " << aSize;
+ return nullptr;
+ }
+
RefPtr<DrawTarget> retVal;
switch (aBackend) {
#ifdef USE_SKIA
- case BACKEND_SKIA:
+ case BackendType::SKIA:
{
RefPtr<DrawTargetSkia> newTarget;
newTarget = new DrawTargetSkia();
newTarget->Init(aData, aSize, aStride, aFormat);
retVal = newTarget;
+ break;
}
#endif
#ifdef XP_MACOSX
- case BACKEND_COREGRAPHICS:
+ case BackendType::COREGRAPHICS:
{
RefPtr<DrawTargetCG> newTarget = new DrawTargetCG();
if (newTarget->Init(aBackend, aData, aSize, aStride, aFormat))
- return newTarget;
+ return newTarget.forget();
+ break;
+ }
+#endif
+#ifdef USE_CAIRO
+ case BackendType::CAIRO:
+ {
+ RefPtr<DrawTargetCairo> newTarget;
+ newTarget = new DrawTargetCairo();
+ if (newTarget->Init(aData, aSize, aStride, aFormat)) {
+ retVal = newTarget.forget();
+ }
break;
}
#endif
@@ -265,13 +456,69 @@ Factory::CreateDrawTargetForData(BackendType aBackend,
}
if (mRecorder && retVal) {
- RefPtr<DrawTarget> recordDT = new DrawTargetRecording(mRecorder, retVal);
- return recordDT;
+ return new DrawTargetRecording(mRecorder, retVal, true);
}
- gfxDebug() << "Failed to create DrawTarget, Type: " << aBackend << " Size: " << aSize;
- // Failed
- return nullptr;
+ if (!retVal) {
+ gfxDebug() << "Failed to create DrawTarget, Type: " << int(aBackend) << " Size: " << aSize;
+ }
+
+ return retVal.forget();
+}
+
+TemporaryRef<DrawTarget>
+Factory::CreateTiledDrawTarget(const TileSet& aTileSet)
+{
+ RefPtr<DrawTargetTiled> dt = new DrawTargetTiled();
+
+ if (!dt->Init(aTileSet)) {
+ return nullptr;
+ }
+
+ return dt.forget();
+}
+
+bool
+Factory::DoesBackendSupportDataDrawtarget(BackendType aType)
+{
+ switch (aType) {
+ case BackendType::DIRECT2D:
+ case BackendType::DIRECT2D1_1:
+ case BackendType::RECORDING:
+ case BackendType::NONE:
+ case BackendType::COREGRAPHICS_ACCELERATED:
+ return false;
+ case BackendType::CAIRO:
+ case BackendType::COREGRAPHICS:
+ case BackendType::SKIA:
+ return true;
+ }
+
+ return false;
+}
+
+uint32_t
+Factory::GetMaxSurfaceSize(BackendType aType)
+{
+ switch (aType) {
+ case BackendType::CAIRO:
+ case BackendType::COREGRAPHICS:
+ return DrawTargetCairo::GetMaxSurfaceSize();
+#ifdef XP_MACOSX
+ case BackendType::COREGRAPHICS_ACCELERATED:
+ return DrawTargetCG::GetMaxSurfaceSize();
+#endif
+ case BackendType::SKIA:
+ return INT_MAX;
+#ifdef WIN32
+ case BackendType::DIRECT2D:
+ return DrawTargetD2D::GetMaxSurfaceSize();
+ case BackendType::DIRECT2D1_1:
+ return DrawTargetD2D1::GetMaxSurfaceSize();
+#endif
+ default:
+ return 0;
+ }
}
TemporaryRef<ScaledFont>
@@ -279,37 +526,27 @@ Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSiz
{
switch (aNativeFont.mType) {
#ifdef WIN32
- case NATIVE_FONT_DWRITE_FONT_FACE:
+ case NativeFontType::DWRITE_FONT_FACE:
{
return new ScaledFontDWrite(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aSize);
}
#if defined(USE_CAIRO) || defined(USE_SKIA)
- case NATIVE_FONT_GDI_FONT_FACE:
+ case NativeFontType::GDI_FONT_FACE:
{
return new ScaledFontWin(static_cast<LOGFONT*>(aNativeFont.mFont), aSize);
}
#endif
#endif
#ifdef XP_MACOSX
- case NATIVE_FONT_MAC_FONT_FACE:
+ case NativeFontType::MAC_FONT_FACE:
{
return new ScaledFontMac(static_cast<CGFontRef>(aNativeFont.mFont), aSize);
}
#endif
-#ifdef USE_SKIA
-#ifdef MOZ_ENABLE_FREETYPE
- case NATIVE_FONT_SKIA_FONT_FACE:
+#if defined(USE_CAIRO) || defined(USE_SKIA_FREETYPE)
+ case NativeFontType::CAIRO_FONT_FACE:
{
- return new ScaledFontFreetype(static_cast<FontOptions*>(aNativeFont.mFont), aSize);
- }
-#endif
-#endif
-#ifdef USE_CAIRO
- case NATIVE_FONT_CAIRO_FONT_FACE:
- {
- ScaledFontBase* fontBase = new ScaledFontBase(aSize);
- fontBase->SetCairoScaledFont(static_cast<cairo_scaled_font_t*>(aNativeFont.mFont));
- return fontBase;
+ return new ScaledFontCairo(static_cast<cairo_scaled_font_t*>(aNativeFont.mFont), aSize);
}
#endif
default:
@@ -325,7 +562,7 @@ Factory::CreateScaledFontForTrueTypeData(uint8_t *aData, uint32_t aSize,
{
switch (aType) {
#ifdef WIN32
- case FONT_DWRITE:
+ case FontType::DWRITE:
{
return new ScaledFontDWrite(aData, aSize, aFaceIndex, aGlyphSize);
}
@@ -346,16 +583,36 @@ Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont, Float aSize, c
// Therefore, we just reuse CreateScaledFontForNativeFont's implementation.
RefPtr<ScaledFont> font = CreateScaledFontForNativeFont(aNativeFont, aSize);
static_cast<ScaledFontBase*>(font.get())->SetCairoScaledFont(aScaledFont);
- return font;
+ return font.forget();
#else
return nullptr;
#endif
}
+TemporaryRef<DrawTarget>
+Factory::CreateDualDrawTarget(DrawTarget *targetA, DrawTarget *targetB)
+{
+ MOZ_ASSERT(targetA && targetB);
+
+ RefPtr<DrawTarget> newTarget =
+ new DrawTargetDual(targetA, targetB);
+
+ RefPtr<DrawTarget> retVal = newTarget;
+
+ if (mRecorder) {
+ retVal = new DrawTargetRecording(mRecorder, retVal);
+ }
+
+ return retVal.forget();
+}
+
+
#ifdef WIN32
TemporaryRef<DrawTarget>
Factory::CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat)
{
+ MOZ_ASSERT(aTexture);
+
RefPtr<DrawTargetD2D> newTarget;
newTarget = new DrawTargetD2D();
@@ -363,10 +620,10 @@ Factory::CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceForma
RefPtr<DrawTarget> retVal = newTarget;
if (mRecorder) {
- retVal = new DrawTargetRecording(mRecorder, retVal);
+ retVal = new DrawTargetRecording(mRecorder, retVal, true);
}
- return retVal;
+ return retVal.forget();
}
gfxWarning() << "Failed to create draw target for D3D10 texture.";
@@ -380,18 +637,19 @@ Factory::CreateDualDrawTargetForD3D10Textures(ID3D10Texture2D *aTextureA,
ID3D10Texture2D *aTextureB,
SurfaceFormat aFormat)
{
+ MOZ_ASSERT(aTextureA && aTextureB);
RefPtr<DrawTargetD2D> newTargetA;
RefPtr<DrawTargetD2D> newTargetB;
newTargetA = new DrawTargetD2D();
if (!newTargetA->Init(aTextureA, aFormat)) {
- gfxWarning() << "Failed to create draw target for D3D10 texture.";
+ gfxWarning() << "Failed to create dual draw target for D3D10 texture.";
return nullptr;
}
newTargetB = new DrawTargetD2D();
if (!newTargetB->Init(aTextureB, aFormat)) {
- gfxWarning() << "Failed to create draw target for D3D10 texture.";
+ gfxWarning() << "Failed to create new draw target for D3D10 texture.";
return nullptr;
}
@@ -404,28 +662,98 @@ Factory::CreateDualDrawTargetForD3D10Textures(ID3D10Texture2D *aTextureA,
retVal = new DrawTargetRecording(mRecorder, retVal);
}
- return retVal;
+ return retVal.forget();
}
void
Factory::SetDirect3D10Device(ID3D10Device1 *aDevice)
{
+ // do not throw on failure; return error codes and disconnect the device
+ // On Windows 8 error codes are the default, but on Windows 7 the
+ // default is to throw (or perhaps only with some drivers?)
+ aDevice->SetExceptionMode(0);
mD3D10Device = aDevice;
}
ID3D10Device1*
Factory::GetDirect3D10Device()
{
+#ifdef DEBUG
+ if (mD3D10Device) {
+ UINT mode = mD3D10Device->GetExceptionMode();
+ MOZ_ASSERT(0 == mode);
+ }
+#endif
return mD3D10Device;
}
+TemporaryRef<DrawTarget>
+Factory::CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat)
+{
+ MOZ_ASSERT(aTexture);
+
+ RefPtr<DrawTargetD2D1> newTarget;
+
+ newTarget = new DrawTargetD2D1();
+ if (newTarget->Init(aTexture, aFormat)) {
+ RefPtr<DrawTarget> retVal = newTarget;
+
+ if (mRecorder) {
+ retVal = new DrawTargetRecording(mRecorder, retVal, true);
+ }
+
+ return retVal;
+ }
+
+ gfxWarning() << "Failed to create draw target for D3D11 texture.";
+
+ // Failed
+ return nullptr;
+}
+
+void
+Factory::SetDirect3D11Device(ID3D11Device *aDevice)
+{
+ mD3D11Device = aDevice;
+
+ if (mD2D1Device) {
+ mD2D1Device->Release();
+ mD2D1Device = nullptr;
+ }
+
+ if (!aDevice) {
+ return;
+ }
+
+ RefPtr<ID2D1Factory1> factory = D2DFactory1();
+
+ RefPtr<IDXGIDevice> device;
+ aDevice->QueryInterface((IDXGIDevice**)byRef(device));
+ factory->CreateDevice(device, &mD2D1Device);
+}
+
+ID3D11Device*
+Factory::GetDirect3D11Device()
+{
+ return mD3D11Device;
+}
+
+ID2D1Device*
+Factory::GetD2D1Device()
+{
+ return mD2D1Device;
+}
+
+bool
+Factory::SupportsD2D1()
+{
+ return !!D2DFactory1();
+}
+
TemporaryRef<GlyphRenderingOptions>
Factory::CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams)
{
- RefPtr<GlyphRenderingOptions> options =
- new GlyphRenderingOptionsDWrite(aParams);
-
- return options;
+ return new GlyphRenderingOptionsDWrite(aParams);
}
uint64_t
@@ -443,6 +771,11 @@ Factory::GetD2DVRAMUsageSourceSurface()
void
Factory::D2DCleanup()
{
+ if (mD2D1Device) {
+ mD2D1Device->Release();
+ mD2D1Device = nullptr;
+ }
+ DrawTargetD2D1::CleanupD2D();
DrawTargetD2D::CleanupD2D();
}
@@ -450,45 +783,137 @@ Factory::D2DCleanup()
#ifdef USE_SKIA_GPU
TemporaryRef<DrawTarget>
-Factory::CreateSkiaDrawTargetForFBO(unsigned int aFBOID, GrContext *aGrContext, const IntSize &aSize, SurfaceFormat aFormat)
+Factory::CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat)
{
- RefPtr<DrawTargetSkia> newTarget = new DrawTargetSkia();
- newTarget->InitWithFBO(aFBOID, aGrContext, aSize, aFormat);
- return newTarget;
+ RefPtr<DrawTarget> newTarget = new DrawTargetSkia();
+ if (!newTarget->InitWithGrContext(aGrContext, aSize, aFormat)) {
+ return nullptr;
+ }
+ return newTarget.forget();
}
+
#endif // USE_SKIA_GPU
+void
+Factory::PurgeAllCaches()
+{
+}
+
+#ifdef USE_SKIA_FREETYPE
+TemporaryRef<GlyphRenderingOptions>
+Factory::CreateCairoGlyphRenderingOptions(FontHinting aHinting, bool aAutoHinting)
+{
+ RefPtr<GlyphRenderingOptionsCairo> options =
+ new GlyphRenderingOptionsCairo();
+
+ options->SetHinting(aHinting);
+ options->SetAutoHinting(aAutoHinting);
+ return options.forget();
+}
+#endif
+
TemporaryRef<DrawTarget>
-Factory::CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize)
+Factory::CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
{
RefPtr<DrawTarget> retVal;
#ifdef USE_CAIRO
RefPtr<DrawTargetCairo> newTarget = new DrawTargetCairo();
- if (newTarget->Init(aSurface, aSize)) {
+ if (newTarget->Init(aSurface, aSize, aFormat)) {
retVal = newTarget;
}
if (mRecorder && retVal) {
- RefPtr<DrawTarget> recordDT = new DrawTargetRecording(mRecorder, retVal);
- return recordDT;
+ RefPtr<DrawTarget> recordDT = new DrawTargetRecording(mRecorder, retVal, true);
+ return recordDT.forget();
}
#endif
- return retVal;
+ return retVal.forget();
+}
+
+#ifdef XP_MACOSX
+TemporaryRef<DrawTarget>
+Factory::CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize)
+{
+ RefPtr<DrawTarget> retVal;
+
+ RefPtr<DrawTargetCG> newTarget = new DrawTargetCG();
+
+ if (newTarget->Init(cg, aSize)) {
+ retVal = newTarget;
+ }
+
+ if (mRecorder && retVal) {
+ return new DrawTargetRecording(mRecorder, retVal);
+ }
+ return retVal.forget();
+}
+
+TemporaryRef<GlyphRenderingOptions>
+Factory::CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor)
+{
+ return new GlyphRenderingOptionsCG(aFontSmoothingBackgroundColor);
}
+#endif
TemporaryRef<DataSourceSurface>
Factory::CreateWrappingDataSourceSurface(uint8_t *aData, int32_t aStride,
const IntSize &aSize,
SurfaceFormat aFormat)
{
+ MOZ_ASSERT(aData);
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ return nullptr;
+ }
+
RefPtr<SourceSurfaceRawData> newSurf = new SourceSurfaceRawData();
if (newSurf->InitWrappingData(aData, aSize, aStride, aFormat, false)) {
- return newSurf;
+ return newSurf.forget();
+ }
+
+ return nullptr;
+}
+
+TemporaryRef<DataSourceSurface>
+Factory::CreateDataSourceSurface(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ bool aZero)
+{
+ if (!AllowedSurfaceSize(aSize)) {
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size " << aSize;
+ return nullptr;
+ }
+
+ RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData();
+ if (newSurf->Init(aSize, aFormat, aZero)) {
+ return newSurf.forget();
}
+ gfxWarning() << "CreateDataSourceSurface failed in init";
+ return nullptr;
+}
+
+TemporaryRef<DataSourceSurface>
+Factory::CreateDataSourceSurfaceWithStride(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ int32_t aStride,
+ bool aZero)
+{
+ if (aStride < aSize.width * BytesPerPixel(aFormat)) {
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed with bad stride " << aStride << ", " << aSize << ", " << aFormat;
+ return nullptr;
+ }
+
+ RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData();
+ if (newSurf->InitWithStride(aSize, aFormat, aStride, aZero)) {
+ return newSurf.forget();
+ }
+
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed to initialize " << aSize << ", " << aFormat << ", " << aStride << ", " << aZero;
return nullptr;
}
@@ -504,5 +929,24 @@ Factory::SetGlobalEventRecorder(DrawEventRecorder *aRecorder)
mRecorder = aRecorder;
}
+// static
+void
+Factory::SetLogForwarder(LogForwarder* aLogFwd) {
+ sConfig->mLogForwarder = aLogFwd;
+}
+
+
+// static
+void
+CriticalLogger::OutputMessage(const std::string &aString,
+ int aLevel, bool aNoNewline)
+{
+ if (Factory::GetLogForwarder()) {
+ Factory::GetLogForwarder()->Log(aString);
+ }
+
+ BasicLogger::OutputMessage(aString, aLevel, aNoNewline);
+}
+
}
}
diff --git a/gfx/2d/FilterNodeD2D1.cpp b/gfx/2d/FilterNodeD2D1.cpp
new file mode 100644
index 000000000..37992546a
--- /dev/null
+++ b/gfx/2d/FilterNodeD2D1.cpp
@@ -0,0 +1,1082 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "FilterNodeD2D1.h"
+
+#include "Logging.h"
+
+#include "SourceSurfaceD2D1.h"
+#include "SourceSurfaceD2D.h"
+#include "SourceSurfaceD2DTarget.h"
+#include "DrawTargetD2D.h"
+#include "DrawTargetD2D1.h"
+#include "ExtendInputEffectD2D1.h"
+
+namespace mozilla {
+namespace gfx {
+
+D2D1_COLORMATRIX_ALPHA_MODE D2DAlphaMode(uint32_t aMode)
+{
+ switch (aMode) {
+ case ALPHA_MODE_PREMULTIPLIED:
+ return D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED;
+ case ALPHA_MODE_STRAIGHT:
+ return D2D1_COLORMATRIX_ALPHA_MODE_STRAIGHT;
+ default:
+ MOZ_CRASH("Unknown enum value!");
+ }
+
+ return D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED;
+}
+
+D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE D2DAffineTransformInterpolationMode(Filter aFilter)
+{
+ switch (aFilter) {
+ case Filter::GOOD:
+ return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
+ case Filter::LINEAR:
+ return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
+ case Filter::POINT:
+ return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+ default:
+ MOZ_CRASH("Unknown enum value!");
+ }
+
+ return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
+}
+
+D2D1_BLEND_MODE D2DBlendMode(uint32_t aMode)
+{
+ switch (aMode) {
+ case BLEND_MODE_DARKEN:
+ return D2D1_BLEND_MODE_DARKEN;
+ case BLEND_MODE_LIGHTEN:
+ return D2D1_BLEND_MODE_LIGHTEN;
+ case BLEND_MODE_MULTIPLY:
+ return D2D1_BLEND_MODE_MULTIPLY;
+ case BLEND_MODE_SCREEN:
+ return D2D1_BLEND_MODE_SCREEN;
+ case BLEND_MODE_OVERLAY:
+ return D2D1_BLEND_MODE_OVERLAY;
+ case BLEND_MODE_COLOR_DODGE:
+ return D2D1_BLEND_MODE_COLOR_DODGE;
+ case BLEND_MODE_COLOR_BURN:
+ return D2D1_BLEND_MODE_COLOR_BURN;
+ case BLEND_MODE_HARD_LIGHT:
+ return D2D1_BLEND_MODE_HARD_LIGHT;
+ case BLEND_MODE_SOFT_LIGHT:
+ return D2D1_BLEND_MODE_SOFT_LIGHT;
+ case BLEND_MODE_DIFFERENCE:
+ return D2D1_BLEND_MODE_DIFFERENCE;
+ case BLEND_MODE_EXCLUSION:
+ return D2D1_BLEND_MODE_EXCLUSION;
+ case BLEND_MODE_HUE:
+ return D2D1_BLEND_MODE_HUE;
+ case BLEND_MODE_SATURATION:
+ return D2D1_BLEND_MODE_SATURATION;
+ case BLEND_MODE_COLOR:
+ return D2D1_BLEND_MODE_COLOR;
+ case BLEND_MODE_LUMINOSITY:
+ return D2D1_BLEND_MODE_LUMINOSITY;
+
+ default:
+ MOZ_CRASH("Unknown enum value!");
+ }
+
+ return D2D1_BLEND_MODE_DARKEN;
+}
+
+D2D1_MORPHOLOGY_MODE D2DMorphologyMode(uint32_t aMode)
+{
+ switch (aMode) {
+ case MORPHOLOGY_OPERATOR_DILATE:
+ return D2D1_MORPHOLOGY_MODE_DILATE;
+ case MORPHOLOGY_OPERATOR_ERODE:
+ return D2D1_MORPHOLOGY_MODE_ERODE;
+ }
+
+ MOZ_CRASH("Unknown enum value!");
+ return D2D1_MORPHOLOGY_MODE_DILATE;
+}
+
+D2D1_TURBULENCE_NOISE D2DTurbulenceNoise(uint32_t aMode)
+{
+ switch (aMode) {
+ case TURBULENCE_TYPE_FRACTAL_NOISE:
+ return D2D1_TURBULENCE_NOISE_FRACTAL_SUM;
+ case TURBULENCE_TYPE_TURBULENCE:
+ return D2D1_TURBULENCE_NOISE_TURBULENCE;
+ }
+
+ MOZ_CRASH("Unknown enum value!");
+ return D2D1_TURBULENCE_NOISE_TURBULENCE;
+}
+
+D2D1_COMPOSITE_MODE D2DFilterCompositionMode(uint32_t aMode)
+{
+ switch (aMode) {
+ case COMPOSITE_OPERATOR_OVER:
+ return D2D1_COMPOSITE_MODE_SOURCE_OVER;
+ case COMPOSITE_OPERATOR_IN:
+ return D2D1_COMPOSITE_MODE_SOURCE_IN;
+ case COMPOSITE_OPERATOR_OUT:
+ return D2D1_COMPOSITE_MODE_SOURCE_OUT;
+ case COMPOSITE_OPERATOR_ATOP:
+ return D2D1_COMPOSITE_MODE_SOURCE_ATOP;
+ case COMPOSITE_OPERATOR_XOR:
+ return D2D1_COMPOSITE_MODE_XOR;
+ }
+
+ MOZ_CRASH("Unknown enum value!");
+ return D2D1_COMPOSITE_MODE_SOURCE_OVER;
+}
+
+D2D1_CHANNEL_SELECTOR D2DChannelSelector(uint32_t aMode)
+{
+ switch (aMode) {
+ case COLOR_CHANNEL_R:
+ return D2D1_CHANNEL_SELECTOR_R;
+ case COLOR_CHANNEL_G:
+ return D2D1_CHANNEL_SELECTOR_G;
+ case COLOR_CHANNEL_B:
+ return D2D1_CHANNEL_SELECTOR_B;
+ case COLOR_CHANNEL_A:
+ return D2D1_CHANNEL_SELECTOR_A;
+ }
+
+ MOZ_CRASH("Unknown enum value!");
+ return D2D1_CHANNEL_SELECTOR_R;
+}
+
+TemporaryRef<ID2D1Image> GetImageForSourceSurface(DrawTarget *aDT, SourceSurface *aSurface)
+{
+ if (aDT->IsTiledDrawTarget() || aDT->IsDualDrawTarget()) {
+ MOZ_CRASH("Incompatible draw target type!");
+ return nullptr;
+ }
+ switch (aDT->GetBackendType()) {
+ case BackendType::DIRECT2D1_1:
+ return static_cast<DrawTargetD2D1*>(aDT)->GetImageForSurface(aSurface, ExtendMode::CLAMP);
+ case BackendType::DIRECT2D:
+ return static_cast<DrawTargetD2D*>(aDT)->GetImageForSurface(aSurface);
+ default:
+ MOZ_CRASH("Unknown draw target type!");
+ return nullptr;
+ }
+}
+
+uint32_t ConvertValue(FilterType aType, uint32_t aAttribute, uint32_t aValue)
+{
+ switch (aType) {
+ case FilterType::COLOR_MATRIX:
+ if (aAttribute == ATT_COLOR_MATRIX_ALPHA_MODE) {
+ aValue = D2DAlphaMode(aValue);
+ }
+ break;
+ case FilterType::TRANSFORM:
+ if (aAttribute == ATT_TRANSFORM_FILTER) {
+ aValue = D2DAffineTransformInterpolationMode(Filter(aValue));
+ }
+ break;
+ case FilterType::BLEND:
+ if (aAttribute == ATT_BLEND_BLENDMODE) {
+ aValue = D2DBlendMode(aValue);
+ }
+ break;
+ case FilterType::MORPHOLOGY:
+ if (aAttribute == ATT_MORPHOLOGY_OPERATOR) {
+ aValue = D2DMorphologyMode(aValue);
+ }
+ break;
+ case FilterType::DISPLACEMENT_MAP:
+ if (aAttribute == ATT_DISPLACEMENT_MAP_X_CHANNEL ||
+ aAttribute == ATT_DISPLACEMENT_MAP_Y_CHANNEL) {
+ aValue = D2DChannelSelector(aValue);
+ }
+ break;
+ case FilterType::TURBULENCE:
+ if (aAttribute == ATT_TURBULENCE_TYPE) {
+ aValue = D2DTurbulenceNoise(aValue);
+ }
+ break;
+ case FilterType::COMPOSITE:
+ if (aAttribute == ATT_COMPOSITE_OPERATOR) {
+ aValue = D2DFilterCompositionMode(aValue);
+ }
+ break;
+ }
+
+ return aValue;
+}
+
+void ConvertValue(FilterType aType, uint32_t aAttribute, IntSize &aValue)
+{
+ switch (aType) {
+ case FilterType::MORPHOLOGY:
+ if (aAttribute == ATT_MORPHOLOGY_RADII) {
+ aValue.width *= 2;
+ aValue.width += 1;
+ aValue.height *= 2;
+ aValue.height += 1;
+ }
+ break;
+ }
+}
+
+UINT32
+GetD2D1InputForInput(FilterType aType, uint32_t aIndex)
+{
+ return aIndex;
+}
+
+#define CONVERT_PROP(moz2dname, d2dname) \
+ case ATT_##moz2dname: \
+ return D2D1_##d2dname
+
+UINT32
+GetD2D1PropForAttribute(FilterType aType, uint32_t aIndex)
+{
+ switch (aType) {
+ case FilterType::COLOR_MATRIX:
+ switch (aIndex) {
+ CONVERT_PROP(COLOR_MATRIX_MATRIX, COLORMATRIX_PROP_COLOR_MATRIX);
+ CONVERT_PROP(COLOR_MATRIX_ALPHA_MODE, COLORMATRIX_PROP_ALPHA_MODE);
+ }
+ break;
+ case FilterType::TRANSFORM:
+ switch (aIndex) {
+ CONVERT_PROP(TRANSFORM_MATRIX, 2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX);
+ CONVERT_PROP(TRANSFORM_FILTER, 2DAFFINETRANSFORM_PROP_INTERPOLATION_MODE);
+ }
+ case FilterType::BLEND:
+ switch (aIndex) {
+ CONVERT_PROP(BLEND_BLENDMODE, BLEND_PROP_MODE);
+ }
+ break;
+ case FilterType::MORPHOLOGY:
+ switch (aIndex) {
+ CONVERT_PROP(MORPHOLOGY_OPERATOR, MORPHOLOGY_PROP_MODE);
+ }
+ break;
+ case FilterType::FLOOD:
+ switch (aIndex) {
+ CONVERT_PROP(FLOOD_COLOR, FLOOD_PROP_COLOR);
+ }
+ break;
+ case FilterType::TILE:
+ switch (aIndex) {
+ CONVERT_PROP(TILE_SOURCE_RECT, TILE_PROP_RECT);
+ }
+ break;
+ case FilterType::TABLE_TRANSFER:
+ switch (aIndex) {
+ CONVERT_PROP(TABLE_TRANSFER_DISABLE_R, TABLETRANSFER_PROP_RED_DISABLE);
+ CONVERT_PROP(TABLE_TRANSFER_DISABLE_G, TABLETRANSFER_PROP_GREEN_DISABLE);
+ CONVERT_PROP(TABLE_TRANSFER_DISABLE_B, TABLETRANSFER_PROP_BLUE_DISABLE);
+ CONVERT_PROP(TABLE_TRANSFER_DISABLE_A, TABLETRANSFER_PROP_ALPHA_DISABLE);
+ CONVERT_PROP(TABLE_TRANSFER_TABLE_R, TABLETRANSFER_PROP_RED_TABLE);
+ CONVERT_PROP(TABLE_TRANSFER_TABLE_G, TABLETRANSFER_PROP_GREEN_TABLE);
+ CONVERT_PROP(TABLE_TRANSFER_TABLE_B, TABLETRANSFER_PROP_BLUE_TABLE);
+ CONVERT_PROP(TABLE_TRANSFER_TABLE_A, TABLETRANSFER_PROP_ALPHA_TABLE);
+ }
+ break;
+ case FilterType::DISCRETE_TRANSFER:
+ switch (aIndex) {
+ CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_R, DISCRETETRANSFER_PROP_RED_DISABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_G, DISCRETETRANSFER_PROP_GREEN_DISABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_B, DISCRETETRANSFER_PROP_BLUE_DISABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_A, DISCRETETRANSFER_PROP_ALPHA_DISABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_TABLE_R, DISCRETETRANSFER_PROP_RED_TABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_TABLE_G, DISCRETETRANSFER_PROP_GREEN_TABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_TABLE_B, DISCRETETRANSFER_PROP_BLUE_TABLE);
+ CONVERT_PROP(DISCRETE_TRANSFER_TABLE_A, DISCRETETRANSFER_PROP_ALPHA_TABLE);
+ }
+ break;
+ case FilterType::LINEAR_TRANSFER:
+ switch (aIndex) {
+ CONVERT_PROP(LINEAR_TRANSFER_DISABLE_R, LINEARTRANSFER_PROP_RED_DISABLE);
+ CONVERT_PROP(LINEAR_TRANSFER_DISABLE_G, LINEARTRANSFER_PROP_GREEN_DISABLE);
+ CONVERT_PROP(LINEAR_TRANSFER_DISABLE_B, LINEARTRANSFER_PROP_BLUE_DISABLE);
+ CONVERT_PROP(LINEAR_TRANSFER_DISABLE_A, LINEARTRANSFER_PROP_ALPHA_DISABLE);
+ CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_R, LINEARTRANSFER_PROP_RED_Y_INTERCEPT);
+ CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_G, LINEARTRANSFER_PROP_GREEN_Y_INTERCEPT);
+ CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_B, LINEARTRANSFER_PROP_BLUE_Y_INTERCEPT);
+ CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_A, LINEARTRANSFER_PROP_ALPHA_Y_INTERCEPT);
+ CONVERT_PROP(LINEAR_TRANSFER_SLOPE_R, LINEARTRANSFER_PROP_RED_SLOPE);
+ CONVERT_PROP(LINEAR_TRANSFER_SLOPE_G, LINEARTRANSFER_PROP_GREEN_SLOPE);
+ CONVERT_PROP(LINEAR_TRANSFER_SLOPE_B, LINEARTRANSFER_PROP_BLUE_SLOPE);
+ CONVERT_PROP(LINEAR_TRANSFER_SLOPE_A, LINEARTRANSFER_PROP_ALPHA_SLOPE);
+ }
+ break;
+ case FilterType::GAMMA_TRANSFER:
+ switch (aIndex) {
+ CONVERT_PROP(GAMMA_TRANSFER_DISABLE_R, GAMMATRANSFER_PROP_RED_DISABLE);
+ CONVERT_PROP(GAMMA_TRANSFER_DISABLE_G, GAMMATRANSFER_PROP_GREEN_DISABLE);
+ CONVERT_PROP(GAMMA_TRANSFER_DISABLE_B, GAMMATRANSFER_PROP_BLUE_DISABLE);
+ CONVERT_PROP(GAMMA_TRANSFER_DISABLE_A, GAMMATRANSFER_PROP_ALPHA_DISABLE);
+ CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_R, GAMMATRANSFER_PROP_RED_AMPLITUDE);
+ CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_G, GAMMATRANSFER_PROP_GREEN_AMPLITUDE);
+ CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_B, GAMMATRANSFER_PROP_BLUE_AMPLITUDE);
+ CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_A, GAMMATRANSFER_PROP_ALPHA_AMPLITUDE);
+ CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_R, GAMMATRANSFER_PROP_RED_EXPONENT);
+ CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_G, GAMMATRANSFER_PROP_GREEN_EXPONENT);
+ CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_B, GAMMATRANSFER_PROP_BLUE_EXPONENT);
+ CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_A, GAMMATRANSFER_PROP_ALPHA_EXPONENT);
+ CONVERT_PROP(GAMMA_TRANSFER_OFFSET_R, GAMMATRANSFER_PROP_RED_OFFSET);
+ CONVERT_PROP(GAMMA_TRANSFER_OFFSET_G, GAMMATRANSFER_PROP_GREEN_OFFSET);
+ CONVERT_PROP(GAMMA_TRANSFER_OFFSET_B, GAMMATRANSFER_PROP_BLUE_OFFSET);
+ CONVERT_PROP(GAMMA_TRANSFER_OFFSET_A, GAMMATRANSFER_PROP_ALPHA_OFFSET);
+ }
+ break;
+ case FilterType::CONVOLVE_MATRIX:
+ switch (aIndex) {
+ CONVERT_PROP(CONVOLVE_MATRIX_BIAS, CONVOLVEMATRIX_PROP_BIAS);
+ CONVERT_PROP(CONVOLVE_MATRIX_KERNEL_MATRIX, CONVOLVEMATRIX_PROP_KERNEL_MATRIX);
+ CONVERT_PROP(CONVOLVE_MATRIX_DIVISOR, CONVOLVEMATRIX_PROP_DIVISOR);
+ CONVERT_PROP(CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH, CONVOLVEMATRIX_PROP_KERNEL_UNIT_LENGTH);
+ CONVERT_PROP(CONVOLVE_MATRIX_PRESERVE_ALPHA, CONVOLVEMATRIX_PROP_PRESERVE_ALPHA);
+ }
+ case FilterType::DISPLACEMENT_MAP:
+ switch (aIndex) {
+ CONVERT_PROP(DISPLACEMENT_MAP_SCALE, DISPLACEMENTMAP_PROP_SCALE);
+ CONVERT_PROP(DISPLACEMENT_MAP_X_CHANNEL, DISPLACEMENTMAP_PROP_X_CHANNEL_SELECT);
+ CONVERT_PROP(DISPLACEMENT_MAP_Y_CHANNEL, DISPLACEMENTMAP_PROP_Y_CHANNEL_SELECT);
+ }
+ break;
+ case FilterType::TURBULENCE:
+ switch (aIndex) {
+ CONVERT_PROP(TURBULENCE_BASE_FREQUENCY, TURBULENCE_PROP_BASE_FREQUENCY);
+ CONVERT_PROP(TURBULENCE_NUM_OCTAVES, TURBULENCE_PROP_NUM_OCTAVES);
+ CONVERT_PROP(TURBULENCE_SEED, TURBULENCE_PROP_SEED);
+ CONVERT_PROP(TURBULENCE_STITCHABLE, TURBULENCE_PROP_STITCHABLE);
+ CONVERT_PROP(TURBULENCE_TYPE, TURBULENCE_PROP_NOISE);
+ }
+ break;
+ case FilterType::ARITHMETIC_COMBINE:
+ switch (aIndex) {
+ CONVERT_PROP(ARITHMETIC_COMBINE_COEFFICIENTS, ARITHMETICCOMPOSITE_PROP_COEFFICIENTS);
+ }
+ break;
+ case FilterType::COMPOSITE:
+ switch (aIndex) {
+ CONVERT_PROP(COMPOSITE_OPERATOR, COMPOSITE_PROP_MODE);
+ }
+ break;
+ case FilterType::GAUSSIAN_BLUR:
+ switch (aIndex) {
+ CONVERT_PROP(GAUSSIAN_BLUR_STD_DEVIATION, GAUSSIANBLUR_PROP_STANDARD_DEVIATION);
+ }
+ break;
+ case FilterType::DIRECTIONAL_BLUR:
+ switch (aIndex) {
+ CONVERT_PROP(DIRECTIONAL_BLUR_STD_DEVIATION, DIRECTIONALBLUR_PROP_STANDARD_DEVIATION);
+ CONVERT_PROP(DIRECTIONAL_BLUR_DIRECTION, DIRECTIONALBLUR_PROP_ANGLE);
+ }
+ break;
+ case FilterType::POINT_DIFFUSE:
+ switch (aIndex) {
+ CONVERT_PROP(POINT_DIFFUSE_DIFFUSE_CONSTANT, POINTDIFFUSE_PROP_DIFFUSE_CONSTANT);
+ CONVERT_PROP(POINT_DIFFUSE_POSITION, POINTDIFFUSE_PROP_LIGHT_POSITION);
+ CONVERT_PROP(POINT_DIFFUSE_COLOR, POINTDIFFUSE_PROP_COLOR);
+ CONVERT_PROP(POINT_DIFFUSE_SURFACE_SCALE, POINTDIFFUSE_PROP_SURFACE_SCALE);
+ CONVERT_PROP(POINT_DIFFUSE_KERNEL_UNIT_LENGTH, POINTDIFFUSE_PROP_KERNEL_UNIT_LENGTH);
+ }
+ break;
+ case FilterType::SPOT_DIFFUSE:
+ switch (aIndex) {
+ CONVERT_PROP(SPOT_DIFFUSE_DIFFUSE_CONSTANT, SPOTDIFFUSE_PROP_DIFFUSE_CONSTANT);
+ CONVERT_PROP(SPOT_DIFFUSE_POINTS_AT, SPOTDIFFUSE_PROP_POINTS_AT);
+ CONVERT_PROP(SPOT_DIFFUSE_FOCUS, SPOTDIFFUSE_PROP_FOCUS);
+ CONVERT_PROP(SPOT_DIFFUSE_LIMITING_CONE_ANGLE, SPOTDIFFUSE_PROP_LIMITING_CONE_ANGLE);
+ CONVERT_PROP(SPOT_DIFFUSE_POSITION, SPOTDIFFUSE_PROP_LIGHT_POSITION);
+ CONVERT_PROP(SPOT_DIFFUSE_COLOR, SPOTDIFFUSE_PROP_COLOR);
+ CONVERT_PROP(SPOT_DIFFUSE_SURFACE_SCALE, SPOTDIFFUSE_PROP_SURFACE_SCALE);
+ CONVERT_PROP(SPOT_DIFFUSE_KERNEL_UNIT_LENGTH, SPOTDIFFUSE_PROP_KERNEL_UNIT_LENGTH);
+ }
+ break;
+ case FilterType::DISTANT_DIFFUSE:
+ switch (aIndex) {
+ CONVERT_PROP(DISTANT_DIFFUSE_DIFFUSE_CONSTANT, DISTANTDIFFUSE_PROP_DIFFUSE_CONSTANT);
+ CONVERT_PROP(DISTANT_DIFFUSE_AZIMUTH, DISTANTDIFFUSE_PROP_AZIMUTH);
+ CONVERT_PROP(DISTANT_DIFFUSE_ELEVATION, DISTANTDIFFUSE_PROP_ELEVATION);
+ CONVERT_PROP(DISTANT_DIFFUSE_COLOR, DISTANTDIFFUSE_PROP_COLOR);
+ CONVERT_PROP(DISTANT_DIFFUSE_SURFACE_SCALE, DISTANTDIFFUSE_PROP_SURFACE_SCALE);
+ CONVERT_PROP(DISTANT_DIFFUSE_KERNEL_UNIT_LENGTH, DISTANTDIFFUSE_PROP_KERNEL_UNIT_LENGTH);
+ }
+ break;
+ case FilterType::POINT_SPECULAR:
+ switch (aIndex) {
+ CONVERT_PROP(POINT_SPECULAR_SPECULAR_CONSTANT, POINTSPECULAR_PROP_SPECULAR_CONSTANT);
+ CONVERT_PROP(POINT_SPECULAR_SPECULAR_EXPONENT, POINTSPECULAR_PROP_SPECULAR_EXPONENT);
+ CONVERT_PROP(POINT_SPECULAR_POSITION, POINTSPECULAR_PROP_LIGHT_POSITION);
+ CONVERT_PROP(POINT_SPECULAR_COLOR, POINTSPECULAR_PROP_COLOR);
+ CONVERT_PROP(POINT_SPECULAR_SURFACE_SCALE, POINTSPECULAR_PROP_SURFACE_SCALE);
+ CONVERT_PROP(POINT_SPECULAR_KERNEL_UNIT_LENGTH, POINTSPECULAR_PROP_KERNEL_UNIT_LENGTH);
+ }
+ break;
+ case FilterType::SPOT_SPECULAR:
+ switch (aIndex) {
+ CONVERT_PROP(SPOT_SPECULAR_SPECULAR_CONSTANT, SPOTSPECULAR_PROP_SPECULAR_CONSTANT);
+ CONVERT_PROP(SPOT_SPECULAR_SPECULAR_EXPONENT, SPOTSPECULAR_PROP_SPECULAR_EXPONENT);
+ CONVERT_PROP(SPOT_SPECULAR_POINTS_AT, SPOTSPECULAR_PROP_POINTS_AT);
+ CONVERT_PROP(SPOT_SPECULAR_FOCUS, SPOTSPECULAR_PROP_FOCUS);
+ CONVERT_PROP(SPOT_SPECULAR_LIMITING_CONE_ANGLE, SPOTSPECULAR_PROP_LIMITING_CONE_ANGLE);
+ CONVERT_PROP(SPOT_SPECULAR_POSITION, SPOTSPECULAR_PROP_LIGHT_POSITION);
+ CONVERT_PROP(SPOT_SPECULAR_COLOR, SPOTSPECULAR_PROP_COLOR);
+ CONVERT_PROP(SPOT_SPECULAR_SURFACE_SCALE, SPOTSPECULAR_PROP_SURFACE_SCALE);
+ CONVERT_PROP(SPOT_SPECULAR_KERNEL_UNIT_LENGTH, SPOTSPECULAR_PROP_KERNEL_UNIT_LENGTH);
+ }
+ break;
+ case FilterType::DISTANT_SPECULAR:
+ switch (aIndex) {
+ CONVERT_PROP(DISTANT_SPECULAR_SPECULAR_CONSTANT, DISTANTSPECULAR_PROP_SPECULAR_CONSTANT);
+ CONVERT_PROP(DISTANT_SPECULAR_SPECULAR_EXPONENT, DISTANTSPECULAR_PROP_SPECULAR_EXPONENT);
+ CONVERT_PROP(DISTANT_SPECULAR_AZIMUTH, DISTANTSPECULAR_PROP_AZIMUTH);
+ CONVERT_PROP(DISTANT_SPECULAR_ELEVATION, DISTANTSPECULAR_PROP_ELEVATION);
+ CONVERT_PROP(DISTANT_SPECULAR_COLOR, DISTANTSPECULAR_PROP_COLOR);
+ CONVERT_PROP(DISTANT_SPECULAR_SURFACE_SCALE, DISTANTSPECULAR_PROP_SURFACE_SCALE);
+ CONVERT_PROP(DISTANT_SPECULAR_KERNEL_UNIT_LENGTH, DISTANTSPECULAR_PROP_KERNEL_UNIT_LENGTH);
+ }
+ break;
+ case FilterType::CROP:
+ switch (aIndex) {
+ CONVERT_PROP(CROP_RECT, CROP_PROP_RECT);
+ }
+ break;
+ }
+
+ return UINT32_MAX;
+}
+
+bool
+GetD2D1PropsForIntSize(FilterType aType, uint32_t aIndex, UINT32 *aPropWidth, UINT32 *aPropHeight)
+{
+ switch (aType) {
+ case FilterType::MORPHOLOGY:
+ if (aIndex == ATT_MORPHOLOGY_RADII) {
+ *aPropWidth = D2D1_MORPHOLOGY_PROP_WIDTH;
+ *aPropHeight = D2D1_MORPHOLOGY_PROP_HEIGHT;
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+static inline REFCLSID GetCLDIDForFilterType(FilterType aType)
+{
+ switch (aType) {
+ case FilterType::COLOR_MATRIX:
+ return CLSID_D2D1ColorMatrix;
+ case FilterType::TRANSFORM:
+ return CLSID_D2D12DAffineTransform;
+ case FilterType::BLEND:
+ return CLSID_D2D1Blend;
+ case FilterType::MORPHOLOGY:
+ return CLSID_D2D1Morphology;
+ case FilterType::FLOOD:
+ return CLSID_D2D1Flood;
+ case FilterType::TILE:
+ return CLSID_D2D1Tile;
+ case FilterType::TABLE_TRANSFER:
+ return CLSID_D2D1TableTransfer;
+ case FilterType::LINEAR_TRANSFER:
+ return CLSID_D2D1LinearTransfer;
+ case FilterType::DISCRETE_TRANSFER:
+ return CLSID_D2D1DiscreteTransfer;
+ case FilterType::GAMMA_TRANSFER:
+ return CLSID_D2D1GammaTransfer;
+ case FilterType::DISPLACEMENT_MAP:
+ return CLSID_D2D1DisplacementMap;
+ case FilterType::TURBULENCE:
+ return CLSID_D2D1Turbulence;
+ case FilterType::ARITHMETIC_COMBINE:
+ return CLSID_D2D1ArithmeticComposite;
+ case FilterType::COMPOSITE:
+ return CLSID_D2D1Composite;
+ case FilterType::GAUSSIAN_BLUR:
+ return CLSID_D2D1GaussianBlur;
+ case FilterType::DIRECTIONAL_BLUR:
+ return CLSID_D2D1DirectionalBlur;
+ case FilterType::POINT_DIFFUSE:
+ return CLSID_D2D1PointDiffuse;
+ case FilterType::POINT_SPECULAR:
+ return CLSID_D2D1PointSpecular;
+ case FilterType::SPOT_DIFFUSE:
+ return CLSID_D2D1SpotDiffuse;
+ case FilterType::SPOT_SPECULAR:
+ return CLSID_D2D1SpotSpecular;
+ case FilterType::DISTANT_DIFFUSE:
+ return CLSID_D2D1DistantDiffuse;
+ case FilterType::DISTANT_SPECULAR:
+ return CLSID_D2D1DistantSpecular;
+ case FilterType::CROP:
+ return CLSID_D2D1Crop;
+ case FilterType::PREMULTIPLY:
+ return CLSID_D2D1Premultiply;
+ case FilterType::UNPREMULTIPLY:
+ return CLSID_D2D1UnPremultiply;
+ }
+ return GUID_NULL;
+}
+
+static bool
+IsTransferFilterType(FilterType aType)
+{
+ switch (aType) {
+ case FilterType::LINEAR_TRANSFER:
+ case FilterType::GAMMA_TRANSFER:
+ case FilterType::TABLE_TRANSFER:
+ case FilterType::DISCRETE_TRANSFER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* static */
+TemporaryRef<FilterNode>
+FilterNodeD2D1::Create(ID2D1DeviceContext *aDC, FilterType aType)
+{
+ if (aType == FilterType::CONVOLVE_MATRIX) {
+ return new FilterNodeConvolveD2D1(aDC);
+ }
+
+ RefPtr<ID2D1Effect> effect;
+ HRESULT hr;
+
+ hr = aDC->CreateEffect(GetCLDIDForFilterType(aType), byRef(effect));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create effect for FilterType: " << hexa(hr);
+ return nullptr;
+ }
+
+ RefPtr<FilterNodeD2D1> filter = new FilterNodeD2D1(effect, aType);
+
+ if (IsTransferFilterType(aType) || aType == FilterType::COLOR_MATRIX) {
+ // These filters can produce non-transparent output from transparent
+ // input pixels, and we want them to have an unbounded output region.
+ filter = new FilterNodeExtendInputAdapterD2D1(aDC, filter, aType);
+ }
+
+ if (IsTransferFilterType(aType)) {
+ // Component transfer filters should appear to apply on unpremultiplied
+ // colors, but the D2D1 effects apply on premultiplied colors.
+ filter = new FilterNodePremultiplyAdapterD2D1(aDC, filter, aType);
+ }
+
+ return filter.forget();
+}
+
+void
+FilterNodeD2D1::InitUnmappedProperties()
+{
+ switch (mType) {
+ case FilterType::TRANSFORM:
+ mEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_BORDER_MODE, D2D1_BORDER_MODE_HARD);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+FilterNodeD2D1::SetInput(uint32_t aIndex, SourceSurface *aSurface)
+{
+ UINT32 input = GetD2D1InputForInput(mType, aIndex);
+ ID2D1Effect* effect = InputEffect();
+ MOZ_ASSERT(input < effect->GetInputCount());
+
+ if (mType == FilterType::COMPOSITE) {
+ UINT32 inputCount = effect->GetInputCount();
+
+ if (aIndex == inputCount - 1 && aSurface == nullptr) {
+ effect->SetInputCount(inputCount - 1);
+ } else if (aIndex >= inputCount && aSurface) {
+ effect->SetInputCount(aIndex + 1);
+ }
+ }
+
+ MOZ_ASSERT(input < effect->GetInputCount());
+
+ mInputSurfaces.resize(effect->GetInputCount());
+ mInputFilters.resize(effect->GetInputCount());
+
+ // In order to convert aSurface into an ID2D1Image, we need to know what
+ // DrawTarget we paint into. However, the same FilterNode object can be
+ // used on different DrawTargets, so we need to hold on to the SourceSurface
+ // objects and delay the conversion until we're actually painted and know
+ // our target DrawTarget.
+ // The conversion happens in WillDraw().
+
+ mInputSurfaces[input] = aSurface;
+ mInputFilters[input] = nullptr;
+
+ // Clear the existing image from the effect.
+ effect->SetInput(input, nullptr);
+}
+
+void
+FilterNodeD2D1::SetInput(uint32_t aIndex, FilterNode *aFilter)
+{
+ UINT32 input = GetD2D1InputForInput(mType, aIndex);
+ ID2D1Effect* effect = InputEffect();
+
+ if (mType == FilterType::COMPOSITE) {
+ UINT32 inputCount = effect->GetInputCount();
+
+ if (aIndex == inputCount - 1 && aFilter == nullptr) {
+ effect->SetInputCount(inputCount - 1);
+ } else if (aIndex >= inputCount && aFilter) {
+ effect->SetInputCount(aIndex + 1);
+ }
+ }
+
+ MOZ_ASSERT(input < effect->GetInputCount());
+
+ if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) {
+ gfxWarning() << "Unknown input FilterNode set on effect.";
+ MOZ_ASSERT(0);
+ return;
+ }
+
+ FilterNodeD2D1* filter = static_cast<FilterNodeD2D1*>(aFilter);
+
+ mInputSurfaces.resize(effect->GetInputCount());
+ mInputFilters.resize(effect->GetInputCount());
+
+ // We hold on to the FilterNode object so that we can call WillDraw() on it.
+ mInputSurfaces[input] = nullptr;
+ mInputFilters[input] = filter;
+
+ if (filter) {
+ effect->SetInputEffect(input, filter->OutputEffect());
+ }
+}
+
+void
+FilterNodeD2D1::WillDraw(DrawTarget *aDT)
+{
+ // Convert input SourceSurfaces into ID2D1Images and set them on the effect.
+ for (size_t inputIndex = 0; inputIndex < mInputSurfaces.size(); inputIndex++) {
+ if (mInputSurfaces[inputIndex]) {
+ ID2D1Effect* effect = InputEffect();
+ RefPtr<ID2D1Image> image = GetImageForSourceSurface(aDT, mInputSurfaces[inputIndex]);
+ effect->SetInput(inputIndex, image);
+ }
+ }
+
+ // Call WillDraw() on our input filters.
+ for (std::vector<RefPtr<FilterNodeD2D1>>::iterator it = mInputFilters.begin();
+ it != mInputFilters.end(); it++) {
+ if (*it) {
+ (*it)->WillDraw(aDT);
+ }
+ }
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, uint32_t aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ if (mType == FilterType::TURBULENCE && aIndex == ATT_TURBULENCE_BASE_FREQUENCY) {
+ mEffect->SetValue(input, D2D1::Vector2F(FLOAT(aValue), FLOAT(aValue)));
+ return;
+ } else if (mType == FilterType::DIRECTIONAL_BLUR && aIndex == ATT_DIRECTIONAL_BLUR_DIRECTION) {
+ mEffect->SetValue(input, aValue == BLUR_DIRECTION_X ? 0 : 90.0f);
+ return;
+ }
+
+ mEffect->SetValue(input, ConvertValue(mType, aIndex, aValue));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, aValue);
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Point &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2DPoint(aValue));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Matrix5x4 &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2DMatrix5x4(aValue));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Point3D &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2DVector3D(aValue));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Size &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2D1::Vector2F(aValue.width, aValue.height));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntSize &aValue)
+{
+ UINT32 widthProp, heightProp;
+
+ if (!GetD2D1PropsForIntSize(mType, aIndex, &widthProp, &heightProp)) {
+ return;
+ }
+
+ IntSize value = aValue;
+ ConvertValue(mType, aIndex, value);
+
+ mEffect->SetValue(widthProp, (UINT)value.width);
+ mEffect->SetValue(heightProp, (UINT)value.height);
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Color &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ switch (mType) {
+ case FilterType::POINT_DIFFUSE:
+ case FilterType::SPOT_DIFFUSE:
+ case FilterType::DISTANT_DIFFUSE:
+ case FilterType::POINT_SPECULAR:
+ case FilterType::SPOT_SPECULAR:
+ case FilterType::DISTANT_SPECULAR:
+ mEffect->SetValue(input, D2D1::Vector3F(aValue.r, aValue.g, aValue.b));
+ break;
+ default:
+ mEffect->SetValue(input, D2D1::Vector4F(aValue.r * aValue.a, aValue.g * aValue.a, aValue.b * aValue.a, aValue.a));
+ }
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Rect &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2DRect(aValue));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntRect &aValue)
+{
+ if (mType == FilterType::TURBULENCE) {
+ MOZ_ASSERT(aIndex == ATT_TURBULENCE_RECT);
+
+ mEffect->SetValue(D2D1_TURBULENCE_PROP_OFFSET, D2D1::Vector2F(Float(aValue.x), Float(aValue.y)));
+ mEffect->SetValue(D2D1_TURBULENCE_PROP_SIZE, D2D1::Vector2F(Float(aValue.width), Float(aValue.height)));
+ return;
+ }
+
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2D1::RectF(Float(aValue.x), Float(aValue.y),
+ Float(aValue.XMost()), Float(aValue.YMost())));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, bool aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, (BOOL)aValue);
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Float *aValues, uint32_t aSize)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, (BYTE*)aValues, sizeof(Float) * aSize);
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntPoint &aValue)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2DPoint(aValue));
+}
+
+void
+FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Matrix &aMatrix)
+{
+ UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
+ MOZ_ASSERT(input < mEffect->GetPropertyCount());
+
+ mEffect->SetValue(input, D2DMatrix(aMatrix));
+}
+
+FilterNodeConvolveD2D1::FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC)
+ : FilterNodeD2D1(nullptr, FilterType::CONVOLVE_MATRIX)
+ , mEdgeMode(EDGE_MODE_DUPLICATE)
+{
+ // Correctly handling the interaction of edge mode and source rect is a bit
+ // tricky with D2D1 effects. We want the edge mode to only apply outside of
+ // the source rect (as specified by the ATT_CONVOLVE_MATRIX_SOURCE_RECT
+ // attribute). So if our input surface or filter is smaller than the source
+ // rect, we need to add transparency around it until we reach the edges of
+ // the source rect, and only then do any repeating or edge duplicating.
+ // Unfortunately, the border effect does not have a source rect attribute -
+ // it only looks at the output rect of its input filter or surface. So we use
+ // our custom ExtendInput effect to adjust the output rect of our input.
+ // All of this is only necessary when our edge mode is not EDGE_MODE_NONE, so
+ // we update the filter chain dynamically in UpdateChain().
+
+ HRESULT hr;
+
+ hr = aDC->CreateEffect(CLSID_D2D1ConvolveMatrix, byRef(mEffect));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create ConvolveMatrix filter!";
+ return;
+ }
+
+ mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_BORDER_MODE, D2D1_BORDER_MODE_SOFT);
+
+ hr = aDC->CreateEffect(CLSID_ExtendInputEffect, byRef(mExtendInputEffect));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create ConvolveMatrix filter!";
+ return;
+ }
+
+ hr = aDC->CreateEffect(CLSID_D2D1Border, byRef(mBorderEffect));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create ConvolveMatrix filter!";
+ return;
+ }
+
+ mBorderEffect->SetInputEffect(0, mExtendInputEffect.get());
+
+ UpdateChain();
+ UpdateSourceRect();
+}
+
+void
+FilterNodeConvolveD2D1::SetInput(uint32_t aIndex, FilterNode *aFilter)
+{
+ FilterNodeD2D1::SetInput(aIndex, aFilter);
+
+ UpdateChain();
+}
+
+void
+FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, uint32_t aValue)
+{
+ if (aIndex != ATT_CONVOLVE_MATRIX_EDGE_MODE) {
+ return FilterNodeD2D1::SetAttribute(aIndex, aValue);
+ }
+
+ mEdgeMode = (ConvolveMatrixEdgeMode)aValue;
+
+ UpdateChain();
+}
+
+ID2D1Effect*
+FilterNodeConvolveD2D1::InputEffect()
+{
+ return mEdgeMode == EDGE_MODE_NONE ? mEffect.get() : mExtendInputEffect.get();
+}
+
+void
+FilterNodeConvolveD2D1::UpdateChain()
+{
+ // The shape of the filter graph:
+ //
+ // EDGE_MODE_NONE:
+ // input --> convolvematrix
+ //
+ // EDGE_MODE_DUPLICATE or EDGE_MODE_WRAP:
+ // input --> extendinput --> border --> convolvematrix
+ //
+ // mEffect is convolvematrix.
+
+ if (mEdgeMode != EDGE_MODE_NONE) {
+ mEffect->SetInputEffect(0, mBorderEffect.get());
+ }
+
+ RefPtr<ID2D1Effect> inputEffect;
+ if (mInputFilters.size() > 0 && mInputFilters[0]) {
+ inputEffect = mInputFilters[0]->OutputEffect();
+ }
+ InputEffect()->SetInputEffect(0, inputEffect);
+
+ if (mEdgeMode == EDGE_MODE_DUPLICATE) {
+ mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X, D2D1_BORDER_EDGE_MODE_CLAMP);
+ mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_CLAMP);
+ } else if (mEdgeMode == EDGE_MODE_WRAP) {
+ mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X, D2D1_BORDER_EDGE_MODE_WRAP);
+ mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_WRAP);
+ }
+}
+
+void
+FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, const IntSize &aValue)
+{
+ if (aIndex != ATT_CONVOLVE_MATRIX_KERNEL_SIZE) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ mKernelSize = aValue;
+
+ mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_KERNEL_SIZE_X, aValue.width);
+ mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_KERNEL_SIZE_Y, aValue.height);
+
+ UpdateOffset();
+}
+
+void
+FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, const IntPoint &aValue)
+{
+ if (aIndex != ATT_CONVOLVE_MATRIX_TARGET) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ mTarget = aValue;
+
+ UpdateOffset();
+}
+
+void
+FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, const IntRect &aValue)
+{
+ if (aIndex != ATT_CONVOLVE_MATRIX_SOURCE_RECT) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ mSourceRect = aValue;
+
+ UpdateSourceRect();
+}
+
+void
+FilterNodeConvolveD2D1::UpdateOffset()
+{
+ D2D1_VECTOR_2F vector =
+ D2D1::Vector2F((Float(mKernelSize.width) - 1.0f) / 2.0f - Float(mTarget.x),
+ (Float(mKernelSize.height) - 1.0f) / 2.0f - Float(mTarget.y));
+
+ mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_KERNEL_OFFSET, vector);
+}
+
+void
+FilterNodeConvolveD2D1::UpdateSourceRect()
+{
+ mExtendInputEffect->SetValue(EXTENDINPUT_PROP_OUTPUT_RECT,
+ D2D1::Vector4F(Float(mSourceRect.x), Float(mSourceRect.y),
+ Float(mSourceRect.XMost()), Float(mSourceRect.YMost())));
+}
+
+FilterNodeExtendInputAdapterD2D1::FilterNodeExtendInputAdapterD2D1(ID2D1DeviceContext *aDC,
+ FilterNodeD2D1 *aFilterNode, FilterType aType)
+ : FilterNodeD2D1(aFilterNode->MainEffect(), aType)
+ , mWrappedFilterNode(aFilterNode)
+{
+ // We have an mEffect that looks at the bounds of the input effect, and we
+ // want mEffect to regard its input as unbounded. So we take the input,
+ // pipe it through an ExtendInput effect (which has an infinite output rect
+ // by default), and feed the resulting unbounded composition into mEffect.
+
+ HRESULT hr;
+
+ hr = aDC->CreateEffect(CLSID_ExtendInputEffect, byRef(mExtendInputEffect));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create extend input effect for filter: " << hexa(hr);
+ return;
+ }
+
+ aFilterNode->InputEffect()->SetInputEffect(0, mExtendInputEffect.get());
+}
+
+FilterNodePremultiplyAdapterD2D1::FilterNodePremultiplyAdapterD2D1(ID2D1DeviceContext *aDC,
+ FilterNodeD2D1 *aFilterNode, FilterType aType)
+ : FilterNodeD2D1(aFilterNode->MainEffect(), aType)
+{
+ // D2D1 component transfer effects do strange things when it comes to
+ // premultiplication.
+ // For our purposes we only need the transfer filters to apply straight to
+ // unpremultiplied source channels and output unpremultiplied results.
+ // However, the D2D1 effects are designed differently: They can apply to both
+ // premultiplied and unpremultiplied inputs, and they always premultiply
+ // their result - at least in those color channels that have not been
+ // disabled.
+ // In order to determine whether the input needs to be unpremultiplied as
+ // part of the transfer, the effect consults the alpha mode metadata of the
+ // input surface or the input effect. We don't have such a concept in Moz2D,
+ // and giving Moz2D users different results based on something that cannot be
+ // influenced through Moz2D APIs seems like a bad idea.
+ // We solve this by applying a premultiply effect to the input before feeding
+ // it into the transfer effect. The premultiply effect always premultiplies
+ // regardless of any alpha mode metadata on inputs, and it always marks its
+ // output as premultiplied so that the transfer effect will unpremultiply
+ // consistently. Feeding always-premultiplied input into the transfer effect
+ // also avoids another problem that would appear when individual color
+ // channels disable the transfer: In that case, the disabled channels would
+ // pass through unchanged in their unpremultiplied form and the other
+ // channels would be premultiplied, giving a mixed result.
+ // But since we now ensure that the input is premultiplied, disabled channels
+ // will pass premultiplied values through to the result, which is consistent
+ // with the enabled channels.
+ // We also add an unpremultiply effect that postprocesses the result of the
+ // transfer effect because getting unpremultiplied results from the transfer
+ // filters is part of the FilterNode API.
+ HRESULT hr;
+
+ hr = aDC->CreateEffect(CLSID_D2D1Premultiply, byRef(mPrePremultiplyEffect));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create ComponentTransfer filter!";
+ return;
+ }
+
+ hr = aDC->CreateEffect(CLSID_D2D1UnPremultiply, byRef(mPostUnpremultiplyEffect));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create ComponentTransfer filter!";
+ return;
+ }
+
+ aFilterNode->InputEffect()->SetInputEffect(0, mPrePremultiplyEffect.get());
+ mPostUnpremultiplyEffect->SetInputEffect(0, aFilterNode->OutputEffect());
+}
+
+}
+}
diff --git a/gfx/2d/FilterNodeD2D1.h b/gfx/2d/FilterNodeD2D1.h
new file mode 100644
index 000000000..612dbfbef
--- /dev/null
+++ b/gfx/2d/FilterNodeD2D1.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_FILTERNODED2D1_H_
+#define MOZILLA_GFX_FILTERNODED2D1_H_
+
+#include "2D.h"
+#include "Filters.h"
+#include <vector>
+#include <d2d1_1.h>
+#include <cguid.h>
+
+namespace mozilla {
+namespace gfx {
+
+class FilterNodeD2D1 : public FilterNode
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeD2D1)
+ static TemporaryRef<FilterNode> Create(ID2D1DeviceContext *aDC, FilterType aType);
+
+ FilterNodeD2D1(ID2D1Effect *aEffect, FilterType aType)
+ : mEffect(aEffect)
+ , mType(aType)
+ {
+ InitUnmappedProperties();
+ }
+
+ virtual FilterBackend GetBackendType() { return FILTER_BACKEND_DIRECT2D1_1; }
+
+ virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface);
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter);
+
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aValue);
+ virtual void SetAttribute(uint32_t aIndex, Float aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Point &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Point3D &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Size &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const IntSize &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Color &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Rect &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue);
+ virtual void SetAttribute(uint32_t aIndex, bool aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Float *aValues, uint32_t aSize);
+ virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const Matrix &aValue);
+
+ // Called by DrawTarget before it draws our OutputEffect, and recursively
+ // by the filter nodes that have this filter as one of their inputs. This
+ // gives us a chance to convert any input surfaces to the target format for
+ // the DrawTarget that we will draw to.
+ virtual void WillDraw(DrawTarget *aDT);
+
+ virtual ID2D1Effect* MainEffect() { return mEffect.get(); }
+ virtual ID2D1Effect* InputEffect() { return mEffect.get(); }
+ virtual ID2D1Effect* OutputEffect() { return mEffect.get(); }
+
+protected:
+ friend class DrawTargetD2D1;
+ friend class DrawTargetD2D;
+ friend class FilterNodeConvolveD2D1;
+
+ void InitUnmappedProperties();
+
+ RefPtr<ID2D1Effect> mEffect;
+ std::vector<RefPtr<FilterNodeD2D1>> mInputFilters;
+ std::vector<RefPtr<SourceSurface>> mInputSurfaces;
+ FilterType mType;
+};
+
+class FilterNodeConvolveD2D1 : public FilterNodeD2D1
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeConvolveD2D1)
+ FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC);
+
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter);
+
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aValue);
+ virtual void SetAttribute(uint32_t aIndex, const IntSize &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue);
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue);
+
+ virtual ID2D1Effect* InputEffect() override;
+
+private:
+ void UpdateChain();
+ void UpdateOffset();
+ void UpdateSourceRect();
+
+ RefPtr<ID2D1Effect> mExtendInputEffect;
+ RefPtr<ID2D1Effect> mBorderEffect;
+ ConvolveMatrixEdgeMode mEdgeMode;
+ IntPoint mTarget;
+ IntSize mKernelSize;
+ IntRect mSourceRect;
+};
+
+class FilterNodeExtendInputAdapterD2D1 : public FilterNodeD2D1
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeExtendInputAdapterD2D1)
+ FilterNodeExtendInputAdapterD2D1(ID2D1DeviceContext *aDC, FilterNodeD2D1 *aFilterNode, FilterType aType);
+
+ virtual ID2D1Effect* InputEffect() override { return mExtendInputEffect.get(); }
+ virtual ID2D1Effect* OutputEffect() override { return mWrappedFilterNode->OutputEffect(); }
+
+private:
+ RefPtr<FilterNodeD2D1> mWrappedFilterNode;
+ RefPtr<ID2D1Effect> mExtendInputEffect;
+};
+
+class FilterNodePremultiplyAdapterD2D1 : public FilterNodeD2D1
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodePremultiplyAdapterD2D1)
+ FilterNodePremultiplyAdapterD2D1(ID2D1DeviceContext *aDC, FilterNodeD2D1 *aFilterNode, FilterType aType);
+
+ virtual ID2D1Effect* InputEffect() override { return mPrePremultiplyEffect.get(); }
+ virtual ID2D1Effect* OutputEffect() override { return mPostUnpremultiplyEffect.get(); }
+
+private:
+ RefPtr<ID2D1Effect> mPrePremultiplyEffect;
+ RefPtr<ID2D1Effect> mPostUnpremultiplyEffect;
+};
+
+}
+}
+
+#endif
diff --git a/gfx/2d/FilterNodeSoftware.cpp b/gfx/2d/FilterNodeSoftware.cpp
new file mode 100644
index 000000000..64e4026d2
--- /dev/null
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -0,0 +1,3545 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#define _USE_MATH_DEFINES
+
+#include <cmath>
+#include "DataSurfaceHelpers.h"
+#include "FilterNodeSoftware.h"
+#include "2D.h"
+#include "Tools.h"
+#include "Blur.h"
+#include <map>
+#include "FilterProcessing.h"
+#include "Logging.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/DebugOnly.h"
+
+// #define DEBUG_DUMP_SURFACES
+
+#ifdef DEBUG_DUMP_SURFACES
+#include "gfxUtils.h" // not part of Moz2D
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+namespace {
+
+/**
+ * This class provides a way to get a pow() results in constant-time. It works
+ * by caching 256 values for bases between 0 and 1 and a fixed exponent.
+ **/
+class PowCache
+{
+public:
+ PowCache()
+ {
+ CacheForExponent(0.0f);
+ }
+
+ void CacheForExponent(Float aExponent)
+ {
+ mExponent = aExponent;
+ int numPreSquares = 0;
+ while (numPreSquares < 5 && mExponent > (1 << (numPreSquares + 2))) {
+ numPreSquares++;
+ }
+ mNumPowTablePreSquares = numPreSquares;
+ for (size_t i = 0; i < sCacheSize; i++) {
+ // sCacheSize is chosen in such a way that a takes values
+ // from 0.0 to 1.0 inclusive.
+ Float a = i / Float(1 << sCacheIndexPrecisionBits);
+ MOZ_ASSERT(0.0f <= a && a <= 1.0f, "We only want to cache for bases between 0 and 1.");
+
+ for (int j = 0; j < mNumPowTablePreSquares; j++) {
+ a = sqrt(a);
+ }
+ uint32_t cachedInt = pow(a, mExponent) * (1 << sOutputIntPrecisionBits);
+ MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)), "mPowCache integer type too small");
+
+ mPowTable[i] = cachedInt;
+ }
+ }
+
+ uint16_t Pow(uint16_t aBase)
+ {
+ // Results should be similar to what the following code would produce:
+ // Float x = Float(aBase) / (1 << sInputIntPrecisionBits);
+ // return uint16_t(pow(x, mExponent) * (1 << sOutputIntPrecisionBits));
+
+ MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits), "aBase needs to be between 0 and 1!");
+
+ uint32_t a = aBase;
+ for (int j = 0; j < mNumPowTablePreSquares; j++) {
+ a = a * a >> sInputIntPrecisionBits;
+ }
+ uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits);
+ MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access");
+ return mPowTable[i];
+ }
+
+ static const int sInputIntPrecisionBits = 15;
+ static const int sOutputIntPrecisionBits = 15;
+ static const int sCacheIndexPrecisionBits = 7;
+
+private:
+ static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1;
+
+ Float mExponent;
+ int mNumPowTablePreSquares;
+ uint16_t mPowTable[sCacheSize];
+};
+
+class PointLightSoftware
+{
+public:
+ bool SetAttribute(uint32_t aIndex, Float) { return false; }
+ bool SetAttribute(uint32_t aIndex, const Point3D &);
+ void Prepare() {}
+ Point3D GetVectorToLight(const Point3D &aTargetPoint);
+ uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
+
+private:
+ Point3D mPosition;
+};
+
+class SpotLightSoftware
+{
+public:
+ SpotLightSoftware();
+ bool SetAttribute(uint32_t aIndex, Float);
+ bool SetAttribute(uint32_t aIndex, const Point3D &);
+ void Prepare();
+ Point3D GetVectorToLight(const Point3D &aTargetPoint);
+ uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
+
+private:
+ Point3D mPosition;
+ Point3D mPointsAt;
+ Point3D mVectorFromFocusPointToLight;
+ Float mSpecularFocus;
+ Float mLimitingConeAngle;
+ Float mLimitingConeCos;
+ PowCache mPowCache;
+};
+
+class DistantLightSoftware
+{
+public:
+ DistantLightSoftware();
+ bool SetAttribute(uint32_t aIndex, Float);
+ bool SetAttribute(uint32_t aIndex, const Point3D &) { return false; }
+ void Prepare();
+ Point3D GetVectorToLight(const Point3D &aTargetPoint);
+ uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
+
+private:
+ Float mAzimuth;
+ Float mElevation;
+ Point3D mVectorToLight;
+};
+
+class DiffuseLightingSoftware
+{
+public:
+ DiffuseLightingSoftware();
+ bool SetAttribute(uint32_t aIndex, Float);
+ void Prepare() {}
+ uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight,
+ uint32_t aColor);
+
+private:
+ Float mDiffuseConstant;
+};
+
+class SpecularLightingSoftware
+{
+public:
+ SpecularLightingSoftware();
+ bool SetAttribute(uint32_t aIndex, Float);
+ void Prepare();
+ uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight,
+ uint32_t aColor);
+
+private:
+ Float mSpecularConstant;
+ Float mSpecularExponent;
+ uint32_t mSpecularConstantInt;
+ PowCache mPowCache;
+};
+
+} // unnamed namespace
+
+// from xpcom/ds/nsMathUtils.h
+static int32_t
+NS_lround(double x)
+{
+ return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5);
+}
+
+TemporaryRef<DataSourceSurface>
+CloneAligned(DataSourceSurface* aSource)
+{
+ return CreateDataSourceSurfaceByCloning(aSource);
+}
+
+static void
+FillRectWithPixel(DataSourceSurface *aSurface, const IntRect &aFillRect, IntPoint aPixelPos)
+{
+ MOZ_ASSERT(!aFillRect.Overflows());
+ MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
+ "aFillRect needs to be completely inside the surface");
+ MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos),
+ "aPixelPos needs to be inside the surface");
+
+ int32_t stride = aSurface->Stride();
+ uint8_t* sourcePixelData = DataAtOffset(aSurface, aPixelPos);
+ uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft());
+ int bpp = BytesPerPixel(aSurface->GetFormat());
+
+ // Fill the first row by hand.
+ if (bpp == 4) {
+ uint32_t sourcePixel = *(uint32_t*)sourcePixelData;
+ for (int32_t x = 0; x < aFillRect.width; x++) {
+ *((uint32_t*)data + x) = sourcePixel;
+ }
+ } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
+ uint8_t sourcePixel = *sourcePixelData;
+ memset(data, sourcePixel, aFillRect.width);
+ }
+
+ // Copy the first row into the other rows.
+ for (int32_t y = 1; y < aFillRect.height; y++) {
+ PodCopy(data + y * stride, data, aFillRect.width * bpp);
+ }
+}
+
+static void
+FillRectWithVerticallyRepeatingHorizontalStrip(DataSourceSurface *aSurface,
+ const IntRect &aFillRect,
+ const IntRect &aSampleRect)
+{
+ MOZ_ASSERT(!aFillRect.Overflows());
+ MOZ_ASSERT(!aSampleRect.Overflows());
+ MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
+ "aFillRect needs to be completely inside the surface");
+ MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
+ "aSampleRect needs to be completely inside the surface");
+
+ int32_t stride = aSurface->Stride();
+ uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft());
+ uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft());
+ if (BytesPerPixel(aSurface->GetFormat()) == 4) {
+ for (int32_t y = 0; y < aFillRect.height; y++) {
+ PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.width);
+ data += stride;
+ }
+ } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
+ for (int32_t y = 0; y < aFillRect.height; y++) {
+ PodCopy(data, sampleData, aFillRect.width);
+ data += stride;
+ }
+ }
+}
+
+static void
+FillRectWithHorizontallyRepeatingVerticalStrip(DataSourceSurface *aSurface,
+ const IntRect &aFillRect,
+ const IntRect &aSampleRect)
+{
+ MOZ_ASSERT(!aFillRect.Overflows());
+ MOZ_ASSERT(!aSampleRect.Overflows());
+ MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
+ "aFillRect needs to be completely inside the surface");
+ MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
+ "aSampleRect needs to be completely inside the surface");
+
+ int32_t stride = aSurface->Stride();
+ uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft());
+ uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft());
+ if (BytesPerPixel(aSurface->GetFormat()) == 4) {
+ for (int32_t y = 0; y < aFillRect.height; y++) {
+ int32_t sampleColor = *((uint32_t*)sampleData);
+ for (int32_t x = 0; x < aFillRect.width; x++) {
+ *((uint32_t*)data + x) = sampleColor;
+ }
+ data += stride;
+ sampleData += stride;
+ }
+ } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
+ for (int32_t y = 0; y < aFillRect.height; y++) {
+ uint8_t sampleColor = *sampleData;
+ memset(data, sampleColor, aFillRect.width);
+ data += stride;
+ sampleData += stride;
+ }
+ }
+}
+
+static void
+DuplicateEdges(DataSourceSurface* aSurface, const IntRect &aFromRect)
+{
+ MOZ_ASSERT(!aFromRect.Overflows());
+ MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect),
+ "aFromRect needs to be completely inside the surface");
+
+ IntSize size = aSurface->GetSize();
+ IntRect fill;
+ IntRect sampleRect;
+ for (int32_t ix = 0; ix < 3; ix++) {
+ switch (ix) {
+ case 0:
+ fill.x = 0;
+ fill.width = aFromRect.x;
+ sampleRect.x = fill.XMost();
+ sampleRect.width = 1;
+ break;
+ case 1:
+ fill.x = aFromRect.x;
+ fill.width = aFromRect.width;
+ sampleRect.x = fill.x;
+ sampleRect.width = fill.width;
+ break;
+ case 2:
+ fill.x = aFromRect.XMost();
+ fill.width = size.width - fill.x;
+ sampleRect.x = fill.x - 1;
+ sampleRect.width = 1;
+ break;
+ }
+ if (fill.width <= 0) {
+ continue;
+ }
+ bool xIsMiddle = (ix == 1);
+ for (int32_t iy = 0; iy < 3; iy++) {
+ switch (iy) {
+ case 0:
+ fill.y = 0;
+ fill.height = aFromRect.y;
+ sampleRect.y = fill.YMost();
+ sampleRect.height = 1;
+ break;
+ case 1:
+ fill.y = aFromRect.y;
+ fill.height = aFromRect.height;
+ sampleRect.y = fill.y;
+ sampleRect.height = fill.height;
+ break;
+ case 2:
+ fill.y = aFromRect.YMost();
+ fill.height = size.height - fill.y;
+ sampleRect.y = fill.y - 1;
+ sampleRect.height = 1;
+ break;
+ }
+ if (fill.height <= 0) {
+ continue;
+ }
+ bool yIsMiddle = (iy == 1);
+ if (!xIsMiddle && !yIsMiddle) {
+ // Corner
+ FillRectWithPixel(aSurface, fill, sampleRect.TopLeft());
+ }
+ if (xIsMiddle && !yIsMiddle) {
+ // Top middle or bottom middle
+ FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill, sampleRect);
+ }
+ if (!xIsMiddle && yIsMiddle) {
+ // Left middle or right middle
+ FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill, sampleRect);
+ }
+ }
+ }
+}
+
+static IntPoint
+TileIndex(const IntRect &aFirstTileRect, const IntPoint &aPoint)
+{
+ return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.x) / aFirstTileRect.width)),
+ int32_t(floor(double(aPoint.y - aFirstTileRect.y) / aFirstTileRect.height)));
+}
+
+static void
+TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget, const IntPoint &aOffset)
+{
+ IntRect sourceRect(aOffset, aSource->GetSize());
+ IntRect targetRect(IntPoint(0, 0), aTarget->GetSize());
+ IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft());
+ IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight());
+
+ for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
+ for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
+ IntPoint destPoint(sourceRect.x + ix * sourceRect.width,
+ sourceRect.y + iy * sourceRect.height);
+ IntRect destRect(destPoint, sourceRect.Size());
+ destRect = destRect.Intersect(targetRect);
+ IntRect srcRect = destRect - destPoint;
+ CopyRect(aSource, aTarget, srcRect, destRect.TopLeft());
+ }
+ }
+}
+
+static TemporaryRef<DataSourceSurface>
+GetDataSurfaceInRect(SourceSurface *aSurface,
+ const IntRect &aSurfaceRect,
+ const IntRect &aDestRect,
+ ConvolveMatrixEdgeMode aEdgeMode)
+{
+ MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize() : aSurfaceRect.IsEmpty());
+
+ if (aSurfaceRect.Overflows() || aDestRect.Overflows()) {
+ // We can't rely on the intersection calculations below to make sense when
+ // XMost() or YMost() overflow. Bail out.
+ return nullptr;
+ }
+
+ IntRect sourceRect = aSurfaceRect;
+
+ if (sourceRect.IsEqualEdges(aDestRect)) {
+ return aSurface ? aSurface->GetDataSurface() : nullptr;
+ }
+
+ IntRect intersect = sourceRect.Intersect(aDestRect);
+ IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft();
+ IntRect intersectInDestSpace = intersect - aDestRect.TopLeft();
+ SurfaceFormat format = aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8);
+
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aDestRect.Size(), format, true);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ if (!aSurface) {
+ return target.forget();
+ }
+
+ RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface();
+ MOZ_ASSERT(dataSource);
+
+ if (aEdgeMode == EDGE_MODE_WRAP) {
+ TileSurface(dataSource, target, intersectInDestSpace.TopLeft());
+ return target.forget();
+ }
+
+ CopyRect(dataSource, target, intersectInSourceSpace,
+ intersectInDestSpace.TopLeft());
+
+ if (aEdgeMode == EDGE_MODE_DUPLICATE) {
+ DuplicateEdges(target, intersectInDestSpace);
+ }
+
+ return target.forget();
+}
+
+/* static */ TemporaryRef<FilterNode>
+FilterNodeSoftware::Create(FilterType aType)
+{
+ RefPtr<FilterNodeSoftware> filter;
+ switch (aType) {
+ case FilterType::BLEND:
+ filter = new FilterNodeBlendSoftware();
+ break;
+ case FilterType::TRANSFORM:
+ filter = new FilterNodeTransformSoftware();
+ break;
+ case FilterType::MORPHOLOGY:
+ filter = new FilterNodeMorphologySoftware();
+ break;
+ case FilterType::COLOR_MATRIX:
+ filter = new FilterNodeColorMatrixSoftware();
+ break;
+ case FilterType::FLOOD:
+ filter = new FilterNodeFloodSoftware();
+ break;
+ case FilterType::TILE:
+ filter = new FilterNodeTileSoftware();
+ break;
+ case FilterType::TABLE_TRANSFER:
+ filter = new FilterNodeTableTransferSoftware();
+ break;
+ case FilterType::DISCRETE_TRANSFER:
+ filter = new FilterNodeDiscreteTransferSoftware();
+ break;
+ case FilterType::LINEAR_TRANSFER:
+ filter = new FilterNodeLinearTransferSoftware();
+ break;
+ case FilterType::GAMMA_TRANSFER:
+ filter = new FilterNodeGammaTransferSoftware();
+ break;
+ case FilterType::CONVOLVE_MATRIX:
+ filter = new FilterNodeConvolveMatrixSoftware();
+ break;
+ case FilterType::DISPLACEMENT_MAP:
+ filter = new FilterNodeDisplacementMapSoftware();
+ break;
+ case FilterType::TURBULENCE:
+ filter = new FilterNodeTurbulenceSoftware();
+ break;
+ case FilterType::ARITHMETIC_COMBINE:
+ filter = new FilterNodeArithmeticCombineSoftware();
+ break;
+ case FilterType::COMPOSITE:
+ filter = new FilterNodeCompositeSoftware();
+ break;
+ case FilterType::GAUSSIAN_BLUR:
+ filter = new FilterNodeGaussianBlurSoftware();
+ break;
+ case FilterType::DIRECTIONAL_BLUR:
+ filter = new FilterNodeDirectionalBlurSoftware();
+ break;
+ case FilterType::CROP:
+ filter = new FilterNodeCropSoftware();
+ break;
+ case FilterType::PREMULTIPLY:
+ filter = new FilterNodePremultiplySoftware();
+ break;
+ case FilterType::UNPREMULTIPLY:
+ filter = new FilterNodeUnpremultiplySoftware();
+ break;
+ case FilterType::POINT_DIFFUSE:
+ filter = new FilterNodeLightingSoftware<PointLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<PointLight, DiffuseLighting>");
+ break;
+ case FilterType::POINT_SPECULAR:
+ filter = new FilterNodeLightingSoftware<PointLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<PointLight, SpecularLighting>");
+ break;
+ case FilterType::SPOT_DIFFUSE:
+ filter = new FilterNodeLightingSoftware<SpotLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<SpotLight, DiffuseLighting>");
+ break;
+ case FilterType::SPOT_SPECULAR:
+ filter = new FilterNodeLightingSoftware<SpotLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<SpotLight, SpecularLighting>");
+ break;
+ case FilterType::DISTANT_DIFFUSE:
+ filter = new FilterNodeLightingSoftware<DistantLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<DistantLight, DiffuseLighting>");
+ break;
+ case FilterType::DISTANT_SPECULAR:
+ filter = new FilterNodeLightingSoftware<DistantLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<DistantLight, SpecularLighting>");
+ break;
+ }
+ return filter.forget();
+}
+
+void
+FilterNodeSoftware::Draw(DrawTarget* aDrawTarget,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+#ifdef DEBUG_DUMP_SURFACES
+ printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n", GetName());
+#endif
+
+ Rect renderRect = aSourceRect;
+ renderRect.RoundOut();
+ IntRect renderIntRect;
+ if (!renderRect.ToIntRect(&renderIntRect)) {
+#ifdef DEBUG_DUMP_SURFACES
+ printf("render rect overflowed, not painting anything\n");
+ printf("</pre>\n");
+#endif
+ return;
+ }
+
+ IntRect outputRect = GetOutputRectInRect(renderIntRect);
+ if (outputRect.Overflows()) {
+#ifdef DEBUG_DUMP_SURFACES
+ printf("output rect overflowed, not painting anything\n");
+ printf("</pre>\n");
+#endif
+ return;
+ }
+
+ RefPtr<DataSourceSurface> result;
+ if (!outputRect.IsEmpty()) {
+ result = GetOutput(outputRect);
+ }
+
+ if (!result) {
+ // Null results are allowed and treated as transparent. Don't draw anything.
+#ifdef DEBUG_DUMP_SURFACES
+ printf("output returned null\n");
+ printf("</pre>\n");
+#endif
+ return;
+ }
+
+#ifdef DEBUG_DUMP_SURFACES
+ printf("output from %s:\n", GetName());
+ printf("<img src='"); gfxUtils::DumpAsDataURL(result); printf("'>\n");
+ printf("</pre>\n");
+#endif
+
+ Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft();
+ Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect);
+ Rect renderedDestRect = renderedSourceRect + sourceToDestOffset;
+ if (result->GetFormat() == SurfaceFormat::A8) {
+ // Interpret the result as having implicitly black color channels.
+ aDrawTarget->PushClipRect(renderedDestRect);
+ aDrawTarget->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)),
+ result,
+ Point(outputRect.TopLeft()) + sourceToDestOffset,
+ aOptions);
+ aDrawTarget->PopClip();
+ } else {
+ aDrawTarget->DrawSurface(result, renderedDestRect,
+ renderedSourceRect - Point(outputRect.TopLeft()),
+ DrawSurfaceOptions(), aOptions);
+ }
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeSoftware::GetOutput(const IntRect &aRect)
+{
+ MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect));
+
+ if (aRect.Overflows()) {
+ return nullptr;
+ }
+
+ if (!mCachedRect.Contains(aRect)) {
+ RequestRect(aRect);
+ mCachedOutput = Render(mRequestedRect);
+ if (!mCachedOutput) {
+ mCachedRect = IntRect();
+ mRequestedRect = IntRect();
+ return nullptr;
+ }
+ mCachedRect = mRequestedRect;
+ mRequestedRect = IntRect();
+ } else {
+ MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?");
+ }
+ return GetDataSurfaceInRect(mCachedOutput, mCachedRect, aRect, EDGE_MODE_NONE);
+}
+
+void
+FilterNodeSoftware::RequestRect(const IntRect &aRect)
+{
+ mRequestedRect = mRequestedRect.Union(aRect);
+ RequestFromInputsForRect(aRect);
+}
+
+void
+FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect)
+{
+ if (aRect.Overflows()) {
+ return;
+ }
+
+ int32_t inputIndex = InputIndex(aInputEnumIndex);
+ if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
+ MOZ_CRASH();
+ }
+ if (mInputSurfaces[inputIndex]) {
+ return;
+ }
+ RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
+ MOZ_ASSERT(filter, "missing input");
+ filter->RequestRect(filter->GetOutputRectInRect(aRect));
+}
+
+SurfaceFormat
+FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat,
+ FormatHint aFormatHint)
+{
+ if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) {
+ return SurfaceFormat::A8;
+ }
+ return SurfaceFormat::B8G8R8A8;
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeSoftware::GetInputDataSourceSurface(uint32_t aInputEnumIndex,
+ const IntRect& aRect,
+ FormatHint aFormatHint,
+ ConvolveMatrixEdgeMode aEdgeMode,
+ const IntRect *aTransparencyPaddedSourceRect)
+{
+ if (aRect.Overflows()) {
+ return nullptr;
+ }
+
+#ifdef DEBUG_DUMP_SURFACES
+ printf("<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, %d</h1>\n",
+ aRect.x, aRect.y, aRect.width, aRect.height);
+#endif
+ int32_t inputIndex = InputIndex(aInputEnumIndex);
+ if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
+ MOZ_CRASH();
+ return nullptr;
+ }
+
+ if (aRect.IsEmpty()) {
+ return nullptr;
+ }
+
+ RefPtr<SourceSurface> surface;
+ IntRect surfaceRect;
+
+ if (mInputSurfaces[inputIndex]) {
+ // Input from input surface
+ surface = mInputSurfaces[inputIndex];
+#ifdef DEBUG_DUMP_SURFACES
+ printf("input from input surface:\n");
+#endif
+ surfaceRect = IntRect(IntPoint(0, 0), surface->GetSize());
+ } else {
+ // Input from input filter
+#ifdef DEBUG_DUMP_SURFACES
+ printf("getting input from input filter %s...\n", mInputFilters[inputIndex]->GetName());
+#endif
+ RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
+ MOZ_ASSERT(filter, "missing input");
+ IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect);
+ if (!inputFilterOutput.IsEmpty()) {
+ surface = filter->GetOutput(inputFilterOutput);
+ }
+#ifdef DEBUG_DUMP_SURFACES
+ printf("input from input filter %s:\n", mInputFilters[inputIndex]->GetName());
+#endif
+ surfaceRect = inputFilterOutput;
+ MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize());
+ }
+
+ if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) {
+#ifdef DEBUG_DUMP_SURFACES
+ printf("wrong input format</section>\n\n");
+#endif
+ return nullptr;
+ }
+
+ if (!surfaceRect.IsEmpty() && !surface) {
+#ifdef DEBUG_DUMP_SURFACES
+ printf(" -- no input --</section>\n\n");
+#endif
+ return nullptr;
+ }
+
+ if (aTransparencyPaddedSourceRect && !aTransparencyPaddedSourceRect->IsEmpty()) {
+ IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect);
+ surface = GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE);
+ surfaceRect = srcRect;
+ }
+
+ RefPtr<DataSourceSurface> result =
+ GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode);
+
+ if (result) {
+ // TODO: This isn't safe since we don't have a guarantee
+ // that future Maps will have the same stride
+ DataSourceSurface::MappedSurface map;
+ if (result->Map(DataSourceSurface::READ, &map)) {
+ // Unmap immediately since CloneAligned hasn't been updated
+ // to use the Map API yet. We can still read the stride/data
+ // values as long as we don't try to dereference them.
+ result->Unmap();
+ if (map.mStride != GetAlignedStride<16>(map.mStride) ||
+ reinterpret_cast<uintptr_t>(map.mData) % 16 != 0) {
+ // Align unaligned surface.
+ result = CloneAligned(result);
+ }
+ } else {
+ result = nullptr;
+ }
+ }
+
+
+ if (!result) {
+#ifdef DEBUG_DUMP_SURFACES
+ printf(" -- no input --</section>\n\n");
+#endif
+ return nullptr;
+ }
+
+ SurfaceFormat currentFormat = result->GetFormat();
+ if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 &&
+ currentFormat != SurfaceFormat::B8G8R8A8) {
+ result = FilterProcessing::ConvertToB8G8R8A8(result);
+ }
+
+#ifdef DEBUG_DUMP_SURFACES
+ printf("<img src='"); gfxUtils::DumpAsDataURL(result); printf("'></section>");
+#endif
+
+ MOZ_ASSERT(!result || result->GetSize() == aRect.Size(), "wrong surface size");
+
+ return result.forget();
+}
+
+IntRect
+FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex,
+ const IntRect &aInRect)
+{
+ if (aInRect.Overflows()) {
+ return IntRect();
+ }
+
+ int32_t inputIndex = InputIndex(aInputEnumIndex);
+ if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
+ MOZ_CRASH();
+ return IntRect();
+ }
+ if (mInputSurfaces[inputIndex]) {
+ return aInRect.Intersect(IntRect(IntPoint(0, 0),
+ mInputSurfaces[inputIndex]->GetSize()));
+ }
+ RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
+ MOZ_ASSERT(filter, "missing input");
+ return filter->GetOutputRectInRect(aInRect);
+}
+
+size_t
+FilterNodeSoftware::NumberOfSetInputs()
+{
+ return std::max(mInputSurfaces.size(), mInputFilters.size());
+}
+
+void
+FilterNodeSoftware::AddInvalidationListener(FilterInvalidationListener* aListener)
+{
+ MOZ_ASSERT(aListener, "null listener");
+ mInvalidationListeners.push_back(aListener);
+}
+
+void
+FilterNodeSoftware::RemoveInvalidationListener(FilterInvalidationListener* aListener)
+{
+ MOZ_ASSERT(aListener, "null listener");
+ std::vector<FilterInvalidationListener*>::iterator it =
+ std::find(mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener);
+ mInvalidationListeners.erase(it);
+}
+
+void
+FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter)
+{
+ Invalidate();
+}
+
+void
+FilterNodeSoftware::Invalidate()
+{
+ mCachedOutput = nullptr;
+ mCachedRect = IntRect();
+ for (std::vector<FilterInvalidationListener*>::iterator it = mInvalidationListeners.begin();
+ it != mInvalidationListeners.end(); it++) {
+ (*it)->FilterInvalidated(this);
+ }
+}
+
+FilterNodeSoftware::~FilterNodeSoftware()
+{
+ MOZ_ASSERT(!mInvalidationListeners.size(),
+ "All invalidation listeners should have unsubscribed themselves by now!");
+
+ for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it = mInputFilters.begin();
+ it != mInputFilters.end(); it++) {
+ if (*it) {
+ (*it)->RemoveInvalidationListener(this);
+ }
+ }
+}
+
+void
+FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode *aFilter)
+{
+ if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
+ MOZ_ASSERT(false, "can only take software filters as inputs");
+ return;
+ }
+ SetInput(aIndex, nullptr, static_cast<FilterNodeSoftware*>(aFilter));
+}
+
+void
+FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface *aSurface)
+{
+ SetInput(aIndex, aSurface, nullptr);
+}
+
+void
+FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex,
+ SourceSurface *aSurface,
+ FilterNodeSoftware *aFilter)
+{
+ int32_t inputIndex = InputIndex(aInputEnumIndex);
+ if (inputIndex < 0) {
+ MOZ_CRASH();
+ return;
+ }
+ if ((uint32_t)inputIndex >= NumberOfSetInputs()) {
+ mInputSurfaces.resize(inputIndex + 1);
+ mInputFilters.resize(inputIndex + 1);
+ }
+ mInputSurfaces[inputIndex] = aSurface;
+ if (mInputFilters[inputIndex]) {
+ mInputFilters[inputIndex]->RemoveInvalidationListener(this);
+ }
+ if (aFilter) {
+ aFilter->AddInvalidationListener(this);
+ }
+ mInputFilters[inputIndex] = aFilter;
+ if (!aSurface && !aFilter && (size_t)inputIndex == NumberOfSetInputs()) {
+ mInputSurfaces.resize(inputIndex);
+ mInputFilters.resize(inputIndex);
+ }
+ Invalidate();
+}
+
+FilterNodeBlendSoftware::FilterNodeBlendSoftware()
+ : mBlendMode(BLEND_MODE_MULTIPLY)
+{}
+
+int32_t
+FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_BLEND_IN: return 0;
+ case IN_BLEND_IN2: return 1;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex, uint32_t aBlendMode)
+{
+ MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE);
+ mBlendMode = static_cast<BlendMode>(aBlendMode);
+ Invalidate();
+}
+
+static CompositionOp ToBlendOp(BlendMode aOp)
+{
+ switch (aOp) {
+ case BLEND_MODE_MULTIPLY:
+ return CompositionOp::OP_MULTIPLY;
+ case BLEND_MODE_SCREEN:
+ return CompositionOp::OP_SCREEN;
+ case BLEND_MODE_OVERLAY:
+ return CompositionOp::OP_OVERLAY;
+ case BLEND_MODE_DARKEN:
+ return CompositionOp::OP_DARKEN;
+ case BLEND_MODE_LIGHTEN:
+ return CompositionOp::OP_LIGHTEN;
+ case BLEND_MODE_COLOR_DODGE:
+ return CompositionOp::OP_COLOR_DODGE;
+ case BLEND_MODE_COLOR_BURN:
+ return CompositionOp::OP_COLOR_BURN;
+ case BLEND_MODE_HARD_LIGHT:
+ return CompositionOp::OP_HARD_LIGHT;
+ case BLEND_MODE_SOFT_LIGHT:
+ return CompositionOp::OP_SOFT_LIGHT;
+ case BLEND_MODE_DIFFERENCE:
+ return CompositionOp::OP_DIFFERENCE;
+ case BLEND_MODE_EXCLUSION:
+ return CompositionOp::OP_EXCLUSION;
+ case BLEND_MODE_HUE:
+ return CompositionOp::OP_HUE;
+ case BLEND_MODE_SATURATION:
+ return CompositionOp::OP_SATURATION;
+ case BLEND_MODE_COLOR:
+ return CompositionOp::OP_COLOR;
+ case BLEND_MODE_LUMINOSITY:
+ return CompositionOp::OP_LUMINOSITY;
+ default:
+ return CompositionOp::OP_OVER;
+ }
+
+ return CompositionOp::OP_OVER;
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeBlendSoftware::Render(const IntRect& aRect)
+{
+ RefPtr<DataSourceSurface> input1 =
+ GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS);
+ RefPtr<DataSourceSurface> input2 =
+ GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS);
+
+ // Null inputs need to be treated as transparent.
+
+ // First case: both are transparent.
+ if (!input1 && !input2) {
+ // Then the result is transparent, too.
+ return nullptr;
+ }
+
+ // Second case: one of them is transparent. Return the non-transparent one.
+ if (!input1 || !input2) {
+ return input1 ? input1.forget() : input2.forget();
+ }
+
+ // Third case: both are non-transparent.
+ // Apply normal filtering.
+ RefPtr<DataSourceSurface> target = FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
+ if (target != nullptr) {
+ return target.forget();
+ }
+
+ IntSize size = input1->GetSize();
+ target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ CopyRect(input1, target, IntRect(IntPoint(), size), IntPoint());
+
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForData(BackendType::CAIRO,
+ target->GetData(),
+ target->GetSize(),
+ target->Stride(),
+ target->GetFormat());
+
+ Rect r(0, 0, size.width, size.height);
+ dt->DrawSurface(input2, r, r, DrawSurfaceOptions(), DrawOptions(1.0f, ToBlendOp(mBlendMode)));
+ dt->Flush();
+ return target.forget();
+}
+
+void
+FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_BLEND_IN, aRect);
+ RequestInputRect(IN_BLEND_IN2, aRect);
+}
+
+IntRect
+FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ return GetInputRectInRect(IN_BLEND_IN, aRect).Union(
+ GetInputRectInRect(IN_BLEND_IN2, aRect)).Intersect(aRect);
+}
+
+FilterNodeTransformSoftware::FilterNodeTransformSoftware()
+ : mFilter(Filter::GOOD)
+{}
+
+int32_t
+FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_TRANSFORM_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, uint32_t aFilter)
+{
+ MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER);
+ mFilter = static_cast<Filter>(aFilter);
+ Invalidate();
+}
+
+void
+FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, const Matrix &aMatrix)
+{
+ MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
+ mMatrix = aMatrix;
+ Invalidate();
+}
+
+IntRect
+FilterNodeTransformSoftware::SourceRectForOutputRect(const IntRect &aRect)
+{
+ if (aRect.IsEmpty()) {
+ return IntRect();
+ }
+
+ Matrix inverted(mMatrix);
+ if (!inverted.Invert()) {
+ return IntRect();
+ }
+
+ Rect neededRect = inverted.TransformBounds(Rect(aRect));
+ neededRect.RoundOut();
+ IntRect neededIntRect;
+ if (!neededRect.ToIntRect(&neededIntRect)) {
+ return IntRect();
+ }
+ return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect);
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeTransformSoftware::Render(const IntRect& aRect)
+{
+ IntRect srcRect = SourceRectForOutputRect(aRect);
+
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect);
+
+ if (!input) {
+ return nullptr;
+ }
+
+ Matrix transform = Matrix::Translation(srcRect.x, srcRect.y) * mMatrix *
+ Matrix::Translation(-aRect.x, -aRect.y);
+ if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) {
+ return input.forget();
+ }
+
+ RefPtr<DataSourceSurface> surf =
+ Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat(), true);
+
+ if (!surf) {
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface mapping;
+ if (!surf->Map(DataSourceSurface::MapType::WRITE, &mapping)) {
+ gfxCriticalError() << "FilterNodeTransformSoftware::Render failed to map surface";
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForData(BackendType::CAIRO,
+ mapping.mData,
+ surf->GetSize(),
+ mapping.mStride,
+ surf->GetFormat());
+ if (!dt) {
+ return nullptr;
+ }
+
+ Rect r(0, 0, srcRect.width, srcRect.height);
+ dt->SetTransform(transform);
+ dt->DrawSurface(input, r, r, DrawSurfaceOptions(mFilter));
+
+ dt->Flush();
+ surf->Unmap();
+ return surf.forget();
+}
+
+void
+FilterNodeTransformSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
+}
+
+IntRect
+FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ IntRect srcRect = SourceRectForOutputRect(aRect);
+ if (srcRect.IsEmpty()) {
+ return IntRect();
+ }
+
+ Rect outRect = mMatrix.TransformBounds(Rect(srcRect));
+ outRect.RoundOut();
+ IntRect outIntRect;
+ if (!outRect.ToIntRect(&outIntRect)) {
+ return IntRect();
+ }
+ return outIntRect.Intersect(aRect);
+}
+
+FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
+ : mOperator(MORPHOLOGY_OPERATOR_ERODE)
+{}
+
+int32_t
+FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_MORPHOLOGY_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
+ const IntSize &aRadii)
+{
+ MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII);
+ mRadii.width = std::min(std::max(aRadii.width, 0), 100000);
+ mRadii.height = std::min(std::max(aRadii.height, 0), 100000);
+ Invalidate();
+}
+
+void
+FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
+ uint32_t aOperator)
+{
+ MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR);
+ mOperator = static_cast<MorphologyOperator>(aOperator);
+ Invalidate();
+}
+
+static TemporaryRef<DataSourceSurface>
+ApplyMorphology(const IntRect& aSourceRect, DataSourceSurface* aInput,
+ const IntRect& aDestRect, int32_t rx, int32_t ry,
+ MorphologyOperator aOperator)
+{
+ IntRect srcRect = aSourceRect - aDestRect.TopLeft();
+ IntRect destRect = aDestRect - aDestRect.TopLeft();
+ IntRect tmpRect(destRect.x, srcRect.y, destRect.width, srcRect.height);
+#ifdef DEBUG
+ IntMargin margin = srcRect - destRect;
+ MOZ_ASSERT(margin.top >= ry && margin.right >= rx &&
+ margin.bottom >= ry && margin.left >= rx, "insufficient margin");
+#endif
+
+ RefPtr<DataSourceSurface> tmp;
+ if (rx == 0) {
+ tmp = aInput;
+ } else {
+ tmp = Factory::CreateDataSourceSurface(tmpRect.Size(), SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!tmp)) {
+ return nullptr;
+ }
+
+ int32_t sourceStride = aInput->Stride();
+ uint8_t* sourceData = DataAtOffset(aInput, destRect.TopLeft() - srcRect.TopLeft());
+
+ int32_t tmpStride = tmp->Stride();
+ uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft());
+
+ FilterProcessing::ApplyMorphologyHorizontal(
+ sourceData, sourceStride, tmpData, tmpStride, tmpRect, rx, aOperator);
+ }
+
+ RefPtr<DataSourceSurface> dest;
+ if (ry == 0) {
+ dest = tmp;
+ } else {
+ dest = Factory::CreateDataSourceSurface(destRect.Size(), SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!dest)) {
+ return nullptr;
+ }
+
+ int32_t tmpStride = tmp->Stride();
+ uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft());
+
+ int32_t destStride = dest->Stride();
+ uint8_t* destData = dest->GetData();
+
+ FilterProcessing::ApplyMorphologyVertical(
+ tmpData, tmpStride, destData, destStride, destRect, ry, aOperator);
+ }
+
+ return dest.forget();
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeMorphologySoftware::Render(const IntRect& aRect)
+{
+ IntRect srcRect = aRect;
+ srcRect.Inflate(mRadii);
+
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS);
+ if (!input) {
+ return nullptr;
+ }
+
+ int32_t rx = mRadii.width;
+ int32_t ry = mRadii.height;
+
+ if (rx == 0 && ry == 0) {
+ return input.forget();
+ }
+
+ return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator);
+}
+
+void
+FilterNodeMorphologySoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ IntRect srcRect = aRect;
+ srcRect.Inflate(mRadii);
+ RequestInputRect(IN_MORPHOLOGY_IN, srcRect);
+}
+
+IntRect
+FilterNodeMorphologySoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ IntRect inflatedSourceRect = aRect;
+ inflatedSourceRect.Inflate(mRadii);
+ IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect);
+ if (mOperator == MORPHOLOGY_OPERATOR_ERODE) {
+ inputRect.Deflate(mRadii);
+ } else {
+ inputRect.Inflate(mRadii);
+ }
+ return inputRect.Intersect(aRect);
+}
+
+int32_t
+FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_COLOR_MATRIX_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
+ const Matrix5x4 &aMatrix)
+{
+ MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX);
+ mMatrix = aMatrix;
+ Invalidate();
+}
+
+void
+FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
+ uint32_t aAlphaMode)
+{
+ MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE);
+ mAlphaMode = (AlphaMode)aAlphaMode;
+ Invalidate();
+}
+
+static TemporaryRef<DataSourceSurface>
+Premultiply(DataSourceSurface* aSurface)
+{
+ if (aSurface->GetFormat() == SurfaceFormat::A8) {
+ return aSurface;
+ }
+
+ IntSize size = aSurface->GetSize();
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ uint8_t* inputData = aSurface->GetData();
+ int32_t inputStride = aSurface->Stride();
+ uint8_t* targetData = target->GetData();
+ int32_t targetStride = target->Stride();
+
+ FilterProcessing::DoPremultiplicationCalculation(
+ size, targetData, targetStride, inputData, inputStride);
+
+ return target.forget();
+}
+
+static TemporaryRef<DataSourceSurface>
+Unpremultiply(DataSourceSurface* aSurface)
+{
+ if (aSurface->GetFormat() == SurfaceFormat::A8) {
+ return aSurface;
+ }
+
+ IntSize size = aSurface->GetSize();
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ uint8_t* inputData = aSurface->GetData();
+ int32_t inputStride = aSurface->Stride();
+ uint8_t* targetData = target->GetData();
+ int32_t targetStride = target->Stride();
+
+ FilterProcessing::DoUnpremultiplicationCalculation(
+ size, targetData, targetStride, inputData, inputStride);
+
+ return target.forget();
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeColorMatrixSoftware::Render(const IntRect& aRect)
+{
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS);
+ if (!input) {
+ return nullptr;
+ }
+
+ if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
+ input = Unpremultiply(input);
+ }
+
+ RefPtr<DataSourceSurface> result =
+ FilterProcessing::ApplyColorMatrix(input, mMatrix);
+
+ if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
+ result = Premultiply(result);
+ }
+
+ return result.forget();
+}
+
+void
+FilterNodeColorMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_COLOR_MATRIX_IN, aRect);
+}
+
+IntRect
+FilterNodeColorMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ if (mMatrix._54 > 0.0f) {
+ return aRect;
+ }
+ return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
+}
+
+void
+FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex, const Color &aColor)
+{
+ MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR);
+ mColor = aColor;
+ Invalidate();
+}
+
+static uint32_t
+ColorToBGRA(const Color& aColor)
+{
+ union {
+ uint32_t color;
+ uint8_t components[4];
+ };
+ components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = NS_lround(aColor.r * aColor.a * 255.0f);
+ components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = NS_lround(aColor.g * aColor.a * 255.0f);
+ components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = NS_lround(aColor.b * aColor.a * 255.0f);
+ components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f);
+ return color;
+}
+
+static SurfaceFormat
+FormatForColor(Color aColor)
+{
+ if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) {
+ return SurfaceFormat::A8;
+ }
+ return SurfaceFormat::B8G8R8A8;
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeFloodSoftware::Render(const IntRect& aRect)
+{
+ SurfaceFormat format = FormatForColor(mColor);
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aRect.Size(), format);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ uint8_t* targetData = target->GetData();
+ uint32_t stride = target->Stride();
+
+ if (format == SurfaceFormat::B8G8R8A8) {
+ uint32_t color = ColorToBGRA(mColor);
+ for (int32_t y = 0; y < aRect.height; y++) {
+ for (int32_t x = 0; x < aRect.width; x++) {
+ *((uint32_t*)targetData + x) = color;
+ }
+ targetData += stride;
+ }
+ } else if (format == SurfaceFormat::A8) {
+ uint8_t alpha = NS_lround(mColor.a * 255.0f);
+ for (int32_t y = 0; y < aRect.height; y++) {
+ for (int32_t x = 0; x < aRect.width; x++) {
+ targetData[x] = alpha;
+ }
+ targetData += stride;
+ }
+ } else {
+ MOZ_CRASH();
+ }
+
+ return target.forget();
+}
+
+// Override GetOutput to get around caching. Rendering simple floods is
+// comparatively fast.
+TemporaryRef<DataSourceSurface>
+FilterNodeFloodSoftware::GetOutput(const IntRect& aRect)
+{
+ return Render(aRect);
+}
+
+IntRect
+FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ if (mColor.a == 0.0f) {
+ return IntRect();
+ }
+ return aRect;
+}
+
+int32_t
+FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_TILE_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeTileSoftware::SetAttribute(uint32_t aIndex,
+ const IntRect &aSourceRect)
+{
+ MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT);
+ mSourceRect = IntRect(int32_t(aSourceRect.x), int32_t(aSourceRect.y),
+ int32_t(aSourceRect.width), int32_t(aSourceRect.height));
+ Invalidate();
+}
+
+namespace {
+struct CompareIntRects
+{
+ bool operator()(const IntRect& a, const IntRect& b) const
+ {
+ if (a.x != b.x) {
+ return a.x < b.x;
+ }
+ if (a.y != b.y) {
+ return a.y < b.y;
+ }
+ if (a.width != b.width) {
+ return a.width < b.width;
+ }
+ return a.height < b.height;
+ }
+};
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeTileSoftware::Render(const IntRect& aRect)
+{
+ if (mSourceRect.IsEmpty()) {
+ return nullptr;
+ }
+
+ if (mSourceRect.Contains(aRect)) {
+ return GetInputDataSourceSurface(IN_TILE_IN, aRect);
+ }
+
+ RefPtr<DataSourceSurface> target;
+
+ typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects> InputMap;
+ InputMap inputs;
+
+ IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft());
+ IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight());
+ for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
+ for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
+ IntPoint sourceToDestOffset(ix * mSourceRect.width,
+ iy * mSourceRect.height);
+ IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset);
+ IntRect srcRect = destRect - sourceToDestOffset;
+ if (srcRect.IsEmpty()) {
+ continue;
+ }
+
+ RefPtr<DataSourceSurface> input;
+ InputMap::iterator it = inputs.find(srcRect);
+ if (it == inputs.end()) {
+ input = GetInputDataSourceSurface(IN_TILE_IN, srcRect);
+ inputs[srcRect] = input;
+ } else {
+ input = it->second;
+ }
+ if (!input) {
+ return nullptr;
+ }
+ if (!target) {
+ // We delay creating the target until now because we want to use the
+ // same format as our input filter, and we do not actually know the
+ // input format before we call GetInputDataSourceSurface.
+ target = Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat());
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+ }
+
+ if (input->GetFormat() != target->GetFormat()) {
+ // Different rectangles of the input can have different formats. If
+ // that happens, just convert everything to B8G8R8A8.
+ target = FilterProcessing::ConvertToB8G8R8A8(target);
+ input = FilterProcessing::ConvertToB8G8R8A8(input);
+ if (MOZ2D_WARN_IF(!target) || MOZ2D_WARN_IF(!input)) {
+ return nullptr;
+ }
+ }
+
+ CopyRect(input, target, srcRect - srcRect.TopLeft(), destRect.TopLeft() - aRect.TopLeft());
+ }
+ }
+
+ return target.forget();
+}
+
+void
+FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ // Do not request anything.
+ // Source rects for the tile filter can be discontinuous with large gaps
+ // between them. Requesting those from our input filter might cause it to
+ // render the whole bounding box of all of them, which would be wasteful.
+}
+
+IntRect
+FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ return aRect;
+}
+
+FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
+ : mDisableR(true)
+ , mDisableG(true)
+ , mDisableB(true)
+ , mDisableA(true)
+{}
+
+void
+FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex,
+ bool aDisable)
+{
+ switch (aIndex) {
+ case ATT_TRANSFER_DISABLE_R:
+ mDisableR = aDisable;
+ break;
+ case ATT_TRANSFER_DISABLE_G:
+ mDisableG = aDisable;
+ break;
+ case ATT_TRANSFER_DISABLE_B:
+ mDisableB = aDisable;
+ break;
+ case ATT_TRANSFER_DISABLE_A:
+ mDisableA = aDisable;
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+void
+FilterNodeComponentTransferSoftware::GenerateLookupTable(ptrdiff_t aComponent,
+ uint8_t aTables[4][256],
+ bool aDisabled)
+{
+ if (aDisabled) {
+ static uint8_t sIdentityLookupTable[256];
+ static bool sInitializedIdentityLookupTable = false;
+ if (!sInitializedIdentityLookupTable) {
+ for (int32_t i = 0; i < 256; i++) {
+ sIdentityLookupTable[i] = i;
+ }
+ sInitializedIdentityLookupTable = true;
+ }
+ memcpy(aTables[aComponent], sIdentityLookupTable, 256);
+ } else {
+ FillLookupTable(aComponent, aTables[aComponent]);
+ }
+}
+
+template<uint32_t BytesPerPixel>
+static void TransferComponents(DataSourceSurface* aInput,
+ DataSourceSurface* aTarget,
+ const uint8_t aLookupTables[BytesPerPixel][256])
+{
+ MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats");
+ IntSize size = aInput->GetSize();
+
+ uint8_t* sourceData = aInput->GetData();
+ uint8_t* targetData = aTarget->GetData();
+ uint32_t sourceStride = aInput->Stride();
+ uint32_t targetStride = aTarget->Stride();
+
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel;
+ uint32_t targetIndex = y * targetStride + x * BytesPerPixel;
+ for (uint32_t i = 0; i < BytesPerPixel; i++) {
+ targetData[targetIndex + i] = aLookupTables[i][sourceData[sourceIndex + i]];
+ }
+ }
+ }
+}
+
+bool
+IsAllZero(uint8_t aLookupTable[256])
+{
+ for (int32_t i = 0; i < 256; i++) {
+ if (aLookupTable[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeComponentTransferSoftware::Render(const IntRect& aRect)
+{
+ if (mDisableR && mDisableG && mDisableB && mDisableA) {
+ return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect);
+ }
+
+ uint8_t lookupTables[4][256];
+ GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR);
+ GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG);
+ GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB);
+ GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA);
+
+ bool needColorChannels =
+ lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 ||
+ lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 ||
+ lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0;
+
+ FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8;
+
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref);
+ if (!input) {
+ return nullptr;
+ }
+
+ if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) {
+ bool colorChannelsBecomeBlack =
+ IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) &&
+ IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) &&
+ IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]);
+
+ if (colorChannelsBecomeBlack) {
+ input = FilterProcessing::ExtractAlpha(input);
+ }
+ }
+
+ SurfaceFormat format = input->GetFormat();
+ if (format == SurfaceFormat::A8 && mDisableA) {
+ return input.forget();
+ }
+
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aRect.Size(), format);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ if (format == SurfaceFormat::A8) {
+ TransferComponents<1>(input, target, &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]);
+ } else {
+ TransferComponents<4>(input, target, lookupTables);
+ }
+
+ return target.forget();
+}
+
+void
+FilterNodeComponentTransferSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_TRANSFER_IN, aRect);
+}
+
+IntRect
+FilterNodeComponentTransferSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ if (mDisableA) {
+ return GetInputRectInRect(IN_TRANSFER_IN, aRect);
+ }
+ return aRect;
+}
+
+int32_t
+FilterNodeComponentTransferSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_TRANSFER_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex,
+ const Float* aFloat,
+ uint32_t aSize)
+{
+ std::vector<Float> table(aFloat, aFloat + aSize);
+ switch (aIndex) {
+ case ATT_TABLE_TRANSFER_TABLE_R:
+ mTableR = table;
+ break;
+ case ATT_TABLE_TRANSFER_TABLE_G:
+ mTableG = table;
+ break;
+ case ATT_TABLE_TRANSFER_TABLE_B:
+ mTableB = table;
+ break;
+ case ATT_TABLE_TRANSFER_TABLE_A:
+ mTableA = table;
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+void
+FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
+ uint8_t aTable[256])
+{
+ switch (aComponent) {
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
+ FillLookupTableImpl(mTableR, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
+ FillLookupTableImpl(mTableG, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
+ FillLookupTableImpl(mTableB, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
+ FillLookupTableImpl(mTableA, aTable);
+ break;
+ default:
+ MOZ_ASSERT(false, "unknown component");
+ break;
+ }
+}
+
+void
+FilterNodeTableTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues,
+ uint8_t aTable[256])
+{
+ uint32_t tvLength = aTableValues.size();
+ if (tvLength < 2) {
+ return;
+ }
+
+ for (size_t i = 0; i < 256; i++) {
+ uint32_t k = (i * (tvLength - 1)) / 255;
+ Float v1 = aTableValues[k];
+ Float v2 = aTableValues[std::min(k + 1, tvLength - 1)];
+ int32_t val =
+ int32_t(255 * (v1 + (i/255.0f - k/float(tvLength-1))*(tvLength - 1)*(v2 - v1)));
+ val = std::min(255, val);
+ val = std::max(0, val);
+ aTable[i] = val;
+ }
+}
+
+void
+FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
+ const Float* aFloat,
+ uint32_t aSize)
+{
+ std::vector<Float> discrete(aFloat, aFloat + aSize);
+ switch (aIndex) {
+ case ATT_DISCRETE_TRANSFER_TABLE_R:
+ mTableR = discrete;
+ break;
+ case ATT_DISCRETE_TRANSFER_TABLE_G:
+ mTableG = discrete;
+ break;
+ case ATT_DISCRETE_TRANSFER_TABLE_B:
+ mTableB = discrete;
+ break;
+ case ATT_DISCRETE_TRANSFER_TABLE_A:
+ mTableA = discrete;
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+void
+FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
+ uint8_t aTable[256])
+{
+ switch (aComponent) {
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
+ FillLookupTableImpl(mTableR, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
+ FillLookupTableImpl(mTableG, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
+ FillLookupTableImpl(mTableB, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
+ FillLookupTableImpl(mTableA, aTable);
+ break;
+ default:
+ MOZ_ASSERT(false, "unknown component");
+ break;
+ }
+}
+
+void
+FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues,
+ uint8_t aTable[256])
+{
+ uint32_t tvLength = aTableValues.size();
+ if (tvLength < 1) {
+ return;
+ }
+
+ for (size_t i = 0; i < 256; i++) {
+ uint32_t k = (i * tvLength) / 255;
+ k = std::min(k, tvLength - 1);
+ Float v = aTableValues[k];
+ int32_t val = NS_lround(255 * v);
+ val = std::min(255, val);
+ val = std::max(0, val);
+ aTable[i] = val;
+ }
+}
+
+FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
+ : mSlopeR(0)
+ , mSlopeG(0)
+ , mSlopeB(0)
+ , mSlopeA(0)
+ , mInterceptR(0)
+ , mInterceptG(0)
+ , mInterceptB(0)
+ , mInterceptA(0)
+{}
+
+void
+FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex,
+ Float aValue)
+{
+ switch (aIndex) {
+ case ATT_LINEAR_TRANSFER_SLOPE_R:
+ mSlopeR = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_INTERCEPT_R:
+ mInterceptR = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_SLOPE_G:
+ mSlopeG = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_INTERCEPT_G:
+ mInterceptG = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_SLOPE_B:
+ mSlopeB = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_INTERCEPT_B:
+ mInterceptB = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_SLOPE_A:
+ mSlopeA = aValue;
+ break;
+ case ATT_LINEAR_TRANSFER_INTERCEPT_A:
+ mInterceptA = aValue;
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+void
+FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
+ uint8_t aTable[256])
+{
+ switch (aComponent) {
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
+ FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
+ FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
+ FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
+ FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
+ break;
+ default:
+ MOZ_ASSERT(false, "unknown component");
+ break;
+ }
+}
+
+void
+FilterNodeLinearTransferSoftware::FillLookupTableImpl(Float aSlope,
+ Float aIntercept,
+ uint8_t aTable[256])
+{
+ for (size_t i = 0; i < 256; i++) {
+ int32_t val = NS_lround(aSlope * i + 255 * aIntercept);
+ val = std::min(255, val);
+ val = std::max(0, val);
+ aTable[i] = val;
+ }
+}
+
+FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
+ : mAmplitudeR(0)
+ , mAmplitudeG(0)
+ , mAmplitudeB(0)
+ , mAmplitudeA(0)
+ , mExponentR(0)
+ , mExponentG(0)
+ , mExponentB(0)
+ , mExponentA(0)
+{}
+
+void
+FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex,
+ Float aValue)
+{
+ switch (aIndex) {
+ case ATT_GAMMA_TRANSFER_AMPLITUDE_R:
+ mAmplitudeR = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_EXPONENT_R:
+ mExponentR = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_OFFSET_R:
+ mOffsetR = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_AMPLITUDE_G:
+ mAmplitudeG = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_EXPONENT_G:
+ mExponentG = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_OFFSET_G:
+ mOffsetG = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_AMPLITUDE_B:
+ mAmplitudeB = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_EXPONENT_B:
+ mExponentB = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_OFFSET_B:
+ mOffsetB = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_AMPLITUDE_A:
+ mAmplitudeA = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_EXPONENT_A:
+ mExponentA = aValue;
+ break;
+ case ATT_GAMMA_TRANSFER_OFFSET_A:
+ mOffsetA = aValue;
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+void
+FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
+ uint8_t aTable[256])
+{
+ switch (aComponent) {
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
+ FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
+ FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
+ FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
+ break;
+ case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
+ FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
+ break;
+ default:
+ MOZ_ASSERT(false, "unknown component");
+ break;
+ }
+}
+
+void
+FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
+ Float aExponent,
+ Float aOffset,
+ uint8_t aTable[256])
+{
+ for (size_t i = 0; i < 256; i++) {
+ int32_t val = NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset));
+ val = std::min(255, val);
+ val = std::max(0, val);
+ aTable[i] = val;
+ }
+}
+
+FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
+ : mDivisor(0)
+ , mBias(0)
+ , mEdgeMode(EDGE_MODE_DUPLICATE)
+ , mPreserveAlpha(false)
+{}
+
+int32_t
+FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_CONVOLVE_MATRIX_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
+ const IntSize &aKernelSize)
+{
+ MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE);
+ mKernelSize = aKernelSize;
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
+ const Float *aMatrix,
+ uint32_t aSize)
+{
+ MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX);
+ mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize);
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ switch (aIndex) {
+ case ATT_CONVOLVE_MATRIX_DIVISOR:
+ mDivisor = aValue;
+ break;
+ case ATT_CONVOLVE_MATRIX_BIAS:
+ mBias = aValue;
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
+{
+ switch (aIndex) {
+ case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
+ mKernelUnitLength = aKernelUnitLength;
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
+ const IntPoint &aTarget)
+{
+ MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET);
+ mTarget = aTarget;
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
+ const IntRect &aSourceRect)
+{
+ MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT);
+ mSourceRect = aSourceRect;
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
+ uint32_t aEdgeMode)
+{
+ MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE);
+ mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode);
+ Invalidate();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
+ bool aPreserveAlpha)
+{
+ MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA);
+ mPreserveAlpha = aPreserveAlpha;
+ Invalidate();
+}
+
+#ifdef DEBUG
+static bool sColorSamplingAccessControlEnabled = false;
+static uint8_t* sColorSamplingAccessControlStart = nullptr;
+static uint8_t* sColorSamplingAccessControlEnd = nullptr;
+
+struct DebugOnlyAutoColorSamplingAccessControl
+{
+ explicit DebugOnlyAutoColorSamplingAccessControl(DataSourceSurface* aSurface)
+ {
+ sColorSamplingAccessControlStart = aSurface->GetData();
+ sColorSamplingAccessControlEnd = sColorSamplingAccessControlStart +
+ aSurface->Stride() * aSurface->GetSize().height;
+ sColorSamplingAccessControlEnabled = true;
+ }
+
+ ~DebugOnlyAutoColorSamplingAccessControl()
+ {
+ sColorSamplingAccessControlEnabled = false;
+ }
+};
+
+static inline void
+DebugOnlyCheckColorSamplingAccess(const uint8_t* aSampleAddress)
+{
+ if (sColorSamplingAccessControlEnabled) {
+ MOZ_ASSERT(aSampleAddress >= sColorSamplingAccessControlStart, "accessing before start");
+ MOZ_ASSERT(aSampleAddress < sColorSamplingAccessControlEnd, "accessing after end");
+ }
+}
+#else
+typedef DebugOnly<DataSourceSurface*> DebugOnlyAutoColorSamplingAccessControl;
+#define DebugOnlyCheckColorSamplingAccess(address)
+#endif
+
+static inline uint8_t
+ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y, size_t bpp, ptrdiff_t c)
+{
+ DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c]);
+ return aData[y * aStride + bpp * x + c];
+}
+
+static inline int32_t
+ColorAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y)
+{
+ DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x);
+ return *(uint32_t*)(aData + y * aStride + 4 * x);
+}
+
+// Accepts fractional x & y and does bilinear interpolation.
+// Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
+static inline uint8_t
+ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, Float x, Float y, size_t bpp, ptrdiff_t c)
+{
+ const uint32_t f = 256;
+ const int32_t lx = floor(x);
+ const int32_t ly = floor(y);
+ const int32_t tux = uint32_t((x - lx) * f);
+ const int32_t tlx = f - tux;
+ const int32_t tuy = uint32_t((y - ly) * f);
+ const int32_t tly = f - tuy;
+ const uint8_t &cll = ColorComponentAtPoint(aData, aStride, lx, ly, bpp, c);
+ const uint8_t &cul = ColorComponentAtPoint(aData, aStride, lx + 1, ly, bpp, c);
+ const uint8_t &clu = ColorComponentAtPoint(aData, aStride, lx, ly + 1, bpp, c);
+ const uint8_t &cuu = ColorComponentAtPoint(aData, aStride, lx + 1, ly + 1, bpp, c);
+ return ((cll * tlx + cul * tux) * tly +
+ (clu * tlx + cuu * tux) * tuy + f * f / 2) / (f * f);
+}
+
+static int32_t
+ClampToNonZero(int32_t a)
+{
+ return a * (a >= 0);
+}
+
+template<typename CoordType>
+static void
+ConvolvePixel(const uint8_t *aSourceData,
+ uint8_t *aTargetData,
+ int32_t aWidth, int32_t aHeight,
+ int32_t aSourceStride, int32_t aTargetStride,
+ int32_t aX, int32_t aY,
+ const int32_t *aKernel,
+ int32_t aBias, int32_t shiftL, int32_t shiftR,
+ bool aPreserveAlpha,
+ int32_t aOrderX, int32_t aOrderY,
+ int32_t aTargetX, int32_t aTargetY,
+ CoordType aKernelUnitLengthX,
+ CoordType aKernelUnitLengthY)
+{
+ int32_t sum[4] = {0, 0, 0, 0};
+ int32_t offsets[4] = { B8G8R8A8_COMPONENT_BYTEOFFSET_R,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_G,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_B,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_A };
+ int32_t channels = aPreserveAlpha ? 3 : 4;
+ int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1);
+
+ for (int32_t y = 0; y < aOrderY; y++) {
+ CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY;
+ for (int32_t x = 0; x < aOrderX; x++) {
+ CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX;
+ for (int32_t i = 0; i < channels; i++) {
+ sum[i] += aKernel[aOrderX * y + x] *
+ ColorComponentAtPoint(aSourceData, aSourceStride,
+ sampleX, sampleY, 4, offsets[i]);
+ }
+ }
+ }
+ for (int32_t i = 0; i < channels; i++) {
+ int32_t clamped = umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR);
+ aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] =
+ (clamped + roundingAddition) << shiftR >> shiftL;
+ }
+ if (aPreserveAlpha) {
+ aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
+ aSourceData[aY * aSourceStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
+ }
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeConvolveMatrixSoftware::Render(const IntRect& aRect)
+{
+ if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
+ mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
+ return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
+ }
+ return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
+}
+
+static std::vector<Float>
+ReversedVector(const std::vector<Float> &aVector)
+{
+ size_t length = aVector.size();
+ std::vector<Float> result(length, 0);
+ for (size_t i = 0; i < length; i++) {
+ result[length - 1 - i] = aVector[i];
+ }
+ return result;
+}
+
+static std::vector<Float>
+ScaledVector(const std::vector<Float> &aVector, Float aDivisor)
+{
+ size_t length = aVector.size();
+ std::vector<Float> result(length, 0);
+ for (size_t i = 0; i < length; i++) {
+ result[i] = aVector[i] / aDivisor;
+ }
+ return result;
+}
+
+static Float
+MaxVectorSum(const std::vector<Float> &aVector)
+{
+ Float sum = 0;
+ size_t length = aVector.size();
+ for (size_t i = 0; i < length; i++) {
+ if (aVector[i] > 0) {
+ sum += aVector[i];
+ }
+ }
+ return sum;
+}
+
+// Returns shiftL and shiftR in such a way that
+// a << shiftL >> shiftR is roughly a * aFloat.
+static void
+TranslateDoubleToShifts(double aDouble, int32_t &aShiftL, int32_t &aShiftR)
+{
+ aShiftL = 0;
+ aShiftR = 0;
+ if (aDouble <= 0) {
+ MOZ_CRASH();
+ }
+ if (aDouble < 1) {
+ while (1 << (aShiftR + 1) < 1 / aDouble) {
+ aShiftR++;
+ }
+ } else {
+ while (1 << (aShiftL + 1) < aDouble) {
+ aShiftL++;
+ }
+ }
+}
+
+template<typename CoordType>
+TemporaryRef<DataSourceSurface>
+FilterNodeConvolveMatrixSoftware::DoRender(const IntRect& aRect,
+ CoordType aKernelUnitLengthX,
+ CoordType aKernelUnitLengthY)
+{
+ if (mKernelSize.width <= 0 || mKernelSize.height <= 0 ||
+ mKernelMatrix.size() != uint32_t(mKernelSize.width * mKernelSize.height) ||
+ !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
+ mDivisor == 0) {
+ return Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
+ }
+
+ IntRect srcRect = InflatedSourceRect(aRect);
+
+ // Inflate the source rect by another pixel because the bilinear filtering in
+ // ColorComponentAtPoint may want to access the margins.
+ srcRect.Inflate(1);
+
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect, NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect);
+
+ if (!input) {
+ return nullptr;
+ }
+
+ DebugOnlyAutoColorSamplingAccessControl accessControl(input);
+
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
+
+ uint8_t* sourceData = DataAtOffset(input, offset);
+ int32_t sourceStride = input->Stride();
+ uint8_t* targetData = target->GetData();
+ int32_t targetStride = target->Stride();
+
+ // Why exactly are we reversing the kernel?
+ std::vector<Float> kernel = ReversedVector(mKernelMatrix);
+ kernel = ScaledVector(kernel, mDivisor);
+ Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias,
+ MaxVectorSum(ScaledVector(kernel, -1)) - mBias);
+ maxResultAbs = std::max(maxResultAbs, 1.0f);
+
+ double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999;
+ MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0, "badly chosen float-to-int scale");
+ int32_t shiftL, shiftR;
+ TranslateDoubleToShifts(idealFactor, shiftL, shiftR);
+ double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR);
+ MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0, "badly chosen float-to-int scale");
+
+ int32_t* intKernel = new int32_t[kernel.size()];
+ for (size_t i = 0; i < kernel.size(); i++) {
+ intKernel[i] = NS_lround(kernel[i] * factorFromShifts);
+ }
+ int32_t bias = NS_lround(mBias * 255 * factorFromShifts);
+
+ for (int32_t y = 0; y < aRect.height; y++) {
+ for (int32_t x = 0; x < aRect.width; x++) {
+ ConvolvePixel(sourceData, targetData,
+ aRect.width, aRect.height, sourceStride, targetStride,
+ x, y, intKernel, bias, shiftL, shiftR, mPreserveAlpha,
+ mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y,
+ aKernelUnitLengthX, aKernelUnitLengthY);
+ }
+ }
+ delete[] intKernel;
+
+ return target.forget();
+}
+
+void
+FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
+}
+
+IntRect
+FilterNodeConvolveMatrixSoftware::InflatedSourceRect(const IntRect &aDestRect)
+{
+ if (aDestRect.IsEmpty()) {
+ return IntRect();
+ }
+
+ IntMargin margin;
+ margin.left = ceil(mTarget.x * mKernelUnitLength.width);
+ margin.top = ceil(mTarget.y * mKernelUnitLength.height);
+ margin.right = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
+ margin.bottom = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
+
+ IntRect srcRect = aDestRect;
+ srcRect.Inflate(margin);
+ return srcRect;
+}
+
+IntRect
+FilterNodeConvolveMatrixSoftware::InflatedDestRect(const IntRect &aSourceRect)
+{
+ if (aSourceRect.IsEmpty()) {
+ return IntRect();
+ }
+
+ IntMargin margin;
+ margin.left = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
+ margin.top = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
+ margin.right = ceil(mTarget.x * mKernelUnitLength.width);
+ margin.bottom = ceil(mTarget.y * mKernelUnitLength.height);
+
+ IntRect destRect = aSourceRect;
+ destRect.Inflate(margin);
+ return destRect;
+}
+
+IntRect
+FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ IntRect srcRequest = InflatedSourceRect(aRect);
+ IntRect srcOutput = GetInputRectInRect(IN_COLOR_MATRIX_IN, srcRequest);
+ return InflatedDestRect(srcOutput).Intersect(aRect);
+}
+
+FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
+ : mScale(0.0f)
+ , mChannelX(COLOR_CHANNEL_R)
+ , mChannelY(COLOR_CHANNEL_G)
+{}
+
+int32_t
+FilterNodeDisplacementMapSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_DISPLACEMENT_MAP_IN: return 0;
+ case IN_DISPLACEMENT_MAP_IN2: return 1;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
+ Float aScale)
+{
+ MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE);
+ mScale = aScale;
+ Invalidate();
+}
+
+void
+FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
+{
+ switch (aIndex) {
+ case ATT_DISPLACEMENT_MAP_X_CHANNEL:
+ mChannelX = static_cast<ColorChannel>(aValue);
+ break;
+ case ATT_DISPLACEMENT_MAP_Y_CHANNEL:
+ mChannelY = static_cast<ColorChannel>(aValue);
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeDisplacementMapSoftware::Render(const IntRect& aRect)
+{
+ IntRect srcRect = InflatedSourceOrDestRect(aRect);
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS);
+ RefPtr<DataSourceSurface> map =
+ GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS);
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!(input && map && target))) {
+ return nullptr;
+ }
+
+ IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
+
+ uint8_t* sourceData = DataAtOffset(input, offset);
+ int32_t sourceStride = input->Stride();
+ uint8_t* mapData = map->GetData();
+ int32_t mapStride = map->Stride();
+ uint8_t* targetData = target->GetData();
+ int32_t targetStride = target->Stride();
+
+ static const ptrdiff_t channelMap[4] = {
+ B8G8R8A8_COMPONENT_BYTEOFFSET_R,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_G,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_B,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_A };
+ uint16_t xChannel = channelMap[mChannelX];
+ uint16_t yChannel = channelMap[mChannelY];
+
+ float scaleOver255 = mScale / 255.0f;
+ float scaleAdjustment = -0.5f * mScale;
+
+ for (int32_t y = 0; y < aRect.height; y++) {
+ for (int32_t x = 0; x < aRect.width; x++) {
+ uint32_t mapIndex = y * mapStride + 4 * x;
+ uint32_t targIndex = y * targetStride + 4 * x;
+ int32_t sourceX = x +
+ scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment;
+ int32_t sourceY = y +
+ scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment;
+ *(uint32_t*)(targetData + targIndex) =
+ ColorAtPoint(sourceData, sourceStride, sourceX, sourceY);
+ }
+ }
+
+ return target.forget();
+}
+
+void
+FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect));
+ RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect);
+}
+
+IntRect
+FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect)
+{
+ IntRect sourceOrDestRect = aDestOrSourceRect;
+ sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2));
+ return sourceOrDestRect;
+}
+
+IntRect
+FilterNodeDisplacementMapSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ IntRect srcRequest = InflatedSourceOrDestRect(aRect);
+ IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest);
+ return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
+}
+
+FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
+ : mNumOctaves(0)
+ , mSeed(0)
+ , mStitchable(false)
+ , mType(TURBULENCE_TYPE_TURBULENCE)
+{}
+
+int32_t
+FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ return -1;
+}
+
+void
+FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const Size &aBaseFrequency)
+{
+ switch (aIndex) {
+ case ATT_TURBULENCE_BASE_FREQUENCY:
+ mBaseFrequency = aBaseFrequency;
+ break;
+ default:
+ MOZ_CRASH();
+ break;
+ }
+ Invalidate();
+}
+
+void
+FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const IntRect &aRect)
+{
+ switch (aIndex) {
+ case ATT_TURBULENCE_RECT:
+ mRenderRect = aRect;
+ break;
+ default:
+ MOZ_CRASH();
+ break;
+ }
+ Invalidate();
+}
+
+void
+FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, bool aStitchable)
+{
+ MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE);
+ mStitchable = aStitchable;
+ Invalidate();
+}
+
+void
+FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
+{
+ switch (aIndex) {
+ case ATT_TURBULENCE_NUM_OCTAVES:
+ mNumOctaves = aValue;
+ break;
+ case ATT_TURBULENCE_SEED:
+ mSeed = aValue;
+ break;
+ case ATT_TURBULENCE_TYPE:
+ mType = static_cast<TurbulenceType>(aValue);
+ break;
+ default:
+ MOZ_CRASH();
+ break;
+ }
+ Invalidate();
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeTurbulenceSoftware::Render(const IntRect& aRect)
+{
+ return FilterProcessing::RenderTurbulence(
+ aRect.Size(), aRect.TopLeft(), mBaseFrequency,
+ mSeed, mNumOctaves, mType, mStitchable, Rect(mRenderRect));
+}
+
+IntRect
+FilterNodeTurbulenceSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ return aRect.Intersect(mRenderRect);
+}
+
+FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
+ : mK1(0), mK2(0), mK3(0), mK4(0)
+{
+}
+
+int32_t
+FilterNodeArithmeticCombineSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_ARITHMETIC_COMBINE_IN: return 0;
+ case IN_ARITHMETIC_COMBINE_IN2: return 1;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex,
+ const Float* aFloat,
+ uint32_t aSize)
+{
+ MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS);
+ MOZ_ASSERT(aSize == 4);
+
+ mK1 = aFloat[0];
+ mK2 = aFloat[1];
+ mK3 = aFloat[2];
+ mK4 = aFloat[3];
+
+ Invalidate();
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeArithmeticCombineSoftware::Render(const IntRect& aRect)
+{
+ RefPtr<DataSourceSurface> input1 =
+ GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS);
+ RefPtr<DataSourceSurface> input2 =
+ GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS);
+ if (!input1 && !input2) {
+ return nullptr;
+ }
+
+ // If one input is null, treat it as transparent by adjusting the factors.
+ Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4;
+ if (!input1) {
+ k1 = 0.0f;
+ k2 = 0.0f;
+ input1 = input2;
+ }
+
+ if (!input2) {
+ k1 = 0.0f;
+ k3 = 0.0f;
+ input2 = input1;
+ }
+
+ return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3, k4);
+}
+
+void
+FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
+ RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
+}
+
+IntRect
+FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ if (mK4 > 0.0f) {
+ return aRect;
+ }
+ IntRect rectFrom1 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect);
+ IntRect rectFrom2 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect);
+ IntRect result;
+ if (mK1 > 0.0f) {
+ result = rectFrom1.Intersect(rectFrom2);
+ }
+ if (mK2 > 0.0f) {
+ result = result.Union(rectFrom1);
+ }
+ if (mK3 > 0.0f) {
+ result = result.Union(rectFrom2);
+ }
+ return result;
+}
+
+FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
+ : mOperator(COMPOSITE_OPERATOR_OVER)
+{}
+
+int32_t
+FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ return aInputEnumIndex - IN_COMPOSITE_IN_START;
+}
+
+void
+FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex, uint32_t aCompositeOperator)
+{
+ MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR);
+ mOperator = static_cast<CompositeOperator>(aCompositeOperator);
+ Invalidate();
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeCompositeSoftware::Render(const IntRect& aRect)
+{
+ RefPtr<DataSourceSurface> start =
+ GetInputDataSourceSurface(IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS);
+ RefPtr<DataSourceSurface> dest =
+ Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
+ if (MOZ2D_WARN_IF(!dest)) {
+ return nullptr;
+ }
+
+ if (start) {
+ CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint());
+ }
+
+ for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) {
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS);
+ if (input) {
+ FilterProcessing::ApplyComposition(input, dest, mOperator);
+ } else {
+ // We need to treat input as transparent. Depending on the composite
+ // operator, different things happen to dest.
+ switch (mOperator) {
+ case COMPOSITE_OPERATOR_OVER:
+ case COMPOSITE_OPERATOR_ATOP:
+ case COMPOSITE_OPERATOR_XOR:
+ // dest is unchanged.
+ break;
+ case COMPOSITE_OPERATOR_OUT:
+ // dest is now transparent, but it can become non-transparent again
+ // when compositing additional inputs.
+ ClearDataSourceSurface(dest);
+ break;
+ case COMPOSITE_OPERATOR_IN:
+ // Transparency always wins. We're completely transparent now and
+ // no additional input can get rid of that transparency.
+ return nullptr;
+ }
+ }
+ }
+ return dest.forget();
+}
+
+void
+FilterNodeCompositeSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
+ RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
+ }
+}
+
+IntRect
+FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ IntRect rect;
+ for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
+ IntRect inputRect = GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
+ if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) {
+ rect = rect.Intersect(inputRect);
+ } else {
+ rect = rect.Union(inputRect);
+ }
+ }
+ return rect;
+}
+
+int32_t
+FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_GAUSSIAN_BLUR_IN: return 0;
+ default: return -1;
+ }
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeBlurXYSoftware::Render(const IntRect& aRect)
+{
+ Size sigmaXY = StdDeviationXY();
+ IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
+
+ if (d.width == 0 && d.height == 0) {
+ return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect);
+ }
+
+ IntRect srcRect = InflatedSourceOrDestRect(aRect);
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect);
+ if (!input) {
+ return nullptr;
+ }
+
+ RefPtr<DataSourceSurface> target;
+ Rect r(0, 0, srcRect.width, srcRect.height);
+
+ if (input->GetFormat() == SurfaceFormat::A8) {
+ target = Factory::CreateDataSourceSurface(srcRect.Size(), SurfaceFormat::A8);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+ CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint());
+ AlphaBoxBlur blur(r, target->Stride(), sigmaXY.width, sigmaXY.height);
+ blur.Blur(target->GetData());
+ } else {
+ RefPtr<DataSourceSurface> channel0, channel1, channel2, channel3;
+ FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2, channel3);
+ if (MOZ2D_WARN_IF(!(channel0 && channel1 && channel2))) {
+ return nullptr;
+ }
+ AlphaBoxBlur blur(r, channel0->Stride(), sigmaXY.width, sigmaXY.height);
+ blur.Blur(channel0->GetData());
+ blur.Blur(channel1->GetData());
+ blur.Blur(channel2->GetData());
+ blur.Blur(channel3->GetData());
+ target = FilterProcessing::CombineColorChannels(channel0, channel1, channel2, channel3);
+ }
+
+ return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE);
+}
+
+void
+FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect));
+}
+
+IntRect
+FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(const IntRect &aDestRect)
+{
+ Size sigmaXY = StdDeviationXY();
+ IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
+ IntRect srcRect = aDestRect;
+ srcRect.Inflate(d);
+ return srcRect;
+}
+
+IntRect
+FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ IntRect srcRequest = InflatedSourceOrDestRect(aRect);
+ IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest);
+ return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
+}
+
+FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
+ : mStdDeviation(0)
+{}
+
+static float
+ClampStdDeviation(float aStdDeviation)
+{
+ // Cap software blur radius for performance reasons.
+ return std::min(std::max(0.0f, aStdDeviation), 100.0f);
+}
+
+void
+FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex,
+ float aStdDeviation)
+{
+ switch (aIndex) {
+ case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
+ mStdDeviation = ClampStdDeviation(aStdDeviation);
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+Size
+FilterNodeGaussianBlurSoftware::StdDeviationXY()
+{
+ return Size(mStdDeviation, mStdDeviation);
+}
+
+FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
+ : mBlurDirection(BLUR_DIRECTION_X)
+{}
+
+void
+FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
+ Float aStdDeviation)
+{
+ switch (aIndex) {
+ case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
+ mStdDeviation = ClampStdDeviation(aStdDeviation);
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+void
+FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
+ uint32_t aBlurDirection)
+{
+ switch (aIndex) {
+ case ATT_DIRECTIONAL_BLUR_DIRECTION:
+ mBlurDirection = (BlurDirection)aBlurDirection;
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+Size
+FilterNodeDirectionalBlurSoftware::StdDeviationXY()
+{
+ float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0;
+ float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0;
+ return Size(sigmaX, sigmaY);
+}
+
+int32_t
+FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_CROP_IN: return 0;
+ default: return -1;
+ }
+}
+
+void
+FilterNodeCropSoftware::SetAttribute(uint32_t aIndex,
+ const Rect &aSourceRect)
+{
+ MOZ_ASSERT(aIndex == ATT_CROP_RECT);
+ Rect srcRect = aSourceRect;
+ srcRect.Round();
+ if (!srcRect.ToIntRect(&mCropRect)) {
+ mCropRect = IntRect();
+ }
+ Invalidate();
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeCropSoftware::Render(const IntRect& aRect)
+{
+ return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
+}
+
+void
+FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect));
+}
+
+IntRect
+FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect);
+}
+
+int32_t
+FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_PREMULTIPLY_IN: return 0;
+ default: return -1;
+ }
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodePremultiplySoftware::Render(const IntRect& aRect)
+{
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect);
+ return input ? Premultiply(input) : nullptr;
+}
+
+void
+FilterNodePremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_PREMULTIPLY_IN, aRect);
+}
+
+IntRect
+FilterNodePremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect);
+}
+
+int32_t
+FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_UNPREMULTIPLY_IN: return 0;
+ default: return -1;
+ }
+}
+
+TemporaryRef<DataSourceSurface>
+FilterNodeUnpremultiplySoftware::Render(const IntRect& aRect)
+{
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect);
+ return input ? Unpremultiply(input) : nullptr;
+}
+
+void
+FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
+{
+ RequestInputRect(IN_UNPREMULTIPLY_IN, aRect);
+}
+
+IntRect
+FilterNodeUnpremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
+{
+ return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect);
+}
+
+bool
+PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
+{
+ switch (aIndex) {
+ case ATT_POINT_LIGHT_POSITION:
+ mPosition = aPoint;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+SpotLightSoftware::SpotLightSoftware()
+ : mSpecularFocus(0)
+ , mLimitingConeAngle(0)
+ , mLimitingConeCos(1)
+{
+}
+
+bool
+SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
+{
+ switch (aIndex) {
+ case ATT_SPOT_LIGHT_POSITION:
+ mPosition = aPoint;
+ break;
+ case ATT_SPOT_LIGHT_POINTS_AT:
+ mPointsAt = aPoint;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool
+SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ switch (aIndex) {
+ case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE:
+ mLimitingConeAngle = aValue;
+ break;
+ case ATT_SPOT_LIGHT_FOCUS:
+ mSpecularFocus = aValue;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+DistantLightSoftware::DistantLightSoftware()
+ : mAzimuth(0)
+ , mElevation(0)
+{
+}
+
+bool
+DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ switch (aIndex) {
+ case ATT_DISTANT_LIGHT_AZIMUTH:
+ mAzimuth = aValue;
+ break;
+ case ATT_DISTANT_LIGHT_ELEVATION:
+ mElevation = aValue;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static inline Point3D Normalized(const Point3D &vec) {
+ Point3D copy(vec);
+ copy.Normalize();
+ return copy;
+}
+
+template<typename LightType, typename LightingType>
+FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(const char* aTypeName)
+ : mSurfaceScale(0)
+#if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
+ , mTypeName(aTypeName)
+#endif
+{}
+
+template<typename LightType, typename LightingType>
+int32_t
+FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(uint32_t aInputEnumIndex)
+{
+ switch (aInputEnumIndex) {
+ case IN_LIGHTING_IN: return 0;
+ default: return -1;
+ }
+}
+
+template<typename LightType, typename LightingType>
+void
+FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
+{
+ if (mLight.SetAttribute(aIndex, aPoint)) {
+ Invalidate();
+ return;
+ }
+ MOZ_CRASH();
+}
+
+template<typename LightType, typename LightingType>
+void
+FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ if (mLight.SetAttribute(aIndex, aValue) ||
+ mLighting.SetAttribute(aIndex, aValue)) {
+ Invalidate();
+ return;
+ }
+ switch (aIndex) {
+ case ATT_LIGHTING_SURFACE_SCALE:
+ mSurfaceScale = aValue;
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+template<typename LightType, typename LightingType>
+void
+FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
+{
+ switch (aIndex) {
+ case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
+ mKernelUnitLength = aKernelUnitLength;
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ Invalidate();
+}
+
+template<typename LightType, typename LightingType>
+void
+FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Color &aColor)
+{
+ MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR);
+ mColor = aColor;
+ Invalidate();
+}
+
+template<typename LightType, typename LightingType>
+IntRect
+FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(const IntRect& aRect)
+{
+ return GetInputRectInRect(IN_LIGHTING_IN, aRect);
+}
+
+Point3D
+PointLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
+{
+ return Normalized(mPosition - aTargetPoint);
+}
+
+uint32_t
+PointLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
+{
+ return aLightColor;
+}
+
+void
+SpotLightSoftware::Prepare()
+{
+ mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition);
+ mLimitingConeCos = std::max<double>(cos(mLimitingConeAngle * M_PI/180.0), 0.0);
+ mPowCache.CacheForExponent(mSpecularFocus);
+}
+
+Point3D
+SpotLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
+{
+ return Normalized(mPosition - aTargetPoint);
+}
+
+uint32_t
+SpotLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
+{
+ union {
+ uint32_t color;
+ uint8_t colorC[4];
+ };
+ color = aLightColor;
+ Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight);
+ uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits);
+ uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos);
+ MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits), "pow() result must not exceed 1.0");
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >> PowCache::sOutputIntPrecisionBits);
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >> PowCache::sOutputIntPrecisionBits);
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >> PowCache::sOutputIntPrecisionBits);
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
+ return color;
+}
+
+void
+DistantLightSoftware::Prepare()
+{
+ const double radPerDeg = M_PI / 180.0;
+ mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
+ mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
+ mVectorToLight.z = sin(mElevation * radPerDeg);
+}
+
+Point3D
+DistantLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
+{
+ return mVectorToLight;
+}
+
+uint32_t
+DistantLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
+{
+ return aLightColor;
+}
+
+template<typename CoordType>
+static Point3D
+GenerateNormal(const uint8_t *data, int32_t stride,
+ int32_t x, int32_t y, float surfaceScale,
+ CoordType dx, CoordType dy)
+{
+ const uint8_t *index = data + y * stride + x;
+
+ CoordType zero = 0;
+
+ // See this for source of constants:
+ // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
+ int16_t normalX =
+ -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) +
+ 1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) +
+ -2 * ColorComponentAtPoint(index, stride, -dx, zero, 1, 0) +
+ 2 * ColorComponentAtPoint(index, stride, dx, zero, 1, 0) +
+ -1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) +
+ 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0);
+
+ int16_t normalY =
+ -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) +
+ -2 * ColorComponentAtPoint(index, stride, zero, -dy, 1, 0) +
+ -1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) +
+ 1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) +
+ 2 * ColorComponentAtPoint(index, stride, zero, dy, 1, 0) +
+ 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0);
+
+ Point3D normal;
+ normal.x = -surfaceScale * normalX / 4.0f;
+ normal.y = -surfaceScale * normalY / 4.0f;
+ normal.z = 255;
+ return Normalized(normal);
+}
+
+template<typename LightType, typename LightingType>
+TemporaryRef<DataSourceSurface>
+FilterNodeLightingSoftware<LightType, LightingType>::Render(const IntRect& aRect)
+{
+ if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
+ mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
+ return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
+ }
+ return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
+}
+
+template<typename LightType, typename LightingType>
+void
+FilterNodeLightingSoftware<LightType, LightingType>::RequestFromInputsForRect(const IntRect &aRect)
+{
+ IntRect srcRect = aRect;
+ srcRect.Inflate(ceil(mKernelUnitLength.width),
+ ceil(mKernelUnitLength.height));
+ RequestInputRect(IN_LIGHTING_IN, srcRect);
+}
+
+template<typename LightType, typename LightingType> template<typename CoordType>
+TemporaryRef<DataSourceSurface>
+FilterNodeLightingSoftware<LightType, LightingType>::DoRender(const IntRect& aRect,
+ CoordType aKernelUnitLengthX,
+ CoordType aKernelUnitLengthY)
+{
+ IntRect srcRect = aRect;
+ IntSize size = aRect.Size();
+ srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
+ ceil(float(aKernelUnitLengthY)));
+
+ // Inflate the source rect by another pixel because the bilinear filtering in
+ // ColorComponentAtPoint may want to access the margins.
+ srcRect.Inflate(1);
+
+ RefPtr<DataSourceSurface> input =
+ GetInputDataSourceSurface(IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8,
+ EDGE_MODE_DUPLICATE);
+
+ if (!input) {
+ return nullptr;
+ }
+
+ if (input->GetFormat() != SurfaceFormat::A8) {
+ input = FilterProcessing::ExtractAlpha(input);
+ }
+
+ DebugOnlyAutoColorSamplingAccessControl accessControl(input);
+
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
+
+ uint8_t* sourceData = DataAtOffset(input, offset);
+ int32_t sourceStride = input->Stride();
+ uint8_t* targetData = target->GetData();
+ int32_t targetStride = target->Stride();
+
+ uint32_t lightColor = ColorToBGRA(mColor);
+ mLight.Prepare();
+ mLighting.Prepare();
+
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t sourceIndex = y * sourceStride + x;
+ int32_t targetIndex = y * targetStride + 4 * x;
+
+ Point3D normal = GenerateNormal(sourceData, sourceStride,
+ x, y, mSurfaceScale,
+ aKernelUnitLengthX, aKernelUnitLengthY);
+
+ IntPoint pointInFilterSpace(aRect.x + x, aRect.y + y);
+ Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f;
+ Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z);
+ Point3D rayDir = mLight.GetVectorToLight(pt);
+ uint32_t color = mLight.GetColor(lightColor, rayDir);
+
+ *(uint32_t*)(targetData + targetIndex) = mLighting.LightPixel(normal, rayDir, color);
+ }
+ }
+
+ return target.forget();
+}
+
+DiffuseLightingSoftware::DiffuseLightingSoftware()
+ : mDiffuseConstant(0)
+{
+}
+
+bool
+DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ switch (aIndex) {
+ case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT:
+ mDiffuseConstant = aValue;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+uint32_t
+DiffuseLightingSoftware::LightPixel(const Point3D &aNormal,
+ const Point3D &aVectorToLight,
+ uint32_t aColor)
+{
+ Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight));
+ Float diffuseNL = mDiffuseConstant * dotNL;
+
+ union {
+ uint32_t bgra;
+ uint8_t components[4];
+ } color = { aColor };
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
+ umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]), 255U);
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
+ umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]), 255U);
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
+ umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]), 255U);
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
+ return color.bgra;
+}
+
+SpecularLightingSoftware::SpecularLightingSoftware()
+ : mSpecularConstant(0)
+ , mSpecularExponent(0)
+{
+}
+
+bool
+SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue)
+{
+ switch (aIndex) {
+ case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT:
+ mSpecularConstant = std::min(std::max(aValue, 0.0f), 255.0f);
+ break;
+ case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT:
+ mSpecularExponent = std::min(std::max(aValue, 1.0f), 128.0f);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+void
+SpecularLightingSoftware::Prepare()
+{
+ mPowCache.CacheForExponent(mSpecularExponent);
+ mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8));
+}
+
+uint32_t
+SpecularLightingSoftware::LightPixel(const Point3D &aNormal,
+ const Point3D &aVectorToLight,
+ uint32_t aColor)
+{
+ Point3D vectorToEye(0, 0, 1);
+ Point3D halfwayVector = Normalized(aVectorToLight + vectorToEye);
+ Float dotNH = aNormal.DotProduct(halfwayVector);
+ uint16_t dotNHi = uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits));
+ uint32_t specularNHi = uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8;
+
+ union {
+ uint32_t bgra;
+ uint8_t components[4];
+ } color = { aColor };
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
+ umin(
+ (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >> PowCache::sOutputIntPrecisionBits, 255U);
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
+ umin(
+ (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >> PowCache::sOutputIntPrecisionBits, 255U);
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
+ umin(
+ (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >> PowCache::sOutputIntPrecisionBits, 255U);
+
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
+ umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B],
+ umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G],
+ color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]));
+ return color.bgra;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/FilterNodeSoftware.h b/gfx/2d/FilterNodeSoftware.h
new file mode 100644
index 000000000..dfef8ba01
--- /dev/null
+++ b/gfx/2d/FilterNodeSoftware.h
@@ -0,0 +1,724 @@
+/* -*- Mode: C++; tab-width: 20; 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 _MOZILLA_GFX_FILTERNODESOFTWARE_H_
+#define _MOZILLA_GFX_FILTERNODESOFTWARE_H_
+
+#include "Filters.h"
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class DataSourceSurface;
+class DrawTarget;
+struct DrawOptions;
+class FilterNodeSoftware;
+
+/**
+ * Can be attached to FilterNodeSoftware instances using
+ * AddInvalidationListener. FilterInvalidated is called whenever the output of
+ * the observed filter may have changed; that is, whenever cached GetOutput()
+ * results (and results derived from them) need to discarded.
+ */
+class FilterInvalidationListener
+{
+public:
+ virtual void FilterInvalidated(FilterNodeSoftware* aFilter) = 0;
+};
+
+/**
+ * This is the base class for the software (i.e. pure CPU, non-accelerated)
+ * FilterNode implementation. The software implementation is backend-agnostic,
+ * so it can be used as a fallback for all DrawTarget implementations.
+ */
+class FilterNodeSoftware : public FilterNode,
+ public FilterInvalidationListener
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeSoftware, override)
+ virtual ~FilterNodeSoftware();
+
+ // Factory method, intended to be called from DrawTarget*::CreateFilter.
+ static TemporaryRef<FilterNode> Create(FilterType aType);
+
+ // Draw the filter, intended to be called by DrawTarget*::DrawFilter.
+ void Draw(DrawTarget* aDrawTarget, const Rect &aSourceRect,
+ const Point &aDestPoint, const DrawOptions &aOptions);
+
+ virtual FilterBackend GetBackendType() override { return FILTER_BACKEND_SOFTWARE; }
+ virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override;
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override;
+
+ virtual const char* GetName() { return "Unknown"; }
+
+ virtual void AddInvalidationListener(FilterInvalidationListener* aListener);
+ virtual void RemoveInvalidationListener(FilterInvalidationListener* aListener);
+
+ // FilterInvalidationListener implementation
+ virtual void FilterInvalidated(FilterNodeSoftware* aFilter) override;
+
+protected:
+
+ // The following methods are intended to be overriden by subclasses.
+
+ /**
+ * Translates a *FilterInputs enum value into an index for the
+ * mInputFilters / mInputSurfaces arrays. Returns -1 for invalid inputs.
+ * If somebody calls SetInput(enumValue, input) with an enumValue for which
+ * InputIndex(enumValue) is -1, we abort.
+ */
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) { return -1; }
+
+ /**
+ * Every filter node has an output rect, which can also be infinite. The
+ * output rect can depend on the values of any set attributes and on the
+ * output rects of any input filters or surfaces.
+ * This method returns the intersection of the filter's output rect with
+ * aInRect. Filters with unconstrained output always return aInRect.
+ */
+ virtual IntRect GetOutputRectInRect(const IntRect& aInRect) = 0;
+
+ /**
+ * Return a surface with the rendered output which is of size aRect.Size().
+ * aRect is required to be a subrect of this filter's output rect; in other
+ * words, aRect == GetOutputRectInRect(aRect) must always be true.
+ * May return nullptr in error conditions or for an empty aRect.
+ * Implementations are not required to allocate a new surface and may even
+ * pass through input surfaces unchanged.
+ * Callers need to treat the returned surface as immutable.
+ */
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) = 0;
+
+ /**
+ * Call RequestRect (see below) on any input filters with the desired input
+ * rect, so that the input filter knows what to cache the next time it
+ * renders.
+ */
+ virtual void RequestFromInputsForRect(const IntRect &aRect) {}
+
+ /**
+ * This method provides a caching default implementation but can be overriden
+ * by subclasses that don't want to cache their output. Those classes should
+ * call Render(aRect) directly from here.
+ */
+ virtual TemporaryRef<DataSourceSurface> GetOutput(const IntRect &aRect);
+
+ // The following methods are non-virtual helper methods.
+
+ /**
+ * Format hints for GetInputDataSourceSurface. Some callers of
+ * GetInputDataSourceSurface can handle both B8G8R8A8 and A8 surfaces, these
+ * should pass CAN_HANDLE_A8 in order to avoid unnecessary conversions.
+ * Callers that can only handle B8G8R8A8 surfaces pass NEED_COLOR_CHANNELS.
+ */
+ enum FormatHint {
+ CAN_HANDLE_A8,
+ NEED_COLOR_CHANNELS
+ };
+
+ /**
+ * Returns SurfaceFormat::B8G8R8A8 or SurfaceFormat::A8, depending on the current surface
+ * format and the format hint.
+ */
+ SurfaceFormat DesiredFormat(SurfaceFormat aCurrentFormat,
+ FormatHint aFormatHint);
+
+ /**
+ * Intended to be called by FilterNodeSoftware::Render implementations.
+ * Returns a surface of size aRect.Size() or nullptr in error conditions. The
+ * returned surface contains the output of the specified input filter or
+ * input surface in aRect. If aRect extends beyond the input filter's output
+ * rect (or the input surface's dimensions), the remaining area is filled
+ * according to aEdgeMode: The default, EDGE_MODE_NONE, simply pads with
+ * transparent black.
+ * If non-null, the returned surface is guaranteed to be of SurfaceFormat::A8 or
+ * SurfaceFormat::B8G8R8A8. If aFormatHint is NEED_COLOR_CHANNELS, the returned
+ * surface is guaranteed to be of SurfaceFormat::B8G8R8A8 always.
+ * Each pixel row of the returned surface is guaranteed to be 16-byte aligned.
+ */
+ TemporaryRef<DataSourceSurface>
+ GetInputDataSourceSurface(uint32_t aInputEnumIndex, const IntRect& aRect,
+ FormatHint aFormatHint = CAN_HANDLE_A8,
+ ConvolveMatrixEdgeMode aEdgeMode = EDGE_MODE_NONE,
+ const IntRect *aTransparencyPaddedSourceRect = nullptr);
+
+ /**
+ * Returns the intersection of the input filter's or surface's output rect
+ * with aInRect.
+ */
+ IntRect GetInputRectInRect(uint32_t aInputEnumIndex, const IntRect& aInRect);
+
+ /**
+ * Calls RequestRect on the specified input, if it's a filter.
+ */
+ void RequestInputRect(uint32_t aInputEnumIndex, const IntRect& aRect);
+
+ /**
+ * Returns the number of set input filters or surfaces. Needed for filters
+ * which can have an arbitrary number of inputs.
+ */
+ size_t NumberOfSetInputs();
+
+ /**
+ * Discard the cached surface that was stored in the GetOutput default
+ * implementation. Needs to be called whenever attributes or inputs are set
+ * that might change the result of a Render() call.
+ */
+ void Invalidate();
+
+ /**
+ * Called in order to let this filter know what to cache during the next
+ * GetOutput call. Expected to call RequestRect on this filter's input
+ * filters.
+ */
+ void RequestRect(const IntRect &aRect);
+
+ /**
+ * Set input filter and clear input surface for this input index, or set
+ * input surface and clear input filter. One of aSurface and aFilter should
+ * be null.
+ */
+ void SetInput(uint32_t aIndex, SourceSurface *aSurface,
+ FilterNodeSoftware *aFilter);
+
+protected:
+ /**
+ * mInputSurfaces / mInputFilters: For each input index, either a surface or
+ * a filter is set, and the other is null.
+ */
+ std::vector<RefPtr<SourceSurface> > mInputSurfaces;
+ std::vector<RefPtr<FilterNodeSoftware> > mInputFilters;
+
+ /**
+ * Weak pointers to our invalidation listeners, i.e. to those filters who
+ * have this filter as an input. Invalidation listeners are required to
+ * unsubscribe themselves from us when they let go of their reference to us.
+ * This ensures that the pointers in this array are never stale.
+ */
+ std::vector<FilterInvalidationListener*> mInvalidationListeners;
+
+ /**
+ * Stores the rect which we want to render and cache on the next call to
+ * GetOutput.
+ */
+ IntRect mRequestedRect;
+
+ /**
+ * Stores our cached output.
+ */
+ IntRect mCachedRect;
+ RefPtr<DataSourceSurface> mCachedOutput;
+};
+
+// Subclasses for specific filters.
+
+class FilterNodeTransformSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTransformSoftware, override)
+ FilterNodeTransformSoftware();
+ virtual const char* GetName() override { return "Transform"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aGraphicsFilter) override;
+ virtual void SetAttribute(uint32_t aIndex, const Matrix &aMatrix) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+ IntRect SourceRectForOutputRect(const IntRect &aRect);
+
+private:
+ Matrix mMatrix;
+ Filter mFilter;
+};
+
+class FilterNodeBlendSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeBlendSoftware, override)
+ FilterNodeBlendSoftware();
+ virtual const char* GetName() override { return "Blend"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aBlendMode) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ BlendMode mBlendMode;
+};
+
+class FilterNodeMorphologySoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeMorphologySoftware, override)
+ FilterNodeMorphologySoftware();
+ virtual const char* GetName() override { return "Morphology"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const IntSize &aRadii) override;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aOperator) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ IntSize mRadii;
+ MorphologyOperator mOperator;
+};
+
+class FilterNodeColorMatrixSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeColorMatrixSoftware, override)
+ virtual const char* GetName() override { return "ColorMatrix"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &aMatrix) override;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aAlphaMode) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ Matrix5x4 mMatrix;
+ AlphaMode mAlphaMode;
+};
+
+class FilterNodeFloodSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeFloodSoftware, override)
+ virtual const char* GetName() override { return "Flood"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Color &aColor) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> GetOutput(const IntRect &aRect) override;
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+
+private:
+ Color mColor;
+};
+
+class FilterNodeTileSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTileSoftware, override)
+ virtual const char* GetName() override { return "Tile"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &aSourceRect) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ IntRect mSourceRect;
+};
+
+/**
+ * Baseclass for the four different component transfer filters.
+ */
+class FilterNodeComponentTransferSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeComponentTransferSoftware, override)
+ FilterNodeComponentTransferSoftware();
+
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, bool aDisable) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+ virtual void GenerateLookupTable(ptrdiff_t aComponent, uint8_t aTables[4][256],
+ bool aDisabled);
+ virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) = 0;
+
+ bool mDisableR;
+ bool mDisableG;
+ bool mDisableB;
+ bool mDisableA;
+};
+
+class FilterNodeTableTransferSoftware : public FilterNodeComponentTransferSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTableTransferSoftware, override)
+ virtual const char* GetName() override { return "TableTransfer"; }
+ using FilterNodeComponentTransferSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override;
+
+protected:
+ virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
+
+private:
+ void FillLookupTableImpl(std::vector<Float>& aTableValues, uint8_t aTable[256]);
+
+ std::vector<Float> mTableR;
+ std::vector<Float> mTableG;
+ std::vector<Float> mTableB;
+ std::vector<Float> mTableA;
+};
+
+class FilterNodeDiscreteTransferSoftware : public FilterNodeComponentTransferSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDiscreteTransferSoftware, override)
+ virtual const char* GetName() override { return "DiscreteTransfer"; }
+ using FilterNodeComponentTransferSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override;
+
+protected:
+ virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
+
+private:
+ void FillLookupTableImpl(std::vector<Float>& aTableValues, uint8_t aTable[256]);
+
+ std::vector<Float> mTableR;
+ std::vector<Float> mTableG;
+ std::vector<Float> mTableB;
+ std::vector<Float> mTableA;
+};
+
+class FilterNodeLinearTransferSoftware : public FilterNodeComponentTransferSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeLinearTransformSoftware, override)
+ FilterNodeLinearTransferSoftware();
+ virtual const char* GetName() override { return "LinearTransfer"; }
+ using FilterNodeComponentTransferSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, Float aValue) override;
+
+protected:
+ virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
+
+private:
+ void FillLookupTableImpl(Float aSlope, Float aIntercept, uint8_t aTable[256]);
+
+ Float mSlopeR;
+ Float mSlopeG;
+ Float mSlopeB;
+ Float mSlopeA;
+ Float mInterceptR;
+ Float mInterceptG;
+ Float mInterceptB;
+ Float mInterceptA;
+};
+
+class FilterNodeGammaTransferSoftware : public FilterNodeComponentTransferSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeGammaTransferSoftware, override)
+ FilterNodeGammaTransferSoftware();
+ virtual const char* GetName() override { return "GammaTransfer"; }
+ using FilterNodeComponentTransferSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, Float aValue) override;
+
+protected:
+ virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
+
+private:
+ void FillLookupTableImpl(Float aAmplitude, Float aExponent, Float aOffset, uint8_t aTable[256]);
+
+ Float mAmplitudeR;
+ Float mAmplitudeG;
+ Float mAmplitudeB;
+ Float mAmplitudeA;
+ Float mExponentR;
+ Float mExponentG;
+ Float mExponentB;
+ Float mExponentA;
+ Float mOffsetR;
+ Float mOffsetG;
+ Float mOffsetB;
+ Float mOffsetA;
+};
+
+class FilterNodeConvolveMatrixSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeConvolveMatrixSoftware, override)
+ FilterNodeConvolveMatrixSoftware();
+ virtual const char* GetName() override { return "ConvolveMatrix"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const IntSize &aKernelSize) override;
+ virtual void SetAttribute(uint32_t aIndex, const Float* aMatrix, uint32_t aSize) override;
+ virtual void SetAttribute(uint32_t aIndex, Float aValue) override;
+ virtual void SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength) override;
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &aSourceRect) override;
+ virtual void SetAttribute(uint32_t aIndex, const IntPoint &aTarget) override;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aEdgeMode) override;
+ virtual void SetAttribute(uint32_t aIndex, bool aPreserveAlpha) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ template<typename CoordType>
+ TemporaryRef<DataSourceSurface> DoRender(const IntRect& aRect,
+ CoordType aKernelUnitLengthX,
+ CoordType aKernelUnitLengthY);
+
+ IntRect InflatedSourceRect(const IntRect &aDestRect);
+ IntRect InflatedDestRect(const IntRect &aSourceRect);
+
+ IntSize mKernelSize;
+ std::vector<Float> mKernelMatrix;
+ Float mDivisor;
+ Float mBias;
+ IntPoint mTarget;
+ IntRect mSourceRect;
+ ConvolveMatrixEdgeMode mEdgeMode;
+ Size mKernelUnitLength;
+ bool mPreserveAlpha;
+};
+
+class FilterNodeDisplacementMapSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDisplacementMapSoftware, override)
+ FilterNodeDisplacementMapSoftware();
+ virtual const char* GetName() override { return "DisplacementMap"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, Float aScale) override;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ IntRect InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect);
+
+ Float mScale;
+ ColorChannel mChannelX;
+ ColorChannel mChannelY;
+};
+
+class FilterNodeTurbulenceSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTurbulenceSoftware, override)
+ FilterNodeTurbulenceSoftware();
+ virtual const char* GetName() override { return "Turbulence"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Size &aSize) override;
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &aRenderRect) override;
+ virtual void SetAttribute(uint32_t aIndex, bool aStitchable) override;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+
+private:
+ IntRect mRenderRect;
+ Size mBaseFrequency;
+ uint32_t mNumOctaves;
+ uint32_t mSeed;
+ bool mStitchable;
+ TurbulenceType mType;
+};
+
+class FilterNodeArithmeticCombineSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeArithmeticCombineSoftware, override)
+ FilterNodeArithmeticCombineSoftware();
+ virtual const char* GetName() override { return "ArithmeticCombine"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ Float mK1;
+ Float mK2;
+ Float mK3;
+ Float mK4;
+};
+
+class FilterNodeCompositeSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeCompositeSoftware, override)
+ FilterNodeCompositeSoftware();
+ virtual const char* GetName() override { return "Composite"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aOperator) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ CompositeOperator mOperator;
+};
+
+// Base class for FilterNodeGaussianBlurSoftware and
+// FilterNodeDirectionalBlurSoftware.
+class FilterNodeBlurXYSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeBlurXYSoftware, override)
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ IntRect InflatedSourceOrDestRect(const IntRect &aDestRect);
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+ // Implemented by subclasses.
+ virtual Size StdDeviationXY() = 0;
+};
+
+class FilterNodeGaussianBlurSoftware : public FilterNodeBlurXYSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeGaussianBlurSoftware, override)
+ FilterNodeGaussianBlurSoftware();
+ virtual const char* GetName() override { return "GaussianBlur"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, Float aStdDeviation) override;
+
+protected:
+ virtual Size StdDeviationXY() override;
+
+private:
+ Float mStdDeviation;
+};
+
+class FilterNodeDirectionalBlurSoftware : public FilterNodeBlurXYSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDirectionalBlurSoftware, override)
+ FilterNodeDirectionalBlurSoftware();
+ virtual const char* GetName() override { return "DirectionalBlur"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, Float aStdDeviation) override;
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aBlurDirection) override;
+
+protected:
+ virtual Size StdDeviationXY() override;
+
+private:
+ Float mStdDeviation;
+ BlurDirection mBlurDirection;
+};
+
+class FilterNodeCropSoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeCropSoftware, override)
+ virtual const char* GetName() override { return "Crop"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, const Rect &aSourceRect) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ IntRect mCropRect;
+};
+
+class FilterNodePremultiplySoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodePremultiplySoftware, override)
+ virtual const char* GetName() override { return "Premultiply"; }
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+};
+
+class FilterNodeUnpremultiplySoftware : public FilterNodeSoftware
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeUnpremultiplySoftware, override)
+ virtual const char* GetName() override { return "Unpremultiply"; }
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+};
+
+template<typename LightType, typename LightingType>
+class FilterNodeLightingSoftware : public FilterNodeSoftware
+{
+public:
+#if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
+ // Helpers for refcounted
+ virtual const char* typeName() const override { return mTypeName; }
+ virtual size_t typeSize() const override { return sizeof(*this); }
+#endif
+ explicit FilterNodeLightingSoftware(const char* aTypeName);
+ virtual const char* GetName() override { return "Lighting"; }
+ using FilterNodeSoftware::SetAttribute;
+ virtual void SetAttribute(uint32_t aIndex, Float) override;
+ virtual void SetAttribute(uint32_t aIndex, const Size &) override;
+ virtual void SetAttribute(uint32_t aIndex, const Point3D &) override;
+ virtual void SetAttribute(uint32_t aIndex, const Color &) override;
+
+protected:
+ virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override;
+ virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+ virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
+ virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+
+private:
+ template<typename CoordType>
+ TemporaryRef<DataSourceSurface> DoRender(const IntRect& aRect,
+ CoordType aKernelUnitLengthX,
+ CoordType aKernelUnitLengthY);
+
+ LightType mLight;
+ LightingType mLighting;
+ Float mSurfaceScale;
+ Size mKernelUnitLength;
+ Color mColor;
+#if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
+ const char* mTypeName;
+#endif
+};
+
+}
+}
+
+#endif // _MOZILLA_GFX_FILTERNODESOFTWARE_H_
diff --git a/gfx/2d/FilterProcessing.cpp b/gfx/2d/FilterProcessing.cpp
new file mode 100644
index 000000000..084f9476c
--- /dev/null
+++ b/gfx/2d/FilterProcessing.cpp
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "FilterProcessing.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::ExtractAlpha(DataSourceSurface* aSource)
+{
+ IntSize size = aSource->GetSize();
+ RefPtr<DataSourceSurface> alpha = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
+ if (MOZ2D_WARN_IF(!alpha)) {
+ return nullptr;
+ }
+ uint8_t* sourceData = aSource->GetData();
+ int32_t sourceStride = aSource->Stride();
+ uint8_t* alphaData = alpha->GetData();
+ int32_t alphaStride = alpha->Stride();
+
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ ExtractAlpha_SSE2(size, sourceData, sourceStride, alphaData, alphaStride);
+#endif
+ } else {
+ ExtractAlpha_Scalar(size, sourceData, sourceStride, alphaData, alphaStride);
+ }
+
+ return alpha.forget();
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::ConvertToB8G8R8A8(SourceSurface* aSurface)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ return ConvertToB8G8R8A8_SSE2(aSurface);
+#endif
+ }
+ return ConvertToB8G8R8A8_Scalar(aSurface);
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::ApplyBlending(DataSourceSurface* aInput1, DataSourceSurface* aInput2,
+ BlendMode aBlendMode)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ return ApplyBlending_SSE2(aInput1, aInput2, aBlendMode);
+#endif
+ }
+ return nullptr;
+}
+
+void
+FilterProcessing::ApplyMorphologyHorizontal(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ ApplyMorphologyHorizontal_SSE2(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
+#endif
+ } else {
+ ApplyMorphologyHorizontal_Scalar(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
+ }
+}
+
+void
+FilterProcessing::ApplyMorphologyVertical(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ ApplyMorphologyVertical_SSE2(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
+#endif
+ } else {
+ ApplyMorphologyVertical_Scalar(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
+ }
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::ApplyColorMatrix(DataSourceSurface* aInput, const Matrix5x4 &aMatrix)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ return ApplyColorMatrix_SSE2(aInput, aMatrix);
+#endif
+ }
+ return ApplyColorMatrix_Scalar(aInput, aMatrix);
+}
+
+void
+FilterProcessing::ApplyComposition(DataSourceSurface* aSource, DataSourceSurface* aDest,
+ CompositeOperator aOperator)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ ApplyComposition_SSE2(aSource, aDest, aOperator);
+#endif
+ } else {
+ ApplyComposition_Scalar(aSource, aDest, aOperator);
+ }
+}
+
+void
+FilterProcessing::SeparateColorChannels(DataSourceSurface* aSource,
+ RefPtr<DataSourceSurface>& aChannel0,
+ RefPtr<DataSourceSurface>& aChannel1,
+ RefPtr<DataSourceSurface>& aChannel2,
+ RefPtr<DataSourceSurface>& aChannel3)
+{
+ IntSize size = aSource->GetSize();
+ aChannel0 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
+ aChannel1 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
+ aChannel2 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
+ aChannel3 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
+ if (MOZ2D_WARN_IF(!(aChannel0 && aChannel1 && aChannel2 && aChannel3))) {
+ return;
+ }
+
+ uint8_t* sourceData = aSource->GetData();
+ int32_t sourceStride = aSource->Stride();
+ uint8_t* channel0Data = aChannel0->GetData();
+ uint8_t* channel1Data = aChannel1->GetData();
+ uint8_t* channel2Data = aChannel2->GetData();
+ uint8_t* channel3Data = aChannel3->GetData();
+ int32_t channelStride = aChannel0->Stride();
+
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ SeparateColorChannels_SSE2(size, sourceData, sourceStride, channel0Data, channel1Data, channel2Data, channel3Data, channelStride);
+#endif
+ } else {
+ SeparateColorChannels_Scalar(size, sourceData, sourceStride, channel0Data, channel1Data, channel2Data, channel3Data, channelStride);
+ }
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::CombineColorChannels(DataSourceSurface* aChannel0, DataSourceSurface* aChannel1,
+ DataSourceSurface* aChannel2, DataSourceSurface* aChannel3)
+{
+ IntSize size = aChannel0->GetSize();
+ RefPtr<DataSourceSurface> result =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!result)) {
+ return nullptr;
+ }
+ int32_t resultStride = result->Stride();
+ uint8_t* resultData = result->GetData();
+ int32_t channelStride = aChannel0->Stride();
+ uint8_t* channel0Data = aChannel0->GetData();
+ uint8_t* channel1Data = aChannel1->GetData();
+ uint8_t* channel2Data = aChannel2->GetData();
+ uint8_t* channel3Data = aChannel3->GetData();
+
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ CombineColorChannels_SSE2(size, resultStride, resultData, channelStride, channel0Data, channel1Data, channel2Data, channel3Data);
+#endif
+ } else {
+ CombineColorChannels_Scalar(size, resultStride, resultData, channelStride, channel0Data, channel1Data, channel2Data, channel3Data);
+ }
+
+ return result.forget();
+}
+
+void
+FilterProcessing::DoPremultiplicationCalculation(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ DoPremultiplicationCalculation_SSE2(
+ aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
+#endif
+ } else {
+ DoPremultiplicationCalculation_Scalar(
+ aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
+ }
+}
+
+void
+FilterProcessing::DoUnpremultiplicationCalculation(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ DoUnpremultiplicationCalculation_SSE2(
+ aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
+#endif
+ } else {
+ DoUnpremultiplicationCalculation_Scalar(
+ aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
+ }
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::RenderTurbulence(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ return RenderTurbulence_SSE2(aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect);
+#endif
+ }
+ return RenderTurbulence_Scalar(aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect);
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::ApplyArithmeticCombine(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ return ApplyArithmeticCombine_SSE2(aInput1, aInput2, aK1, aK2, aK3, aK4);
+#endif
+ }
+ return ApplyArithmeticCombine_Scalar(aInput1, aInput2, aK1, aK2, aK3, aK4);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/FilterProcessing.h b/gfx/2d/FilterProcessing.h
new file mode 100644
index 000000000..253d16543
--- /dev/null
+++ b/gfx/2d/FilterProcessing.h
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 20; 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 _MOZILLA_GFX_FILTERPROCESSING_H_
+#define _MOZILLA_GFX_FILTERPROCESSING_H_
+
+#include "2D.h"
+#include "Filters.h"
+
+namespace mozilla {
+namespace gfx {
+
+const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_B = 0;
+const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_G = 1;
+const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_R = 2;
+const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_A = 3;
+
+class FilterProcessing
+{
+public:
+
+ // Fast approximate division by 255. It has the property that
+ // for all 0 <= v <= 255*255, FastDivideBy255(v) == v/255.
+ // But it only uses two adds and two shifts instead of an
+ // integer division (which is expensive on many processors).
+ template<class B, class A>
+ static B FastDivideBy255(A v)
+ {
+ return ((v << 8) + v + 255) >> 16;
+ }
+
+ static TemporaryRef<DataSourceSurface> ExtractAlpha(DataSourceSurface* aSource);
+ static TemporaryRef<DataSourceSurface> ConvertToB8G8R8A8(SourceSurface* aSurface);
+ static TemporaryRef<DataSourceSurface> ApplyBlending(DataSourceSurface* aInput1, DataSourceSurface* aInput2, BlendMode aBlendMode);
+ static void ApplyMorphologyHorizontal(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOperator);
+ static void ApplyMorphologyVertical(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOperator);
+ static TemporaryRef<DataSourceSurface> ApplyColorMatrix(DataSourceSurface* aInput, const Matrix5x4 &aMatrix);
+ static void ApplyComposition(DataSourceSurface* aSource, DataSourceSurface* aDest, CompositeOperator aOperator);
+ static void SeparateColorChannels(DataSourceSurface* aSource,
+ RefPtr<DataSourceSurface>& aChannel0,
+ RefPtr<DataSourceSurface>& aChannel1,
+ RefPtr<DataSourceSurface>& aChannel2,
+ RefPtr<DataSourceSurface>& aChannel3);
+ static TemporaryRef<DataSourceSurface>
+ CombineColorChannels(DataSourceSurface* aChannel0, DataSourceSurface* aChannel1,
+ DataSourceSurface* aChannel2, DataSourceSurface* aChannel3);
+ static void DoPremultiplicationCalculation(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride);
+ static void DoUnpremultiplicationCalculation(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride);
+ static TemporaryRef<DataSourceSurface>
+ RenderTurbulence(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect);
+ static TemporaryRef<DataSourceSurface>
+ ApplyArithmeticCombine(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4);
+
+protected:
+ static void ExtractAlpha_Scalar(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride);
+ static TemporaryRef<DataSourceSurface> ConvertToB8G8R8A8_Scalar(SourceSurface* aSurface);
+ static void ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOperator);
+ static void ApplyMorphologyVertical_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOperator);
+ static TemporaryRef<DataSourceSurface> ApplyColorMatrix_Scalar(DataSourceSurface* aInput, const Matrix5x4 &aMatrix);
+ static void ApplyComposition_Scalar(DataSourceSurface* aSource, DataSourceSurface* aDest, CompositeOperator aOperator);
+
+ static void SeparateColorChannels_Scalar(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride);
+ static void CombineColorChannels_Scalar(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data);
+ static void DoPremultiplicationCalculation_Scalar(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride);
+ static void DoUnpremultiplicationCalculation_Scalar(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride);
+ static TemporaryRef<DataSourceSurface>
+ RenderTurbulence_Scalar(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect);
+ static TemporaryRef<DataSourceSurface>
+ ApplyArithmeticCombine_Scalar(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4);
+
+#ifdef USE_SSE2
+ static void ExtractAlpha_SSE2(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride);
+ static TemporaryRef<DataSourceSurface> ConvertToB8G8R8A8_SSE2(SourceSurface* aSurface);
+ static TemporaryRef<DataSourceSurface> ApplyBlending_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, BlendMode aBlendMode);
+ static void ApplyMorphologyHorizontal_SSE2(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOperator);
+ static void ApplyMorphologyVertical_SSE2(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOperator);
+ static TemporaryRef<DataSourceSurface> ApplyColorMatrix_SSE2(DataSourceSurface* aInput, const Matrix5x4 &aMatrix);
+ static void ApplyComposition_SSE2(DataSourceSurface* aSource, DataSourceSurface* aDest, CompositeOperator aOperator);
+ static void SeparateColorChannels_SSE2(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride);
+ static void CombineColorChannels_SSE2(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data);
+ static void DoPremultiplicationCalculation_SSE2(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride);
+ static void DoUnpremultiplicationCalculation_SSE2(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride);
+ static TemporaryRef<DataSourceSurface>
+ RenderTurbulence_SSE2(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect);
+ static TemporaryRef<DataSourceSurface>
+ ApplyArithmeticCombine_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4);
+#endif
+};
+
+// Constant-time max and min functions for unsigned arguments
+static inline unsigned
+umax(unsigned a, unsigned b)
+{
+ return a - ((a - b) & -(a < b));
+}
+
+static inline unsigned
+umin(unsigned a, unsigned b)
+{
+ return a - ((a - b) & -(a > b));
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_FILTERPROCESSING_H_
diff --git a/gfx/2d/FilterProcessingSIMD-inl.h b/gfx/2d/FilterProcessingSIMD-inl.h
new file mode 100644
index 000000000..8f58809ec
--- /dev/null
+++ b/gfx/2d/FilterProcessingSIMD-inl.h
@@ -0,0 +1,1081 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "FilterProcessing.h"
+
+#include "SIMD.h"
+#include "SVGTurbulenceRenderer-inl.h"
+
+namespace mozilla {
+namespace gfx {
+
+template<typename u8x16_t>
+inline TemporaryRef<DataSourceSurface>
+ConvertToB8G8R8A8_SIMD(SourceSurface* aSurface)
+{
+ IntSize size = aSurface->GetSize();
+ RefPtr<DataSourceSurface> input = aSurface->GetDataSurface();
+ RefPtr<DataSourceSurface> output =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ uint8_t *inputData = input->GetData();
+ uint8_t *outputData = output->GetData();
+ int32_t inputStride = input->Stride();
+ int32_t outputStride = output->Stride();
+ switch (input->GetFormat()) {
+ case SurfaceFormat::B8G8R8A8:
+ output = input;
+ break;
+ case SurfaceFormat::B8G8R8X8:
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t inputIndex = y * inputStride + 4 * x;
+ int32_t outputIndex = y * outputStride + 4 * x;
+ outputData[outputIndex + 0] = inputData[inputIndex + 0];
+ outputData[outputIndex + 1] = inputData[inputIndex + 1];
+ outputData[outputIndex + 2] = inputData[inputIndex + 2];
+ outputData[outputIndex + 3] = 255;
+ }
+ }
+ break;
+ case SurfaceFormat::R8G8B8A8:
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t inputIndex = y * inputStride + 4 * x;
+ int32_t outputIndex = y * outputStride + 4 * x;
+ outputData[outputIndex + 2] = inputData[inputIndex + 0];
+ outputData[outputIndex + 1] = inputData[inputIndex + 1];
+ outputData[outputIndex + 0] = inputData[inputIndex + 2];
+ outputData[outputIndex + 3] = inputData[inputIndex + 3];
+ }
+ }
+ break;
+ case SurfaceFormat::R8G8B8X8:
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t inputIndex = y * inputStride + 4 * x;
+ int32_t outputIndex = y * outputStride + 4 * x;
+ outputData[outputIndex + 2] = inputData[inputIndex + 0];
+ outputData[outputIndex + 1] = inputData[inputIndex + 1];
+ outputData[outputIndex + 0] = inputData[inputIndex + 2];
+ outputData[outputIndex + 3] = 255;
+ }
+ }
+ break;
+ case SurfaceFormat::A8:
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 16) {
+ int32_t inputIndex = y * inputStride + x;
+ int32_t outputIndex = y * outputStride + 4 * x;
+ u8x16_t p1To16 = simd::Load8<u8x16_t>(&inputData[inputIndex]);
+ // Turn AAAAAAAAAAAAAAAA into four chunks of 000A000A000A000A by
+ // interleaving with 0000000000000000 twice.
+ u8x16_t zero = simd::FromZero8<u8x16_t>();
+ u8x16_t p1To8 = simd::InterleaveLo8(zero, p1To16);
+ u8x16_t p9To16 = simd::InterleaveHi8(zero, p1To16);
+ u8x16_t p1To4 = simd::InterleaveLo8(zero, p1To8);
+ u8x16_t p5To8 = simd::InterleaveHi8(zero, p1To8);
+ u8x16_t p9To12 = simd::InterleaveLo8(zero, p9To16);
+ u8x16_t p13To16 = simd::InterleaveHi8(zero, p9To16);
+ simd::Store8(&outputData[outputIndex], p1To4);
+ if ((x + 4) * 4 < outputStride) {
+ simd::Store8(&outputData[outputIndex + 4 * 4], p5To8);
+ }
+ if ((x + 8) * 4 < outputStride) {
+ simd::Store8(&outputData[outputIndex + 4 * 8], p9To12);
+ }
+ if ((x + 12) * 4 < outputStride) {
+ simd::Store8(&outputData[outputIndex + 4 * 12], p13To16);
+ }
+ }
+ }
+ break;
+ default:
+ output = nullptr;
+ break;
+ }
+ return output;
+}
+
+template<typename u8x16_t>
+inline void
+ExtractAlpha_SIMD(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride)
+{
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 16) {
+ // Process 16 pixels at a time.
+ // Turn up to four chunks of BGRABGRABGRABGRA into one chunk of AAAAAAAAAAAAAAAA.
+ int32_t sourceIndex = y * sourceStride + 4 * x;
+ int32_t targetIndex = y * alphaStride + x;
+
+ u8x16_t bgrabgrabgrabgra1 = simd::FromZero8<u8x16_t>();
+ u8x16_t bgrabgrabgrabgra2 = simd::FromZero8<u8x16_t>();
+ u8x16_t bgrabgrabgrabgra3 = simd::FromZero8<u8x16_t>();
+ u8x16_t bgrabgrabgrabgra4 = simd::FromZero8<u8x16_t>();
+
+ bgrabgrabgrabgra1 = simd::Load8<u8x16_t>(&sourceData[sourceIndex]);
+ if (4 * (x + 4) < sourceStride) {
+ bgrabgrabgrabgra2 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 4]);
+ }
+ if (4 * (x + 8) < sourceStride) {
+ bgrabgrabgrabgra3 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 8]);
+ }
+ if (4 * (x + 12) < sourceStride) {
+ bgrabgrabgrabgra4 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 12]);
+ }
+
+ u8x16_t bbggrraabbggrraa1 = simd::InterleaveLo8(bgrabgrabgrabgra1, bgrabgrabgrabgra3);
+ u8x16_t bbggrraabbggrraa2 = simd::InterleaveHi8(bgrabgrabgrabgra1, bgrabgrabgrabgra3);
+ u8x16_t bbggrraabbggrraa3 = simd::InterleaveLo8(bgrabgrabgrabgra2, bgrabgrabgrabgra4);
+ u8x16_t bbggrraabbggrraa4 = simd::InterleaveHi8(bgrabgrabgrabgra2, bgrabgrabgrabgra4);
+ u8x16_t bbbbggggrrrraaaa1 = simd::InterleaveLo8(bbggrraabbggrraa1, bbggrraabbggrraa3);
+ u8x16_t bbbbggggrrrraaaa2 = simd::InterleaveHi8(bbggrraabbggrraa1, bbggrraabbggrraa3);
+ u8x16_t bbbbggggrrrraaaa3 = simd::InterleaveLo8(bbggrraabbggrraa2, bbggrraabbggrraa4);
+ u8x16_t bbbbggggrrrraaaa4 = simd::InterleaveHi8(bbggrraabbggrraa2, bbggrraabbggrraa4);
+ u8x16_t rrrrrrrraaaaaaaa1 = simd::InterleaveHi8(bbbbggggrrrraaaa1, bbbbggggrrrraaaa3);
+ u8x16_t rrrrrrrraaaaaaaa2 = simd::InterleaveHi8(bbbbggggrrrraaaa2, bbbbggggrrrraaaa4);
+ u8x16_t aaaaaaaaaaaaaaaa = simd::InterleaveHi8(rrrrrrrraaaaaaaa1, rrrrrrrraaaaaaaa2);
+
+ simd::Store8(&alphaData[targetIndex], aaaaaaaaaaaaaaaa);
+ }
+ }
+}
+
+// This function calculates the result color values for four pixels, but for
+// only two color channels - either b & r or g & a. However, the a result will
+// not be used.
+// source and dest each contain 8 values, either bbbb gggg or rrrr aaaa.
+// sourceAlpha and destAlpha are of the form aaaa aaaa, where each aaaa is the
+// alpha of all four pixels (and both aaaa's are the same).
+// blendendComponent1 and blendedComponent2 are the out parameters.
+template<typename i16x8_t, typename i32x4_t, uint32_t aBlendMode>
+inline void
+BlendTwoComponentsOfFourPixels(i16x8_t source, i16x8_t sourceAlpha,
+ i16x8_t dest, const i16x8_t& destAlpha,
+ i32x4_t& blendedComponent1, i32x4_t& blendedComponent2)
+{
+ i16x8_t x255 = simd::FromI16<i16x8_t>(255);
+
+ switch (aBlendMode) {
+
+ case BLEND_MODE_MULTIPLY:
+ {
+ // val = ((255 - destAlpha) * source + (255 - sourceAlpha + source) * dest);
+ i16x8_t twoFiftyFiveMinusDestAlpha = simd::Sub16(x255, destAlpha);
+ i16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha);
+ i16x8_t twoFiftyFiveMinusSourceAlphaPlusSource = simd::Add16(twoFiftyFiveMinusSourceAlpha, source);
+
+ i16x8_t sourceInterleavedWithDest1 = simd::InterleaveLo16(source, dest);
+ i16x8_t leftFactor1 = simd::InterleaveLo16(twoFiftyFiveMinusDestAlpha, twoFiftyFiveMinusSourceAlphaPlusSource);
+ blendedComponent1 = simd::MulAdd16x8x2To32x4(sourceInterleavedWithDest1, leftFactor1);
+ blendedComponent1 = simd::FastDivideBy255(blendedComponent1);
+
+ i16x8_t sourceInterleavedWithDest2 = simd::InterleaveHi16(source, dest);
+ i16x8_t leftFactor2 = simd::InterleaveHi16(twoFiftyFiveMinusDestAlpha, twoFiftyFiveMinusSourceAlphaPlusSource);
+ blendedComponent2 = simd::MulAdd16x8x2To32x4(sourceInterleavedWithDest2, leftFactor2);
+ blendedComponent2 = simd::FastDivideBy255(blendedComponent2);
+
+ break;
+ }
+
+ case BLEND_MODE_SCREEN:
+ {
+ // val = 255 * (source + dest) + (0 - dest) * source;
+ i16x8_t sourcePlusDest = simd::Add16(source, dest);
+ i16x8_t zeroMinusDest = simd::Sub16(simd::FromI16<i16x8_t>(0), dest);
+
+ i16x8_t twoFiftyFiveInterleavedWithZeroMinusDest1 = simd::InterleaveLo16(x255, zeroMinusDest);
+ i16x8_t sourcePlusDestInterleavedWithSource1 = simd::InterleaveLo16(sourcePlusDest, source);
+ blendedComponent1 = simd::MulAdd16x8x2To32x4(twoFiftyFiveInterleavedWithZeroMinusDest1, sourcePlusDestInterleavedWithSource1);
+ blendedComponent1 = simd::FastDivideBy255(blendedComponent1);
+
+ i16x8_t twoFiftyFiveInterleavedWithZeroMinusDest2 = simd::InterleaveHi16(x255, zeroMinusDest);
+ i16x8_t sourcePlusDestInterleavedWithSource2 = simd::InterleaveHi16(sourcePlusDest, source);
+ blendedComponent2 = simd::MulAdd16x8x2To32x4(twoFiftyFiveInterleavedWithZeroMinusDest2, sourcePlusDestInterleavedWithSource2);
+ blendedComponent2 = simd::FastDivideBy255(blendedComponent2);
+
+ break;
+ }
+
+ case BLEND_MODE_DARKEN:
+ case BLEND_MODE_LIGHTEN:
+ {
+ // Darken:
+ // val = min((255 - destAlpha) * source + 255 * dest,
+ // 255 * source + (255 - sourceAlpha) * dest);
+ //
+ // Lighten:
+ // val = max((255 - destAlpha) * source + 255 * dest,
+ // 255 * source + (255 - sourceAlpha) * dest);
+
+ i16x8_t twoFiftyFiveMinusDestAlpha = simd::Sub16(x255, destAlpha);
+ i16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha);
+
+ i16x8_t twoFiftyFiveMinusDestAlphaInterleavedWithTwoFiftyFive1 = simd::InterleaveLo16(twoFiftyFiveMinusDestAlpha, x255);
+ i16x8_t twoFiftyFiveInterleavedWithTwoFiftyFiveMinusSourceAlpha1 = simd::InterleaveLo16(x255, twoFiftyFiveMinusSourceAlpha);
+ i16x8_t sourceInterleavedWithDest1 = simd::InterleaveLo16(source, dest);
+ i32x4_t product1_1 = simd::MulAdd16x8x2To32x4(twoFiftyFiveMinusDestAlphaInterleavedWithTwoFiftyFive1, sourceInterleavedWithDest1);
+ i32x4_t product1_2 = simd::MulAdd16x8x2To32x4(twoFiftyFiveInterleavedWithTwoFiftyFiveMinusSourceAlpha1, sourceInterleavedWithDest1);
+ blendedComponent1 = aBlendMode == BLEND_MODE_DARKEN ? simd::Min32(product1_1, product1_2) : simd::Max32(product1_1, product1_2);
+ blendedComponent1 = simd::FastDivideBy255(blendedComponent1);
+
+ i16x8_t twoFiftyFiveMinusDestAlphaInterleavedWithTwoFiftyFive2 = simd::InterleaveHi16(twoFiftyFiveMinusDestAlpha, x255);
+ i16x8_t twoFiftyFiveInterleavedWithTwoFiftyFiveMinusSourceAlpha2 = simd::InterleaveHi16(x255, twoFiftyFiveMinusSourceAlpha);
+ i16x8_t sourceInterleavedWithDest2 = simd::InterleaveHi16(source, dest);
+ i32x4_t product2_1 = simd::MulAdd16x8x2To32x4(twoFiftyFiveMinusDestAlphaInterleavedWithTwoFiftyFive2, sourceInterleavedWithDest2);
+ i32x4_t product2_2 = simd::MulAdd16x8x2To32x4(twoFiftyFiveInterleavedWithTwoFiftyFiveMinusSourceAlpha2, sourceInterleavedWithDest2);
+ blendedComponent2 = aBlendMode == BLEND_MODE_DARKEN ? simd::Min32(product2_1, product2_2) : simd::Max32(product2_1, product2_2);
+ blendedComponent2 = simd::FastDivideBy255(blendedComponent2);
+
+ break;
+ }
+
+ }
+}
+
+// The alpha channel is subject to a different calculation than the RGB
+// channels, and this calculation is the same for all blend modes:
+// resultAlpha * 255 = 255 * 255 - (255 - sourceAlpha) * (255 - destAlpha)
+template<typename i16x8_t, typename i32x4_t>
+inline i32x4_t
+BlendAlphaOfFourPixels(i16x8_t s_rrrraaaa1234, i16x8_t d_rrrraaaa1234)
+{
+ // We're using MulAdd16x8x2To32x4, so we need to interleave our factors
+ // appropriately. The calculation is rewritten as follows:
+ // resultAlpha[0] * 255 = 255 * 255 - (255 - sourceAlpha[0]) * (255 - destAlpha[0])
+ // = 255 * 255 + (255 - sourceAlpha[0]) * (destAlpha[0] - 255)
+ // = (255 - 0) * (510 - 255) + (255 - sourceAlpha[0]) * (destAlpha[0] - 255)
+ // = MulAdd(255 - IntLv(0, sourceAlpha), IntLv(510, destAlpha) - 255)[0]
+ i16x8_t zeroInterleavedWithSourceAlpha = simd::InterleaveHi16(simd::FromI16<i16x8_t>(0), s_rrrraaaa1234);
+ i16x8_t fiveTenInterleavedWithDestAlpha = simd::InterleaveHi16(simd::FromI16<i16x8_t>(510), d_rrrraaaa1234);
+ i16x8_t f1 = simd::Sub16(simd::FromI16<i16x8_t>(255), zeroInterleavedWithSourceAlpha);
+ i16x8_t f2 = simd::Sub16(fiveTenInterleavedWithDestAlpha, simd::FromI16<i16x8_t>(255));
+ return simd::FastDivideBy255(simd::MulAdd16x8x2To32x4(f1, f2));
+}
+
+template<typename u8x16_t, typename i16x8_t>
+inline void
+UnpackAndShuffleComponents(u8x16_t bgrabgrabgrabgra1234,
+ i16x8_t& bbbbgggg1234, i16x8_t& rrrraaaa1234)
+{
+ // bgrabgrabgrabgra1234 -> bbbbgggg1234, rrrraaaa1234
+ i16x8_t bgrabgra12 = simd::UnpackLo8x8ToI16x8(bgrabgrabgrabgra1234);
+ i16x8_t bgrabgra34 = simd::UnpackHi8x8ToI16x8(bgrabgrabgrabgra1234);
+ i16x8_t bbggrraa13 = simd::InterleaveLo16(bgrabgra12, bgrabgra34);
+ i16x8_t bbggrraa24 = simd::InterleaveHi16(bgrabgra12, bgrabgra34);
+ bbbbgggg1234 = simd::InterleaveLo16(bbggrraa13, bbggrraa24);
+ rrrraaaa1234 = simd::InterleaveHi16(bbggrraa13, bbggrraa24);
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t>
+inline u8x16_t
+ShuffleAndPackComponents(i32x4_t bbbb1234, i32x4_t gggg1234,
+ i32x4_t rrrr1234, const i32x4_t& aaaa1234)
+{
+ // bbbb1234, gggg1234, rrrr1234, aaaa1234 -> bgrabgrabgrabgra1234
+ i16x8_t bbbbgggg1234 = simd::PackAndSaturate32To16(bbbb1234, gggg1234);
+ i16x8_t rrrraaaa1234 = simd::PackAndSaturate32To16(rrrr1234, aaaa1234);
+ i16x8_t brbrbrbr1234 = simd::InterleaveLo16(bbbbgggg1234, rrrraaaa1234);
+ i16x8_t gagagaga1234 = simd::InterleaveHi16(bbbbgggg1234, rrrraaaa1234);
+ i16x8_t bgrabgra12 = simd::InterleaveLo16(brbrbrbr1234, gagagaga1234);
+ i16x8_t bgrabgra34 = simd::InterleaveHi16(brbrbrbr1234, gagagaga1234);
+ return simd::PackAndSaturate16To8(bgrabgra12, bgrabgra34);
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t, BlendMode mode>
+inline TemporaryRef<DataSourceSurface>
+ApplyBlending_SIMD(DataSourceSurface* aInput1, DataSourceSurface* aInput2)
+{
+ IntSize size = aInput1->GetSize();
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (!target) {
+ return nullptr;
+ }
+
+ uint8_t* source1Data = aInput1->GetData();
+ uint8_t* source2Data = aInput2->GetData();
+ uint8_t* targetData = target->GetData();
+ int32_t targetStride = target->Stride();
+ int32_t source1Stride = aInput1->Stride();
+ int32_t source2Stride = aInput2->Stride();
+
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 4) {
+ int32_t targetIndex = y * targetStride + 4 * x;
+ int32_t source1Index = y * source1Stride + 4 * x;
+ int32_t source2Index = y * source2Stride + 4 * x;
+
+ u8x16_t s1234 = simd::Load8<u8x16_t>(&source2Data[source2Index]);
+ u8x16_t d1234 = simd::Load8<u8x16_t>(&source1Data[source1Index]);
+
+ // The blending calculation for the RGB channels all need access to the
+ // alpha channel of their pixel, and the alpha calculation is different,
+ // so it makes sense to separate by channel.
+
+ i16x8_t s_bbbbgggg1234, s_rrrraaaa1234;
+ i16x8_t d_bbbbgggg1234, d_rrrraaaa1234;
+ UnpackAndShuffleComponents(s1234, s_bbbbgggg1234, s_rrrraaaa1234);
+ UnpackAndShuffleComponents(d1234, d_bbbbgggg1234, d_rrrraaaa1234);
+ i16x8_t s_aaaaaaaa1234 = simd::Shuffle32<3,2,3,2>(s_rrrraaaa1234);
+ i16x8_t d_aaaaaaaa1234 = simd::Shuffle32<3,2,3,2>(d_rrrraaaa1234);
+
+ // We only use blendedB, blendedG and blendedR.
+ i32x4_t blendedB, blendedG, blendedR, blendedA;
+ BlendTwoComponentsOfFourPixels<i16x8_t,i32x4_t,mode>(s_bbbbgggg1234, s_aaaaaaaa1234, d_bbbbgggg1234, d_aaaaaaaa1234, blendedB, blendedG);
+ BlendTwoComponentsOfFourPixels<i16x8_t,i32x4_t,mode>(s_rrrraaaa1234, s_aaaaaaaa1234, d_rrrraaaa1234, d_aaaaaaaa1234, blendedR, blendedA);
+
+ // Throw away blendedA and overwrite it with the correct blended alpha.
+ blendedA = BlendAlphaOfFourPixels<i16x8_t,i32x4_t>(s_rrrraaaa1234, d_rrrraaaa1234);
+
+ u8x16_t result1234 = ShuffleAndPackComponents<i32x4_t,i16x8_t,u8x16_t>(blendedB, blendedG, blendedR, blendedA);
+ simd::Store8(&targetData[targetIndex], result1234);
+ }
+ }
+
+ return target;
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t>
+static TemporaryRef<DataSourceSurface>
+ApplyBlending_SIMD(DataSourceSurface* aInput1, DataSourceSurface* aInput2,
+ BlendMode aBlendMode)
+{
+ switch (aBlendMode) {
+ case BLEND_MODE_MULTIPLY:
+ return ApplyBlending_SIMD<i32x4_t,i16x8_t,u8x16_t, BLEND_MODE_MULTIPLY>(aInput1, aInput2);
+ case BLEND_MODE_SCREEN:
+ return ApplyBlending_SIMD<i32x4_t,i16x8_t,u8x16_t, BLEND_MODE_SCREEN>(aInput1, aInput2);
+ case BLEND_MODE_DARKEN:
+ return ApplyBlending_SIMD<i32x4_t,i16x8_t,u8x16_t, BLEND_MODE_DARKEN>(aInput1, aInput2);
+ case BLEND_MODE_LIGHTEN:
+ return ApplyBlending_SIMD<i32x4_t,i16x8_t,u8x16_t, BLEND_MODE_LIGHTEN>(aInput1, aInput2);
+ default:
+ return nullptr;
+ }
+}
+
+template<MorphologyOperator Operator, typename u8x16_t>
+static u8x16_t
+Morph8(u8x16_t a, u8x16_t b)
+{
+ return Operator == MORPHOLOGY_OPERATOR_ERODE ?
+ simd::Min8(a, b) : simd::Max8(a, b);
+}
+
+// Set every pixel to the per-component minimum or maximum of the pixels around
+// it that are up to aRadius pixels away from it (horizontally).
+template<MorphologyOperator op, typename i16x8_t, typename u8x16_t>
+inline void ApplyMorphologyHorizontal_SIMD(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius)
+{
+ static_assert(op == MORPHOLOGY_OPERATOR_ERODE ||
+ op == MORPHOLOGY_OPERATOR_DILATE,
+ "unexpected morphology operator");
+
+ int32_t kernelSize = aRadius + 1 + aRadius;
+ MOZ_ASSERT(kernelSize >= 3, "don't call this with aRadius <= 0");
+ MOZ_ASSERT(kernelSize % 4 == 1 || kernelSize % 4 == 3);
+ int32_t completeKernelSizeForFourPixels = kernelSize + 3;
+ MOZ_ASSERT(completeKernelSizeForFourPixels % 4 == 0 ||
+ completeKernelSizeForFourPixels % 4 == 2);
+
+ // aSourceData[-aRadius] and aDestData[0] are both aligned to 16 bytes, just
+ // the way we need them to be.
+
+ IntRect sourceRect = aDestRect;
+ sourceRect.Inflate(aRadius, 0);
+
+ for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++) {
+ int32_t kernelStartX = aDestRect.x - aRadius;
+ for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x += 4, kernelStartX += 4) {
+ // We process four pixels (16 color values) at a time.
+ // aSourceData[0] points to the pixel located at aDestRect.TopLeft();
+ // source values can be read beyond that because the source is extended
+ // by aRadius pixels.
+
+ int32_t sourceIndex = y * aSourceStride + 4 * kernelStartX;
+ u8x16_t p1234 = simd::Load8<u8x16_t>(&aSourceData[sourceIndex]);
+ u8x16_t m1234 = p1234;
+
+ for (int32_t i = 4; i < completeKernelSizeForFourPixels; i += 4) {
+ u8x16_t p5678 = (kernelStartX + i < sourceRect.XMost()) ?
+ simd::Load8<u8x16_t>(&aSourceData[sourceIndex + 4 * i]) :
+ simd::FromZero8<u8x16_t>();
+ u8x16_t p2345 = simd::Rotate8<4>(p1234, p5678);
+ u8x16_t p3456 = simd::Rotate8<8>(p1234, p5678);
+ m1234 = Morph8<op,u8x16_t>(m1234, p2345);
+ m1234 = Morph8<op,u8x16_t>(m1234, p3456);
+ if (i + 2 < completeKernelSizeForFourPixels) {
+ u8x16_t p4567 = simd::Rotate8<12>(p1234, p5678);
+ m1234 = Morph8<op,u8x16_t>(m1234, p4567);
+ m1234 = Morph8<op,u8x16_t>(m1234, p5678);
+ }
+ p1234 = p5678;
+ }
+
+ int32_t destIndex = y * aDestStride + 4 * x;
+ simd::Store8(&aDestData[destIndex], m1234);
+ }
+ }
+}
+
+template<typename i16x8_t, typename u8x16_t>
+inline void ApplyMorphologyHorizontal_SIMD(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ if (aOp == MORPHOLOGY_OPERATOR_ERODE) {
+ ApplyMorphologyHorizontal_SIMD<MORPHOLOGY_OPERATOR_ERODE,i16x8_t,u8x16_t>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ } else {
+ ApplyMorphologyHorizontal_SIMD<MORPHOLOGY_OPERATOR_DILATE,i16x8_t,u8x16_t>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ }
+}
+
+// Set every pixel to the per-component minimum or maximum of the pixels around
+// it that are up to aRadius pixels away from it (vertically).
+template<MorphologyOperator op, typename i16x8_t, typename u8x16_t>
+static void ApplyMorphologyVertical_SIMD(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius)
+{
+ static_assert(op == MORPHOLOGY_OPERATOR_ERODE ||
+ op == MORPHOLOGY_OPERATOR_DILATE,
+ "unexpected morphology operator");
+
+ int32_t startY = aDestRect.y - aRadius;
+ int32_t endY = aDestRect.y + aRadius;
+ for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++, startY++, endY++) {
+ for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x += 4) {
+ int32_t sourceIndex = startY * aSourceStride + 4 * x;
+ u8x16_t u = simd::Load8<u8x16_t>(&aSourceData[sourceIndex]);
+ sourceIndex += aSourceStride;
+ for (int32_t iy = startY + 1; iy <= endY; iy++, sourceIndex += aSourceStride) {
+ u8x16_t u2 = simd::Load8<u8x16_t>(&aSourceData[sourceIndex]);
+ u = Morph8<op,u8x16_t>(u, u2);
+ }
+
+ int32_t destIndex = y * aDestStride + 4 * x;
+ simd::Store8(&aDestData[destIndex], u);
+ }
+ }
+}
+
+template<typename i16x8_t, typename u8x16_t>
+inline void ApplyMorphologyVertical_SIMD(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ if (aOp == MORPHOLOGY_OPERATOR_ERODE) {
+ ApplyMorphologyVertical_SIMD<MORPHOLOGY_OPERATOR_ERODE,i16x8_t,u8x16_t>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ } else {
+ ApplyMorphologyVertical_SIMD<MORPHOLOGY_OPERATOR_DILATE,i16x8_t,u8x16_t>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ }
+}
+
+template<typename i32x4_t, typename i16x8_t>
+static i32x4_t
+ColorMatrixMultiply(i16x8_t p, i16x8_t rows_bg, i16x8_t rows_ra, const i32x4_t& bias)
+{
+ // int16_t p[8] == { b, g, r, a, b, g, r, a }.
+ // int16_t rows_bg[8] == { bB, bG, bR, bA, gB, gG, gR, gA }.
+ // int16_t rows_ra[8] == { rB, rG, rR, rA, aB, aG, aR, aA }.
+ // int32_t bias[4] == { _B, _G, _R, _A }.
+
+ i32x4_t sum = bias;
+
+ // int16_t bg[8] = { b, g, b, g, b, g, b, g };
+ i16x8_t bg = simd::ShuffleHi16<1,0,1,0>(simd::ShuffleLo16<1,0,1,0>(p));
+ // int32_t prodsum_bg[4] = { b * bB + g * gB, b * bG + g * gG, b * bR + g * gR, b * bA + g * gA }
+ i32x4_t prodsum_bg = simd::MulAdd16x8x2To32x4(bg, rows_bg);
+ sum = simd::Add32(sum, prodsum_bg);
+
+ // uint16_t ra[8] = { r, a, r, a, r, a, r, a };
+ i16x8_t ra = simd::ShuffleHi16<3,2,3,2>(simd::ShuffleLo16<3,2,3,2>(p));
+ // int32_t prodsum_ra[4] = { r * rB + a * aB, r * rG + a * aG, r * rR + a * aR, r * rA + a * aA }
+ i32x4_t prodsum_ra = simd::MulAdd16x8x2To32x4(ra, rows_ra);
+ sum = simd::Add32(sum, prodsum_ra);
+
+ // int32_t sum[4] == { b * bB + g * gB + r * rB + a * aB + _B, ... }.
+ return sum;
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t>
+static TemporaryRef<DataSourceSurface>
+ApplyColorMatrix_SIMD(DataSourceSurface* aInput, const Matrix5x4 &aMatrix)
+{
+ IntSize size = aInput->GetSize();
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (!target) {
+ return nullptr;
+ }
+
+ uint8_t* sourceData = aInput->GetData();
+ uint8_t* targetData = target->GetData();
+ int32_t sourceStride = aInput->Stride();
+ int32_t targetStride = target->Stride();
+
+ const int16_t factor = 128;
+ const Float floatElementMax = INT16_MAX / factor; // 255
+ MOZ_ASSERT((floatElementMax * factor) <= INT16_MAX, "badly chosen float-to-int scale");
+
+ const Float *floats = &aMatrix._11;
+
+ ptrdiff_t componentOffsets[4] = {
+ B8G8R8A8_COMPONENT_BYTEOFFSET_R,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_G,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_B,
+ B8G8R8A8_COMPONENT_BYTEOFFSET_A
+ };
+
+ // We store the color matrix in rows_bgra in the following format:
+ // { bB, bG, bR, bA, gB, gG, gR, gA }.
+ // { bB, gB, bG, gG, bR, gR, bA, gA }
+ // The way this is interleaved allows us to use the intrinsic _mm_madd_epi16
+ // which works especially well for our use case.
+ int16_t rows_bgra[2][8];
+ for (size_t rowIndex = 0; rowIndex < 4; rowIndex++) {
+ for (size_t colIndex = 0; colIndex < 4; colIndex++) {
+ const Float& floatMatrixElement = floats[rowIndex * 4 + colIndex];
+ Float clampedFloatMatrixElement = std::min(std::max(floatMatrixElement, -floatElementMax), floatElementMax);
+ int16_t scaledIntMatrixElement = int16_t(clampedFloatMatrixElement * factor + 0.5);
+ int8_t bg_or_ra = componentOffsets[rowIndex] / 2;
+ int8_t g_or_a = componentOffsets[rowIndex] % 2;
+ int8_t B_or_G_or_R_or_A = componentOffsets[colIndex];
+ rows_bgra[bg_or_ra][B_or_G_or_R_or_A * 2 + g_or_a] = scaledIntMatrixElement;
+ }
+ }
+
+ int32_t rowBias[4];
+ Float biasMax = (INT32_MAX - 4 * 255 * INT16_MAX) / (factor * 255);
+ for (size_t colIndex = 0; colIndex < 4; colIndex++) {
+ size_t rowIndex = 4;
+ const Float& floatMatrixElement = floats[rowIndex * 4 + colIndex];
+ Float clampedFloatMatrixElement = std::min(std::max(floatMatrixElement, -biasMax), biasMax);
+ int32_t scaledIntMatrixElement = int32_t(clampedFloatMatrixElement * factor * 255 + 0.5);
+ rowBias[componentOffsets[colIndex]] = scaledIntMatrixElement;
+ }
+
+ i16x8_t row_bg_v = simd::FromI16<i16x8_t>(
+ rows_bgra[0][0], rows_bgra[0][1], rows_bgra[0][2], rows_bgra[0][3],
+ rows_bgra[0][4], rows_bgra[0][5], rows_bgra[0][6], rows_bgra[0][7]);
+
+ i16x8_t row_ra_v = simd::FromI16<i16x8_t>(
+ rows_bgra[1][0], rows_bgra[1][1], rows_bgra[1][2], rows_bgra[1][3],
+ rows_bgra[1][4], rows_bgra[1][5], rows_bgra[1][6], rows_bgra[1][7]);
+
+ i32x4_t rowsBias_v =
+ simd::From32<i32x4_t>(rowBias[0], rowBias[1], rowBias[2], rowBias[3]);
+
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 4) {
+ MOZ_ASSERT(sourceStride >= 4 * (x + 4), "need to be able to read 4 pixels at this position");
+ MOZ_ASSERT(targetStride >= 4 * (x + 4), "need to be able to write 4 pixels at this position");
+ int32_t sourceIndex = y * sourceStride + 4 * x;
+ int32_t targetIndex = y * targetStride + 4 * x;
+
+ // We load 4 pixels, unpack them, process them 1 pixel at a time, and
+ // finally pack and store the 4 result pixels.
+
+ u8x16_t p1234 = simd::Load8<u8x16_t>(&sourceData[sourceIndex]);
+
+ // Splat needed to get each pixel twice into i16x8
+ i16x8_t p11 = simd::UnpackLo8x8ToI16x8(simd::Splat32On8<0>(p1234));
+ i16x8_t p22 = simd::UnpackLo8x8ToI16x8(simd::Splat32On8<1>(p1234));
+ i16x8_t p33 = simd::UnpackLo8x8ToI16x8(simd::Splat32On8<2>(p1234));
+ i16x8_t p44 = simd::UnpackLo8x8ToI16x8(simd::Splat32On8<3>(p1234));
+
+ i32x4_t result_p1 = ColorMatrixMultiply(p11, row_bg_v, row_ra_v, rowsBias_v);
+ i32x4_t result_p2 = ColorMatrixMultiply(p22, row_bg_v, row_ra_v, rowsBias_v);
+ i32x4_t result_p3 = ColorMatrixMultiply(p33, row_bg_v, row_ra_v, rowsBias_v);
+ i32x4_t result_p4 = ColorMatrixMultiply(p44, row_bg_v, row_ra_v, rowsBias_v);
+
+ static_assert(factor == 1 << 7, "Please adapt the calculation in the lines below for a different factor.");
+ u8x16_t result_p1234 = simd::PackAndSaturate32To8(simd::ShiftRight32<7>(result_p1),
+ simd::ShiftRight32<7>(result_p2),
+ simd::ShiftRight32<7>(result_p3),
+ simd::ShiftRight32<7>(result_p4));
+ simd::Store8(&targetData[targetIndex], result_p1234);
+ }
+ }
+
+ return target;
+}
+
+// source / dest: bgra bgra
+// sourceAlpha / destAlpha: aaaa aaaa
+// result: bgra bgra
+template<typename i32x4_t, typename u16x8_t, uint32_t aCompositeOperator>
+static inline u16x8_t
+CompositeTwoPixels(u16x8_t source, u16x8_t sourceAlpha, u16x8_t dest, const u16x8_t& destAlpha)
+{
+ u16x8_t x255 = simd::FromU16<u16x8_t>(255);
+
+ switch (aCompositeOperator) {
+
+ case COMPOSITE_OPERATOR_OVER:
+ {
+ // val = dest * (255 - sourceAlpha) + source * 255;
+ u16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha);
+
+ u16x8_t destSourceInterleaved1 = simd::InterleaveLo16(dest, source);
+ u16x8_t rightFactor1 = simd::InterleaveLo16(twoFiftyFiveMinusSourceAlpha, x255);
+ i32x4_t result1 = simd::MulAdd16x8x2To32x4(destSourceInterleaved1, rightFactor1);
+
+ u16x8_t destSourceInterleaved2 = simd::InterleaveHi16(dest, source);
+ u16x8_t rightFactor2 = simd::InterleaveHi16(twoFiftyFiveMinusSourceAlpha, x255);
+ i32x4_t result2 = simd::MulAdd16x8x2To32x4(destSourceInterleaved2, rightFactor2);
+
+ return simd::PackAndSaturate32ToU16(simd::FastDivideBy255(result1),
+ simd::FastDivideBy255(result2));
+ }
+
+ case COMPOSITE_OPERATOR_IN:
+ {
+ // val = source * destAlpha;
+ return simd::FastDivideBy255_16(simd::Mul16(source, destAlpha));
+ }
+
+ case COMPOSITE_OPERATOR_OUT:
+ {
+ // val = source * (255 - destAlpha);
+ u16x8_t prod = simd::Mul16(source, simd::Sub16(x255, destAlpha));
+ return simd::FastDivideBy255_16(prod);
+ }
+
+ case COMPOSITE_OPERATOR_ATOP:
+ {
+ // val = dest * (255 - sourceAlpha) + source * destAlpha;
+ u16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha);
+
+ u16x8_t destSourceInterleaved1 = simd::InterleaveLo16(dest, source);
+ u16x8_t rightFactor1 = simd::InterleaveLo16(twoFiftyFiveMinusSourceAlpha, destAlpha);
+ i32x4_t result1 = simd::MulAdd16x8x2To32x4(destSourceInterleaved1, rightFactor1);
+
+ u16x8_t destSourceInterleaved2 = simd::InterleaveHi16(dest, source);
+ u16x8_t rightFactor2 = simd::InterleaveHi16(twoFiftyFiveMinusSourceAlpha, destAlpha);
+ i32x4_t result2 = simd::MulAdd16x8x2To32x4(destSourceInterleaved2, rightFactor2);
+
+ return simd::PackAndSaturate32ToU16(simd::FastDivideBy255(result1),
+ simd::FastDivideBy255(result2));
+ }
+
+ case COMPOSITE_OPERATOR_XOR:
+ {
+ // val = dest * (255 - sourceAlpha) + source * (255 - destAlpha);
+ u16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha);
+ u16x8_t twoFiftyFiveMinusDestAlpha = simd::Sub16(x255, destAlpha);
+
+ u16x8_t destSourceInterleaved1 = simd::InterleaveLo16(dest, source);
+ u16x8_t rightFactor1 = simd::InterleaveLo16(twoFiftyFiveMinusSourceAlpha,
+ twoFiftyFiveMinusDestAlpha);
+ i32x4_t result1 = simd::MulAdd16x8x2To32x4(destSourceInterleaved1, rightFactor1);
+
+ u16x8_t destSourceInterleaved2 = simd::InterleaveHi16(dest, source);
+ u16x8_t rightFactor2 = simd::InterleaveHi16(twoFiftyFiveMinusSourceAlpha,
+ twoFiftyFiveMinusDestAlpha);
+ i32x4_t result2 = simd::MulAdd16x8x2To32x4(destSourceInterleaved2, rightFactor2);
+
+ return simd::PackAndSaturate32ToU16(simd::FastDivideBy255(result1),
+ simd::FastDivideBy255(result2));
+ }
+
+ default:
+ return simd::FromU16<u16x8_t>(0);
+
+ }
+}
+
+template<typename i32x4_t, typename u16x8_t, typename u8x16_t, uint32_t op>
+static void
+ApplyComposition(DataSourceSurface* aSource, DataSourceSurface* aDest)
+{
+ IntSize size = aDest->GetSize();
+
+ uint8_t* sourceData = aSource->GetData();
+ uint8_t* destData = aDest->GetData();
+ uint32_t sourceStride = aSource->Stride();
+ uint32_t destStride = aDest->Stride();
+
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 4) {
+ uint32_t sourceIndex = y * sourceStride + 4 * x;
+ uint32_t destIndex = y * destStride + 4 * x;
+
+ u8x16_t s1234 = simd::Load8<u8x16_t>(&sourceData[sourceIndex]);
+ u8x16_t d1234 = simd::Load8<u8x16_t>(&destData[destIndex]);
+
+ u16x8_t s12 = simd::UnpackLo8x8ToU16x8(s1234);
+ u16x8_t d12 = simd::UnpackLo8x8ToU16x8(d1234);
+ u16x8_t sa12 = simd::Splat16<3,3>(s12);
+ u16x8_t da12 = simd::Splat16<3,3>(d12);
+ u16x8_t result12 = CompositeTwoPixels<i32x4_t,u16x8_t,op>(s12, sa12, d12, da12);
+
+ u16x8_t s34 = simd::UnpackHi8x8ToU16x8(s1234);
+ u16x8_t d34 = simd::UnpackHi8x8ToU16x8(d1234);
+ u16x8_t sa34 = simd::Splat16<3,3>(s34);
+ u16x8_t da34 = simd::Splat16<3,3>(d34);
+ u16x8_t result34 = CompositeTwoPixels<i32x4_t,u16x8_t,op>(s34, sa34, d34, da34);
+
+ u8x16_t result1234 = simd::PackAndSaturate16To8(result12, result34);
+ simd::Store8(&destData[destIndex], result1234);
+ }
+ }
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t>
+static void
+ApplyComposition_SIMD(DataSourceSurface* aSource, DataSourceSurface* aDest,
+ CompositeOperator aOperator)
+{
+ switch (aOperator) {
+ case COMPOSITE_OPERATOR_OVER:
+ ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_OVER>(aSource, aDest);
+ break;
+ case COMPOSITE_OPERATOR_IN:
+ ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_IN>(aSource, aDest);
+ break;
+ case COMPOSITE_OPERATOR_OUT:
+ ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_OUT>(aSource, aDest);
+ break;
+ case COMPOSITE_OPERATOR_ATOP:
+ ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_ATOP>(aSource, aDest);
+ break;
+ case COMPOSITE_OPERATOR_XOR:
+ ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_XOR>(aSource, aDest);
+ break;
+ default:
+ MOZ_CRASH();
+ }
+}
+
+template<typename u8x16_t>
+static void
+SeparateColorChannels_SIMD(const IntSize &size, uint8_t* sourceData, int32_t sourceStride,
+ uint8_t* channel0Data, uint8_t* channel1Data,
+ uint8_t* channel2Data, uint8_t* channel3Data,
+ int32_t channelStride)
+{
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 16) {
+ // Process 16 pixels at a time.
+ int32_t sourceIndex = y * sourceStride + 4 * x;
+ int32_t targetIndex = y * channelStride + x;
+
+ u8x16_t bgrabgrabgrabgra1 = simd::FromZero8<u8x16_t>();
+ u8x16_t bgrabgrabgrabgra2 = simd::FromZero8<u8x16_t>();
+ u8x16_t bgrabgrabgrabgra3 = simd::FromZero8<u8x16_t>();
+ u8x16_t bgrabgrabgrabgra4 = simd::FromZero8<u8x16_t>();
+
+ bgrabgrabgrabgra1 = simd::Load8<u8x16_t>(&sourceData[sourceIndex]);
+ if (4 * (x + 4) < sourceStride) {
+ bgrabgrabgrabgra2 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 4]);
+ }
+ if (4 * (x + 8) < sourceStride) {
+ bgrabgrabgrabgra3 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 8]);
+ }
+ if (4 * (x + 12) < sourceStride) {
+ bgrabgrabgrabgra4 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 12]);
+ }
+
+ u8x16_t bbggrraabbggrraa1 = simd::InterleaveLo8(bgrabgrabgrabgra1, bgrabgrabgrabgra3);
+ u8x16_t bbggrraabbggrraa2 = simd::InterleaveHi8(bgrabgrabgrabgra1, bgrabgrabgrabgra3);
+ u8x16_t bbggrraabbggrraa3 = simd::InterleaveLo8(bgrabgrabgrabgra2, bgrabgrabgrabgra4);
+ u8x16_t bbggrraabbggrraa4 = simd::InterleaveHi8(bgrabgrabgrabgra2, bgrabgrabgrabgra4);
+ u8x16_t bbbbggggrrrraaaa1 = simd::InterleaveLo8(bbggrraabbggrraa1, bbggrraabbggrraa3);
+ u8x16_t bbbbggggrrrraaaa2 = simd::InterleaveHi8(bbggrraabbggrraa1, bbggrraabbggrraa3);
+ u8x16_t bbbbggggrrrraaaa3 = simd::InterleaveLo8(bbggrraabbggrraa2, bbggrraabbggrraa4);
+ u8x16_t bbbbggggrrrraaaa4 = simd::InterleaveHi8(bbggrraabbggrraa2, bbggrraabbggrraa4);
+ u8x16_t bbbbbbbbgggggggg1 = simd::InterleaveLo8(bbbbggggrrrraaaa1, bbbbggggrrrraaaa3);
+ u8x16_t rrrrrrrraaaaaaaa1 = simd::InterleaveHi8(bbbbggggrrrraaaa1, bbbbggggrrrraaaa3);
+ u8x16_t bbbbbbbbgggggggg2 = simd::InterleaveLo8(bbbbggggrrrraaaa2, bbbbggggrrrraaaa4);
+ u8x16_t rrrrrrrraaaaaaaa2 = simd::InterleaveHi8(bbbbggggrrrraaaa2, bbbbggggrrrraaaa4);
+ u8x16_t bbbbbbbbbbbbbbbb = simd::InterleaveLo8(bbbbbbbbgggggggg1, bbbbbbbbgggggggg2);
+ u8x16_t gggggggggggggggg = simd::InterleaveHi8(bbbbbbbbgggggggg1, bbbbbbbbgggggggg2);
+ u8x16_t rrrrrrrrrrrrrrrr = simd::InterleaveLo8(rrrrrrrraaaaaaaa1, rrrrrrrraaaaaaaa2);
+ u8x16_t aaaaaaaaaaaaaaaa = simd::InterleaveHi8(rrrrrrrraaaaaaaa1, rrrrrrrraaaaaaaa2);
+
+ simd::Store8(&channel0Data[targetIndex], bbbbbbbbbbbbbbbb);
+ simd::Store8(&channel1Data[targetIndex], gggggggggggggggg);
+ simd::Store8(&channel2Data[targetIndex], rrrrrrrrrrrrrrrr);
+ simd::Store8(&channel3Data[targetIndex], aaaaaaaaaaaaaaaa);
+ }
+ }
+}
+
+template<typename u8x16_t>
+static void
+CombineColorChannels_SIMD(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data)
+{
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 16) {
+ // Process 16 pixels at a time.
+ int32_t resultIndex = y * resultStride + 4 * x;
+ int32_t channelIndex = y * channelStride + x;
+
+ u8x16_t bbbbbbbbbbbbbbbb = simd::Load8<u8x16_t>(&channel0Data[channelIndex]);
+ u8x16_t gggggggggggggggg = simd::Load8<u8x16_t>(&channel1Data[channelIndex]);
+ u8x16_t rrrrrrrrrrrrrrrr = simd::Load8<u8x16_t>(&channel2Data[channelIndex]);
+ u8x16_t aaaaaaaaaaaaaaaa = simd::Load8<u8x16_t>(&channel3Data[channelIndex]);
+
+ u8x16_t brbrbrbrbrbrbrbr1 = simd::InterleaveLo8(bbbbbbbbbbbbbbbb, rrrrrrrrrrrrrrrr);
+ u8x16_t brbrbrbrbrbrbrbr2 = simd::InterleaveHi8(bbbbbbbbbbbbbbbb, rrrrrrrrrrrrrrrr);
+ u8x16_t gagagagagagagaga1 = simd::InterleaveLo8(gggggggggggggggg, aaaaaaaaaaaaaaaa);
+ u8x16_t gagagagagagagaga2 = simd::InterleaveHi8(gggggggggggggggg, aaaaaaaaaaaaaaaa);
+
+ u8x16_t bgrabgrabgrabgra1 = simd::InterleaveLo8(brbrbrbrbrbrbrbr1, gagagagagagagaga1);
+ u8x16_t bgrabgrabgrabgra2 = simd::InterleaveHi8(brbrbrbrbrbrbrbr1, gagagagagagagaga1);
+ u8x16_t bgrabgrabgrabgra3 = simd::InterleaveLo8(brbrbrbrbrbrbrbr2, gagagagagagagaga2);
+ u8x16_t bgrabgrabgrabgra4 = simd::InterleaveHi8(brbrbrbrbrbrbrbr2, gagagagagagagaga2);
+
+ simd::Store8(&resultData[resultIndex], bgrabgrabgrabgra1);
+ if (4 * (x + 4) < resultStride) {
+ simd::Store8(&resultData[resultIndex + 4 * 4], bgrabgrabgrabgra2);
+ }
+ if (4 * (x + 8) < resultStride) {
+ simd::Store8(&resultData[resultIndex + 8 * 4], bgrabgrabgrabgra3);
+ }
+ if (4 * (x + 12) < resultStride) {
+ simd::Store8(&resultData[resultIndex + 12 * 4], bgrabgrabgrabgra4);
+ }
+ }
+ }
+}
+
+
+template<typename i32x4_t, typename u16x8_t, typename u8x16_t>
+static void
+DoPremultiplicationCalculation_SIMD(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ const u8x16_t alphaMask = simd::From8<u8x16_t>(0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff);
+ for (int32_t y = 0; y < aSize.height; y++) {
+ for (int32_t x = 0; x < aSize.width; x += 4) {
+ int32_t inputIndex = y * aSourceStride + 4 * x;
+ int32_t targetIndex = y * aTargetStride + 4 * x;
+
+ u8x16_t p1234 = simd::Load8<u8x16_t>(&aSourceData[inputIndex]);
+ u16x8_t p12 = simd::UnpackLo8x8ToU16x8(p1234);
+ u16x8_t p34 = simd::UnpackHi8x8ToU16x8(p1234);
+
+ // Multiply all components with alpha.
+ p12 = simd::Mul16(p12, simd::Splat16<3,3>(p12));
+ p34 = simd::Mul16(p34, simd::Splat16<3,3>(p34));
+
+ // Divide by 255 and pack.
+ u8x16_t result = simd::PackAndSaturate16To8(simd::FastDivideBy255_16(p12),
+ simd::FastDivideBy255_16(p34));
+
+ // Get the original alpha channel value back from p1234.
+ result = simd::Pick(alphaMask, result, p1234);
+
+ simd::Store8(&aTargetData[targetIndex], result);
+ }
+ }
+}
+
+// We use a table of precomputed factors for unpremultiplying.
+// We want to compute round(r / (alpha / 255.0f)) for arbitrary values of
+// r and alpha in constant time. This table of factors has the property that
+// (r * sAlphaFactors[alpha] + 128) >> 8 roughly gives the result we want (with
+// a maximum deviation of 1).
+//
+// sAlphaFactors[alpha] == round(255.0 * (1 << 8) / alpha)
+//
+// This table has been created using the python code
+// ", ".join("%d" % (round(255.0 * 256 / alpha) if alpha > 0 else 0) for alpha in range(256))
+static const uint16_t sAlphaFactors[256] = {
+ 0, 65280, 32640, 21760, 16320, 13056, 10880, 9326, 8160, 7253, 6528, 5935,
+ 5440, 5022, 4663, 4352, 4080, 3840, 3627, 3436, 3264, 3109, 2967, 2838, 2720,
+ 2611, 2511, 2418, 2331, 2251, 2176, 2106, 2040, 1978, 1920, 1865, 1813, 1764,
+ 1718, 1674, 1632, 1592, 1554, 1518, 1484, 1451, 1419, 1389, 1360, 1332, 1306,
+ 1280, 1255, 1232, 1209, 1187, 1166, 1145, 1126, 1106, 1088, 1070, 1053, 1036,
+ 1020, 1004, 989, 974, 960, 946, 933, 919, 907, 894, 882, 870, 859, 848, 837,
+ 826, 816, 806, 796, 787, 777, 768, 759, 750, 742, 733, 725, 717, 710, 702,
+ 694, 687, 680, 673, 666, 659, 653, 646, 640, 634, 628, 622, 616, 610, 604,
+ 599, 593, 588, 583, 578, 573, 568, 563, 558, 553, 549, 544, 540, 535, 531,
+ 526, 522, 518, 514, 510, 506, 502, 498, 495, 491, 487, 484, 480, 476, 473,
+ 470, 466, 463, 460, 457, 453, 450, 447, 444, 441, 438, 435, 432, 429, 427,
+ 424, 421, 418, 416, 413, 411, 408, 405, 403, 400, 398, 396, 393, 391, 389,
+ 386, 384, 382, 380, 377, 375, 373, 371, 369, 367, 365, 363, 361, 359, 357,
+ 355, 353, 351, 349, 347, 345, 344, 342, 340, 338, 336, 335, 333, 331, 330,
+ 328, 326, 325, 323, 322, 320, 318, 317, 315, 314, 312, 311, 309, 308, 306,
+ 305, 304, 302, 301, 299, 298, 297, 295, 294, 293, 291, 290, 289, 288, 286,
+ 285, 284, 283, 281, 280, 279, 278, 277, 275, 274, 273, 272, 271, 270, 269,
+ 268, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256
+};
+
+template<typename u16x8_t, typename u8x16_t>
+static void
+DoUnpremultiplicationCalculation_SIMD(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ for (int32_t y = 0; y < aSize.height; y++) {
+ for (int32_t x = 0; x < aSize.width; x += 4) {
+ int32_t inputIndex = y * aSourceStride + 4 * x;
+ int32_t targetIndex = y * aTargetStride + 4 * x;
+ union {
+ u8x16_t p1234;
+ uint8_t u8[4][4];
+ };
+ p1234 = simd::Load8<u8x16_t>(&aSourceData[inputIndex]);
+
+ // Prepare the alpha factors.
+ uint16_t aF1 = sAlphaFactors[u8[0][B8G8R8A8_COMPONENT_BYTEOFFSET_A]];
+ uint16_t aF2 = sAlphaFactors[u8[1][B8G8R8A8_COMPONENT_BYTEOFFSET_A]];
+ uint16_t aF3 = sAlphaFactors[u8[2][B8G8R8A8_COMPONENT_BYTEOFFSET_A]];
+ uint16_t aF4 = sAlphaFactors[u8[3][B8G8R8A8_COMPONENT_BYTEOFFSET_A]];
+ u16x8_t aF12 = simd::FromU16<u16x8_t>(aF1, aF1, aF1, 1 << 8, aF2, aF2, aF2, 1 << 8);
+ u16x8_t aF34 = simd::FromU16<u16x8_t>(aF3, aF3, aF3, 1 << 8, aF4, aF4, aF4, 1 << 8);
+
+ u16x8_t p12 = simd::UnpackLo8x8ToU16x8(p1234);
+ u16x8_t p34 = simd::UnpackHi8x8ToU16x8(p1234);
+
+ // Multiply with the alpha factors, add 128 for rounding, and shift right by 8 bits.
+ p12 = simd::ShiftRight16<8>(simd::Add16(simd::Mul16(p12, aF12), simd::FromU16<u16x8_t>(128)));
+ p34 = simd::ShiftRight16<8>(simd::Add16(simd::Mul16(p34, aF34), simd::FromU16<u16x8_t>(128)));
+
+ u8x16_t result = simd::PackAndSaturate16To8(p12, p34);
+ simd::Store8(&aTargetData[targetIndex], result);
+ }
+ }
+}
+
+template<typename f32x4_t, typename i32x4_t, typename u8x16_t>
+static TemporaryRef<DataSourceSurface>
+RenderTurbulence_SIMD(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect)
+{
+#define RETURN_TURBULENCE(Type, Stitch) \
+ SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t> \
+ renderer(aBaseFrequency, aSeed, aNumOctaves, aTileRect); \
+ return renderer.Render(aSize, aOffset);
+
+ switch (aType) {
+ case TURBULENCE_TYPE_TURBULENCE:
+ {
+ if (aStitch) {
+ RETURN_TURBULENCE(TURBULENCE_TYPE_TURBULENCE, true);
+ }
+ RETURN_TURBULENCE(TURBULENCE_TYPE_TURBULENCE, false);
+ }
+ case TURBULENCE_TYPE_FRACTAL_NOISE:
+ {
+ if (aStitch) {
+ RETURN_TURBULENCE(TURBULENCE_TYPE_FRACTAL_NOISE, true);
+ }
+ RETURN_TURBULENCE(TURBULENCE_TYPE_FRACTAL_NOISE, false);
+ }
+ }
+ return nullptr;
+#undef RETURN_TURBULENCE
+}
+
+// k1 * in1 * in2 + k2 * in1 + k3 * in2 + k4
+template<typename i32x4_t, typename i16x8_t>
+static MOZ_ALWAYS_INLINE i16x8_t
+ArithmeticCombineTwoPixels(i16x8_t in1, i16x8_t in2,
+ const i16x8_t &k1And4, const i16x8_t &k2And3)
+{
+ // Calculate input product: inProd = (in1 * in2) / 255.
+ i32x4_t inProd_1, inProd_2;
+ simd::Mul16x4x2x2To32x4x2(in1, in2, inProd_1, inProd_2);
+ i16x8_t inProd = simd::PackAndSaturate32To16(simd::FastDivideBy255(inProd_1), simd::FastDivideBy255(inProd_2));
+
+ // Calculate k1 * ((in1 * in2) / 255) + (k4/128) * 128
+ i16x8_t oneTwentyEight = simd::FromI16<i16x8_t>(128);
+ i16x8_t inProd1AndOneTwentyEight = simd::InterleaveLo16(inProd, oneTwentyEight);
+ i16x8_t inProd2AndOneTwentyEight = simd::InterleaveHi16(inProd, oneTwentyEight);
+ i32x4_t inProdTimesK1PlusK4_1 = simd::MulAdd16x8x2To32x4(k1And4, inProd1AndOneTwentyEight);
+ i32x4_t inProdTimesK1PlusK4_2 = simd::MulAdd16x8x2To32x4(k1And4, inProd2AndOneTwentyEight);
+
+ // Calculate k2 * in1 + k3 * in2
+ i16x8_t in12_1 = simd::InterleaveLo16(in1, in2);
+ i16x8_t in12_2 = simd::InterleaveHi16(in1, in2);
+ i32x4_t inTimesK2K3_1 = simd::MulAdd16x8x2To32x4(k2And3, in12_1);
+ i32x4_t inTimesK2K3_2 = simd::MulAdd16x8x2To32x4(k2And3, in12_2);
+
+ // Sum everything up and truncate the fractional part.
+ i32x4_t result_1 = simd::ShiftRight32<7>(simd::Add32(inProdTimesK1PlusK4_1, inTimesK2K3_1));
+ i32x4_t result_2 = simd::ShiftRight32<7>(simd::Add32(inProdTimesK1PlusK4_2, inTimesK2K3_2));
+ return simd::PackAndSaturate32To16(result_1, result_2);
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t>
+static TemporaryRef<DataSourceSurface>
+ApplyArithmeticCombine_SIMD(DataSourceSurface* aInput1, DataSourceSurface* aInput2,
+ Float aK1, Float aK2, Float aK3, Float aK4)
+{
+ IntSize size = aInput1->GetSize();
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (!target) {
+ return nullptr;
+ }
+
+ uint8_t* source1Data = aInput1->GetData();
+ uint8_t* source2Data = aInput2->GetData();
+ uint8_t* targetData = target->GetData();
+ uint32_t source1Stride = aInput1->Stride();
+ uint32_t source2Stride = aInput2->Stride();
+ uint32_t targetStride = target->Stride();
+
+ // The arithmetic combine filter does the following calculation:
+ // result = k1 * in1 * in2 + k2 * in1 + k3 * in2 + k4
+ //
+ // Or, with in1/2 integers between 0 and 255:
+ // result = (k1 * in1 * in2) / 255 + k2 * in1 + k3 * in2 + k4 * 255
+ //
+ // We want the whole calculation to happen in integer, with 16-bit factors.
+ // So we convert our factors to fixed-point with precision 1.8.7.
+ // K4 is premultiplied with 255, and it will be multiplied with 128 later
+ // during the actual calculation, because premultiplying it with 255 * 128
+ // would overflow int16.
+
+ i16x8_t k1 = simd::FromI16<i16x8_t>(int16_t(floorf(std::min(std::max(aK1, -255.0f), 255.0f) * 128 + 0.5f)));
+ i16x8_t k2 = simd::FromI16<i16x8_t>(int16_t(floorf(std::min(std::max(aK2, -255.0f), 255.0f) * 128 + 0.5f)));
+ i16x8_t k3 = simd::FromI16<i16x8_t>(int16_t(floorf(std::min(std::max(aK3, -255.0f), 255.0f) * 128 + 0.5f)));
+ i16x8_t k4 = simd::FromI16<i16x8_t>(int16_t(floorf(std::min(std::max(aK4, -128.0f), 128.0f) * 255 + 0.5f)));
+
+ i16x8_t k1And4 = simd::InterleaveLo16(k1, k4);
+ i16x8_t k2And3 = simd::InterleaveLo16(k2, k3);
+
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x += 4) {
+ uint32_t source1Index = y * source1Stride + 4 * x;
+ uint32_t source2Index = y * source2Stride + 4 * x;
+ uint32_t targetIndex = y * targetStride + 4 * x;
+
+ // Load and unpack.
+ u8x16_t in1 = simd::Load8<u8x16_t>(&source1Data[source1Index]);
+ u8x16_t in2 = simd::Load8<u8x16_t>(&source2Data[source2Index]);
+ i16x8_t in1_12 = simd::UnpackLo8x8ToI16x8(in1);
+ i16x8_t in1_34 = simd::UnpackHi8x8ToI16x8(in1);
+ i16x8_t in2_12 = simd::UnpackLo8x8ToI16x8(in2);
+ i16x8_t in2_34 = simd::UnpackHi8x8ToI16x8(in2);
+
+ // Multiply and add.
+ i16x8_t result_12 = ArithmeticCombineTwoPixels<i32x4_t,i16x8_t>(in1_12, in2_12, k1And4, k2And3);
+ i16x8_t result_34 = ArithmeticCombineTwoPixels<i32x4_t,i16x8_t>(in1_34, in2_34, k1And4, k2And3);
+
+ // Pack and store.
+ simd::Store8(&targetData[targetIndex], simd::PackAndSaturate16To8(result_12, result_34));
+ }
+ }
+
+ return target;
+}
+
+} // namespace mozilla
+} // namespace gfx
diff --git a/gfx/2d/FilterProcessingSSE2.cpp b/gfx/2d/FilterProcessingSSE2.cpp
new file mode 100644
index 000000000..ca1d2dbef
--- /dev/null
+++ b/gfx/2d/FilterProcessingSSE2.cpp
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#define SIMD_COMPILE_SSE2
+
+#include "FilterProcessingSIMD-inl.h"
+
+#ifndef USE_SSE2
+static_assert(false, "If this file is built, FilterProcessing.h should know about it!");
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+void
+FilterProcessing::ExtractAlpha_SSE2(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride)
+{
+ ExtractAlpha_SIMD<__m128i>(size, sourceData, sourceStride, alphaData, alphaStride);
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::ConvertToB8G8R8A8_SSE2(SourceSurface* aSurface)
+{
+ return ConvertToB8G8R8A8_SIMD<__m128i>(aSurface);
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::ApplyBlending_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2,
+ BlendMode aBlendMode)
+{
+ return ApplyBlending_SIMD<__m128i,__m128i,__m128i>(aInput1, aInput2, aBlendMode);
+}
+
+void
+FilterProcessing::ApplyMorphologyHorizontal_SSE2(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ ApplyMorphologyHorizontal_SIMD<__m128i,__m128i>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
+}
+
+void
+FilterProcessing::ApplyMorphologyVertical_SSE2(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ ApplyMorphologyVertical_SIMD<__m128i,__m128i>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::ApplyColorMatrix_SSE2(DataSourceSurface* aInput, const Matrix5x4 &aMatrix)
+{
+ return ApplyColorMatrix_SIMD<__m128i,__m128i,__m128i>(aInput, aMatrix);
+}
+
+void
+FilterProcessing::ApplyComposition_SSE2(DataSourceSurface* aSource, DataSourceSurface* aDest,
+ CompositeOperator aOperator)
+{
+ return ApplyComposition_SIMD<__m128i,__m128i,__m128i>(aSource, aDest, aOperator);
+}
+
+void
+FilterProcessing::SeparateColorChannels_SSE2(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride)
+{
+ SeparateColorChannels_SIMD<__m128i>(size, sourceData, sourceStride, channel0Data, channel1Data, channel2Data, channel3Data, channelStride);
+}
+
+void
+FilterProcessing::CombineColorChannels_SSE2(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data)
+{
+ CombineColorChannels_SIMD<__m128i>(size, resultStride, resultData, channelStride, channel0Data, channel1Data, channel2Data, channel3Data);
+}
+
+void
+FilterProcessing::DoPremultiplicationCalculation_SSE2(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ DoPremultiplicationCalculation_SIMD<__m128i,__m128i,__m128i>(aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
+}
+
+void
+FilterProcessing::DoUnpremultiplicationCalculation_SSE2(
+ const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ DoUnpremultiplicationCalculation_SIMD<__m128i,__m128i>(aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::RenderTurbulence_SSE2(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect)
+{
+ return RenderTurbulence_SIMD<__m128,__m128i,__m128i>(aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect);
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::ApplyArithmeticCombine_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4)
+{
+ return ApplyArithmeticCombine_SIMD<__m128i,__m128i,__m128i>(aInput1, aInput2, aK1, aK2, aK3, aK4);
+}
+
+} // namespace mozilla
+} // namespace gfx
diff --git a/gfx/2d/FilterProcessingScalar.cpp b/gfx/2d/FilterProcessingScalar.cpp
new file mode 100644
index 000000000..3a38118ee
--- /dev/null
+++ b/gfx/2d/FilterProcessingScalar.cpp
@@ -0,0 +1,244 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#define FILTER_PROCESSING_SCALAR
+
+#include "FilterProcessingSIMD-inl.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+void
+FilterProcessing::ExtractAlpha_Scalar(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride)
+{
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t sourceIndex = y * sourceStride + 4 * x;
+ int32_t targetIndex = y * alphaStride + x;
+ alphaData[targetIndex] = sourceData[sourceIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
+ }
+ }
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::ConvertToB8G8R8A8_Scalar(SourceSurface* aSurface)
+{
+ return ConvertToB8G8R8A8_SIMD<simd::Scalaru8x16_t>(aSurface);
+}
+
+template<MorphologyOperator Operator>
+static void
+ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius)
+{
+ static_assert(Operator == MORPHOLOGY_OPERATOR_ERODE ||
+ Operator == MORPHOLOGY_OPERATOR_DILATE,
+ "unexpected morphology operator");
+
+ for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++) {
+ int32_t startX = aDestRect.x - aRadius;
+ int32_t endX = aDestRect.x + aRadius;
+ for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x++, startX++, endX++) {
+ int32_t sourceIndex = y * aSourceStride + 4 * startX;
+ uint8_t u[4];
+ for (size_t i = 0; i < 4; i++) {
+ u[i] = aSourceData[sourceIndex + i];
+ }
+ sourceIndex += 4;
+ for (int32_t ix = startX + 1; ix <= endX; ix++, sourceIndex += 4) {
+ for (size_t i = 0; i < 4; i++) {
+ if (Operator == MORPHOLOGY_OPERATOR_ERODE) {
+ u[i] = umin(u[i], aSourceData[sourceIndex + i]);
+ } else {
+ u[i] = umax(u[i], aSourceData[sourceIndex + i]);
+ }
+ }
+ }
+
+ int32_t destIndex = y * aDestStride + 4 * x;
+ for (size_t i = 0; i < 4; i++) {
+ aDestData[destIndex+i] = u[i];
+ }
+ }
+ }
+}
+
+void
+FilterProcessing::ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ if (aOp == MORPHOLOGY_OPERATOR_ERODE) {
+ gfx::ApplyMorphologyHorizontal_Scalar<MORPHOLOGY_OPERATOR_ERODE>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ } else {
+ gfx::ApplyMorphologyHorizontal_Scalar<MORPHOLOGY_OPERATOR_DILATE>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ }
+}
+
+template<MorphologyOperator Operator>
+static void ApplyMorphologyVertical_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius)
+{
+ static_assert(Operator == MORPHOLOGY_OPERATOR_ERODE ||
+ Operator == MORPHOLOGY_OPERATOR_DILATE,
+ "unexpected morphology operator");
+
+ int32_t startY = aDestRect.y - aRadius;
+ int32_t endY = aDestRect.y + aRadius;
+ for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++, startY++, endY++) {
+ for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x++) {
+ int32_t sourceIndex = startY * aSourceStride + 4 * x;
+ uint8_t u[4];
+ for (size_t i = 0; i < 4; i++) {
+ u[i] = aSourceData[sourceIndex + i];
+ }
+ sourceIndex += aSourceStride;
+ for (int32_t iy = startY + 1; iy <= endY; iy++, sourceIndex += aSourceStride) {
+ for (size_t i = 0; i < 4; i++) {
+ if (Operator == MORPHOLOGY_OPERATOR_ERODE) {
+ u[i] = umin(u[i], aSourceData[sourceIndex + i]);
+ } else {
+ u[i] = umax(u[i], aSourceData[sourceIndex + i]);
+ }
+ }
+ }
+
+ int32_t destIndex = y * aDestStride + 4 * x;
+ for (size_t i = 0; i < 4; i++) {
+ aDestData[destIndex+i] = u[i];
+ }
+ }
+ }
+}
+
+void
+FilterProcessing::ApplyMorphologyVertical_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
+ uint8_t* aDestData, int32_t aDestStride,
+ const IntRect& aDestRect, int32_t aRadius,
+ MorphologyOperator aOp)
+{
+ if (aOp == MORPHOLOGY_OPERATOR_ERODE) {
+ gfx::ApplyMorphologyVertical_Scalar<MORPHOLOGY_OPERATOR_ERODE>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ } else {
+ gfx::ApplyMorphologyVertical_Scalar<MORPHOLOGY_OPERATOR_DILATE>(
+ aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
+ }
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::ApplyColorMatrix_Scalar(DataSourceSurface* aInput, const Matrix5x4 &aMatrix)
+{
+ return ApplyColorMatrix_SIMD<simd::Scalari32x4_t,simd::Scalari16x8_t,simd::Scalaru8x16_t>(aInput, aMatrix);
+}
+
+void
+FilterProcessing::ApplyComposition_Scalar(DataSourceSurface* aSource, DataSourceSurface* aDest,
+ CompositeOperator aOperator)
+{
+ return ApplyComposition_SIMD<simd::Scalari32x4_t,simd::Scalaru16x8_t,simd::Scalaru8x16_t>(aSource, aDest, aOperator);
+}
+
+void
+FilterProcessing::SeparateColorChannels_Scalar(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride)
+{
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t sourceIndex = y * sourceStride + 4 * x;
+ int32_t targetIndex = y * channelStride + x;
+ channel0Data[targetIndex] = sourceData[sourceIndex];
+ channel1Data[targetIndex] = sourceData[sourceIndex+1];
+ channel2Data[targetIndex] = sourceData[sourceIndex+2];
+ channel3Data[targetIndex] = sourceData[sourceIndex+3];
+ }
+ }
+}
+
+void
+FilterProcessing::CombineColorChannels_Scalar(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data)
+{
+ for (int32_t y = 0; y < size.height; y++) {
+ for (int32_t x = 0; x < size.width; x++) {
+ int32_t resultIndex = y * resultStride + 4 * x;
+ int32_t channelIndex = y * channelStride + x;
+ resultData[resultIndex] = channel0Data[channelIndex];
+ resultData[resultIndex+1] = channel1Data[channelIndex];
+ resultData[resultIndex+2] = channel2Data[channelIndex];
+ resultData[resultIndex+3] = channel3Data[channelIndex];
+ }
+ }
+}
+
+void
+FilterProcessing::DoPremultiplicationCalculation_Scalar(const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ for (int32_t y = 0; y < aSize.height; y++) {
+ for (int32_t x = 0; x < aSize.width; x++) {
+ int32_t inputIndex = y * aSourceStride + 4 * x;
+ int32_t targetIndex = y * aTargetStride + 4 * x;
+ uint8_t alpha = aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
+ FastDivideBy255<uint8_t>(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] * alpha);
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
+ FastDivideBy255<uint8_t>(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] * alpha);
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
+ FastDivideBy255<uint8_t>(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] * alpha);
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A] = alpha;
+ }
+ }
+}
+
+void
+FilterProcessing::DoUnpremultiplicationCalculation_Scalar(
+ const IntSize& aSize,
+ uint8_t* aTargetData, int32_t aTargetStride,
+ uint8_t* aSourceData, int32_t aSourceStride)
+{
+ for (int32_t y = 0; y < aSize.height; y++) {
+ for (int32_t x = 0; x < aSize.width; x++) {
+ int32_t inputIndex = y * aSourceStride + 4 * x;
+ int32_t targetIndex = y * aTargetStride + 4 * x;
+ uint8_t alpha = aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
+ uint16_t alphaFactor = sAlphaFactors[alpha];
+ // inputColor * alphaFactor + 128 is guaranteed to fit into uint16_t
+ // because the input is premultiplied and thus inputColor <= inputAlpha.
+ // The maximum value this can attain is 65520 (which is less than 65535)
+ // for color == alpha == 244:
+ // 244 * sAlphaFactors[244] + 128 == 244 * 268 + 128 == 65520
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
+ (aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] * alphaFactor + 128) >> 8;
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
+ (aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] * alphaFactor + 128) >> 8;
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
+ (aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] * alphaFactor + 128) >> 8;
+ aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A] = alpha;
+ }
+ }
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::RenderTurbulence_Scalar(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect)
+{
+ return RenderTurbulence_SIMD<simd::Scalarf32x4_t,simd::Scalari32x4_t,simd::Scalaru8x16_t>(
+ aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect);
+}
+
+TemporaryRef<DataSourceSurface>
+FilterProcessing::ApplyArithmeticCombine_Scalar(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4)
+{
+ return ApplyArithmeticCombine_SIMD<simd::Scalari32x4_t,simd::Scalari16x8_t,simd::Scalaru8x16_t>(aInput1, aInput2, aK1, aK2, aK3, aK4);
+}
+
+} // namespace mozilla
+} // namespace gfx
diff --git a/gfx/2d/Filters.h b/gfx/2d/Filters.h
new file mode 100644
index 000000000..31c3e8394
--- /dev/null
+++ b/gfx/2d/Filters.h
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_FILTERS_H_
+#define MOZILLA_GFX_FILTERS_H_
+
+#include "Types.h"
+#include "mozilla/RefPtr.h"
+
+#include "Point.h"
+#include "Matrix.h"
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class SourceSurface;
+
+enum FilterBackend {
+ FILTER_BACKEND_SOFTWARE = 0,
+ FILTER_BACKEND_DIRECT2D1_1,
+ FILTER_BACKEND_RECORDING
+};
+
+enum TransformFilterAtts
+{
+ ATT_TRANSFORM_MATRIX = 0, // Matrix
+ ATT_TRANSFORM_FILTER // Filter
+};
+
+enum TransformFilterInputs
+{
+ IN_TRANSFORM_IN = 0
+};
+
+enum BlendFilterAtts
+{
+ ATT_BLEND_BLENDMODE = 0 // uint32_t
+};
+
+enum BlendMode
+{
+ BLEND_MODE_MULTIPLY = 0,
+ BLEND_MODE_SCREEN,
+ BLEND_MODE_DARKEN,
+ BLEND_MODE_LIGHTEN,
+ BLEND_MODE_OVERLAY,
+ BLEND_MODE_COLOR_DODGE,
+ BLEND_MODE_COLOR_BURN,
+ BLEND_MODE_HARD_LIGHT,
+ BLEND_MODE_SOFT_LIGHT,
+ BLEND_MODE_DIFFERENCE,
+ BLEND_MODE_EXCLUSION,
+ BLEND_MODE_HUE,
+ BLEND_MODE_SATURATION,
+ BLEND_MODE_COLOR,
+ BLEND_MODE_LUMINOSITY
+};
+
+enum BlendFilterInputs
+{
+ IN_BLEND_IN = 0,
+ IN_BLEND_IN2
+};
+
+enum MorphologyFilterAtts
+{
+ ATT_MORPHOLOGY_RADII = 0, // IntSize
+ ATT_MORPHOLOGY_OPERATOR // MorphologyOperator
+};
+
+enum MorphologyOperator
+{
+ MORPHOLOGY_OPERATOR_ERODE = 0,
+ MORPHOLOGY_OPERATOR_DILATE
+};
+
+enum MorphologyFilterInputs
+{
+ IN_MORPHOLOGY_IN = 0
+};
+
+enum AlphaMode
+{
+ ALPHA_MODE_PREMULTIPLIED = 0,
+ ALPHA_MODE_STRAIGHT
+};
+
+enum ColorMatrixFilterAtts
+{
+ ATT_COLOR_MATRIX_MATRIX = 0, // Matrix5x4
+ ATT_COLOR_MATRIX_ALPHA_MODE // AlphaMode
+};
+
+enum ColorMatrixFilterInputs
+{
+ IN_COLOR_MATRIX_IN = 0
+};
+
+enum FloodFilterAtts
+{
+ ATT_FLOOD_COLOR = 0 // Color
+};
+
+enum FloodFilterInputs
+{
+ IN_FLOOD_IN = 0
+};
+
+enum TileFilterAtts
+{
+ ATT_TILE_SOURCE_RECT = 0 // IntRect
+};
+
+enum TileFilterInputs
+{
+ IN_TILE_IN = 0
+};
+
+enum TransferAtts
+{
+ ATT_TRANSFER_DISABLE_R = 0, // bool
+ ATT_TRANSFER_DISABLE_G, // bool
+ ATT_TRANSFER_DISABLE_B, // bool
+ ATT_TRANSFER_DISABLE_A // bool
+};
+
+enum TransferInputs
+{
+ IN_TRANSFER_IN = 0
+};
+
+enum TableTransferAtts
+{
+ ATT_TABLE_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R,
+ ATT_TABLE_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G,
+ ATT_TABLE_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B,
+ ATT_TABLE_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A,
+ ATT_TABLE_TRANSFER_TABLE_R, // Float[]
+ ATT_TABLE_TRANSFER_TABLE_G, // Float[]
+ ATT_TABLE_TRANSFER_TABLE_B, // Float[]
+ ATT_TABLE_TRANSFER_TABLE_A // Float[]
+};
+
+enum TableTransferInputs
+{
+ IN_TABLE_TRANSFER_IN = IN_TRANSFER_IN
+};
+
+enum DiscreteTransferAtts
+{
+ ATT_DISCRETE_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R,
+ ATT_DISCRETE_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G,
+ ATT_DISCRETE_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B,
+ ATT_DISCRETE_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A,
+ ATT_DISCRETE_TRANSFER_TABLE_R, // Float[]
+ ATT_DISCRETE_TRANSFER_TABLE_G, // Float[]
+ ATT_DISCRETE_TRANSFER_TABLE_B, // Float[]
+ ATT_DISCRETE_TRANSFER_TABLE_A // Float[]
+};
+
+enum DiscreteTransferInputs
+{
+ IN_DISCRETE_TRANSFER_IN = IN_TRANSFER_IN
+};
+
+enum LinearTransferAtts
+{
+ ATT_LINEAR_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R,
+ ATT_LINEAR_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G,
+ ATT_LINEAR_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B,
+ ATT_LINEAR_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A,
+ ATT_LINEAR_TRANSFER_SLOPE_R, // Float
+ ATT_LINEAR_TRANSFER_SLOPE_G, // Float
+ ATT_LINEAR_TRANSFER_SLOPE_B, // Float
+ ATT_LINEAR_TRANSFER_SLOPE_A, // Float
+ ATT_LINEAR_TRANSFER_INTERCEPT_R, // Float
+ ATT_LINEAR_TRANSFER_INTERCEPT_G, // Float
+ ATT_LINEAR_TRANSFER_INTERCEPT_B, // Float
+ ATT_LINEAR_TRANSFER_INTERCEPT_A // Float
+};
+
+enum LinearTransferInputs
+{
+ IN_LINEAR_TRANSFER_IN = IN_TRANSFER_IN
+};
+
+enum GammaTransferAtts
+{
+ ATT_GAMMA_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R,
+ ATT_GAMMA_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G,
+ ATT_GAMMA_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B,
+ ATT_GAMMA_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A,
+ ATT_GAMMA_TRANSFER_AMPLITUDE_R, // Float
+ ATT_GAMMA_TRANSFER_AMPLITUDE_G, // Float
+ ATT_GAMMA_TRANSFER_AMPLITUDE_B, // Float
+ ATT_GAMMA_TRANSFER_AMPLITUDE_A, // Float
+ ATT_GAMMA_TRANSFER_EXPONENT_R, // Float
+ ATT_GAMMA_TRANSFER_EXPONENT_G, // Float
+ ATT_GAMMA_TRANSFER_EXPONENT_B, // Float
+ ATT_GAMMA_TRANSFER_EXPONENT_A, // Float
+ ATT_GAMMA_TRANSFER_OFFSET_R, // Float
+ ATT_GAMMA_TRANSFER_OFFSET_G, // Float
+ ATT_GAMMA_TRANSFER_OFFSET_B, // Float
+ ATT_GAMMA_TRANSFER_OFFSET_A // Float
+};
+
+enum GammaTransferInputs
+{
+ IN_GAMMA_TRANSFER_IN = IN_TRANSFER_IN
+};
+
+enum ConvolveMatrixAtts
+{
+ ATT_CONVOLVE_MATRIX_KERNEL_SIZE = 0, // IntSize
+ ATT_CONVOLVE_MATRIX_KERNEL_MATRIX, // Float[]
+ ATT_CONVOLVE_MATRIX_DIVISOR, // Float
+ ATT_CONVOLVE_MATRIX_BIAS, // Float
+ ATT_CONVOLVE_MATRIX_TARGET, // IntPoint
+ ATT_CONVOLVE_MATRIX_SOURCE_RECT, // IntRect
+ ATT_CONVOLVE_MATRIX_EDGE_MODE, // ConvolveMatrixEdgeMode
+ ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH, // Size
+ ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA, // bool
+};
+
+enum ConvolveMatrixEdgeMode
+{
+ EDGE_MODE_DUPLICATE = 0,
+ EDGE_MODE_WRAP,
+ EDGE_MODE_NONE
+};
+
+enum ConvolveMatrixInputs
+{
+ IN_CONVOLVE_MATRIX_IN = 0
+};
+
+enum DisplacementMapAtts
+{
+ ATT_DISPLACEMENT_MAP_SCALE = 0, // Float
+ ATT_DISPLACEMENT_MAP_X_CHANNEL, // ColorChannel
+ ATT_DISPLACEMENT_MAP_Y_CHANNEL // ColorChannel
+};
+
+enum ColorChannel
+{
+ COLOR_CHANNEL_R = 0,
+ COLOR_CHANNEL_G,
+ COLOR_CHANNEL_B,
+ COLOR_CHANNEL_A
+};
+
+enum DisplacementMapInputs
+{
+ IN_DISPLACEMENT_MAP_IN = 0,
+ IN_DISPLACEMENT_MAP_IN2
+};
+
+enum TurbulenceAtts
+{
+ ATT_TURBULENCE_BASE_FREQUENCY = 0, // Size
+ ATT_TURBULENCE_NUM_OCTAVES, // uint32_t
+ ATT_TURBULENCE_SEED, // uint32_t
+ ATT_TURBULENCE_STITCHABLE, // bool
+ ATT_TURBULENCE_TYPE, // TurbulenceType
+ ATT_TURBULENCE_RECT // IntRect
+};
+
+enum TurbulenceType
+{
+ TURBULENCE_TYPE_TURBULENCE = 0,
+ TURBULENCE_TYPE_FRACTAL_NOISE
+};
+
+enum ArithmeticCombineAtts
+{
+ ATT_ARITHMETIC_COMBINE_COEFFICIENTS = 0 // Float[4]
+};
+
+enum ArithmeticCombineInputs
+{
+ IN_ARITHMETIC_COMBINE_IN = 0,
+ IN_ARITHMETIC_COMBINE_IN2
+};
+
+enum CompositeAtts
+{
+ ATT_COMPOSITE_OPERATOR = 0 // CompositeOperator
+};
+
+enum CompositeOperator
+{
+ COMPOSITE_OPERATOR_OVER = 0,
+ COMPOSITE_OPERATOR_IN,
+ COMPOSITE_OPERATOR_OUT,
+ COMPOSITE_OPERATOR_ATOP,
+ COMPOSITE_OPERATOR_XOR
+};
+
+enum CompositeInputs
+{
+ // arbitrary number of inputs
+ IN_COMPOSITE_IN_START = 0
+};
+
+enum GaussianBlurAtts
+{
+ ATT_GAUSSIAN_BLUR_STD_DEVIATION = 0 // Float
+};
+
+enum GaussianBlurInputs
+{
+ IN_GAUSSIAN_BLUR_IN = 0
+};
+
+enum DirectionalBlurAtts
+{
+ ATT_DIRECTIONAL_BLUR_STD_DEVIATION = 0, // Float
+ ATT_DIRECTIONAL_BLUR_DIRECTION // BlurDirection
+};
+
+enum BlurDirection
+{
+ BLUR_DIRECTION_X = 0,
+ BLUR_DIRECTION_Y
+};
+
+enum DirectionalBlurInputs
+{
+ IN_DIRECTIONAL_BLUR_IN = 0
+};
+
+enum LightingAtts
+{
+ ATT_POINT_LIGHT_POSITION = 0, // Point3D
+
+ ATT_SPOT_LIGHT_POSITION, // Point3D
+ ATT_SPOT_LIGHT_POINTS_AT, // Point3D
+ ATT_SPOT_LIGHT_FOCUS, // Float
+ ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE, // Float
+
+ ATT_DISTANT_LIGHT_AZIMUTH, // Float
+ ATT_DISTANT_LIGHT_ELEVATION, // Float
+
+ ATT_LIGHTING_COLOR, // Color
+ ATT_LIGHTING_SURFACE_SCALE, // Float
+ ATT_LIGHTING_KERNEL_UNIT_LENGTH, // Size
+
+ ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT, // Float
+
+ ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT, // Float
+ ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT // Float
+};
+
+enum LightingInputs
+{
+ IN_LIGHTING_IN = 0
+};
+
+enum PointDiffuseAtts
+{
+ ATT_POINT_DIFFUSE_POSITION = ATT_POINT_LIGHT_POSITION,
+ ATT_POINT_DIFFUSE_COLOR = ATT_LIGHTING_COLOR,
+ ATT_POINT_DIFFUSE_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
+ ATT_POINT_DIFFUSE_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ ATT_POINT_DIFFUSE_DIFFUSE_CONSTANT = ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
+};
+
+enum PointDiffuseInputs
+{
+ IN_POINT_DIFFUSE_IN = IN_LIGHTING_IN
+};
+
+enum SpotDiffuseAtts
+{
+ ATT_SPOT_DIFFUSE_POSITION = ATT_SPOT_LIGHT_POSITION,
+ ATT_SPOT_DIFFUSE_POINTS_AT = ATT_SPOT_LIGHT_POINTS_AT,
+ ATT_SPOT_DIFFUSE_FOCUS = ATT_SPOT_LIGHT_FOCUS,
+ ATT_SPOT_DIFFUSE_LIMITING_CONE_ANGLE = ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE,
+ ATT_SPOT_DIFFUSE_COLOR = ATT_LIGHTING_COLOR,
+ ATT_SPOT_DIFFUSE_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
+ ATT_SPOT_DIFFUSE_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ ATT_SPOT_DIFFUSE_DIFFUSE_CONSTANT = ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
+};
+
+enum SpotDiffuseInputs
+{
+ IN_SPOT_DIFFUSE_IN = IN_LIGHTING_IN
+};
+
+enum DistantDiffuseAtts
+{
+ ATT_DISTANT_DIFFUSE_AZIMUTH = ATT_DISTANT_LIGHT_AZIMUTH,
+ ATT_DISTANT_DIFFUSE_ELEVATION = ATT_DISTANT_LIGHT_ELEVATION,
+ ATT_DISTANT_DIFFUSE_COLOR = ATT_LIGHTING_COLOR,
+ ATT_DISTANT_DIFFUSE_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
+ ATT_DISTANT_DIFFUSE_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ ATT_DISTANT_DIFFUSE_DIFFUSE_CONSTANT = ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
+};
+
+enum DistantDiffuseInputs
+{
+ IN_DISTANT_DIFFUSE_IN = IN_LIGHTING_IN
+};
+
+enum PointSpecularAtts
+{
+ ATT_POINT_SPECULAR_POSITION = ATT_POINT_LIGHT_POSITION,
+ ATT_POINT_SPECULAR_COLOR = ATT_LIGHTING_COLOR,
+ ATT_POINT_SPECULAR_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
+ ATT_POINT_SPECULAR_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ ATT_POINT_SPECULAR_SPECULAR_CONSTANT = ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
+ ATT_POINT_SPECULAR_SPECULAR_EXPONENT = ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
+};
+
+enum PointSpecularInputs
+{
+ IN_POINT_SPECULAR_IN = IN_LIGHTING_IN
+};
+
+enum SpotSpecularAtts
+{
+ ATT_SPOT_SPECULAR_POSITION = ATT_SPOT_LIGHT_POSITION,
+ ATT_SPOT_SPECULAR_POINTS_AT = ATT_SPOT_LIGHT_POINTS_AT,
+ ATT_SPOT_SPECULAR_FOCUS = ATT_SPOT_LIGHT_FOCUS,
+ ATT_SPOT_SPECULAR_LIMITING_CONE_ANGLE = ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE,
+ ATT_SPOT_SPECULAR_COLOR = ATT_LIGHTING_COLOR,
+ ATT_SPOT_SPECULAR_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
+ ATT_SPOT_SPECULAR_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ ATT_SPOT_SPECULAR_SPECULAR_CONSTANT = ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
+ ATT_SPOT_SPECULAR_SPECULAR_EXPONENT = ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
+};
+
+enum SpotSpecularInputs
+{
+ IN_SPOT_SPECULAR_IN = IN_LIGHTING_IN
+};
+
+enum DistantSpecularAtts
+{
+ ATT_DISTANT_SPECULAR_AZIMUTH = ATT_DISTANT_LIGHT_AZIMUTH,
+ ATT_DISTANT_SPECULAR_ELEVATION = ATT_DISTANT_LIGHT_ELEVATION,
+ ATT_DISTANT_SPECULAR_COLOR = ATT_LIGHTING_COLOR,
+ ATT_DISTANT_SPECULAR_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
+ ATT_DISTANT_SPECULAR_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ ATT_DISTANT_SPECULAR_SPECULAR_CONSTANT = ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
+ ATT_DISTANT_SPECULAR_SPECULAR_EXPONENT = ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
+};
+
+enum DistantSpecularInputs
+{
+ IN_DISTANT_SPECULAR_IN = IN_LIGHTING_IN
+};
+
+enum CropAtts
+{
+ ATT_CROP_RECT = 0 // Rect
+};
+
+enum CropInputs
+{
+ IN_CROP_IN = 0
+};
+
+enum PremultiplyInputs
+{
+ IN_PREMULTIPLY_IN = 0
+};
+
+enum UnpremultiplyInputs
+{
+ IN_UNPREMULTIPLY_IN = 0
+};
+
+class FilterNode : public RefCounted<FilterNode>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNode)
+ virtual ~FilterNode() {}
+
+ virtual FilterBackend GetBackendType() = 0;
+
+ virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) { MOZ_CRASH(); }
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) { MOZ_CRASH(); }
+
+ virtual void SetAttribute(uint32_t aIndex, bool) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, uint32_t) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, Float) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, const Size &) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, const IntSize &) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, const IntPoint &) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, const Rect &) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, const Point &) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, const Matrix &) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, const Point3D &) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, const Color &) { MOZ_CRASH(); }
+ virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) { MOZ_CRASH(); }
+
+protected:
+ friend class Factory;
+
+ FilterNode() {}
+};
+
+}
+}
+
+#endif
diff --git a/gfx/2d/GenericRefCounted.h b/gfx/2d/GenericRefCounted.h
new file mode 100644
index 000000000..b1de3f754
--- /dev/null
+++ b/gfx/2d/GenericRefCounted.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+// This header provides virtual, non-templated alternatives to MFBT's RefCounted<T>.
+// It intentionally uses MFBT coding style with the intention of moving there
+// should there be other use cases for it.
+
+#ifndef MOZILLA_GENERICREFCOUNTED_H_
+#define MOZILLA_GENERICREFCOUNTED_H_
+
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+
+/**
+ * Common base class for GenericRefCounted and GenericAtomicRefCounted.
+ *
+ * Having this shared base class, common to both the atomic and non-atomic
+ * cases, allows to have RefPtr's that don't care about whether the
+ * objects they're managing have atomic refcounts or not.
+ */
+class GenericRefCountedBase
+{
+ protected:
+ virtual ~GenericRefCountedBase() {};
+
+ public:
+ // AddRef() and Release() method names are for compatibility with nsRefPtr.
+ virtual void AddRef() = 0;
+
+ virtual void Release() = 0;
+
+ // ref() and deref() method names are for compatibility with wtf::RefPtr.
+ // No virtual keywords here: if a subclass wants to override the refcounting
+ // mechanism, it is welcome to do so by overriding AddRef() and Release().
+ void ref() { AddRef(); }
+ void deref() { Release(); }
+
+#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
+ virtual const char* typeName() const = 0;
+ virtual size_t typeSize() const = 0;
+#endif
+};
+
+namespace detail {
+
+template<RefCountAtomicity Atomicity>
+class GenericRefCounted : public GenericRefCountedBase
+{
+ protected:
+ GenericRefCounted() : refCnt(0) { }
+
+ virtual ~GenericRefCounted() {
+ MOZ_ASSERT(refCnt == detail::DEAD);
+ }
+
+ public:
+ virtual void AddRef() override {
+ // Note: this method must be thread safe for GenericAtomicRefCounted.
+ MOZ_ASSERT(int32_t(refCnt) >= 0);
+#ifndef MOZ_REFCOUNTED_LEAK_CHECKING
+ ++refCnt;
+#else
+ const char* type = typeName();
+ uint32_t size = typeSize();
+ const void* ptr = this;
+ MozRefCountType cnt = ++refCnt;
+ detail::RefCountLogger::logAddRef(ptr, cnt, type, size);
+#endif
+ }
+
+ virtual void Release() override {
+ // Note: this method must be thread safe for GenericAtomicRefCounted.
+ MOZ_ASSERT(int32_t(refCnt) > 0);
+#ifndef MOZ_REFCOUNTED_LEAK_CHECKING
+ MozRefCountType cnt = --refCnt;
+#else
+ const char* type = typeName();
+ const void* ptr = this;
+ MozRefCountType cnt = --refCnt;
+ // Note: it's not safe to touch |this| after decrementing the refcount,
+ // except for below.
+ detail::RefCountLogger::logRelease(ptr, cnt, type);
+#endif
+ if (0 == cnt) {
+ // Because we have atomically decremented the refcount above, only
+ // one thread can get a 0 count here, so as long as we can assume that
+ // everything else in the system is accessing this object through
+ // RefPtrs, it's safe to access |this| here.
+#ifdef DEBUG
+ refCnt = detail::DEAD;
+#endif
+ delete this;
+ }
+ }
+
+ MozRefCountType refCount() const { return refCnt; }
+ bool hasOneRef() const {
+ MOZ_ASSERT(refCnt > 0);
+ return refCnt == 1;
+ }
+
+ private:
+ typename Conditional<Atomicity == AtomicRefCount, Atomic<MozRefCountType>, MozRefCountType>::Type refCnt;
+};
+
+} // namespace detail
+
+/**
+ * This reference-counting base class is virtual instead of
+ * being templated, which is useful in cases where one needs
+ * genericity at binary code level, but comes at the cost
+ * of a moderate performance and size overhead, like anything virtual.
+ */
+class GenericRefCounted : public detail::GenericRefCounted<detail::NonAtomicRefCount>
+{
+};
+
+/**
+ * GenericAtomicRefCounted is like GenericRefCounted, with an atomically updated
+ * reference counter.
+ */
+class GenericAtomicRefCounted : public detail::GenericRefCounted<detail::AtomicRefCount>
+{
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/gfx/2d/GradientStopsD2D.h b/gfx/2d/GradientStopsD2D.h
index fe5b4e25c..de6e448de 100644
--- a/gfx/2d/GradientStopsD2D.h
+++ b/gfx/2d/GradientStopsD2D.h
@@ -16,16 +16,22 @@ namespace gfx {
class GradientStopsD2D : public GradientStops
{
public:
- GradientStopsD2D(ID2D1GradientStopCollection *aStopCollection)
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsD2D)
+ GradientStopsD2D(ID2D1GradientStopCollection *aStopCollection, ID3D11Device *aDevice)
: mStopCollection(aStopCollection)
+ , mDevice(aDevice)
{}
- virtual BackendType GetBackendType() const { return BACKEND_DIRECT2D; }
+ virtual BackendType GetBackendType() const { return BackendType::DIRECT2D; }
+
+ virtual bool IsValid() const final{ return mDevice == Factory::GetDirect3D11Device(); }
private:
friend class DrawTargetD2D;
+ friend class DrawTargetD2D1;
mutable RefPtr<ID2D1GradientStopCollection> mStopCollection;
+ RefPtr<ID3D11Device> mDevice;
};
}
diff --git a/gfx/2d/Helpers.h b/gfx/2d/Helpers.h
index e69de29bb..11c7eec5d 100644
--- a/gfx/2d/Helpers.h
+++ b/gfx/2d/Helpers.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_2D_HELPERS_H_
+#define MOZILLA_GFX_2D_HELPERS_H_
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+class AutoRestoreTransform
+{
+ public:
+ AutoRestoreTransform()
+ {
+ }
+
+ explicit AutoRestoreTransform(DrawTarget *aTarget)
+ : mDrawTarget(aTarget),
+ mOldTransform(aTarget->GetTransform())
+ {
+ }
+
+ void Init(DrawTarget *aTarget)
+ {
+ MOZ_ASSERT(!mDrawTarget || aTarget == mDrawTarget);
+ if (!mDrawTarget) {
+ mDrawTarget = aTarget;
+ mOldTransform = aTarget->GetTransform();
+ }
+ }
+
+ ~AutoRestoreTransform()
+ {
+ if (mDrawTarget) {
+ mDrawTarget->SetTransform(mOldTransform);
+ }
+ }
+
+ private:
+ RefPtr<DrawTarget> mDrawTarget;
+ Matrix mOldTransform;
+};
+
+class AutoPopClips
+{
+public:
+ explicit AutoPopClips(DrawTarget *aTarget)
+ : mDrawTarget(aTarget)
+ , mPushCount(0)
+ {
+ MOZ_ASSERT(mDrawTarget);
+ }
+
+ ~AutoPopClips()
+ {
+ PopAll();
+ }
+
+ void PushClip(const Path *aPath)
+ {
+ mDrawTarget->PushClip(aPath);
+ ++mPushCount;
+ }
+
+ void PushClipRect(const Rect &aRect)
+ {
+ mDrawTarget->PushClipRect(aRect);
+ ++mPushCount;
+ }
+
+ void PopClip()
+ {
+ MOZ_ASSERT(mPushCount > 0);
+ mDrawTarget->PopClip();
+ --mPushCount;
+ }
+
+ void PopAll()
+ {
+ while (mPushCount-- > 0) {
+ mDrawTarget->PopClip();
+ }
+ }
+
+private:
+ RefPtr<DrawTarget> mDrawTarget;
+ int32_t mPushCount;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_2D_HELPERS_H_
diff --git a/gfx/2d/HelpersCairo.h b/gfx/2d/HelpersCairo.h
index 5dfcee261..3c5b74340 100644
--- a/gfx/2d/HelpersCairo.h
+++ b/gfx/2d/HelpersCairo.h
@@ -18,73 +18,92 @@ GfxOpToCairoOp(CompositionOp op)
{
switch (op)
{
- case OP_OVER:
+ case CompositionOp::OP_OVER:
return CAIRO_OPERATOR_OVER;
- case OP_ADD:
+ case CompositionOp::OP_ADD:
return CAIRO_OPERATOR_ADD;
- case OP_ATOP:
+ case CompositionOp::OP_ATOP:
return CAIRO_OPERATOR_ATOP;
- case OP_OUT:
+ case CompositionOp::OP_OUT:
return CAIRO_OPERATOR_OUT;
- case OP_IN:
+ case CompositionOp::OP_IN:
return CAIRO_OPERATOR_IN;
- case OP_SOURCE:
+ case CompositionOp::OP_SOURCE:
return CAIRO_OPERATOR_SOURCE;
- case OP_DEST_IN:
+ case CompositionOp::OP_DEST_IN:
return CAIRO_OPERATOR_DEST_IN;
- case OP_DEST_OUT:
+ case CompositionOp::OP_DEST_OUT:
return CAIRO_OPERATOR_DEST_OUT;
- case OP_DEST_OVER:
+ case CompositionOp::OP_DEST_OVER:
return CAIRO_OPERATOR_DEST_OVER;
- case OP_DEST_ATOP:
+ case CompositionOp::OP_DEST_ATOP:
return CAIRO_OPERATOR_DEST_ATOP;
- case OP_XOR:
+ case CompositionOp::OP_XOR:
return CAIRO_OPERATOR_XOR;
- case OP_MULTIPLY:
+ case CompositionOp::OP_MULTIPLY:
return CAIRO_OPERATOR_MULTIPLY;
- case OP_SCREEN:
+ case CompositionOp::OP_SCREEN:
return CAIRO_OPERATOR_SCREEN;
- case OP_OVERLAY:
+ case CompositionOp::OP_OVERLAY:
return CAIRO_OPERATOR_OVERLAY;
- case OP_DARKEN:
+ case CompositionOp::OP_DARKEN:
return CAIRO_OPERATOR_DARKEN;
- case OP_LIGHTEN:
+ case CompositionOp::OP_LIGHTEN:
return CAIRO_OPERATOR_LIGHTEN;
- case OP_COLOR_DODGE:
+ case CompositionOp::OP_COLOR_DODGE:
return CAIRO_OPERATOR_COLOR_DODGE;
- case OP_COLOR_BURN:
+ case CompositionOp::OP_COLOR_BURN:
return CAIRO_OPERATOR_COLOR_BURN;
- case OP_HARD_LIGHT:
+ case CompositionOp::OP_HARD_LIGHT:
return CAIRO_OPERATOR_HARD_LIGHT;
- case OP_SOFT_LIGHT:
+ case CompositionOp::OP_SOFT_LIGHT:
return CAIRO_OPERATOR_SOFT_LIGHT;
- case OP_DIFFERENCE:
+ case CompositionOp::OP_DIFFERENCE:
return CAIRO_OPERATOR_DIFFERENCE;
- case OP_EXCLUSION:
+ case CompositionOp::OP_EXCLUSION:
return CAIRO_OPERATOR_EXCLUSION;
- case OP_HUE:
+ case CompositionOp::OP_HUE:
return CAIRO_OPERATOR_HSL_HUE;
- case OP_SATURATION:
+ case CompositionOp::OP_SATURATION:
return CAIRO_OPERATOR_HSL_SATURATION;
- case OP_COLOR:
+ case CompositionOp::OP_COLOR:
return CAIRO_OPERATOR_HSL_COLOR;
- case OP_LUMINOSITY:
+ case CompositionOp::OP_LUMINOSITY:
return CAIRO_OPERATOR_HSL_LUMINOSITY;
- case OP_COUNT:
+ case CompositionOp::OP_COUNT:
break;
}
return CAIRO_OPERATOR_OVER;
}
+static inline cairo_antialias_t
+GfxAntialiasToCairoAntialias(AntialiasMode antialias)
+{
+ switch (antialias)
+ {
+ case AntialiasMode::NONE:
+ return CAIRO_ANTIALIAS_NONE;
+ case AntialiasMode::GRAY:
+ return CAIRO_ANTIALIAS_GRAY;
+ case AntialiasMode::SUBPIXEL:
+ return CAIRO_ANTIALIAS_SUBPIXEL;
+ case AntialiasMode::DEFAULT:
+ return CAIRO_ANTIALIAS_DEFAULT;
+ }
+ return CAIRO_ANTIALIAS_DEFAULT;
+}
+
static inline cairo_filter_t
GfxFilterToCairoFilter(Filter filter)
{
switch (filter)
{
- case FILTER_LINEAR:
+ case Filter::GOOD:
+ return CAIRO_FILTER_GOOD;
+ case Filter::LINEAR:
return CAIRO_FILTER_BILINEAR;
- case FILTER_POINT:
+ case Filter::POINT:
return CAIRO_FILTER_NEAREST;
}
@@ -96,11 +115,11 @@ GfxExtendToCairoExtend(ExtendMode extend)
{
switch (extend)
{
- case EXTEND_CLAMP:
+ case ExtendMode::CLAMP:
return CAIRO_EXTEND_PAD;
- case EXTEND_REPEAT:
+ case ExtendMode::REPEAT:
return CAIRO_EXTEND_REPEAT;
- case EXTEND_REFLECT:
+ case ExtendMode::REFLECT:
return CAIRO_EXTEND_REFLECT;
}
@@ -112,16 +131,17 @@ GfxFormatToCairoFormat(SurfaceFormat format)
{
switch (format)
{
- case FORMAT_B8G8R8A8:
+ case SurfaceFormat::B8G8R8A8:
return CAIRO_FORMAT_ARGB32;
- case FORMAT_B8G8R8X8:
+ case SurfaceFormat::B8G8R8X8:
return CAIRO_FORMAT_RGB24;
- case FORMAT_A8:
+ case SurfaceFormat::A8:
return CAIRO_FORMAT_A8;
- case FORMAT_R5G6B5:
+ case SurfaceFormat::R5G6B5:
return CAIRO_FORMAT_RGB16_565;
default:
gfxWarning() << "Unknown image format";
+ MOZ_ASSERT(false, "Unknown image format");
return CAIRO_FORMAT_ARGB32;
}
}
@@ -131,15 +151,16 @@ GfxFormatToCairoContent(SurfaceFormat format)
{
switch (format)
{
- case FORMAT_B8G8R8A8:
+ case SurfaceFormat::B8G8R8A8:
return CAIRO_CONTENT_COLOR_ALPHA;
- case FORMAT_B8G8R8X8:
- case FORMAT_R5G6B5: //fall through
+ case SurfaceFormat::B8G8R8X8:
+ case SurfaceFormat::R5G6B5: //fall through
return CAIRO_CONTENT_COLOR;
- case FORMAT_A8:
+ case SurfaceFormat::A8:
return CAIRO_CONTENT_ALPHA;
default:
gfxWarning() << "Unknown image format";
+ MOZ_ASSERT(false, "Unknown image format");
return CAIRO_CONTENT_COLOR_ALPHA;
}
}
@@ -149,13 +170,13 @@ GfxLineJoinToCairoLineJoin(JoinStyle style)
{
switch (style)
{
- case JOIN_BEVEL:
+ case JoinStyle::BEVEL:
return CAIRO_LINE_JOIN_BEVEL;
- case JOIN_ROUND:
+ case JoinStyle::ROUND:
return CAIRO_LINE_JOIN_ROUND;
- case JOIN_MITER:
+ case JoinStyle::MITER:
return CAIRO_LINE_JOIN_MITER;
- case JOIN_MITER_OR_BEVEL:
+ case JoinStyle::MITER_OR_BEVEL:
return CAIRO_LINE_JOIN_MITER;
}
@@ -167,11 +188,11 @@ GfxLineCapToCairoLineCap(CapStyle style)
{
switch (style)
{
- case CAP_BUTT:
+ case CapStyle::BUTT:
return CAIRO_LINE_CAP_BUTT;
- case CAP_ROUND:
+ case CapStyle::ROUND:
return CAIRO_LINE_CAP_ROUND;
- case CAP_SQUARE:
+ case CapStyle::SQUARE:
return CAIRO_LINE_CAP_SQUARE;
}
@@ -184,15 +205,44 @@ CairoContentToGfxFormat(cairo_content_t content)
switch (content)
{
case CAIRO_CONTENT_COLOR_ALPHA:
- return FORMAT_B8G8R8A8;
+ return SurfaceFormat::B8G8R8A8;
case CAIRO_CONTENT_COLOR:
// BEWARE! format may be 565
- return FORMAT_B8G8R8X8;
+ return SurfaceFormat::B8G8R8X8;
case CAIRO_CONTENT_ALPHA:
- return FORMAT_A8;
+ return SurfaceFormat::A8;
+ }
+
+ return SurfaceFormat::B8G8R8A8;
+}
+
+static inline SurfaceFormat
+CairoFormatToGfxFormat(cairo_format_t format)
+{
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ return SurfaceFormat::B8G8R8A8;
+ case CAIRO_FORMAT_RGB24:
+ return SurfaceFormat::B8G8R8X8;
+ case CAIRO_FORMAT_A8:
+ return SurfaceFormat::A8;
+ case CAIRO_FORMAT_RGB16_565:
+ return SurfaceFormat::R5G6B5;
+ default:
+ gfxWarning() << "Unknown cairo format";
+ MOZ_ASSERT(false, "Unknown cairo format");
+ return SurfaceFormat::UNKNOWN;
+ }
+}
+
+static inline SurfaceFormat
+GfxFormatForCairoSurface(cairo_surface_t* surface)
+{
+ if (cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE) {
+ return CairoFormatToGfxFormat(cairo_image_surface_get_format(surface));
}
- return FORMAT_B8G8R8A8;
+ return CairoContentToGfxFormat(cairo_surface_get_content(surface));
}
static inline void
@@ -228,9 +278,9 @@ GfxFillRuleToCairoFillRule(FillRule rule)
{
switch (rule)
{
- case FILL_WINDING:
+ case FillRule::FILL_WINDING:
return CAIRO_FILL_RULE_WINDING;
- case FILL_EVEN_ODD:
+ case FillRule::FILL_EVEN_ODD:
return CAIRO_FILL_RULE_EVEN_ODD;
}
diff --git a/gfx/2d/HelpersD2D.h b/gfx/2d/HelpersD2D.h
index cc836009d..a25f2cf2f 100644
--- a/gfx/2d/HelpersD2D.h
+++ b/gfx/2d/HelpersD2D.h
@@ -6,7 +6,7 @@
#ifndef MOZILLA_GFX_HELPERSD2D_H_
#define MOZILLA_GFX_HELPERSD2D_H_
-#include "moz-d2d1-1.h"
+#include <d2d1_1.h>
#include <vector>
@@ -26,6 +26,8 @@ namespace gfx {
ID2D1Factory* D2DFactory();
+ID2D1Factory1* D2DFactory1();
+
static inline D2D1_POINT_2F D2DPoint(const Point &aPoint)
{
return D2D1::Point2F(aPoint.x, aPoint.y);
@@ -36,7 +38,8 @@ static inline D2D1_SIZE_U D2DIntSize(const IntSize &aSize)
return D2D1::SizeU(aSize.width, aSize.height);
}
-static inline D2D1_RECT_F D2DRect(const Rect &aRect)
+template <typename T>
+static inline D2D1_RECT_F D2DRect(const T &aRect)
{
return D2D1::RectF(aRect.x, aRect.y, aRect.XMost(), aRect.YMost());
}
@@ -45,10 +48,10 @@ static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode)
{
D2D1_EXTEND_MODE extend;
switch (aExtendMode) {
- case EXTEND_REPEAT:
+ case ExtendMode::REPEAT:
extend = D2D1_EXTEND_MODE_WRAP;
break;
- case EXTEND_REFLECT:
+ case ExtendMode::REFLECT:
extend = D2D1_EXTEND_MODE_MIRROR;
break;
default:
@@ -61,17 +64,41 @@ static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode)
static inline D2D1_BITMAP_INTERPOLATION_MODE D2DFilter(const Filter &aFilter)
{
switch (aFilter) {
- case FILTER_POINT:
+ case Filter::POINT:
return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
default:
return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
}
}
+static inline D2D1_INTERPOLATION_MODE D2DInterpolationMode(const Filter &aFilter)
+{
+ switch (aFilter) {
+ case Filter::POINT:
+ return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+ default:
+ return D2D1_INTERPOLATION_MODE_LINEAR;
+ }
+}
+
+static inline D2D1_MATRIX_5X4_F D2DMatrix5x4(const Matrix5x4 &aMatrix)
+{
+ return D2D1::Matrix5x4F(aMatrix._11, aMatrix._12, aMatrix._13, aMatrix._14,
+ aMatrix._21, aMatrix._22, aMatrix._23, aMatrix._24,
+ aMatrix._31, aMatrix._32, aMatrix._33, aMatrix._34,
+ aMatrix._41, aMatrix._42, aMatrix._43, aMatrix._44,
+ aMatrix._51, aMatrix._52, aMatrix._53, aMatrix._54);
+}
+
+static inline D2D1_VECTOR_3F D2DVector3D(const Point3D &aPoint)
+{
+ return D2D1::Vector3F(aPoint.x, aPoint.y, aPoint.z);
+}
+
static inline D2D1_ANTIALIAS_MODE D2DAAMode(AntialiasMode aMode)
{
switch (aMode) {
- case AA_NONE:
+ case AntialiasMode::NONE:
return D2D1_ANTIALIAS_MODE_ALIASED;
default:
return D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
@@ -99,15 +126,15 @@ static inline SurfaceFormat ToPixelFormat(const D2D1_PIXEL_FORMAT &aFormat)
{
switch(aFormat.format) {
case DXGI_FORMAT_A8_UNORM:
- return FORMAT_A8;
+ return SurfaceFormat::A8;
case DXGI_FORMAT_B8G8R8A8_UNORM:
if (aFormat.alphaMode == D2D1_ALPHA_MODE_IGNORE) {
- return FORMAT_B8G8R8X8;
+ return SurfaceFormat::B8G8R8X8;
} else {
- return FORMAT_B8G8R8A8;
+ return SurfaceFormat::B8G8R8A8;
}
default:
- return FORMAT_B8G8R8A8;
+ return SurfaceFormat::B8G8R8A8;
}
}
@@ -123,24 +150,29 @@ static inline Matrix ToMatrix(const D2D1_MATRIX_3X2_F &aTransform)
aTransform._31, aTransform._32);
}
+static inline Point ToPoint(const D2D1_POINT_2F &aPoint)
+{
+ return Point(aPoint.x, aPoint.y);
+}
+
static inline DXGI_FORMAT DXGIFormat(SurfaceFormat aFormat)
{
switch (aFormat) {
- case FORMAT_B8G8R8A8:
+ case SurfaceFormat::B8G8R8A8:
return DXGI_FORMAT_B8G8R8A8_UNORM;
- case FORMAT_B8G8R8X8:
+ case SurfaceFormat::B8G8R8X8:
return DXGI_FORMAT_B8G8R8A8_UNORM;
- case FORMAT_A8:
+ case SurfaceFormat::A8:
return DXGI_FORMAT_A8_UNORM;
default:
return DXGI_FORMAT_UNKNOWN;
}
}
-static inline D2D1_ALPHA_MODE AlphaMode(SurfaceFormat aFormat)
+static inline D2D1_ALPHA_MODE D2DAlphaModeForFormat(SurfaceFormat aFormat)
{
switch (aFormat) {
- case FORMAT_B8G8R8X8:
+ case SurfaceFormat::B8G8R8X8:
return D2D1_ALPHA_MODE_IGNORE;
default:
return D2D1_ALPHA_MODE_PREMULTIPLIED;
@@ -149,12 +181,100 @@ static inline D2D1_ALPHA_MODE AlphaMode(SurfaceFormat aFormat)
static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat)
{
- return D2D1::PixelFormat(DXGIFormat(aFormat), AlphaMode(aFormat));
+ return D2D1::PixelFormat(DXGIFormat(aFormat), D2DAlphaModeForFormat(aFormat));
+}
+
+static inline bool D2DSupportsCompositeMode(CompositionOp aOp)
+{
+ switch(aOp) {
+ case CompositionOp::OP_OVER:
+ case CompositionOp::OP_ADD:
+ case CompositionOp::OP_ATOP:
+ case CompositionOp::OP_OUT:
+ case CompositionOp::OP_IN:
+ case CompositionOp::OP_SOURCE:
+ case CompositionOp::OP_DEST_IN:
+ case CompositionOp::OP_DEST_OUT:
+ case CompositionOp::OP_DEST_OVER:
+ case CompositionOp::OP_DEST_ATOP:
+ case CompositionOp::OP_XOR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline D2D1_COMPOSITE_MODE D2DCompositionMode(CompositionOp aOp)
+{
+ switch(aOp) {
+ case CompositionOp::OP_OVER:
+ return D2D1_COMPOSITE_MODE_SOURCE_OVER;
+ case CompositionOp::OP_ADD:
+ return D2D1_COMPOSITE_MODE_PLUS;
+ case CompositionOp::OP_ATOP:
+ return D2D1_COMPOSITE_MODE_SOURCE_ATOP;
+ case CompositionOp::OP_OUT:
+ return D2D1_COMPOSITE_MODE_SOURCE_OUT;
+ case CompositionOp::OP_IN:
+ return D2D1_COMPOSITE_MODE_SOURCE_IN;
+ case CompositionOp::OP_SOURCE:
+ return D2D1_COMPOSITE_MODE_SOURCE_COPY;
+ case CompositionOp::OP_DEST_IN:
+ return D2D1_COMPOSITE_MODE_DESTINATION_IN;
+ case CompositionOp::OP_DEST_OUT:
+ return D2D1_COMPOSITE_MODE_DESTINATION_OUT;
+ case CompositionOp::OP_DEST_OVER:
+ return D2D1_COMPOSITE_MODE_DESTINATION_OVER;
+ case CompositionOp::OP_DEST_ATOP:
+ return D2D1_COMPOSITE_MODE_DESTINATION_ATOP;
+ case CompositionOp::OP_XOR:
+ return D2D1_COMPOSITE_MODE_XOR;
+ default:
+ return D2D1_COMPOSITE_MODE_SOURCE_OVER;
+ }
+}
+
+static inline D2D1_BLEND_MODE D2DBlendMode(CompositionOp aOp)
+{
+ switch (aOp) {
+ case CompositionOp::OP_MULTIPLY:
+ return D2D1_BLEND_MODE_MULTIPLY;
+ case CompositionOp::OP_SCREEN:
+ return D2D1_BLEND_MODE_SCREEN;
+ case CompositionOp::OP_OVERLAY:
+ return D2D1_BLEND_MODE_OVERLAY;
+ case CompositionOp::OP_DARKEN:
+ return D2D1_BLEND_MODE_DARKEN;
+ case CompositionOp::OP_LIGHTEN:
+ return D2D1_BLEND_MODE_LIGHTEN;
+ case CompositionOp::OP_COLOR_DODGE:
+ return D2D1_BLEND_MODE_COLOR_DODGE;
+ case CompositionOp::OP_COLOR_BURN:
+ return D2D1_BLEND_MODE_COLOR_BURN;
+ case CompositionOp::OP_HARD_LIGHT:
+ return D2D1_BLEND_MODE_HARD_LIGHT;
+ case CompositionOp::OP_SOFT_LIGHT:
+ return D2D1_BLEND_MODE_SOFT_LIGHT;
+ case CompositionOp::OP_DIFFERENCE:
+ return D2D1_BLEND_MODE_DIFFERENCE;
+ case CompositionOp::OP_EXCLUSION:
+ return D2D1_BLEND_MODE_EXCLUSION;
+ case CompositionOp::OP_HUE:
+ return D2D1_BLEND_MODE_HUE;
+ case CompositionOp::OP_SATURATION:
+ return D2D1_BLEND_MODE_SATURATION;
+ case CompositionOp::OP_COLOR:
+ return D2D1_BLEND_MODE_COLOR;
+ case CompositionOp::OP_LUMINOSITY:
+ return D2D1_BLEND_MODE_LUMINOSITY;
+ default:
+ return D2D1_BLEND_MODE_MULTIPLY;
+ }
}
static inline bool IsPatternSupportedByD2D(const Pattern &aPattern)
{
- if (aPattern.GetType() != PATTERN_RADIAL_GRADIENT) {
+ if (aPattern.GetType() != PatternType::RADIAL_GRADIENT) {
return true;
}
@@ -265,7 +385,7 @@ DWriteGlyphRunFromGlyphs(const GlyphBuffer &aGlyphs, ScaledFontDWrite *aFont, Au
run->isSideways = FALSE;
}
-static TemporaryRef<ID2D1Geometry>
+static inline TemporaryRef<ID2D1Geometry>
ConvertRectToGeometry(const D2D1_RECT_F& aRect)
{
RefPtr<ID2D1RectangleGeometry> rectGeom;
@@ -273,7 +393,7 @@ ConvertRectToGeometry(const D2D1_RECT_F& aRect)
return rectGeom.forget();
}
-static TemporaryRef<ID2D1Geometry>
+static inline TemporaryRef<ID2D1Geometry>
GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform)
{
RefPtr<ID2D1PathGeometry> tmpGeometry;
@@ -283,10 +403,10 @@ GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTrans
aGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
aTransform, currentSink);
currentSink->Close();
- return tmpGeometry;
+ return tmpGeometry.forget();
}
-static TemporaryRef<ID2D1Geometry>
+static inline TemporaryRef<ID2D1Geometry>
IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB)
{
RefPtr<ID2D1PathGeometry> pathGeom;
@@ -296,10 +416,10 @@ IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB)
aGeometryA->CombineWithGeometry(aGeometryB, D2D1_COMBINE_MODE_INTERSECT, nullptr, sink);
sink->Close();
- return pathGeom;
+ return pathGeom.forget();
}
-static TemporaryRef<ID2D1StrokeStyle>
+static inline TemporaryRef<ID2D1StrokeStyle>
CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions)
{
RefPtr<ID2D1StrokeStyle> style;
@@ -308,35 +428,38 @@ CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions)
D2D1_LINE_JOIN joinStyle;
switch (aStrokeOptions.mLineCap) {
- case CAP_BUTT:
+ case CapStyle::BUTT:
capStyle = D2D1_CAP_STYLE_FLAT;
break;
- case CAP_ROUND:
+ case CapStyle::ROUND:
capStyle = D2D1_CAP_STYLE_ROUND;
break;
- case CAP_SQUARE:
+ case CapStyle::SQUARE:
capStyle = D2D1_CAP_STYLE_SQUARE;
break;
}
switch (aStrokeOptions.mLineJoin) {
- case JOIN_MITER:
+ case JoinStyle::MITER:
joinStyle = D2D1_LINE_JOIN_MITER;
break;
- case JOIN_MITER_OR_BEVEL:
+ case JoinStyle::MITER_OR_BEVEL:
joinStyle = D2D1_LINE_JOIN_MITER_OR_BEVEL;
break;
- case JOIN_ROUND:
+ case JoinStyle::ROUND:
joinStyle = D2D1_LINE_JOIN_ROUND;
break;
- case JOIN_BEVEL:
+ case JoinStyle::BEVEL:
joinStyle = D2D1_LINE_JOIN_BEVEL;
break;
}
HRESULT hr;
- if (aStrokeOptions.mDashPattern) {
+ // We need to check mDashLength in addition to mDashPattern here since if
+ // mDashPattern is set but mDashLength is zero then the stroke will fail to
+ // paint.
+ if (aStrokeOptions.mDashLength > 0 && aStrokeOptions.mDashPattern) {
typedef std::vector<Float> FloatVector;
// D2D "helpfully" multiplies the dash pattern by the line width.
// That's not what cairo does, or is what <canvas>'s dash wants.
@@ -353,7 +476,7 @@ CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions)
capStyle, joinStyle,
aStrokeOptions.mMiterLimit,
D2D1_DASH_STYLE_CUSTOM,
- aStrokeOptions.mDashOffset),
+ aStrokeOptions.mDashOffset / lineWidth),
&dash[0], // data() is not C++98, although it's in recent gcc
// and VC10's STL
dash.size(),
@@ -370,16 +493,17 @@ CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions)
gfxWarning() << "Failed to create Direct2D stroke style.";
}
- return style;
+ return style.forget();
}
// This creates a (partially) uploaded bitmap for a DataSourceSurface. It
// uploads the minimum requirement and possibly downscales. It adjusts the
// input Matrix to compensate.
-static TemporaryRef<ID2D1Bitmap>
+static inline TemporaryRef<ID2D1Bitmap>
CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestinationTransform,
const IntSize &aDestinationSize, ExtendMode aExtendMode,
- Matrix &aSourceTransform, ID2D1RenderTarget *aRT)
+ Matrix &aSourceTransform, ID2D1RenderTarget *aRT,
+ const IntRect* aSourceRect = nullptr)
{
RefPtr<ID2D1Bitmap> bitmap;
@@ -404,6 +528,9 @@ CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestin
IntSize size = aSurface->GetSize();
Rect uploadRect(0, 0, Float(size.width), Float(size.height));
+ if (aSourceRect) {
+ uploadRect = Rect(aSourceRect->x, aSourceRect->y, aSourceRect->width, aSourceRect->height);
+ }
// Limit the uploadRect as much as possible without supporting discontiguous uploads
//
@@ -422,9 +549,9 @@ CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestin
// Extend mode is irrelevant, the displayed rect is completely contained
// by the source bitmap.
uploadRect = rect;
- } else if (aExtendMode == EXTEND_CLAMP && uploadRect.Intersects(rect)) {
+ } else if (aExtendMode == ExtendMode::CLAMP && uploadRect.Intersects(rect)) {
// Calculate the rectangle on the source bitmap that touches our
- // surface, and upload that, for EXTEND_CLAMP we can actually guarantee
+ // surface, and upload that, for ExtendMode::CLAMP we can actually guarantee
// correct behaviour in this case.
uploadRect = uploadRect.Intersect(rect);
@@ -451,9 +578,9 @@ CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestin
D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
byRef(bitmap));
- aSourceTransform.Translate(uploadRect.x, uploadRect.y);
+ aSourceTransform.PreTranslate(uploadRect.x, uploadRect.y);
- return bitmap;
+ return bitmap.forget();
} else {
int Bpp = BytesPerPixel(aSurface->GetFormat());
@@ -491,15 +618,19 @@ CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestin
scaler.ScaleForSize(scaleSize);
IntSize newSize = scaler.GetSize();
+
+ if (newSize.IsEmpty()) {
+ return nullptr;
+ }
aRT->CreateBitmap(D2D1::SizeU(newSize.width, newSize.height),
scaler.GetScaledData(), scaler.GetStride(),
D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
byRef(bitmap));
- aSourceTransform.Scale(Float(size.width / newSize.width),
- Float(size.height / newSize.height));
- return bitmap;
+ aSourceTransform.PreScale(Float(size.width) / newSize.width,
+ Float(size.height) / newSize.height);
+ return bitmap.forget();
}
}
diff --git a/gfx/2d/HelpersSkia.h b/gfx/2d/HelpersSkia.h
index ad7d5ae98..72ad45372 100644
--- a/gfx/2d/HelpersSkia.h
+++ b/gfx/2d/HelpersSkia.h
@@ -19,24 +19,39 @@
namespace mozilla {
namespace gfx {
-static inline SkBitmap::Config
-GfxFormatToSkiaConfig(SurfaceFormat format)
+static inline SkColorType
+GfxFormatToSkiaColorType(SurfaceFormat format)
{
switch (format)
{
- case FORMAT_B8G8R8A8:
- return SkBitmap::kARGB_8888_Config;
- case FORMAT_B8G8R8X8:
+ case SurfaceFormat::B8G8R8A8:
+ return kBGRA_8888_SkColorType;
+ case SurfaceFormat::B8G8R8X8:
// We probably need to do something here.
- return SkBitmap::kARGB_8888_Config;
- case FORMAT_R5G6B5:
- return SkBitmap::kRGB_565_Config;
- case FORMAT_A8:
- return SkBitmap::kA8_Config;
-
+ return kBGRA_8888_SkColorType;
+ case SurfaceFormat::R5G6B5:
+ return kRGB_565_SkColorType;
+ case SurfaceFormat::A8:
+ return kAlpha_8_SkColorType;
+ default:
+ return kRGBA_8888_SkColorType;
}
+}
- return SkBitmap::kARGB_8888_Config;
+static inline SurfaceFormat
+SkiaColorTypeToGfxFormat(SkColorType type)
+{
+ switch (type)
+ {
+ case kBGRA_8888_SkColorType:
+ return SurfaceFormat::B8G8R8A8;
+ case kRGB_565_SkColorType:
+ return SurfaceFormat::R5G6B5;
+ case kAlpha_8_SkColorType:
+ return SurfaceFormat::A8;
+ default:
+ return SurfaceFormat::B8G8R8A8;
+ }
}
#ifdef USE_SKIA_GPU
@@ -45,14 +60,14 @@ GfxFormatToGrConfig(SurfaceFormat format)
{
switch (format)
{
- case FORMAT_B8G8R8A8:
+ case SurfaceFormat::B8G8R8A8:
return kBGRA_8888_GrPixelConfig;
- case FORMAT_B8G8R8X8:
+ case SurfaceFormat::B8G8R8X8:
// We probably need to do something here.
return kBGRA_8888_GrPixelConfig;
- case FORMAT_R5G6B5:
+ case SurfaceFormat::R5G6B5:
return kRGB_565_GrPixelConfig;
- case FORMAT_A8:
+ case SurfaceFormat::A8:
return kAlpha_8_GrPixelConfig;
default:
return kRGBA_8888_GrPixelConfig;
@@ -73,11 +88,11 @@ CapStyleToSkiaCap(CapStyle aCap)
{
switch (aCap)
{
- case CAP_BUTT:
+ case CapStyle::BUTT:
return SkPaint::kButt_Cap;
- case CAP_ROUND:
+ case CapStyle::ROUND:
return SkPaint::kRound_Cap;
- case CAP_SQUARE:
+ case CapStyle::SQUARE:
return SkPaint::kSquare_Cap;
}
return SkPaint::kDefault_Cap;
@@ -88,12 +103,12 @@ JoinStyleToSkiaJoin(JoinStyle aJoin)
{
switch (aJoin)
{
- case JOIN_BEVEL:
+ case JoinStyle::BEVEL:
return SkPaint::kBevel_Join;
- case JOIN_ROUND:
+ case JoinStyle::ROUND:
return SkPaint::kRound_Join;
- case JOIN_MITER:
- case JOIN_MITER_OR_BEVEL:
+ case JoinStyle::MITER:
+ case JoinStyle::MITER_OR_BEVEL:
return SkPaint::kMiter_Join;
}
return SkPaint::kDefault_Join;
@@ -129,9 +144,9 @@ StrokeOptionsToPaint(SkPaint& aPaint, const StrokeOptions &aOptions)
pattern[i] = SkFloatToScalar(aOptions.mDashPattern[i % aOptions.mDashLength]);
}
- SkDashPathEffect* dash = new SkDashPathEffect(&pattern.front(),
- dashCount,
- SkFloatToScalar(aOptions.mDashOffset));
+ SkDashPathEffect* dash = SkDashPathEffect::Create(&pattern.front(),
+ dashCount,
+ SkFloatToScalar(aOptions.mDashOffset));
SkSafeUnref(aPaint.setPathEffect(dash));
}
@@ -139,86 +154,82 @@ StrokeOptionsToPaint(SkPaint& aPaint, const StrokeOptions &aOptions)
return true;
}
-static inline void
-ConvertBGRXToBGRA(unsigned char* aData, const IntSize &aSize, int32_t aStride)
-{
- uint32_t* pixel = reinterpret_cast<uint32_t*>(aData);
-
- for (int row = 0; row < aSize.height; ++row) {
- for (int column = 0; column < aSize.width; ++column) {
- pixel[column] |= 0xFF000000;
- }
- pixel += (aStride/4);
- }
-}
-
static inline SkXfermode::Mode
GfxOpToSkiaOp(CompositionOp op)
{
switch (op)
{
- case OP_OVER:
+ case CompositionOp::OP_OVER:
return SkXfermode::kSrcOver_Mode;
- case OP_ADD:
+ case CompositionOp::OP_ADD:
return SkXfermode::kPlus_Mode;
- case OP_ATOP:
+ case CompositionOp::OP_ATOP:
return SkXfermode::kSrcATop_Mode;
- case OP_OUT:
+ case CompositionOp::OP_OUT:
return SkXfermode::kSrcOut_Mode;
- case OP_IN:
+ case CompositionOp::OP_IN:
return SkXfermode::kSrcIn_Mode;
- case OP_SOURCE:
+ case CompositionOp::OP_SOURCE:
return SkXfermode::kSrc_Mode;
- case OP_DEST_IN:
+ case CompositionOp::OP_DEST_IN:
return SkXfermode::kDstIn_Mode;
- case OP_DEST_OUT:
+ case CompositionOp::OP_DEST_OUT:
return SkXfermode::kDstOut_Mode;
- case OP_DEST_OVER:
+ case CompositionOp::OP_DEST_OVER:
return SkXfermode::kDstOver_Mode;
- case OP_DEST_ATOP:
+ case CompositionOp::OP_DEST_ATOP:
return SkXfermode::kDstATop_Mode;
- case OP_XOR:
+ case CompositionOp::OP_XOR:
return SkXfermode::kXor_Mode;
- case OP_MULTIPLY:
+ case CompositionOp::OP_MULTIPLY:
return SkXfermode::kMultiply_Mode;
- case OP_SCREEN:
+ case CompositionOp::OP_SCREEN:
return SkXfermode::kScreen_Mode;
- case OP_OVERLAY:
+ case CompositionOp::OP_OVERLAY:
return SkXfermode::kOverlay_Mode;
- case OP_DARKEN:
+ case CompositionOp::OP_DARKEN:
return SkXfermode::kDarken_Mode;
- case OP_LIGHTEN:
+ case CompositionOp::OP_LIGHTEN:
return SkXfermode::kLighten_Mode;
- case OP_COLOR_DODGE:
+ case CompositionOp::OP_COLOR_DODGE:
return SkXfermode::kColorDodge_Mode;
- case OP_COLOR_BURN:
+ case CompositionOp::OP_COLOR_BURN:
return SkXfermode::kColorBurn_Mode;
- case OP_HARD_LIGHT:
+ case CompositionOp::OP_HARD_LIGHT:
return SkXfermode::kHardLight_Mode;
- case OP_SOFT_LIGHT:
+ case CompositionOp::OP_SOFT_LIGHT:
return SkXfermode::kSoftLight_Mode;
- case OP_DIFFERENCE:
+ case CompositionOp::OP_DIFFERENCE:
return SkXfermode::kDifference_Mode;
- case OP_EXCLUSION:
+ case CompositionOp::OP_EXCLUSION:
return SkXfermode::kExclusion_Mode;
- case OP_HUE:
+ case CompositionOp::OP_HUE:
return SkXfermode::kHue_Mode;
- case OP_SATURATION:
+ case CompositionOp::OP_SATURATION:
return SkXfermode::kSaturation_Mode;
- case OP_COLOR:
+ case CompositionOp::OP_COLOR:
return SkXfermode::kColor_Mode;
- case OP_LUMINOSITY:
+ case CompositionOp::OP_LUMINOSITY:
return SkXfermode::kLuminosity_Mode;
default:
return SkXfermode::kSrcOver_Mode;
}
}
-static inline SkColor ColorToSkColor(const Color &color, Float aAlpha)
+/* There's quite a bit of inconsistency about
+ * whether float colors should be rounded with .5f.
+ * We choose to do it to match cairo which also
+ * happens to match the Direct3D specs */
+static inline U8CPU ColorFloatToByte(Float color)
{
//XXX: do a better job converting to int
- return SkColorSetARGB(U8CPU(color.a*aAlpha*255.0), U8CPU(color.r*255.0),
- U8CPU(color.g*255.0), U8CPU(color.b*255.0));
+ return U8CPU(color*255.f + .5f);
+};
+
+static inline SkColor ColorToSkColor(const Color &color, Float aAlpha)
+{
+ return SkColorSetARGB(ColorFloatToByte(color.a*aAlpha), ColorFloatToByte(color.r),
+ ColorFloatToByte(color.g), ColorFloatToByte(color.b));
}
static inline SkRect
@@ -248,21 +259,102 @@ IntRectToSkIRect(const IntRect& aRect)
return SkIRect::MakeXYWH(aRect.x, aRect.y, aRect.width, aRect.height);
}
+static inline Point
+SkPointToPoint(const SkPoint &aPoint)
+{
+ return Point(SkScalarToFloat(aPoint.x()), SkScalarToFloat(aPoint.y()));
+}
+
+static inline Rect
+SkRectToRect(const SkRect &aRect)
+{
+ return Rect(SkScalarToFloat(aRect.x()), SkScalarToFloat(aRect.y()),
+ SkScalarToFloat(aRect.width()), SkScalarToFloat(aRect.height()));
+}
+
static inline SkShader::TileMode
ExtendModeToTileMode(ExtendMode aMode)
{
switch (aMode)
{
- case EXTEND_CLAMP:
+ case ExtendMode::CLAMP:
return SkShader::kClamp_TileMode;
- case EXTEND_REPEAT:
+ case ExtendMode::REPEAT:
return SkShader::kRepeat_TileMode;
- case EXTEND_REFLECT:
+ case ExtendMode::REFLECT:
return SkShader::kMirror_TileMode;
}
return SkShader::kClamp_TileMode;
}
+// The following class was imported from Skia, which is under the
+// following licence:
+//
+// Copyright (c) 2011 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+template <typename T> class RefPtrSkia {
+public:
+ RefPtrSkia() : fObj(NULL) {}
+ explicit RefPtrSkia(T* obj) : fObj(obj) { SkSafeRef(fObj); }
+ RefPtrSkia(const RefPtrSkia& o) : fObj(o.fObj) { SkSafeRef(fObj); }
+ ~RefPtrSkia() { SkSafeUnref(fObj); }
+
+ RefPtrSkia& operator=(const RefPtrSkia& rp) {
+ SkRefCnt_SafeAssign(fObj, rp.fObj);
+ return *this;
+ }
+ RefPtrSkia& operator=(T* obj) {
+ SkRefCnt_SafeAssign(fObj, obj);
+ return *this;
+ }
+
+ T* get() const { return fObj; }
+ T& operator*() const { return *fObj; }
+ T* operator->() const { return fObj; }
+
+ RefPtrSkia& adopt(T* obj) {
+ SkSafeUnref(fObj);
+ fObj = obj;
+ return *this;
+ }
+
+ typedef T* RefPtrSkia::*unspecified_bool_type;
+ operator unspecified_bool_type() const {
+ return fObj ? &RefPtrSkia::fObj : NULL;
+ }
+
+private:
+ T* fObj;
+};
+
+// End of code imported from Skia.
+
}
}
diff --git a/gfx/2d/ImageScaling.cpp b/gfx/2d/ImageScaling.cpp
index 313782e8e..62d08d056 100644
--- a/gfx/2d/ImageScaling.cpp
+++ b/gfx/2d/ImageScaling.cpp
@@ -5,6 +5,7 @@
#include "ImageScaling.h"
#include "2D.h"
+#include "DataSurfaceHelpers.h"
#include <math.h>
#include <algorithm>
@@ -77,7 +78,13 @@ ImageHalfScaler::ScaleForSize(const IntSize &aSize)
delete [] mDataStorage;
// Allocate 15 bytes extra to make sure we can get 16 byte alignment. We
// should add tools for this, see bug 751696.
- mDataStorage = new uint8_t[internalSurfSize.height * mStride + 15];
+ size_t bufLen = BufferSizeFromStrideAndHeight(mStride, internalSurfSize.height, 15);
+ if (bufLen == 0) {
+ mSize.SizeTo(0, 0);
+ mDataStorage = nullptr;
+ return;
+ }
+ mDataStorage = new uint8_t[bufLen];
if (uintptr_t(mDataStorage) % 16) {
// Our storage does not start at a 16-byte boundary. Make sure mData does!
diff --git a/gfx/2d/ImageScalingSSE2.cpp b/gfx/2d/ImageScalingSSE2.cpp
index f87653b4c..92bad43a1 100644
--- a/gfx/2d/ImageScalingSSE2.cpp
+++ b/gfx/2d/ImageScalingSSE2.cpp
@@ -248,7 +248,7 @@ ImageHalfScaler::HalfImageVertical_SSE2(uint8_t *aSource, int32_t aSourceStride,
*storage++ = avg_sse2_4x2_4x1(a, b);
}
- } else if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
+ } else if (!(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
for (; x < (aSourceSize.width - 3); x += 4) {
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
diff --git a/gfx/2d/Logging.h b/gfx/2d/Logging.h
index 4911293a2..54803fdfb 100644
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -9,57 +9,210 @@
#include <string>
#include <sstream>
#include <stdio.h>
+#include <vector>
+#ifdef MOZ_LOGGING
+#include <prlog.h>
+#endif
+
+#if defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID)
+#include "nsDebug.h"
+#endif
#include "Point.h"
+#include "BaseRect.h"
#include "Matrix.h"
#ifdef WIN32
-#include <windows.h>
+// This file gets included from nsGlobalWindow.cpp, which doesn't like
+// having windows.h included in it. Since OutputDebugStringA is the only
+// thing we need from windows.h, we just declare it here directly.
+// Note: the function's documented signature is
+// WINBASEAPI void WINAPI OutputDebugStringA(LPCSTR lpOutputString)
+// but if we don't include windows.h, the macros WINBASEAPI, WINAPI, and
+// LPCSTR are not defined, so we need to replace them with their expansions.
+extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString);
#endif
-#ifdef PR_LOGGING
-#include <prlog.h>
-
-extern PRLogModuleInfo *GetGFX2DLog();
+#if defined(PR_LOGGING)
+extern GFX2D_API PRLogModuleInfo *GetGFX2DLog();
#endif
namespace mozilla {
namespace gfx {
-const int LOG_DEBUG = 1;
+// Attempting to be consistent with prlog values, but that isn't critical
+// (and note that 5 has a special meaning - see the description
+// with sGfxLogLevel)
+const int LOG_CRITICAL = 1;
const int LOG_WARNING = 2;
+const int LOG_DEBUG = 3;
+const int LOG_DEBUG_PRLOG = 4;
+const int LOG_EVERYTHING = 5; // This needs to be the highest value
-#ifdef PR_LOGGING
+#if defined(DEBUG)
+const int LOG_DEFAULT = LOG_EVERYTHING;
+#else
+const int LOG_DEFAULT = LOG_CRITICAL;
+#endif
+
+#if defined(PR_LOGGING)
inline PRLogModuleLevel PRLogLevelForLevel(int aLevel) {
switch (aLevel) {
- case LOG_DEBUG:
- return PR_LOG_DEBUG;
+ case LOG_CRITICAL:
+ return PR_LOG_ERROR;
case LOG_WARNING:
return PR_LOG_WARNING;
+ case LOG_DEBUG:
+ return PR_LOG_DEBUG;
+ case LOG_DEBUG_PRLOG:
+ return PR_LOG_DEBUG;
+ case LOG_EVERYTHING:
+ return PR_LOG_ALWAYS;
}
return PR_LOG_DEBUG;
}
#endif
-extern int sGfxLogLevel;
+class PreferenceAccess
+{
+public:
+ virtual ~PreferenceAccess();
+
+ // This should connect the variable aVar to be updated whenever a preference
+ // aName is modified. aDefault would be used if the preference is undefined,
+ // so that we always get the valid value for aVar.
+ virtual void LivePref(const char* aName, int32_t* aVar, int32_t aDefault);
-static inline void OutputMessage(const std::string &aString, int aLevel) {
-#if defined(WIN32) && !defined(PR_LOGGING)
- if (aLevel >= sGfxLogLevel) {
- ::OutputDebugStringA(aString.c_str());
+public:
+ static void SetAccess(PreferenceAccess* aAccess);
+
+public:
+ // For each preference that needs to be accessed in Moz2D, add a variable
+ // to hold it, as well as the call to LivePref in the RegisterAll() method
+ // below.
+
+ // Used to choose the level of logging we get. The higher the number,
+ // the more logging we get. Value of zero will give you no logging,
+ // 1 just errors, 2 adds warnings and 3 adds logging/debug. 4 is used to
+ // selectively enable logging on the configurations that
+ // support prlog (on other systems, 3 and 4 are the same.) For prlog,
+ // in addition to setting the value to 4, you will need to set an
+ // environment variable NSPR_LOG_MODULES to gfx:4. See prlog.h for details.
+ static int32_t sGfxLogLevel;
+
+private:
+ static void RegisterAll() {
+ // The default values (last parameter) should match the initialization
+ // values in Factory.cpp, otherwise the standalone Moz2D will get different
+ // defaults.
+ sAccess->LivePref("gfx.logging.level", &sGfxLogLevel, LOG_DEFAULT);
}
+ static PreferenceAccess* sAccess;
+};
+
+/// Graphics logging is available in both debug and release builds and is
+/// controlled with a gfx.logging.level preference. If not set, the default
+/// for the preference is 5 in the debug builds, 1 in the release builds.
+///
+/// gfxDebug only works in the debug builds, and is used for information
+/// level messages, helping with debugging. In addition to only working
+/// in the debug builds, the value of the above preference of 3 or higher
+/// is required.
+///
+/// gfxWarning messages are available in both debug and release builds,
+/// on by default in the debug builds, and off by default in the release builds.
+/// Setting the preference gfx.logging.level to a value of 2 or higher will
+/// show the warnings.
+///
+/// gfxCriticalError is available in debug and release builds by default.
+/// It is only unavailable if gfx.logging.level is set to 0 (or less.)
+/// It outputs the message to stderr or equivalent, like gfxWarning.
+/// In the event of a crash, the crash report is annotated with first and
+/// the last few of these errors, under the key GraphicsCriticalError.
+/// The total number of errors stored in the crash report is controlled
+/// by preference gfx.logging.crash.length (default is six, so by default,
+/// the first as well as the last five would show up in the crash log.)
+///
+/// On platforms that support PR_LOGGING, the story is slightly more involved.
+/// In that case, unless gfx.logging.level is set to 4 or higher, the output
+/// is further controlled by "gfx2d" PR logging module. However, in the case
+/// where such module would disable the output, in all but gfxDebug cases,
+/// we will still send a printf.
+struct BasicLogger
+{
+ // For efficiency, this method exists and copies the logic of the
+ // OutputMessage below. If making any changes here, also make it
+ // in the appropriate places in that method.
+ static bool ShouldOutputMessage(int aLevel) {
+ if (PreferenceAccess::sGfxLogLevel >= aLevel) {
+#if defined(WIN32) && !defined(PR_LOGGING)
+ return true;
+#elif defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID)
+ return true;
#elif defined(PR_LOGGING)
- if (PR_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
- PR_LogPrint(aString.c_str());
- }
+ if (PR_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
+ return true;
+ } else if ((PreferenceAccess::sGfxLogLevel >= LOG_DEBUG_PRLOG) ||
+ (aLevel < LOG_DEBUG)) {
+ return true;
+ }
#else
- if (aLevel >= sGfxLogLevel) {
- printf("%s", aString.c_str());
+ return true;
+#endif
+ }
+ return false;
}
+
+ static void OutputMessage(const std::string &aString,
+ int aLevel,
+ bool aNoNewline) {
+ // This behavior (the higher the preference, the more we log)
+ // is consistent with what prlog does in general. Note that if prlog
+ // is in the build, but disabled, we will printf if the preferences
+ // requires us to log something (see sGfxLogLevel for the special
+ // treatment of LOG_DEBUG and LOG_DEBUG_PRLOG)
+ //
+ // If making any logic changes to this method, you should probably
+ // make the corresponding change in the ShouldOutputMessage method
+ // above.
+ if (PreferenceAccess::sGfxLogLevel >= aLevel) {
+#if defined(WIN32) && !defined(PR_LOGGING)
+ ::OutputDebugStringA((aNoNewline ? aString : aString+"\n").c_str());
+#elif defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID)
+ printf_stderr("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
+#elif defined(PR_LOGGING)
+ if (PR_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
+ PR_LogPrint("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
+ } else if ((PreferenceAccess::sGfxLogLevel >= LOG_DEBUG_PRLOG) ||
+ (aLevel < LOG_DEBUG)) {
+ printf("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
+ }
+#else
+ printf("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
#endif
-}
+ }
+ }
+};
+
+struct CriticalLogger {
+ static void OutputMessage(const std::string &aString, int aLevel, bool aNoNewline);
+};
+
+// Implement this interface and init the Factory with an instance to
+// forward critical logs.
+class LogForwarder {
+public:
+ virtual ~LogForwarder() {}
+ virtual void Log(const std::string &aString) = 0;
+
+ // Provide a copy of the logs to the caller. The int is the index
+ // of the Log call, if the number of logs exceeds some preset capacity
+ // we may not get all of them, so the indices help figure out which
+ // ones we did save.
+ virtual std::vector<std::pair<int32_t,std::string> > StringsVectorCopy() = 0;
+};
class NoLog
{
@@ -71,45 +224,402 @@ public:
NoLog &operator <<(const T &aLogText) { return *this; }
};
-template<int L>
+enum class LogOptions : int {
+ NoNewline = 0x01,
+ AutoPrefix = 0x02,
+ AssertOnCall = 0x04
+};
+
+template<typename T>
+struct Hexa {
+ explicit Hexa(T aVal) : mVal(aVal) {}
+ T mVal;
+};
+template<typename T>
+Hexa<T> hexa(T val) { return Hexa<T>(val); }
+
+template<int L, typename Logger = BasicLogger>
class Log
{
public:
- Log() {}
- ~Log() { mMessage << '\n'; WriteLog(mMessage.str()); }
+ // The default is to have the prefix, have the new line, and for critical
+ // logs assert on each call.
+ static int DefaultOptions(bool aWithAssert = true) {
+ return (int(LogOptions::AutoPrefix) |
+ (aWithAssert ? int(LogOptions::AssertOnCall) : 0));
+ }
- Log &operator <<(const std::string &aLogText) { mMessage << aLogText; return *this; }
- Log &operator <<(unsigned int aInt) { mMessage << aInt; return *this; }
- Log &operator <<(const Size &aSize)
- { mMessage << "(" << aSize.width << "x" << aSize.height << ")"; return *this; }
- Log &operator <<(const IntSize &aSize)
- { mMessage << "(" << aSize.width << "x" << aSize.height << ")"; return *this; }
- Log &operator<<(const Matrix& aMatrix)
- { mMessage << "[ " << aMatrix._11 << " " << aMatrix._12 << " ; " << aMatrix._21 << " " << aMatrix._22 << " ; " << aMatrix._31 << " " << aMatrix._32 << " ]"; return *this; }
+ // Note that we're calling BasicLogger::ShouldOutputMessage, rather than
+ // Logger::ShouldOutputMessage. Since we currently don't have a different
+ // version of that method for different loggers, this is OK. Once we do,
+ // change BasicLogger::ShouldOutputMessage to Logger::ShouldOutputMessage.
+ explicit Log(int aOptions = Log::DefaultOptions(L == LOG_CRITICAL))
+ : mOptions(aOptions)
+ , mLogIt(BasicLogger::ShouldOutputMessage(L))
+ {
+ if (mLogIt && AutoPrefix()) {
+ if (mOptions & int(LogOptions::AssertOnCall)) {
+ mMessage << "[GFX" << L << "]: ";
+ } else {
+ mMessage << "[GFX" << L << "-]: ";
+ }
+ }
+ }
+ ~Log() {
+ Flush();
+ }
+ void Flush() {
+ if (MOZ_LIKELY(!LogIt())) return;
-private:
+ std::string str = mMessage.str();
+ if (!str.empty()) {
+ WriteLog(str);
+ }
+ if (AutoPrefix()) {
+ mMessage.str("[GFX");
+ mMessage << L << "]: ";
+ } else {
+ mMessage.str("");
+ }
+ mMessage.clear();
+ }
+ Log &operator <<(char aChar) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aChar;
+ }
+ return *this;
+ }
+ Log &operator <<(const std::string &aLogText) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aLogText;
+ }
+ return *this;
+ }
+ Log &operator <<(const char aStr[]) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << static_cast<const char*>(aStr);
+ }
+ return *this;
+ }
+ Log &operator <<(bool aBool) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << (aBool ? "true" : "false");
+ }
+ return *this;
+ }
+ Log &operator <<(int aInt) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aInt;
+ }
+ return *this;
+ }
+ Log &operator <<(unsigned int aInt) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aInt;
+ }
+ return *this;
+ }
+ Log &operator <<(long aLong) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aLong;
+ }
+ return *this;
+ }
+ Log &operator <<(unsigned long aLong) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aLong;
+ }
+ return *this;
+ }
+ Log &operator <<(long long aLong) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aLong;
+ }
+ return *this;
+ }
+ Log &operator <<(unsigned long long aLong) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aLong;
+ }
+ return *this;
+ }
+ Log &operator <<(Float aFloat) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aFloat;
+ }
+ return *this;
+ }
+ Log &operator <<(double aDouble) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << aDouble;
+ }
+ return *this;
+ }
+ template <typename T, typename Sub, typename Coord>
+ Log &operator <<(const BasePoint<T, Sub, Coord>& aPoint) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << "Point" << aPoint;
+ }
+ return *this;
+ }
+ template <typename T, typename Sub>
+ Log &operator <<(const BaseSize<T, Sub>& aSize) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << "Size(" << aSize.width << "," << aSize.height << ")";
+ }
+ return *this;
+ }
+ template <typename T, typename Sub, typename Point, typename SizeT, typename Margin>
+ Log &operator <<(const BaseRect<T, Sub, Point, SizeT, Margin>& aRect) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << "Rect" << aRect;
+ }
+ return *this;
+ }
+ Log &operator<<(const Matrix& aMatrix) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << "Matrix(" << aMatrix._11 << " " << aMatrix._12 << " ; " << aMatrix._21 << " " << aMatrix._22 << " ; " << aMatrix._31 << " " << aMatrix._32 << ")";
+ }
+ return *this;
+ }
+ template<typename T>
+ Log &operator<<(Hexa<T> aHex) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ mMessage << "0x" << std::hex << aHex.mVal << std::dec;
+ }
+ return *this;
+ }
+
+ Log& operator<<(SurfaceFormat aFormat) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ switch(aFormat) {
+ case SurfaceFormat::B8G8R8A8:
+ mMessage << "SurfaceFormat::B8G8R8A8";
+ break;
+ case SurfaceFormat::B8G8R8X8:
+ mMessage << "SurfaceFormat::B8G8R8X8";
+ break;
+ case SurfaceFormat::R8G8B8A8:
+ mMessage << "SurfaceFormat::R8G8B8A8";
+ break;
+ case SurfaceFormat::R8G8B8X8:
+ mMessage << "SurfaceFormat::R8G8B8X8";
+ break;
+ case SurfaceFormat::R5G6B5:
+ mMessage << "SurfaceFormat::R5G6B5";
+ break;
+ case SurfaceFormat::A8:
+ mMessage << "SurfaceFormat::A8";
+ break;
+ case SurfaceFormat::YUV:
+ mMessage << "SurfaceFormat::YUV";
+ break;
+ case SurfaceFormat::UNKNOWN:
+ mMessage << "SurfaceFormat::UNKNOWN";
+ break;
+ default:
+ mMessage << "Invalid SurfaceFormat (" << (int)aFormat << ")";
+ break;
+ }
+ }
+ return *this;
+ }
+
+ Log& operator<<(SurfaceType aType) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ switch(aType) {
+ case SurfaceType::DATA:
+ mMessage << "SurfaceType::DATA";
+ break;
+ case SurfaceType::D2D1_BITMAP:
+ mMessage << "SurfaceType::D2D1_BITMAP";
+ break;
+ case SurfaceType::D2D1_DRAWTARGET:
+ mMessage << "SurfaceType::D2D1_DRAWTARGET";
+ break;
+ case SurfaceType::CAIRO:
+ mMessage << "SurfaceType::CAIRO";
+ break;
+ case SurfaceType::CAIRO_IMAGE:
+ mMessage << "SurfaceType::CAIRO_IMAGE";
+ break;
+ case SurfaceType::COREGRAPHICS_IMAGE:
+ mMessage << "SurfaceType::COREGRAPHICS_IMAGE";
+ break;
+ case SurfaceType::COREGRAPHICS_CGCONTEXT:
+ mMessage << "SurfaceType::COREGRAPHICS_CGCONTEXT";
+ break;
+ case SurfaceType::SKIA:
+ mMessage << "SurfaceType::SKIA";
+ break;
+ case SurfaceType::DUAL_DT:
+ mMessage << "SurfaceType::DUAL_DT";
+ break;
+ case SurfaceType::D2D1_1_IMAGE:
+ mMessage << "SurfaceType::D2D1_1_IMAGE";
+ break;
+ case SurfaceType::RECORDING:
+ mMessage << "SurfaceType::RECORDING";
+ break;
+ case SurfaceType::TILED:
+ mMessage << "SurfaceType::TILED";
+ break;
+ default:
+ mMessage << "Invalid SurfaceType (" << (int)aType << ")";
+ break;
+ }
+ }
+ return *this;
+ }
+
+ inline bool LogIt() const { return mLogIt; }
+ inline bool NoNewline() const { return mOptions & int(LogOptions::NoNewline); }
+ inline bool AutoPrefix() const { return mOptions & int(LogOptions::AutoPrefix); }
+
+
+private:
void WriteLog(const std::string &aString) {
- OutputMessage(aString, L);
+ if (MOZ_UNLIKELY(LogIt())) {
+ Logger::OutputMessage(aString, L, NoNewline());
+ if (mOptions & int(LogOptions::AssertOnCall)) {
+ MOZ_ASSERT(false, "An assert from the graphics logger");
+ }
+ }
}
std::stringstream mMessage;
+ int mOptions;
+ bool mLogIt;
};
typedef Log<LOG_DEBUG> DebugLog;
typedef Log<LOG_WARNING> WarningLog;
+typedef Log<LOG_CRITICAL, CriticalLogger> CriticalLog;
#ifdef GFX_LOG_DEBUG
-#define gfxDebug DebugLog
+#define gfxDebug mozilla::gfx::DebugLog
#else
-#define gfxDebug if (1) ; else NoLog
+#define gfxDebug if (1) ; else mozilla::gfx::NoLog
#endif
#ifdef GFX_LOG_WARNING
-#define gfxWarning WarningLog
+#define gfxWarning mozilla::gfx::WarningLog
+#else
+#define gfxWarning if (1) ; else mozilla::gfx::NoLog
+#endif
+
+// This log goes into crash reports, use with care.
+#define gfxCriticalError mozilla::gfx::CriticalLog
+
+// See nsDebug.h and the NS_WARN_IF macro
+
+#ifdef __cplusplus
+ // For now, have MOZ2D_ERROR_IF available in debug and non-debug builds
+inline bool MOZ2D_error_if_impl(bool aCondition, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ if (MOZ_UNLIKELY(aCondition)) {
+ gfxCriticalError() << aExpr << " at " << aFile << ":" << aLine;
+ }
+ return aCondition;
+}
+#define MOZ2D_ERROR_IF(condition) \
+ MOZ2D_error_if_impl(condition, #condition, __FILE__, __LINE__)
+
+#ifdef DEBUG
+inline bool MOZ2D_warn_if_impl(bool aCondition, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ if (MOZ_UNLIKELY(aCondition)) {
+ gfxWarning() << aExpr << " at " << aFile << ":" << aLine;
+ }
+ return aCondition;
+}
+#define MOZ2D_WARN_IF(condition) \
+ MOZ2D_warn_if_impl(condition, #condition, __FILE__, __LINE__)
#else
-#define gfxWarning if (1) ; else NoLog
+#define MOZ2D_WARN_IF(condition) (bool)(condition)
#endif
+#endif
+
+const int INDENT_PER_LEVEL = 2;
+
+class TreeLog
+{
+public:
+ explicit TreeLog(const std::string& aPrefix = "")
+ : mLog(int(LogOptions::NoNewline)),
+ mPrefix(aPrefix),
+ mDepth(0),
+ mStartOfLine(true),
+ mConditionedOnPref(false),
+ mPrefFunction(nullptr) {}
+
+ template <typename T>
+ TreeLog& operator<<(const T& aObject) {
+ if (mConditionedOnPref && !mPrefFunction()) {
+ return *this;
+ }
+ if (mStartOfLine) {
+ mLog << '[' << mPrefix << "] " << std::string(mDepth * INDENT_PER_LEVEL, ' ');
+ mStartOfLine = false;
+ }
+ mLog << aObject;
+ if (EndsInNewline(aObject)) {
+ // Don't indent right here as the user may change the indent
+ // between now and the first output to the next line.
+ mLog.Flush();
+ mStartOfLine = true;
+ }
+ return *this;
+ }
+
+ void IncreaseIndent() { ++mDepth; }
+ void DecreaseIndent() { --mDepth; }
+
+ void ConditionOnPrefFunction(bool(*aPrefFunction)()) {
+ mConditionedOnPref = true;
+ mPrefFunction = aPrefFunction;
+ }
+private:
+ Log<LOG_DEBUG> mLog;
+ std::string mPrefix;
+ uint32_t mDepth;
+ bool mStartOfLine;
+ bool mConditionedOnPref;
+ bool (*mPrefFunction)();
+
+ template <typename T>
+ static bool EndsInNewline(const T& aObject) {
+ return false;
+ }
+
+ static bool EndsInNewline(const std::string& aString) {
+ return !aString.empty() && aString[aString.length() - 1] == '\n';
+ }
+
+ static bool EndsInNewline(char aChar) {
+ return aChar == '\n';
+ }
+
+ static bool EndsInNewline(const char* aString) {
+ return EndsInNewline(std::string(aString));
+ }
+};
+
+class TreeAutoIndent
+{
+public:
+ explicit TreeAutoIndent(TreeLog& aTreeLog) : mTreeLog(aTreeLog) {
+ mTreeLog.IncreaseIndent();
+ }
+ ~TreeAutoIndent() {
+ mTreeLog.DecreaseIndent();
+ }
+private:
+ TreeLog& mTreeLog;
+};
}
}
diff --git a/gfx/2d/MacIOSurface.cpp b/gfx/2d/MacIOSurface.cpp
new file mode 100644
index 000000000..8d0696685
--- /dev/null
+++ b/gfx/2d/MacIOSurface.cpp
@@ -0,0 +1,536 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+/* 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/. */
+
+#include "MacIOSurface.h"
+#include <OpenGL/gl.h>
+#include <QuartzCore/QuartzCore.h>
+#include <dlfcn.h>
+#include "mozilla/RefPtr.h"
+#include "mozilla/Assertions.h"
+
+using namespace mozilla;
+// IOSurface signatures
+#define IOSURFACE_FRAMEWORK_PATH \
+ "/System/Library/Frameworks/IOSurface.framework/IOSurface"
+#define OPENGL_FRAMEWORK_PATH \
+ "/System/Library/Frameworks/OpenGL.framework/OpenGL"
+#define COREGRAPHICS_FRAMEWORK_PATH \
+ "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/" \
+ "CoreGraphics.framework/CoreGraphics"
+#define COREVIDEO_FRAMEWORK_PATH \
+ "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/" \
+ "CoreVideo.framework/CoreVideo"
+
+#define GET_CONST(const_name) \
+ ((CFStringRef*) dlsym(sIOSurfaceFramework, const_name))
+#define GET_IOSYM(dest,sym_name) \
+ (typeof(dest)) dlsym(sIOSurfaceFramework, sym_name)
+#define GET_CGLSYM(dest,sym_name) \
+ (typeof(dest)) dlsym(sOpenGLFramework, sym_name)
+#define GET_CGSYM(dest,sym_name) \
+ (typeof(dest)) dlsym(sCoreGraphicsFramework, sym_name)
+#define GET_CVSYM(dest, sym_name) \
+ (typeof(dest)) dlsym(sCoreVideoFramework, sym_name)
+
+MacIOSurfaceLib::LibraryUnloader MacIOSurfaceLib::sLibraryUnloader;
+bool MacIOSurfaceLib::isLoaded = false;
+void* MacIOSurfaceLib::sIOSurfaceFramework;
+void* MacIOSurfaceLib::sOpenGLFramework;
+void* MacIOSurfaceLib::sCoreGraphicsFramework;
+void* MacIOSurfaceLib::sCoreVideoFramework;
+IOSurfaceCreateFunc MacIOSurfaceLib::sCreate;
+IOSurfaceGetIDFunc MacIOSurfaceLib::sGetID;
+IOSurfaceLookupFunc MacIOSurfaceLib::sLookup;
+IOSurfaceGetBaseAddressFunc MacIOSurfaceLib::sGetBaseAddress;
+IOSurfaceGetBaseAddressOfPlaneFunc MacIOSurfaceLib::sGetBaseAddressOfPlane;
+IOSurfaceSizeTFunc MacIOSurfaceLib::sWidth;
+IOSurfaceSizeTFunc MacIOSurfaceLib::sHeight;
+IOSurfaceSizeTFunc MacIOSurfaceLib::sPlaneCount;
+IOSurfaceSizeTFunc MacIOSurfaceLib::sBytesPerRow;
+IOSurfaceGetPropertyMaximumFunc MacIOSurfaceLib::sGetPropertyMaximum;
+IOSurfaceVoidFunc MacIOSurfaceLib::sIncrementUseCount;
+IOSurfaceVoidFunc MacIOSurfaceLib::sDecrementUseCount;
+IOSurfaceLockFunc MacIOSurfaceLib::sLock;
+IOSurfaceUnlockFunc MacIOSurfaceLib::sUnlock;
+CGLTexImageIOSurface2DFunc MacIOSurfaceLib::sTexImage;
+IOSurfaceContextCreateFunc MacIOSurfaceLib::sIOSurfaceContextCreate;
+IOSurfaceContextCreateImageFunc MacIOSurfaceLib::sIOSurfaceContextCreateImage;
+IOSurfaceContextGetSurfaceFunc MacIOSurfaceLib::sIOSurfaceContextGetSurface;
+CVPixelBufferGetIOSurfaceFunc MacIOSurfaceLib::sCVPixelBufferGetIOSurface;
+unsigned int (*MacIOSurfaceLib::sCGContextGetTypePtr) (CGContextRef) = nullptr;
+
+CFStringRef MacIOSurfaceLib::kPropWidth;
+CFStringRef MacIOSurfaceLib::kPropHeight;
+CFStringRef MacIOSurfaceLib::kPropBytesPerElem;
+CFStringRef MacIOSurfaceLib::kPropBytesPerRow;
+CFStringRef MacIOSurfaceLib::kPropIsGlobal;
+
+bool MacIOSurfaceLib::isInit() {
+ // Guard against trying to reload the library
+ // if it is not available.
+ if (!isLoaded)
+ LoadLibrary();
+ MOZ_ASSERT(sIOSurfaceFramework);
+ return sIOSurfaceFramework;
+}
+
+IOSurfacePtr MacIOSurfaceLib::IOSurfaceCreate(CFDictionaryRef properties) {
+ return sCreate(properties);
+}
+
+IOSurfacePtr MacIOSurfaceLib::IOSurfaceLookup(IOSurfaceID aIOSurfaceID) {
+ return sLookup(aIOSurfaceID);
+}
+
+IOSurfaceID MacIOSurfaceLib::IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr) {
+ return sGetID(aIOSurfacePtr);
+}
+
+void* MacIOSurfaceLib::IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr) {
+ return sGetBaseAddress(aIOSurfacePtr);
+}
+
+void* MacIOSurfaceLib::IOSurfaceGetBaseAddressOfPlane(IOSurfacePtr aIOSurfacePtr,
+ size_t planeIndex) {
+ return sGetBaseAddressOfPlane(aIOSurfacePtr, planeIndex);
+}
+
+size_t MacIOSurfaceLib::IOSurfaceGetPlaneCount(IOSurfacePtr aIOSurfacePtr) {
+ return sPlaneCount(aIOSurfacePtr);
+}
+
+size_t MacIOSurfaceLib::IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr) {
+ return sWidth(aIOSurfacePtr);
+}
+
+size_t MacIOSurfaceLib::IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr) {
+ return sHeight(aIOSurfacePtr);
+}
+
+size_t MacIOSurfaceLib::IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr) {
+ return sBytesPerRow(aIOSurfacePtr);
+}
+
+size_t MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(CFStringRef property) {
+ return sGetPropertyMaximum(property);
+}
+
+IOReturn MacIOSurfaceLib::IOSurfaceLock(IOSurfacePtr aIOSurfacePtr,
+ uint32_t options, uint32_t* seed) {
+ return sLock(aIOSurfacePtr, options, seed);
+}
+
+IOReturn MacIOSurfaceLib::IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr,
+ uint32_t options, uint32_t *seed) {
+ return sUnlock(aIOSurfacePtr, options, seed);
+}
+
+void MacIOSurfaceLib::IOSurfaceIncrementUseCount(IOSurfacePtr aIOSurfacePtr) {
+ sIncrementUseCount(aIOSurfacePtr);
+}
+
+void MacIOSurfaceLib::IOSurfaceDecrementUseCount(IOSurfacePtr aIOSurfacePtr) {
+ sDecrementUseCount(aIOSurfacePtr);
+}
+
+CGLError MacIOSurfaceLib::CGLTexImageIOSurface2D(CGLContextObj ctxt,
+ GLenum target, GLenum internalFormat,
+ GLsizei width, GLsizei height,
+ GLenum format, GLenum type,
+ IOSurfacePtr ioSurface, GLuint plane) {
+ return sTexImage(ctxt, target, internalFormat, width, height,
+ format, type, ioSurface, plane);
+}
+
+IOSurfacePtr MacIOSurfaceLib::CVPixelBufferGetIOSurface(CVPixelBufferRef aPixelBuffer) {
+ return sCVPixelBufferGetIOSurface(aPixelBuffer);
+}
+
+CGContextRef MacIOSurfaceLib::IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr,
+ unsigned aWidth, unsigned aHeight,
+ unsigned aBitsPerComponent, unsigned aBytes,
+ CGColorSpaceRef aColorSpace, CGBitmapInfo bitmapInfo) {
+ if (!sIOSurfaceContextCreate)
+ return nullptr;
+ return sIOSurfaceContextCreate(aIOSurfacePtr, aWidth, aHeight, aBitsPerComponent, aBytes, aColorSpace, bitmapInfo);
+}
+
+CGImageRef MacIOSurfaceLib::IOSurfaceContextCreateImage(CGContextRef aContext) {
+ if (!sIOSurfaceContextCreateImage)
+ return nullptr;
+ return sIOSurfaceContextCreateImage(aContext);
+}
+
+IOSurfacePtr MacIOSurfaceLib::IOSurfaceContextGetSurface(CGContextRef aContext) {
+ if (!sIOSurfaceContextGetSurface)
+ return nullptr;
+ return sIOSurfaceContextGetSurface(aContext);
+}
+
+CFStringRef MacIOSurfaceLib::GetIOConst(const char* symbole) {
+ CFStringRef *address = (CFStringRef*)dlsym(sIOSurfaceFramework, symbole);
+ if (!address)
+ return nullptr;
+
+ return *address;
+}
+
+void MacIOSurfaceLib::LoadLibrary() {
+ if (isLoaded) {
+ return;
+ }
+ isLoaded = true;
+ sIOSurfaceFramework = dlopen(IOSURFACE_FRAMEWORK_PATH,
+ RTLD_LAZY | RTLD_LOCAL);
+ sOpenGLFramework = dlopen(OPENGL_FRAMEWORK_PATH,
+ RTLD_LAZY | RTLD_LOCAL);
+
+ sCoreGraphicsFramework = dlopen(COREGRAPHICS_FRAMEWORK_PATH,
+ RTLD_LAZY | RTLD_LOCAL);
+
+ sCoreVideoFramework = dlopen(COREVIDEO_FRAMEWORK_PATH,
+ RTLD_LAZY | RTLD_LOCAL);
+
+ if (!sIOSurfaceFramework || !sOpenGLFramework || !sCoreGraphicsFramework ||
+ !sCoreVideoFramework) {
+ if (sIOSurfaceFramework)
+ dlclose(sIOSurfaceFramework);
+ if (sOpenGLFramework)
+ dlclose(sOpenGLFramework);
+ if (sCoreGraphicsFramework)
+ dlclose(sCoreGraphicsFramework);
+ if (sCoreVideoFramework)
+ dlclose(sCoreVideoFramework);
+ sIOSurfaceFramework = nullptr;
+ sOpenGLFramework = nullptr;
+ sCoreGraphicsFramework = nullptr;
+ sCoreVideoFramework = nullptr;
+ return;
+ }
+
+ kPropWidth = GetIOConst("kIOSurfaceWidth");
+ kPropHeight = GetIOConst("kIOSurfaceHeight");
+ kPropBytesPerElem = GetIOConst("kIOSurfaceBytesPerElement");
+ kPropBytesPerRow = GetIOConst("kIOSurfaceBytesPerRow");
+ kPropIsGlobal = GetIOConst("kIOSurfaceIsGlobal");
+ sCreate = GET_IOSYM(sCreate, "IOSurfaceCreate");
+ sGetID = GET_IOSYM(sGetID, "IOSurfaceGetID");
+ sWidth = GET_IOSYM(sWidth, "IOSurfaceGetWidth");
+ sHeight = GET_IOSYM(sHeight, "IOSurfaceGetHeight");
+ sBytesPerRow = GET_IOSYM(sBytesPerRow, "IOSurfaceGetBytesPerRow");
+ sGetPropertyMaximum = GET_IOSYM(sGetPropertyMaximum, "IOSurfaceGetPropertyMaximum");
+ sLookup = GET_IOSYM(sLookup, "IOSurfaceLookup");
+ sLock = GET_IOSYM(sLock, "IOSurfaceLock");
+ sUnlock = GET_IOSYM(sUnlock, "IOSurfaceUnlock");
+ sIncrementUseCount =
+ GET_IOSYM(sIncrementUseCount, "IOSurfaceIncrementUseCount");
+ sDecrementUseCount =
+ GET_IOSYM(sDecrementUseCount, "IOSurfaceDecrementUseCount");
+ sGetBaseAddress = GET_IOSYM(sGetBaseAddress, "IOSurfaceGetBaseAddress");
+ sGetBaseAddressOfPlane =
+ GET_IOSYM(sGetBaseAddressOfPlane, "IOSurfaceGetBaseAddressOfPlane");
+ sPlaneCount = GET_IOSYM(sPlaneCount, "IOSurfaceGetPlaneCount");
+
+ sTexImage = GET_CGLSYM(sTexImage, "CGLTexImageIOSurface2D");
+ sCGContextGetTypePtr = (unsigned int (*)(CGContext*))dlsym(RTLD_DEFAULT, "CGContextGetType");
+
+ sCVPixelBufferGetIOSurface =
+ GET_CVSYM(sCVPixelBufferGetIOSurface, "CVPixelBufferGetIOSurface");
+
+ // Optional symbols
+ sIOSurfaceContextCreate = GET_CGSYM(sIOSurfaceContextCreate, "CGIOSurfaceContextCreate");
+ sIOSurfaceContextCreateImage = GET_CGSYM(sIOSurfaceContextCreateImage, "CGIOSurfaceContextCreateImage");
+ sIOSurfaceContextGetSurface = GET_CGSYM(sIOSurfaceContextGetSurface, "CGIOSurfaceContextGetSurface");
+
+ if (!sCreate || !sGetID || !sLookup || !sTexImage || !sGetBaseAddress ||
+ !sGetBaseAddressOfPlane || !sPlaneCount ||
+ !kPropWidth || !kPropHeight || !kPropBytesPerElem || !kPropIsGlobal ||
+ !sLock || !sUnlock || !sIncrementUseCount || !sDecrementUseCount ||
+ !sWidth || !sHeight || !kPropBytesPerRow ||
+ !sBytesPerRow || !sGetPropertyMaximum || !sCVPixelBufferGetIOSurface) {
+ CloseLibrary();
+ }
+}
+
+void MacIOSurfaceLib::CloseLibrary() {
+ if (sIOSurfaceFramework) {
+ dlclose(sIOSurfaceFramework);
+ }
+ if (sOpenGLFramework) {
+ dlclose(sOpenGLFramework);
+ }
+ if (sCoreVideoFramework) {
+ dlclose(sCoreVideoFramework);
+ }
+ sIOSurfaceFramework = nullptr;
+ sOpenGLFramework = nullptr;
+ sCoreVideoFramework = nullptr;
+}
+
+MacIOSurface::MacIOSurface(const void* aIOSurfacePtr,
+ double aContentsScaleFactor, bool aHasAlpha)
+ : mIOSurfacePtr(aIOSurfacePtr)
+ , mContentsScaleFactor(aContentsScaleFactor)
+ , mHasAlpha(aHasAlpha)
+{
+ CFRetain(mIOSurfacePtr);
+ IncrementUseCount();
+}
+
+MacIOSurface::~MacIOSurface() {
+ DecrementUseCount();
+ CFRelease(mIOSurfacePtr);
+}
+
+TemporaryRef<MacIOSurface> MacIOSurface::CreateIOSurface(int aWidth, int aHeight,
+ double aContentsScaleFactor,
+ bool aHasAlpha) {
+ if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0)
+ return nullptr;
+
+ CFMutableDictionaryRef props = ::CFDictionaryCreateMutable(
+ kCFAllocatorDefault, 4,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ if (!props)
+ return nullptr;
+
+ MOZ_ASSERT((size_t)aWidth <= GetMaxWidth());
+ MOZ_ASSERT((size_t)aHeight <= GetMaxHeight());
+
+ int32_t bytesPerElem = 4;
+ size_t intScaleFactor = ceil(aContentsScaleFactor);
+ aWidth *= intScaleFactor;
+ aHeight *= intScaleFactor;
+ CFNumberRef cfWidth = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aWidth);
+ CFNumberRef cfHeight = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aHeight);
+ CFNumberRef cfBytesPerElem = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &bytesPerElem);
+ ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropWidth,
+ cfWidth);
+ ::CFRelease(cfWidth);
+ ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropHeight,
+ cfHeight);
+ ::CFRelease(cfHeight);
+ ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropBytesPerElem,
+ cfBytesPerElem);
+ ::CFRelease(cfBytesPerElem);
+ ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropIsGlobal,
+ kCFBooleanTrue);
+
+ IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceCreate(props);
+ ::CFRelease(props);
+
+ if (!surfaceRef)
+ return nullptr;
+
+ RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor, aHasAlpha);
+ if (!ioSurface) {
+ ::CFRelease(surfaceRef);
+ return nullptr;
+ }
+
+ // Release the IOSurface because MacIOSurface retained it
+ CFRelease(surfaceRef);
+
+ return ioSurface.forget();
+}
+
+TemporaryRef<MacIOSurface> MacIOSurface::LookupSurface(IOSurfaceID aIOSurfaceID,
+ double aContentsScaleFactor,
+ bool aHasAlpha) {
+ if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0)
+ return nullptr;
+
+ IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceLookup(aIOSurfaceID);
+ if (!surfaceRef)
+ return nullptr;
+
+ RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor, aHasAlpha);
+ if (!ioSurface) {
+ ::CFRelease(surfaceRef);
+ return nullptr;
+ }
+
+ // Release the IOSurface because MacIOSurface retained it
+ CFRelease(surfaceRef);
+
+ return ioSurface.forget();
+}
+
+IOSurfaceID MacIOSurface::GetIOSurfaceID() {
+ return MacIOSurfaceLib::IOSurfaceGetID(mIOSurfacePtr);
+}
+
+void* MacIOSurface::GetBaseAddress() {
+ return MacIOSurfaceLib::IOSurfaceGetBaseAddress(mIOSurfacePtr);
+}
+
+void* MacIOSurface::GetBaseAddressOfPlane(size_t aPlaneIndex)
+{
+ return MacIOSurfaceLib::IOSurfaceGetBaseAddressOfPlane(mIOSurfacePtr,
+ aPlaneIndex);
+}
+
+size_t MacIOSurface::GetWidth() {
+ size_t intScaleFactor = ceil(mContentsScaleFactor);
+ return GetDevicePixelWidth() / intScaleFactor;
+}
+
+size_t MacIOSurface::GetHeight() {
+ size_t intScaleFactor = ceil(mContentsScaleFactor);
+ return GetDevicePixelHeight() / intScaleFactor;
+}
+
+size_t MacIOSurface::GetPlaneCount() {
+ return MacIOSurfaceLib::IOSurfaceGetPlaneCount(mIOSurfacePtr);
+}
+
+/*static*/ size_t MacIOSurface::GetMaxWidth() {
+ if (!MacIOSurfaceLib::isInit())
+ return -1;
+ return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropWidth);
+}
+
+/*static*/ size_t MacIOSurface::GetMaxHeight() {
+ if (!MacIOSurfaceLib::isInit())
+ return -1;
+ return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropHeight);
+}
+
+size_t MacIOSurface::GetDevicePixelWidth() {
+ return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr);
+}
+
+size_t MacIOSurface::GetDevicePixelHeight() {
+ return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr);
+}
+
+size_t MacIOSurface::GetBytesPerRow() {
+ return MacIOSurfaceLib::IOSurfaceGetBytesPerRow(mIOSurfacePtr);
+}
+
+void MacIOSurface::IncrementUseCount() {
+ MacIOSurfaceLib::IOSurfaceIncrementUseCount(mIOSurfacePtr);
+}
+
+void MacIOSurface::DecrementUseCount() {
+ MacIOSurfaceLib::IOSurfaceDecrementUseCount(mIOSurfacePtr);
+}
+
+#define READ_ONLY 0x1
+void MacIOSurface::Lock() {
+ MacIOSurfaceLib::IOSurfaceLock(mIOSurfacePtr, READ_ONLY, nullptr);
+}
+
+void MacIOSurface::Unlock() {
+ MacIOSurfaceLib::IOSurfaceUnlock(mIOSurfacePtr, READ_ONLY, nullptr);
+}
+
+#include "SourceSurfaceRawData.h"
+using mozilla::gfx::SourceSurface;
+using mozilla::gfx::SourceSurfaceRawData;
+using mozilla::gfx::IntSize;
+using mozilla::gfx::SurfaceFormat;
+
+TemporaryRef<SourceSurface>
+MacIOSurface::GetAsSurface() {
+ Lock();
+ size_t bytesPerRow = GetBytesPerRow();
+ size_t ioWidth = GetDevicePixelWidth();
+ size_t ioHeight = GetDevicePixelHeight();
+
+ unsigned char* ioData = (unsigned char*)GetBaseAddress();
+ unsigned char* dataCpy = (unsigned char*)malloc(bytesPerRow*ioHeight);
+ for (size_t i = 0; i < ioHeight; i++) {
+ memcpy(dataCpy + i * bytesPerRow,
+ ioData + i * bytesPerRow, ioWidth * 4);
+ }
+
+ Unlock();
+
+ SurfaceFormat format = HasAlpha() ? mozilla::gfx::SurfaceFormat::B8G8R8A8 :
+ mozilla::gfx::SurfaceFormat::B8G8R8X8;
+
+ RefPtr<SourceSurfaceRawData> surf = new SourceSurfaceRawData();
+ surf->InitWrappingData(dataCpy, IntSize(ioWidth, ioHeight), bytesPerRow, format, true);
+
+ return surf.forget();
+}
+
+CGLError
+MacIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctx)
+{
+ return MacIOSurfaceLib::CGLTexImageIOSurface2D(ctx,
+ GL_TEXTURE_RECTANGLE_ARB,
+ HasAlpha() ? GL_RGBA : GL_RGB,
+ GetDevicePixelWidth(),
+ GetDevicePixelHeight(),
+ GL_BGRA,
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+ mIOSurfacePtr, 0);
+}
+
+static
+CGColorSpaceRef CreateSystemColorSpace() {
+ CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID());
+ if (!cspace) {
+ cspace = ::CGColorSpaceCreateDeviceRGB();
+ }
+ return cspace;
+}
+
+CGContextRef MacIOSurface::CreateIOSurfaceContext() {
+ CGColorSpaceRef cspace = CreateSystemColorSpace();
+ CGContextRef ref = MacIOSurfaceLib::IOSurfaceContextCreate(mIOSurfacePtr,
+ GetDevicePixelWidth(),
+ GetDevicePixelHeight(),
+ 8, 32, cspace, 0x2002);
+ ::CGColorSpaceRelease(cspace);
+ return ref;
+}
+
+CGImageRef MacIOSurface::CreateImageFromIOSurfaceContext(CGContextRef aContext) {
+ if (!MacIOSurfaceLib::isInit())
+ return nullptr;
+
+ return MacIOSurfaceLib::IOSurfaceContextCreateImage(aContext);
+}
+
+TemporaryRef<MacIOSurface> MacIOSurface::IOSurfaceContextGetSurface(CGContextRef aContext,
+ double aContentsScaleFactor,
+ bool aHasAlpha) {
+ if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0)
+ return nullptr;
+
+ IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceContextGetSurface(aContext);
+ if (!surfaceRef)
+ return nullptr;
+
+ RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor, aHasAlpha);
+ if (!ioSurface) {
+ ::CFRelease(surfaceRef);
+ return nullptr;
+ }
+ return ioSurface.forget();
+}
+
+
+CGContextType GetContextType(CGContextRef ref)
+{
+ if (!MacIOSurfaceLib::isInit() || !MacIOSurfaceLib::sCGContextGetTypePtr)
+ return CG_CONTEXT_TYPE_UNKNOWN;
+
+ unsigned int type = MacIOSurfaceLib::sCGContextGetTypePtr(ref);
+ if (type == CG_CONTEXT_TYPE_BITMAP) {
+ return CG_CONTEXT_TYPE_BITMAP;
+ } else if (type == CG_CONTEXT_TYPE_IOSURFACE) {
+ return CG_CONTEXT_TYPE_IOSURFACE;
+ } else {
+ return CG_CONTEXT_TYPE_UNKNOWN;
+ }
+}
+
+
diff --git a/gfx/2d/MacIOSurface.h b/gfx/2d/MacIOSurface.h
index 853ad3569..62ddcd734 100644
--- a/gfx/2d/MacIOSurface.h
+++ b/gfx/2d/MacIOSurface.h
@@ -7,12 +7,43 @@
#ifndef MacIOSurface_h__
#define MacIOSurface_h__
#ifdef XP_MACOSX
+#include <QuartzCore/QuartzCore.h>
+#include <dlfcn.h>
+#include "mozilla/RefPtr.h"
+
+typedef CFTypeRef IOSurfacePtr;
+typedef IOSurfacePtr (*IOSurfaceCreateFunc) (CFDictionaryRef properties);
+typedef IOSurfacePtr (*IOSurfaceLookupFunc) (uint32_t io_surface_id);
+typedef IOSurfaceID (*IOSurfaceGetIDFunc)(IOSurfacePtr io_surface);
+typedef void (*IOSurfaceVoidFunc)(IOSurfacePtr io_surface);
+typedef IOReturn (*IOSurfaceLockFunc)(IOSurfacePtr io_surface, uint32_t options,
+ uint32_t *seed);
+typedef IOReturn (*IOSurfaceUnlockFunc)(IOSurfacePtr io_surface,
+ uint32_t options, uint32_t *seed);
+typedef void* (*IOSurfaceGetBaseAddressFunc)(IOSurfacePtr io_surface);
+typedef void* (*IOSurfaceGetBaseAddressOfPlaneFunc)(IOSurfacePtr io_surface,
+ size_t planeIndex);
+typedef size_t (*IOSurfaceSizeTFunc)(IOSurfacePtr io_surface);
+typedef size_t (*IOSurfaceGetPropertyMaximumFunc) (CFStringRef property);
+typedef CGLError (*CGLTexImageIOSurface2DFunc) (CGLContextObj ctxt,
+ GLenum target, GLenum internalFormat,
+ GLsizei width, GLsizei height,
+ GLenum format, GLenum type,
+ IOSurfacePtr ioSurface, GLuint plane);
+typedef CGContextRef (*IOSurfaceContextCreateFunc)(CFTypeRef io_surface,
+ unsigned width, unsigned height,
+ unsigned bitsPerComponent, unsigned bytes,
+ CGColorSpaceRef colorSpace, CGBitmapInfo bitmapInfo);
+typedef CGImageRef (*IOSurfaceContextCreateImageFunc)(CGContextRef ref);
+typedef IOSurfacePtr (*IOSurfaceContextGetSurfaceFunc)(CGContextRef ref);
+
+typedef IOSurfacePtr (*CVPixelBufferGetIOSurfaceFunc)(
+ CVPixelBufferRef pixelBuffer);
#import <OpenGL/OpenGL.h>
#include "2D.h"
#include "mozilla/RefPtr.h"
-class gfxASurface;
struct _CGLContextObject;
typedef _CGLContextObject* CGLContextObj;
@@ -20,21 +51,40 @@ typedef struct CGContext* CGContextRef;
typedef struct CGImage* CGImageRef;
typedef uint32_t IOSurfaceID;
+enum CGContextType {
+ CG_CONTEXT_TYPE_UNKNOWN = 0,
+ // These are found by inspection, it's possible they could be changed
+ CG_CONTEXT_TYPE_BITMAP = 4,
+ CG_CONTEXT_TYPE_IOSURFACE = 8
+};
+
+CGContextType GetContextType(CGContextRef ref);
+
class MacIOSurface : public mozilla::RefCounted<MacIOSurface> {
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(MacIOSurface)
typedef mozilla::gfx::SourceSurface SourceSurface;
+ // The usage count of the IOSurface is increased by 1 during the lifetime
+ // of the MacIOSurface instance.
+ // MacIOSurface holds a reference to the corresponding IOSurface.
+
static mozilla::TemporaryRef<MacIOSurface> CreateIOSurface(int aWidth, int aHeight,
- double aContentsScaleFactor = 1.0);
+ double aContentsScaleFactor = 1.0,
+ bool aHasAlpha = true);
static void ReleaseIOSurface(MacIOSurface *aIOSurface);
static mozilla::TemporaryRef<MacIOSurface> LookupSurface(IOSurfaceID aSurfaceID,
- double aContentsScaleFactor = 1.0);
+ double aContentsScaleFactor = 1.0,
+ bool aHasAlpha = true);
- MacIOSurface(const void *aIOSurfacePtr, double aContentsScaleFactor = 1.0)
- : mIOSurfacePtr(aIOSurfacePtr), mContentsScaleFactor(aContentsScaleFactor) {}
- ~MacIOSurface();
+ explicit MacIOSurface(const void *aIOSurfacePtr,
+ double aContentsScaleFactor = 1.0,
+ bool aHasAlpha = true);
+ virtual ~MacIOSurface();
IOSurfaceID GetIOSurfaceID();
void *GetBaseAddress();
+ void *GetBaseAddressOfPlane(size_t planeIndex);
+ size_t GetPlaneCount();
// GetWidth() and GetHeight() return values in "display pixels". A
// "display pixel" is the smallest fully addressable part of a display.
// But in HiDPI modes each "display pixel" corresponds to more than one
@@ -47,23 +97,105 @@ public:
size_t GetBytesPerRow();
void Lock();
void Unlock();
+ void IncrementUseCount();
+ void DecrementUseCount();
+ bool HasAlpha() { return mHasAlpha; }
// We would like to forward declare NSOpenGLContext, but it is an @interface
// and this file is also used from c++, so we use a void *.
- CGLError CGLTexImageIOSurface2D(void *ctxt,
- GLenum internalFormat, GLenum format,
- GLenum type, GLuint plane);
+ CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt);
mozilla::TemporaryRef<SourceSurface> GetAsSurface();
CGContextRef CreateIOSurfaceContext();
// FIXME This doesn't really belong here
static CGImageRef CreateImageFromIOSurfaceContext(CGContextRef aContext);
static mozilla::TemporaryRef<MacIOSurface> IOSurfaceContextGetSurface(CGContextRef aContext,
- double aContentsScaleFactor = 1.0);
+ double aContentsScaleFactor = 1.0,
+ bool aHasAlpha = true);
+ static size_t GetMaxWidth();
+ static size_t GetMaxHeight();
private:
friend class nsCARenderer;
const void* mIOSurfacePtr;
double mContentsScaleFactor;
+ bool mHasAlpha;
+};
+
+class MacIOSurfaceLib: public MacIOSurface {
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(MacIOSurfaceLib)
+ static void *sIOSurfaceFramework;
+ static void *sOpenGLFramework;
+ static void *sCoreGraphicsFramework;
+ static void *sCoreVideoFramework;
+ static bool isLoaded;
+ static IOSurfaceCreateFunc sCreate;
+ static IOSurfaceGetIDFunc sGetID;
+ static IOSurfaceLookupFunc sLookup;
+ static IOSurfaceGetBaseAddressFunc sGetBaseAddress;
+ static IOSurfaceGetBaseAddressOfPlaneFunc sGetBaseAddressOfPlane;
+ static IOSurfaceSizeTFunc sPlaneCount;
+ static IOSurfaceLockFunc sLock;
+ static IOSurfaceUnlockFunc sUnlock;
+ static IOSurfaceVoidFunc sIncrementUseCount;
+ static IOSurfaceVoidFunc sDecrementUseCount;
+ static IOSurfaceSizeTFunc sWidth;
+ static IOSurfaceSizeTFunc sHeight;
+ static IOSurfaceSizeTFunc sBytesPerRow;
+ static IOSurfaceGetPropertyMaximumFunc sGetPropertyMaximum;
+ static CGLTexImageIOSurface2DFunc sTexImage;
+ static IOSurfaceContextCreateFunc sIOSurfaceContextCreate;
+ static IOSurfaceContextCreateImageFunc sIOSurfaceContextCreateImage;
+ static IOSurfaceContextGetSurfaceFunc sIOSurfaceContextGetSurface;
+ static CVPixelBufferGetIOSurfaceFunc sCVPixelBufferGetIOSurface;
+ static CFStringRef kPropWidth;
+ static CFStringRef kPropHeight;
+ static CFStringRef kPropBytesPerElem;
+ static CFStringRef kPropBytesPerRow;
+ static CFStringRef kPropIsGlobal;
+
+ static bool isInit();
+ static CFStringRef GetIOConst(const char* symbole);
+ static IOSurfacePtr IOSurfaceCreate(CFDictionaryRef properties);
+ static IOSurfacePtr IOSurfaceLookup(IOSurfaceID aIOSurfaceID);
+ static IOSurfaceID IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr);
+ static void* IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr);
+ static void* IOSurfaceGetBaseAddressOfPlane(IOSurfacePtr aIOSurfacePtr,
+ size_t aPlaneIndex);
+ static size_t IOSurfaceGetPlaneCount(IOSurfacePtr aIOSurfacePtr);
+ static size_t IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr);
+ static size_t IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr);
+ static size_t IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr);
+ static size_t IOSurfaceGetPropertyMaximum(CFStringRef property);
+ static IOReturn IOSurfaceLock(IOSurfacePtr aIOSurfacePtr,
+ uint32_t options, uint32_t *seed);
+ static IOReturn IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr,
+ uint32_t options, uint32_t *seed);
+ static void IOSurfaceIncrementUseCount(IOSurfacePtr aIOSurfacePtr);
+ static void IOSurfaceDecrementUseCount(IOSurfacePtr aIOSurfacePtr);
+ static CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt,
+ GLenum target, GLenum internalFormat,
+ GLsizei width, GLsizei height,
+ GLenum format, GLenum type,
+ IOSurfacePtr ioSurface, GLuint plane);
+ static CGContextRef IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr,
+ unsigned aWidth, unsigned aHeight,
+ unsigned aBitsPerCompoent, unsigned aBytes,
+ CGColorSpaceRef aColorSpace, CGBitmapInfo bitmapInfo);
+ static CGImageRef IOSurfaceContextCreateImage(CGContextRef ref);
+ static IOSurfacePtr IOSurfaceContextGetSurface(CGContextRef ref);
+ static IOSurfacePtr CVPixelBufferGetIOSurface(CVPixelBufferRef apixelBuffer);
+ static unsigned int (*sCGContextGetTypePtr) (CGContextRef);
+ static void LoadLibrary();
+ static void CloseLibrary();
+
+ // Static deconstructor
+ static class LibraryUnloader {
+ public:
+ ~LibraryUnloader() {
+ CloseLibrary();
+ }
+ } sLibraryUnloader;
};
#endif
diff --git a/gfx/2d/Makefile.in b/gfx/2d/Makefile.in
index c86a089de..711879fa1 100644
--- a/gfx/2d/Makefile.in
+++ b/gfx/2d/Makefile.in
@@ -1,74 +1,10 @@
-#
# 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/.
-DEPTH = @DEPTH@
-topsrcdir = @top_srcdir@
-srcdir = @srcdir@
-VPATH = $(srcdir) $(srcdir)/unittest
-
-include $(DEPTH)/config/autoconf.mk
-
-LIBRARY_NAME = gfx2d
-MSVC_ENABLE_PGO := 1
-LIBXUL_LIBRARY = 1
-EXPORT_LIBRARY = 1
-
-ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
-CMMSRCS = \
- QuartzSupport.mm \
- $(NULL)
-endif
-
-DEFINES += -DMOZ_GFX -DUSE_CAIRO -DGFX2D_INTERNAL
-
-ifeq ($(MOZ_WIDGET_TOOLKIT),$(findstring $(MOZ_WIDGET_TOOLKIT),android gtk2 gtk3 gonk qt))
-DEFINES += -DMOZ_ENABLE_FREETYPE
-endif
-
-DEFINES += -DSK_A32_SHIFT=24 -DSK_R32_SHIFT=16 -DSK_G32_SHIFT=8 -DSK_B32_SHIFT=0
-
-ifdef MOZ_DEBUG
-DEFINES += -DGFX_LOG_DEBUG -DGFX_LOG_WARNING
-endif
-
-# Are we targeting x86 or x64? If so, build SSE2 files.
-ifneq (,$(INTEL_ARCHITECTURE))
-# VC2005 doesn't support _mm_castsi128_ps, so SSE2 is turned off
-ifneq (1400,$(_MSC_VER))
-DEFINES += -DUSE_SSE2
-endif
-endif
-
-ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
-DEFINES += -DWIN32 -DINITGUID
-
-ifdef MOZ_ENABLE_SKIA
-DEFINES += -DSKIA_IMPLEMENTATION=1 -DGR_IMPLEMENTATION=1
-endif
-endif
-
include $(topsrcdir)/config/rules.mk
-include $(topsrcdir)/ipc/chromium/chromium-config.mk
# Due to bug 796023, we can't have -DUNICODE and -D_UNICODE; defining those
# macros changes the type of LOGFONT to LOGFONTW instead of LOGFONTA. This
# changes the symbol names of exported C++ functions that use LOGFONT.
DEFINES := $(filter-out -DUNICODE -D_UNICODE,$(DEFINES))
-
-# The file uses SSE2 intrinsics, so it needs special compile flags on some
-# compilers.
-ifneq (,$(INTEL_ARCHITECTURE))
-ifdef GNU_CC
-ImageScalingSSE2.$(OBJ_SUFFIX): CXXFLAGS+=-msse2
-BlurSSE2.$(OBJ_SUFFIX): CXXFLAGS+=-msse2
-endif
-
-ifdef SOLARIS_SUNPRO_CXX
-ImageScalingSSE2.$(OBJ_SUFFIX): OS_CXXFLAGS += -xarch=sse2 -xO4
-BlurSSE2.$(OBJ_SUFFIX): OS_CXXFLAGS += -xarch=sse2 -xO4
-endif
-endif
-
-CXXFLAGS += $(MOZ_CAIRO_CFLAGS) $(MOZ_PIXMAN_CFLAGS)
diff --git a/gfx/2d/Matrix.cpp b/gfx/2d/Matrix.cpp
index 80e2d3d32..70058fede 100644
--- a/gfx/2d/Matrix.cpp
+++ b/gfx/2d/Matrix.cpp
@@ -5,11 +5,29 @@
#include "Matrix.h"
#include "Tools.h"
+#include <algorithm>
+#include <ostream>
#include <math.h>
+#include "mozilla/FloatingPoint.h" // for UnspecifiedNaN
+
+using namespace std;
+
namespace mozilla {
namespace gfx {
+std::ostream&
+operator<<(std::ostream& aStream, const Matrix& aMatrix)
+{
+ return aStream << "[ " << aMatrix._11
+ << " " << aMatrix._12
+ << "; " << aMatrix._21
+ << " " << aMatrix._22
+ << "; " << aMatrix._31
+ << " " << aMatrix._32
+ << "; ]";
+}
+
Matrix
Matrix::Rotation(Float aAngle)
{
@@ -22,7 +40,7 @@ Matrix::Rotation(Float aAngle)
newMatrix._12 = s;
newMatrix._21 = -s;
newMatrix._22 = c;
-
+
return newMatrix;
}
@@ -57,7 +75,7 @@ Matrix::TransformBounds(const Rect &aRect) const
return Rect(min_x, min_y, max_x - min_x, max_y - min_y);
}
-void
+Matrix&
Matrix::NudgeToIntegers()
{
NudgeToInteger(&_11);
@@ -66,6 +84,176 @@ Matrix::NudgeToIntegers()
NudgeToInteger(&_22);
NudgeToInteger(&_31);
NudgeToInteger(&_32);
+ return *this;
+}
+
+Rect
+Matrix4x4::TransformBounds(const Rect& aRect) const
+{
+ Point quad[4];
+ Float min_x, max_x;
+ Float min_y, max_y;
+
+ quad[0] = *this * aRect.TopLeft();
+ quad[1] = *this * aRect.TopRight();
+ quad[2] = *this * aRect.BottomLeft();
+ quad[3] = *this * aRect.BottomRight();
+
+ min_x = max_x = quad[0].x;
+ min_y = max_y = quad[0].y;
+
+ for (int i = 1; i < 4; i++) {
+ if (quad[i].x < min_x) {
+ min_x = quad[i].x;
+ }
+ if (quad[i].x > max_x) {
+ max_x = quad[i].x;
+ }
+
+ if (quad[i].y < min_y) {
+ min_y = quad[i].y;
+ }
+ if (quad[i].y > max_y) {
+ max_y = quad[i].y;
+ }
+ }
+
+ return Rect(min_x, min_y, max_x - min_x, max_y - min_y);
+}
+
+Point4D ComputePerspectivePlaneIntercept(const Point4D& aFirst,
+ const Point4D& aSecond)
+{
+ // FIXME: See bug 1035611
+ // Since we can't easily deal with points as w=0 (since we divide by w), we
+ // approximate this by finding a point with w just greater than 0. Unfortunately
+ // this is a tradeoff between accuracy and floating point precision.
+
+ // We want to interpolate aFirst and aSecond to find a point as close to
+ // the positive side of the w=0 plane as possible.
+
+ // Since we know what we want the w component to be, we can rearrange the
+ // interpolation equation and solve for t.
+ float w = 0.00001f;
+ float t = (w - aFirst.w) / (aSecond.w - aFirst.w);
+
+ // Use t to find the remainder of the components
+ return aFirst + (aSecond - aFirst) * t;
+}
+
+Rect Matrix4x4::ProjectRectBounds(const Rect& aRect) const
+{
+ Point4D points[4];
+
+ points[0] = ProjectPoint(aRect.TopLeft());
+ points[1] = ProjectPoint(aRect.TopRight());
+ points[2] = ProjectPoint(aRect.BottomRight());
+ points[3] = ProjectPoint(aRect.BottomLeft());
+
+ Float min_x = std::numeric_limits<Float>::max();
+ Float min_y = std::numeric_limits<Float>::max();
+ Float max_x = -std::numeric_limits<Float>::max();
+ Float max_y = -std::numeric_limits<Float>::max();
+
+ bool foundPoint = false;
+ for (int i=0; i<4; i++) {
+ // Only use points that exist above the w=0 plane
+ if (points[i].HasPositiveWCoord()) {
+ foundPoint = true;
+ Point point2d = points[i].As2DPoint();
+ min_x = min<Float>(point2d.x, min_x);
+ max_x = max<Float>(point2d.x, max_x);
+ min_y = min<Float>(point2d.y, min_y);
+ max_y = max<Float>(point2d.y, max_y);
+ }
+
+ int next = (i == 3) ? 0 : i + 1;
+ if (points[i].HasPositiveWCoord() != points[next].HasPositiveWCoord()) {
+ // If the line between two points crosses the w=0 plane, then interpolate a point
+ // as close to the w=0 plane as possible and use that instead.
+ Point4D intercept = ComputePerspectivePlaneIntercept(points[i], points[next]);
+
+ Point point2d = intercept.As2DPoint();
+ min_x = min<Float>(point2d.x, min_x);
+ max_x = max<Float>(point2d.x, max_x);
+ min_y = min<Float>(point2d.y, min_y);
+ max_y = max<Float>(point2d.y, max_y);
+ }
+ }
+
+ if (!foundPoint) {
+ return Rect(0, 0, 0, 0);
+ }
+
+ return Rect(min_x, min_y, max_x - min_x, max_y - min_y);
+}
+
+bool
+Matrix4x4::Invert()
+{
+ Float det = Determinant();
+ if (!det) {
+ return false;
+ }
+
+ Matrix4x4 result;
+ result._11 = _23 * _34 * _42 - _24 * _33 * _42 + _24 * _32 * _43 - _22 * _34 * _43 - _23 * _32 * _44 + _22 * _33 * _44;
+ result._12 = _14 * _33 * _42 - _13 * _34 * _42 - _14 * _32 * _43 + _12 * _34 * _43 + _13 * _32 * _44 - _12 * _33 * _44;
+ result._13 = _13 * _24 * _42 - _14 * _23 * _42 + _14 * _22 * _43 - _12 * _24 * _43 - _13 * _22 * _44 + _12 * _23 * _44;
+ result._14 = _14 * _23 * _32 - _13 * _24 * _32 - _14 * _22 * _33 + _12 * _24 * _33 + _13 * _22 * _34 - _12 * _23 * _34;
+ result._21 = _24 * _33 * _41 - _23 * _34 * _41 - _24 * _31 * _43 + _21 * _34 * _43 + _23 * _31 * _44 - _21 * _33 * _44;
+ result._22 = _13 * _34 * _41 - _14 * _33 * _41 + _14 * _31 * _43 - _11 * _34 * _43 - _13 * _31 * _44 + _11 * _33 * _44;
+ result._23 = _14 * _23 * _41 - _13 * _24 * _41 - _14 * _21 * _43 + _11 * _24 * _43 + _13 * _21 * _44 - _11 * _23 * _44;
+ result._24 = _13 * _24 * _31 - _14 * _23 * _31 + _14 * _21 * _33 - _11 * _24 * _33 - _13 * _21 * _34 + _11 * _23 * _34;
+ result._31 = _22 * _34 * _41 - _24 * _32 * _41 + _24 * _31 * _42 - _21 * _34 * _42 - _22 * _31 * _44 + _21 * _32 * _44;
+ result._32 = _14 * _32 * _41 - _12 * _34 * _41 - _14 * _31 * _42 + _11 * _34 * _42 + _12 * _31 * _44 - _11 * _32 * _44;
+ result._33 = _12 * _24 * _41 - _14 * _22 * _41 + _14 * _21 * _42 - _11 * _24 * _42 - _12 * _21 * _44 + _11 * _22 * _44;
+ result._34 = _14 * _22 * _31 - _12 * _24 * _31 - _14 * _21 * _32 + _11 * _24 * _32 + _12 * _21 * _34 - _11 * _22 * _34;
+ result._41 = _23 * _32 * _41 - _22 * _33 * _41 - _23 * _31 * _42 + _21 * _33 * _42 + _22 * _31 * _43 - _21 * _32 * _43;
+ result._42 = _12 * _33 * _41 - _13 * _32 * _41 + _13 * _31 * _42 - _11 * _33 * _42 - _12 * _31 * _43 + _11 * _32 * _43;
+ result._43 = _13 * _22 * _41 - _12 * _23 * _41 - _13 * _21 * _42 + _11 * _23 * _42 + _12 * _21 * _43 - _11 * _22 * _43;
+ result._44 = _12 * _23 * _31 - _13 * _22 * _31 + _13 * _21 * _32 - _11 * _23 * _32 - _12 * _21 * _33 + _11 * _22 * _33;
+
+ result._11 /= det;
+ result._12 /= det;
+ result._13 /= det;
+ result._14 /= det;
+ result._21 /= det;
+ result._22 /= det;
+ result._23 /= det;
+ result._24 /= det;
+ result._31 /= det;
+ result._32 /= det;
+ result._33 /= det;
+ result._34 /= det;
+ result._41 /= det;
+ result._42 /= det;
+ result._43 /= det;
+ result._44 /= det;
+ *this = result;
+
+ return true;
+}
+
+void
+Matrix4x4::SetNAN()
+{
+ _11 = UnspecifiedNaN<Float>();
+ _21 = UnspecifiedNaN<Float>();
+ _31 = UnspecifiedNaN<Float>();
+ _41 = UnspecifiedNaN<Float>();
+ _12 = UnspecifiedNaN<Float>();
+ _22 = UnspecifiedNaN<Float>();
+ _32 = UnspecifiedNaN<Float>();
+ _42 = UnspecifiedNaN<Float>();
+ _13 = UnspecifiedNaN<Float>();
+ _23 = UnspecifiedNaN<Float>();
+ _33 = UnspecifiedNaN<Float>();
+ _43 = UnspecifiedNaN<Float>();
+ _14 = UnspecifiedNaN<Float>();
+ _24 = UnspecifiedNaN<Float>();
+ _34 = UnspecifiedNaN<Float>();
+ _44 = UnspecifiedNaN<Float>();
}
}
diff --git a/gfx/2d/Matrix.h b/gfx/2d/Matrix.h
index 0fd9fde9c..11dd2412b 100644
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -9,11 +9,19 @@
#include "Types.h"
#include "Rect.h"
#include "Point.h"
+#include <iosfwd>
#include <math.h>
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
namespace mozilla {
namespace gfx {
+static bool FuzzyEqual(Float aV1, Float aV2) {
+ // XXX - Check if fabs does the smart thing and just negates the sign bit.
+ return fabs(aV2 - aV1) < 1e-6;
+}
+
class Matrix
{
public:
@@ -31,6 +39,13 @@ public:
Float _21, _22;
Float _31, _32;
+ MOZ_ALWAYS_INLINE Matrix Copy() const
+ {
+ return Matrix(*this);
+ }
+
+ friend std::ostream& operator<<(std::ostream& aStream, const Matrix& aMatrix);
+
Point operator *(const Point &aPoint) const
{
Point retPoint;
@@ -53,9 +68,81 @@ public:
GFX2D_API Rect TransformBounds(const Rect& rect) const;
- // Apply a scale to this matrix. This scale will be applied -before- the
- // existing transformation of the matrix.
- Matrix &Scale(Float aX, Float aY)
+ static Matrix Translation(Float aX, Float aY)
+ {
+ return Matrix(1.0f, 0.0f, 0.0f, 1.0f, aX, aY);
+ }
+
+ static Matrix Translation(Point aPoint)
+ {
+ return Translation(aPoint.x, aPoint.y);
+ }
+
+ /**
+ * Apply a translation to this matrix.
+ *
+ * The "Pre" in this method's name means that the translation is applied
+ * -before- this matrix's existing transformation. That is, any vector that
+ * is multiplied by the resulting matrix will first be translated, then be
+ * transformed by the original transform.
+ *
+ * Calling this method will result in this matrix having the same value as
+ * the result of:
+ *
+ * Matrix::Translation(x, y) * this
+ *
+ * (Note that in performance critical code multiplying by the result of a
+ * Translation()/Scaling() call is not recommended since that results in a
+ * full matrix multiply involving 12 floating-point multiplications. Calling
+ * this method would be preferred since it only involves four floating-point
+ * multiplications.)
+ */
+ Matrix &PreTranslate(Float aX, Float aY)
+ {
+ _31 += _11 * aX + _21 * aY;
+ _32 += _12 * aX + _22 * aY;
+
+ return *this;
+ }
+
+ Matrix &PreTranslate(const Point &aPoint)
+ {
+ return PreTranslate(aPoint.x, aPoint.y);
+ }
+
+ /**
+ * Similar to PreTranslate, but the translation is applied -after- this
+ * matrix's existing transformation instead of before it.
+ *
+ * This method is generally less used than PreTranslate since typically code
+ * want to adjust an existing user space to device space matrix to create a
+ * transform to device space from a -new- user space (translated from the
+ * previous user space). In that case consumers will need to use the Pre*
+ * variants of the matrix methods rather than using the Post* methods, since
+ * the Post* methods add a transform to the device space end of the
+ * transformation.
+ */
+ Matrix &PostTranslate(Float aX, Float aY)
+ {
+ _31 += aX;
+ _32 += aY;
+ return *this;
+ }
+
+ Matrix &PostTranslate(const Point &aPoint)
+ {
+ return PostTranslate(aPoint.x, aPoint.y);
+ }
+
+ static Matrix Scaling(Float aScaleX, Float aScaleY)
+ {
+ return Matrix(aScaleX, 0.0f, 0.0f, aScaleY, 0.0f, 0.0f);
+ }
+
+ /**
+ * Similar to PreTranslate, but applies a scale instead of a translation.
+ */
+ Matrix &PreScale(Float aX, Float aY)
{
_11 *= aX;
_12 *= aX;
@@ -64,13 +151,15 @@ public:
return *this;
}
+
+ GFX2D_API static Matrix Rotation(Float aAngle);
- Matrix &Translate(Float aX, Float aY)
+ /**
+ * Similar to PreTranslate, but applies a rotation instead of a translation.
+ */
+ Matrix &PreRotate(Float aAngle)
{
- _31 += _11 * aX + _21 * aY;
- _32 += _12 * aX + _22 * aY;
-
- return *this;
+ return *this = Matrix::Rotation(aAngle) * *this;
}
bool Invert()
@@ -101,12 +190,18 @@ public:
return true;
}
+ Matrix Inverse() const
+ {
+ Matrix clone = *this;
+ DebugOnly<bool> inverted = clone.Invert();
+ MOZ_ASSERT(inverted, "Attempted to get the inverse of a non-invertible matrix");
+ return clone;
+ }
+
Float Determinant() const
{
return _11 * _22 - _12 * _21;
}
-
- GFX2D_API static Matrix Rotation(Float aAngle);
Matrix operator*(const Matrix &aMatrix) const
{
@@ -122,6 +217,21 @@ public:
return resultMatrix;
}
+ Matrix& operator*=(const Matrix &aMatrix)
+ {
+ *this = *this * aMatrix;
+ return *this;
+ }
+
+ /**
+ * Multiplies in the opposite order to operator=*.
+ */
+ Matrix &PreMultiply(const Matrix &aMatrix)
+ {
+ *this = aMatrix * *this;
+ return *this;
+ }
+
/* Returns true if the other matrix is fuzzy-equal to this matrix.
* Note that this isn't a cheap comparison!
*/
@@ -150,6 +260,42 @@ public:
return false;
}
+ /**
+ * Returns true if the matrix is anything other than a straight
+ * translation by integers.
+ */
+ bool HasNonIntegerTranslation() const {
+ return HasNonTranslation() ||
+ !FuzzyEqual(_31, floor(_31 + 0.5)) ||
+ !FuzzyEqual(_32, floor(_32 + 0.5));
+ }
+
+ /**
+ * Returns true if the matrix only has an integer translation.
+ */
+ bool HasOnlyIntegerTranslation() const {
+ return !HasNonIntegerTranslation();
+ }
+
+ /**
+ * Returns true if the matrix has any transform other
+ * than a straight translation.
+ */
+ bool HasNonTranslation() const {
+ return !FuzzyEqual(_11, 1.0) || !FuzzyEqual(_22, 1.0) ||
+ !FuzzyEqual(_12, 0.0) || !FuzzyEqual(_21, 0.0);
+ }
+
+ /**
+ * Returns true if the matrix has any transform other
+ * than a translation or a -1 y scale (y axis flip)
+ */
+ bool HasNonTranslationOrFlip() const {
+ return !FuzzyEqual(_11, 1.0) ||
+ (!FuzzyEqual(_22, 1.0) && !FuzzyEqual(_22, -1.0)) ||
+ !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
+ }
+
/* Returns true if the matrix is an identity matrix.
*/
bool IsIdentity() const
@@ -159,12 +305,58 @@ public:
_31 == 0.0f && _32 == 0.0f;
}
- GFX2D_API void NudgeToIntegers();
+ /* Returns true if the matrix is singular.
+ */
+ bool IsSingular() const
+ {
+ return Determinant() == 0;
+ }
+
+ GFX2D_API Matrix &NudgeToIntegers();
-private:
- static bool FuzzyEqual(Float aV1, Float aV2) {
- // XXX - Check if fabs does the smart thing and just negates the sign bit.
- return fabs(aV2 - aV1) < 1e-6;
+ bool IsTranslation() const
+ {
+ return FuzzyEqual(_11, 1.0f) && FuzzyEqual(_12, 0.0f) &&
+ FuzzyEqual(_21, 0.0f) && FuzzyEqual(_22, 1.0f);
+ }
+
+ static bool FuzzyIsInteger(Float aValue)
+ {
+ return FuzzyEqual(aValue, floorf(aValue + 0.5f));
+ }
+
+ bool IsIntegerTranslation() const
+ {
+ return IsTranslation() && FuzzyIsInteger(_31) && FuzzyIsInteger(_32);
+ }
+
+ bool IsAllIntegers() const
+ {
+ return FuzzyIsInteger(_11) && FuzzyIsInteger(_12) &&
+ FuzzyIsInteger(_21) && FuzzyIsInteger(_22) &&
+ FuzzyIsInteger(_31) && FuzzyIsInteger(_32);
+ }
+
+ Point GetTranslation() const {
+ return Point(_31, _32);
+ }
+
+ /**
+ * Returns true if matrix is multiple of 90 degrees rotation with flipping,
+ * scaling and translation.
+ */
+ bool PreservesAxisAlignedRectangles() const {
+ return ((FuzzyEqual(_11, 0.0) && FuzzyEqual(_22, 0.0))
+ || (FuzzyEqual(_12, 0.0) && FuzzyEqual(_21, 0.0)));
+ }
+
+ /**
+ * Returns true if the matrix has any transform other
+ * than a translation or scale; this is, if there is
+ * no rotation.
+ */
+ bool HasNonAxisAlignedTransform() const {
+ return !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
}
};
@@ -178,11 +370,37 @@ public:
, _41(0.0f), _42(0.0f), _43(0.0f), _44(1.0f)
{}
+ Matrix4x4(Float a11, Float a12, Float a13, Float a14,
+ Float a21, Float a22, Float a23, Float a24,
+ Float a31, Float a32, Float a33, Float a34,
+ Float a41, Float a42, Float a43, Float a44)
+ : _11(a11), _12(a12), _13(a13), _14(a14)
+ , _21(a21), _22(a22), _23(a23), _24(a24)
+ , _31(a31), _32(a32), _33(a33), _34(a34)
+ , _41(a41), _42(a42), _43(a43), _44(a44)
+ {}
+
+ Matrix4x4(const Matrix4x4& aOther)
+ {
+ memcpy(this, &aOther, sizeof(*this));
+ }
+
Float _11, _12, _13, _14;
Float _21, _22, _23, _24;
Float _31, _32, _33, _34;
Float _41, _42, _43, _44;
+ Point4D& operator[](int aIndex)
+ {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
+ return *reinterpret_cast<Point4D*>((&_11)+4*aIndex);
+ }
+ const Point4D& operator[](int aIndex) const
+ {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
+ return *reinterpret_cast<const Point4D*>((&_11)+4*aIndex);
+ }
+
/**
* Returns true if the matrix is isomorphic to a 2D affine transformation.
*/
@@ -197,6 +415,21 @@ public:
return true;
}
+ bool Is2D(Matrix* aMatrix) const {
+ if (!Is2D()) {
+ return false;
+ }
+ if (aMatrix) {
+ aMatrix->_11 = _11;
+ aMatrix->_12 = _12;
+ aMatrix->_21 = _21;
+ aMatrix->_22 = _22;
+ aMatrix->_31 = _41;
+ aMatrix->_32 = _42;
+ }
+ return true;
+ }
+
Matrix As2D() const
{
MOZ_ASSERT(Is2D(), "Matrix is not a 2D affine transform");
@@ -204,9 +437,202 @@ public:
return Matrix(_11, _12, _21, _22, _41, _42);
}
- // Apply a scale to this matrix. This scale will be applied -before- the
- // existing transformation of the matrix.
- Matrix4x4 &Scale(Float aX, Float aY, Float aZ)
+ bool CanDraw2D(Matrix* aMatrix = nullptr) const {
+ if (_14 != 0.0f ||
+ _24 != 0.0f ||
+ _44 != 1.0f) {
+ return false;
+ }
+ if (aMatrix) {
+ aMatrix->_11 = _11;
+ aMatrix->_12 = _12;
+ aMatrix->_21 = _21;
+ aMatrix->_22 = _22;
+ aMatrix->_31 = _41;
+ aMatrix->_32 = _42;
+ }
+ return true;
+ }
+
+ Matrix4x4& ProjectTo2D() {
+ _31 = 0.0f;
+ _32 = 0.0f;
+ _13 = 0.0f;
+ _23 = 0.0f;
+ _33 = 1.0f;
+ _43 = 0.0f;
+ _34 = 0.0f;
+ return *this;
+ }
+
+ Point4D ProjectPoint(const Point& aPoint) const {
+ // Find a value for z that will transform to 0.
+
+ // The transformed value of z is computed as:
+ // z' = aPoint.x * _13 + aPoint.y * _23 + z * _33 + _43;
+
+ // Solving for z when z' = 0 gives us:
+ float z = -(aPoint.x * _13 + aPoint.y * _23 + _43) / _33;
+
+ // Compute the transformed point
+ return *this * Point4D(aPoint.x, aPoint.y, z, 1);
+ }
+
+ Rect ProjectRectBounds(const Rect& aRect) const;
+
+ static Matrix4x4 From2D(const Matrix &aMatrix) {
+ Matrix4x4 matrix;
+ matrix._11 = aMatrix._11;
+ matrix._12 = aMatrix._12;
+ matrix._21 = aMatrix._21;
+ matrix._22 = aMatrix._22;
+ matrix._41 = aMatrix._31;
+ matrix._42 = aMatrix._32;
+ return matrix;
+ }
+
+ bool Is2DIntegerTranslation() const
+ {
+ return Is2D() && As2D().IsIntegerTranslation();
+ }
+
+ Point4D TransposeTransform4D(const Point4D& aPoint) const
+ {
+ Float x = aPoint.x * _11 + aPoint.y * _12 + aPoint.z * _13 + aPoint.w * _14;
+ Float y = aPoint.x * _21 + aPoint.y * _22 + aPoint.z * _23 + aPoint.w * _24;
+ Float z = aPoint.x * _31 + aPoint.y * _32 + aPoint.z * _33 + aPoint.w * _34;
+ Float w = aPoint.x * _41 + aPoint.y * _42 + aPoint.z * _43 + aPoint.w * _44;
+
+ return Point4D(x, y, z, w);
+ }
+
+ Point4D operator *(const Point4D& aPoint) const
+ {
+ Point4D retPoint;
+
+ retPoint.x = aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + _41;
+ retPoint.y = aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + _42;
+ retPoint.z = aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + _43;
+ retPoint.w = aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + _44;
+
+ return retPoint;
+ }
+
+ Point3D operator *(const Point3D& aPoint) const
+ {
+ Point4D temp(aPoint.x, aPoint.y, aPoint.z, 1);
+
+ temp = *this * temp;
+ temp /= temp.w;
+
+ return Point3D(temp.x, temp.y, temp.z);
+ }
+
+ Point operator *(const Point &aPoint) const
+ {
+ Point4D temp(aPoint.x, aPoint.y, 0, 1);
+
+ temp = *this * temp;
+ temp /= temp.w;
+
+ return Point(temp.x, temp.y);
+ }
+
+ GFX2D_API Rect TransformBounds(const Rect& rect) const;
+
+
+ static Matrix4x4 Translation(Float aX, Float aY, Float aZ)
+ {
+ return Matrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ aX, aY, aZ, 1.0f);
+ }
+
+ static Matrix4x4 Translation(const Point3D& aP)
+ {
+ return Translation(aP.x, aP.y, aP.z);
+ }
+
+ /**
+ * Apply a translation to this matrix.
+ *
+ * The "Pre" in this method's name means that the translation is applied
+ * -before- this matrix's existing transformation. That is, any vector that
+ * is multiplied by the resulting matrix will first be translated, then be
+ * transformed by the original transform.
+ *
+ * Calling this method will result in this matrix having the same value as
+ * the result of:
+ *
+ * Matrix4x4::Translation(x, y) * this
+ *
+ * (Note that in performance critical code multiplying by the result of a
+ * Translation()/Scaling() call is not recommended since that results in a
+ * full matrix multiply involving 64 floating-point multiplications. Calling
+ * this method would be preferred since it only involves 12 floating-point
+ * multiplications.)
+ */
+ Matrix4x4 &PreTranslate(Float aX, Float aY, Float aZ)
+ {
+ _41 += aX * _11 + aY * _21 + aZ * _31;
+ _42 += aX * _12 + aY * _22 + aZ * _32;
+ _43 += aX * _13 + aY * _23 + aZ * _33;
+ _44 += aX * _14 + aY * _24 + aZ * _34;
+
+ return *this;
+ }
+
+ Matrix4x4 &PreTranslate(const Point3D& aPoint) {
+ return PreTranslate(aPoint.x, aPoint.y, aPoint.z);
+ }
+
+ /**
+ * Similar to PreTranslate, but the translation is applied -after- this
+ * matrix's existing transformation instead of before it.
+ *
+ * This method is generally less used than PreTranslate since typically code
+ * wants to adjust an existing user space to device space matrix to create a
+ * transform to device space from a -new- user space (translated from the
+ * previous user space). In that case consumers will need to use the Pre*
+ * variants of the matrix methods rather than using the Post* methods, since
+ * the Post* methods add a transform to the device space end of the
+ * transformation.
+ */
+ Matrix4x4 &PostTranslate(Float aX, Float aY, Float aZ)
+ {
+ _11 += _14 * aX;
+ _21 += _24 * aX;
+ _31 += _34 * aX;
+ _41 += _44 * aX;
+ _12 += _14 * aY;
+ _22 += _24 * aY;
+ _32 += _34 * aY;
+ _42 += _44 * aY;
+ _13 += _14 * aZ;
+ _23 += _24 * aZ;
+ _33 += _34 * aZ;
+ _43 += _44 * aZ;
+
+ return *this;
+ }
+
+ Matrix4x4 &PostTranslate(const Point3D& aPoint) {
+ return PostTranslate(aPoint.x, aPoint.y, aPoint.z);
+ }
+
+ static Matrix4x4 Scaling(Float aScaleX, Float aScaleY, float aScaleZ)
+ {
+ return Matrix4x4(aScaleX, 0.0f, 0.0f, 0.0f,
+ 0.0f, aScaleY, 0.0f, 0.0f,
+ 0.0f, 0.0f, aScaleZ, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+ }
+
+ /**
+ * Similar to PreTranslate, but applies a scale instead of a translation.
+ */
+ Matrix4x4 &PreScale(Float aX, Float aY, Float aZ)
{
_11 *= aX;
_12 *= aX;
@@ -220,6 +646,311 @@ public:
return *this;
}
+
+ /**
+ * Similar to PostTranslate, but applies a scale instead of a translation.
+ */
+ Matrix4x4 &PostScale(Float aScaleX, Float aScaleY, Float aScaleZ)
+ {
+ _11 *= aScaleX;
+ _21 *= aScaleX;
+ _31 *= aScaleX;
+ _41 *= aScaleX;
+ _12 *= aScaleY;
+ _22 *= aScaleY;
+ _32 *= aScaleY;
+ _42 *= aScaleY;
+ _13 *= aScaleZ;
+ _23 *= aScaleZ;
+ _33 *= aScaleZ;
+ _43 *= aScaleZ;
+
+ return *this;
+ }
+
+ void SkewXY(Float aSkew)
+ {
+ (*this)[1] += (*this)[0] * aSkew;
+ }
+
+ void SkewXZ(Float aSkew)
+ {
+ (*this)[2] += (*this)[0] * aSkew;
+ }
+
+ void SkewYZ(Float aSkew)
+ {
+ (*this)[2] += (*this)[1] * aSkew;
+ }
+
+ Matrix4x4 &ChangeBasis(Float aX, Float aY, Float aZ)
+ {
+ // Translate to the origin before applying this matrix
+ PreTranslate(-aX, -aY, -aZ);
+
+ // Translate back into position after applying this matrix
+ PostTranslate(aX, aY, aZ);
+
+ return *this;
+ }
+
+ Matrix4x4& Transpose() {
+ std::swap(_12, _21);
+ std::swap(_13, _31);
+ std::swap(_14, _41);
+
+ std::swap(_23, _32);
+ std::swap(_24, _42);
+
+ std::swap(_34, _43);
+
+ return *this;
+ }
+
+ bool operator==(const Matrix4x4& o) const
+ {
+ // XXX would be nice to memcmp here, but that breaks IEEE 754 semantics
+ return _11 == o._11 && _12 == o._12 && _13 == o._13 && _14 == o._14 &&
+ _21 == o._21 && _22 == o._22 && _23 == o._23 && _24 == o._24 &&
+ _31 == o._31 && _32 == o._32 && _33 == o._33 && _34 == o._34 &&
+ _41 == o._41 && _42 == o._42 && _43 == o._43 && _44 == o._44;
+ }
+
+ bool operator!=(const Matrix4x4& o) const
+ {
+ return !((*this) == o);
+ }
+
+ Matrix4x4 operator*(const Matrix4x4 &aMatrix) const
+ {
+ Matrix4x4 matrix;
+
+ matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21 + _13 * aMatrix._31 + _14 * aMatrix._41;
+ matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21 + _23 * aMatrix._31 + _24 * aMatrix._41;
+ matrix._31 = _31 * aMatrix._11 + _32 * aMatrix._21 + _33 * aMatrix._31 + _34 * aMatrix._41;
+ matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + _43 * aMatrix._31 + _44 * aMatrix._41;
+ matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22 + _13 * aMatrix._32 + _14 * aMatrix._42;
+ matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22 + _23 * aMatrix._32 + _24 * aMatrix._42;
+ matrix._32 = _31 * aMatrix._12 + _32 * aMatrix._22 + _33 * aMatrix._32 + _34 * aMatrix._42;
+ matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + _43 * aMatrix._32 + _44 * aMatrix._42;
+ matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23 + _13 * aMatrix._33 + _14 * aMatrix._43;
+ matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23 + _23 * aMatrix._33 + _24 * aMatrix._43;
+ matrix._33 = _31 * aMatrix._13 + _32 * aMatrix._23 + _33 * aMatrix._33 + _34 * aMatrix._43;
+ matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + _43 * aMatrix._33 + _44 * aMatrix._43;
+ matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24 + _13 * aMatrix._34 + _14 * aMatrix._44;
+ matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24 + _23 * aMatrix._34 + _24 * aMatrix._44;
+ matrix._34 = _31 * aMatrix._14 + _32 * aMatrix._24 + _33 * aMatrix._34 + _34 * aMatrix._44;
+ matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + _43 * aMatrix._34 + _44 * aMatrix._44;
+
+ return matrix;
+ }
+
+ Matrix4x4& operator*=(const Matrix4x4 &aMatrix)
+ {
+ *this = *this * aMatrix;
+ return *this;
+ }
+
+ /* Returns true if the matrix is an identity matrix.
+ */
+ bool IsIdentity() const
+ {
+ return _11 == 1.0f && _12 == 0.0f && _13 == 0.0f && _14 == 0.0f &&
+ _21 == 0.0f && _22 == 1.0f && _23 == 0.0f && _24 == 0.0f &&
+ _31 == 0.0f && _32 == 0.0f && _33 == 1.0f && _34 == 0.0f &&
+ _41 == 0.0f && _42 == 0.0f && _43 == 0.0f && _44 == 1.0f;
+ }
+
+ bool IsSingular() const
+ {
+ return Determinant() == 0.0;
+ }
+
+ Float Determinant() const
+ {
+ return _14 * _23 * _32 * _41
+ - _13 * _24 * _32 * _41
+ - _14 * _22 * _33 * _41
+ + _12 * _24 * _33 * _41
+ + _13 * _22 * _34 * _41
+ - _12 * _23 * _34 * _41
+ - _14 * _23 * _31 * _42
+ + _13 * _24 * _31 * _42
+ + _14 * _21 * _33 * _42
+ - _11 * _24 * _33 * _42
+ - _13 * _21 * _34 * _42
+ + _11 * _23 * _34 * _42
+ + _14 * _22 * _31 * _43
+ - _12 * _24 * _31 * _43
+ - _14 * _21 * _32 * _43
+ + _11 * _24 * _32 * _43
+ + _12 * _21 * _34 * _43
+ - _11 * _22 * _34 * _43
+ - _13 * _22 * _31 * _44
+ + _12 * _23 * _31 * _44
+ + _13 * _21 * _32 * _44
+ - _11 * _23 * _32 * _44
+ - _12 * _21 * _33 * _44
+ + _11 * _22 * _33 * _44;
+ }
+
+ bool Invert();
+
+ Matrix4x4 Inverse() const
+ {
+ Matrix4x4 clone = *this;
+ DebugOnly<bool> inverted = clone.Invert();
+ MOZ_ASSERT(inverted, "Attempted to get the inverse of a non-invertible matrix");
+ return clone;
+ }
+
+ void Normalize()
+ {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ (*this)[i][j] /= (*this)[3][3];
+ }
+ }
+ }
+
+ bool FuzzyEqual(const Matrix4x4& o) const
+ {
+ return gfx::FuzzyEqual(_11, o._11) && gfx::FuzzyEqual(_12, o._12) &&
+ gfx::FuzzyEqual(_13, o._13) && gfx::FuzzyEqual(_14, o._14) &&
+ gfx::FuzzyEqual(_21, o._21) && gfx::FuzzyEqual(_22, o._22) &&
+ gfx::FuzzyEqual(_23, o._23) && gfx::FuzzyEqual(_24, o._24) &&
+ gfx::FuzzyEqual(_31, o._31) && gfx::FuzzyEqual(_32, o._32) &&
+ gfx::FuzzyEqual(_33, o._33) && gfx::FuzzyEqual(_34, o._34) &&
+ gfx::FuzzyEqual(_41, o._41) && gfx::FuzzyEqual(_42, o._42) &&
+ gfx::FuzzyEqual(_43, o._43) && gfx::FuzzyEqual(_44, o._44);
+ }
+
+ bool IsBackfaceVisible() const
+ {
+ // Inverse()._33 < 0;
+ Float det = Determinant();
+ Float __33 = _12*_24*_41 - _14*_22*_41 +
+ _14*_21*_42 - _11*_24*_42 -
+ _12*_21*_44 + _11*_22*_44;
+ return (__33 * det) < 0;
+ }
+
+ Matrix4x4 &NudgeToIntegersFixedEpsilon()
+ {
+ NudgeToInteger(&_11);
+ NudgeToInteger(&_12);
+ NudgeToInteger(&_13);
+ NudgeToInteger(&_14);
+ NudgeToInteger(&_21);
+ NudgeToInteger(&_22);
+ NudgeToInteger(&_23);
+ NudgeToInteger(&_24);
+ NudgeToInteger(&_31);
+ NudgeToInteger(&_32);
+ NudgeToInteger(&_33);
+ NudgeToInteger(&_34);
+ static const float error = 1e-5f;
+ NudgeToInteger(&_41, error);
+ NudgeToInteger(&_42, error);
+ NudgeToInteger(&_43, error);
+ NudgeToInteger(&_44, error);
+ return *this;
+ }
+
+ Point4D TransposedVector(int aIndex) const
+ {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
+ return Point4D(*((&_11)+aIndex), *((&_21)+aIndex), *((&_31)+aIndex), *((&_41)+aIndex));
+ }
+
+ void SetTransposedVector(int aIndex, Point4D &aVector)
+ {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
+ *((&_11)+aIndex) = aVector.x;
+ *((&_21)+aIndex) = aVector.y;
+ *((&_31)+aIndex) = aVector.z;
+ *((&_41)+aIndex) = aVector.w;
+ }
+
+ // Set all the members of the matrix to NaN
+ void SetNAN();
+};
+
+class Matrix5x4
+{
+public:
+ Matrix5x4()
+ : _11(1.0f), _12(0), _13(0), _14(0)
+ , _21(0), _22(1.0f), _23(0), _24(0)
+ , _31(0), _32(0), _33(1.0f), _34(0)
+ , _41(0), _42(0), _43(0), _44(1.0f)
+ , _51(0), _52(0), _53(0), _54(0)
+ {}
+ Matrix5x4(Float a11, Float a12, Float a13, Float a14,
+ Float a21, Float a22, Float a23, Float a24,
+ Float a31, Float a32, Float a33, Float a34,
+ Float a41, Float a42, Float a43, Float a44,
+ Float a51, Float a52, Float a53, Float a54)
+ : _11(a11), _12(a12), _13(a13), _14(a14)
+ , _21(a21), _22(a22), _23(a23), _24(a24)
+ , _31(a31), _32(a32), _33(a33), _34(a34)
+ , _41(a41), _42(a42), _43(a43), _44(a44)
+ , _51(a51), _52(a52), _53(a53), _54(a54)
+ {}
+
+ bool operator==(const Matrix5x4 &o) const
+ {
+ return _11 == o._11 && _12 == o._12 && _13 == o._13 && _14 == o._14 &&
+ _21 == o._21 && _22 == o._22 && _23 == o._23 && _24 == o._24 &&
+ _31 == o._31 && _32 == o._32 && _33 == o._33 && _34 == o._34 &&
+ _41 == o._41 && _42 == o._42 && _43 == o._43 && _44 == o._44 &&
+ _51 == o._51 && _52 == o._52 && _53 == o._53 && _54 == o._54;
+ }
+
+ bool operator!=(const Matrix5x4 &aMatrix) const
+ {
+ return !(*this == aMatrix);
+ }
+
+ Matrix5x4 operator*(const Matrix5x4 &aMatrix) const
+ {
+ Matrix5x4 resultMatrix;
+
+ resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21 + this->_13 * aMatrix._31 + this->_14 * aMatrix._41;
+ resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22 + this->_13 * aMatrix._32 + this->_14 * aMatrix._42;
+ resultMatrix._13 = this->_11 * aMatrix._13 + this->_12 * aMatrix._23 + this->_13 * aMatrix._33 + this->_14 * aMatrix._43;
+ resultMatrix._14 = this->_11 * aMatrix._14 + this->_12 * aMatrix._24 + this->_13 * aMatrix._34 + this->_14 * aMatrix._44;
+ resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21 + this->_23 * aMatrix._31 + this->_24 * aMatrix._41;
+ resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22 + this->_23 * aMatrix._32 + this->_24 * aMatrix._42;
+ resultMatrix._23 = this->_21 * aMatrix._13 + this->_22 * aMatrix._23 + this->_23 * aMatrix._33 + this->_24 * aMatrix._43;
+ resultMatrix._24 = this->_21 * aMatrix._14 + this->_22 * aMatrix._24 + this->_23 * aMatrix._34 + this->_24 * aMatrix._44;
+ resultMatrix._31 = this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + this->_33 * aMatrix._31 + this->_34 * aMatrix._41;
+ resultMatrix._32 = this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + this->_33 * aMatrix._32 + this->_34 * aMatrix._42;
+ resultMatrix._33 = this->_31 * aMatrix._13 + this->_32 * aMatrix._23 + this->_33 * aMatrix._33 + this->_34 * aMatrix._43;
+ resultMatrix._34 = this->_31 * aMatrix._14 + this->_32 * aMatrix._24 + this->_33 * aMatrix._34 + this->_34 * aMatrix._44;
+ resultMatrix._41 = this->_41 * aMatrix._11 + this->_42 * aMatrix._21 + this->_43 * aMatrix._31 + this->_44 * aMatrix._41;
+ resultMatrix._42 = this->_41 * aMatrix._12 + this->_42 * aMatrix._22 + this->_43 * aMatrix._32 + this->_44 * aMatrix._42;
+ resultMatrix._43 = this->_41 * aMatrix._13 + this->_42 * aMatrix._23 + this->_43 * aMatrix._33 + this->_44 * aMatrix._43;
+ resultMatrix._44 = this->_41 * aMatrix._14 + this->_42 * aMatrix._24 + this->_43 * aMatrix._34 + this->_44 * aMatrix._44;
+ resultMatrix._51 = this->_51 * aMatrix._11 + this->_52 * aMatrix._21 + this->_53 * aMatrix._31 + this->_54 * aMatrix._41 + aMatrix._51;
+ resultMatrix._52 = this->_51 * aMatrix._12 + this->_52 * aMatrix._22 + this->_53 * aMatrix._32 + this->_54 * aMatrix._42 + aMatrix._52;
+ resultMatrix._53 = this->_51 * aMatrix._13 + this->_52 * aMatrix._23 + this->_53 * aMatrix._33 + this->_54 * aMatrix._43 + aMatrix._53;
+ resultMatrix._54 = this->_51 * aMatrix._14 + this->_52 * aMatrix._24 + this->_53 * aMatrix._34 + this->_54 * aMatrix._44 + aMatrix._54;
+
+ return resultMatrix;
+ }
+
+ Matrix5x4& operator*=(const Matrix5x4 &aMatrix)
+ {
+ *this = *this * aMatrix;
+ return *this;
+ }
+
+ Float _11, _12, _13, _14;
+ Float _21, _22, _23, _24;
+ Float _31, _32, _33, _34;
+ Float _41, _42, _43, _44;
+ Float _51, _52, _53, _54;
};
}
diff --git a/gfx/2d/Path.cpp b/gfx/2d/Path.cpp
new file mode 100644
index 000000000..d94639523
--- /dev/null
+++ b/gfx/2d/Path.cpp
@@ -0,0 +1,530 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "2D.h"
+#include "PathAnalysis.h"
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+static float CubicRoot(float aValue) {
+ if (aValue < 0.0) {
+ return -CubicRoot(-aValue);
+ }
+ else {
+ return powf(aValue, 1.0f / 3.0f);
+ }
+}
+
+struct BezierControlPoints
+{
+ BezierControlPoints() {}
+ BezierControlPoints(const Point &aCP1, const Point &aCP2,
+ const Point &aCP3, const Point &aCP4)
+ : mCP1(aCP1), mCP2(aCP2), mCP3(aCP3), mCP4(aCP4)
+ {
+ }
+
+ Point mCP1, mCP2, mCP3, mCP4;
+};
+
+void
+FlattenBezier(const BezierControlPoints &aPoints,
+ PathSink *aSink, Float aTolerance);
+
+
+Path::Path()
+{
+}
+
+Path::~Path()
+{
+}
+
+Float
+Path::ComputeLength()
+{
+ EnsureFlattenedPath();
+ return mFlattenedPath->ComputeLength();
+}
+
+Point
+Path::ComputePointAtLength(Float aLength, Point* aTangent)
+{
+ EnsureFlattenedPath();
+ return mFlattenedPath->ComputePointAtLength(aLength, aTangent);
+}
+
+void
+Path::EnsureFlattenedPath()
+{
+ if (!mFlattenedPath) {
+ mFlattenedPath = new FlattenedPath();
+ StreamToSink(mFlattenedPath);
+ }
+}
+
+// This is the maximum deviation we allow (with an additional ~20% margin of
+// error) of the approximation from the actual Bezier curve.
+const Float kFlatteningTolerance = 0.0001f;
+
+void
+FlattenedPath::MoveTo(const Point &aPoint)
+{
+ MOZ_ASSERT(!mCalculatedLength);
+ FlatPathOp op;
+ op.mType = FlatPathOp::OP_MOVETO;
+ op.mPoint = aPoint;
+ mPathOps.push_back(op);
+
+ mLastMove = aPoint;
+}
+
+void
+FlattenedPath::LineTo(const Point &aPoint)
+{
+ MOZ_ASSERT(!mCalculatedLength);
+ FlatPathOp op;
+ op.mType = FlatPathOp::OP_LINETO;
+ op.mPoint = aPoint;
+ mPathOps.push_back(op);
+}
+
+void
+FlattenedPath::BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3)
+{
+ MOZ_ASSERT(!mCalculatedLength);
+ FlattenBezier(BezierControlPoints(CurrentPoint(), aCP1, aCP2, aCP3), this, kFlatteningTolerance);
+}
+
+void
+FlattenedPath::QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2)
+{
+ MOZ_ASSERT(!mCalculatedLength);
+ // We need to elevate the degree of this quadratic B�zier to cubic, so we're
+ // going to add an intermediate control point, and recompute control point 1.
+ // The first and last control points remain the same.
+ // This formula can be found on http://fontforge.sourceforge.net/bezier.html
+ Point CP0 = CurrentPoint();
+ Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
+ Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
+ Point CP3 = aCP2;
+
+ BezierTo(CP1, CP2, CP3);
+}
+
+void
+FlattenedPath::Close()
+{
+ MOZ_ASSERT(!mCalculatedLength);
+ LineTo(mLastMove);
+}
+
+void
+FlattenedPath::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise)
+{
+ ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
+}
+
+Float
+FlattenedPath::ComputeLength()
+{
+ if (!mCalculatedLength) {
+ Point currentPoint;
+
+ for (uint32_t i = 0; i < mPathOps.size(); i++) {
+ if (mPathOps[i].mType == FlatPathOp::OP_MOVETO) {
+ currentPoint = mPathOps[i].mPoint;
+ } else {
+ mCachedLength += Distance(currentPoint, mPathOps[i].mPoint);
+ currentPoint = mPathOps[i].mPoint;
+ }
+ }
+
+ mCalculatedLength = true;
+ }
+
+ return mCachedLength;
+}
+
+Point
+FlattenedPath::ComputePointAtLength(Float aLength, Point *aTangent)
+{
+ // We track the last point that -wasn't- in the same place as the current
+ // point so if we pass the edge of the path with a bunch of zero length
+ // paths we still get the correct tangent vector.
+ Point lastPointSinceMove;
+ Point currentPoint;
+ for (uint32_t i = 0; i < mPathOps.size(); i++) {
+ if (mPathOps[i].mType == FlatPathOp::OP_MOVETO) {
+ if (Distance(currentPoint, mPathOps[i].mPoint)) {
+ lastPointSinceMove = currentPoint;
+ }
+ currentPoint = mPathOps[i].mPoint;
+ } else {
+ Float segmentLength = Distance(currentPoint, mPathOps[i].mPoint);
+
+ if (segmentLength) {
+ lastPointSinceMove = currentPoint;
+ if (segmentLength > aLength) {
+ Point currentVector = mPathOps[i].mPoint - currentPoint;
+ Point tangent = currentVector / segmentLength;
+ if (aTangent) {
+ *aTangent = tangent;
+ }
+ return currentPoint + tangent * aLength;
+ }
+ }
+
+ aLength -= segmentLength;
+ currentPoint = mPathOps[i].mPoint;
+ }
+ }
+
+ Point currentVector = currentPoint - lastPointSinceMove;
+ if (aTangent) {
+ if (hypotf(currentVector.x, currentVector.y)) {
+ *aTangent = currentVector / hypotf(currentVector.x, currentVector.y);
+ } else {
+ *aTangent = Point();
+ }
+ }
+ return currentPoint;
+}
+
+// This function explicitly permits aControlPoints to refer to the same object
+// as either of the other arguments.
+static void
+SplitBezier(const BezierControlPoints &aControlPoints,
+ BezierControlPoints *aFirstSegmentControlPoints,
+ BezierControlPoints *aSecondSegmentControlPoints,
+ Float t)
+{
+ MOZ_ASSERT(aSecondSegmentControlPoints);
+
+ *aSecondSegmentControlPoints = aControlPoints;
+
+ Point cp1a = aControlPoints.mCP1 + (aControlPoints.mCP2 - aControlPoints.mCP1) * t;
+ Point cp2a = aControlPoints.mCP2 + (aControlPoints.mCP3 - aControlPoints.mCP2) * t;
+ Point cp1aa = cp1a + (cp2a - cp1a) * t;
+ Point cp3a = aControlPoints.mCP3 + (aControlPoints.mCP4 - aControlPoints.mCP3) * t;
+ Point cp2aa = cp2a + (cp3a - cp2a) * t;
+ Point cp1aaa = cp1aa + (cp2aa - cp1aa) * t;
+ aSecondSegmentControlPoints->mCP4 = aControlPoints.mCP4;
+
+ if(aFirstSegmentControlPoints) {
+ aFirstSegmentControlPoints->mCP1 = aControlPoints.mCP1;
+ aFirstSegmentControlPoints->mCP2 = cp1a;
+ aFirstSegmentControlPoints->mCP3 = cp1aa;
+ aFirstSegmentControlPoints->mCP4 = cp1aaa;
+ }
+ aSecondSegmentControlPoints->mCP1 = cp1aaa;
+ aSecondSegmentControlPoints->mCP2 = cp2aa;
+ aSecondSegmentControlPoints->mCP3 = cp3a;
+}
+
+static void
+FlattenBezierCurveSegment(const BezierControlPoints &aControlPoints,
+ PathSink *aSink,
+ Float aTolerance)
+{
+ /* The algorithm implemented here is based on:
+ * http://cis.usouthal.edu/~hain/general/Publications/Bezier/Bezier%20Offset%20Curves.pdf
+ *
+ * The basic premise is that for a small t the third order term in the
+ * equation of a cubic bezier curve is insignificantly small. This can
+ * then be approximated by a quadratic equation for which the maximum
+ * difference from a linear approximation can be much more easily determined.
+ */
+ BezierControlPoints currentCP = aControlPoints;
+
+ Float t = 0;
+ while (t < 1.0f) {
+ Point cp21 = currentCP.mCP2 - currentCP.mCP3;
+ Point cp31 = currentCP.mCP3 - currentCP.mCP1;
+
+ Float s3 = (cp31.x * cp21.y - cp31.y * cp21.x) / hypotf(cp21.x, cp21.y);
+
+ t = 2 * Float(sqrt(aTolerance / (3. * std::abs(s3))));
+
+ if (t >= 1.0f) {
+ aSink->LineTo(aControlPoints.mCP4);
+ break;
+ }
+
+ Point prevCP2, prevCP3, nextCP1, nextCP2, nextCP3;
+ SplitBezier(currentCP, nullptr, &currentCP, t);
+
+ aSink->LineTo(currentCP.mCP1);
+ }
+}
+
+static inline void
+FindInflectionApproximationRange(BezierControlPoints aControlPoints,
+ Float *aMin, Float *aMax, Float aT,
+ Float aTolerance)
+{
+ SplitBezier(aControlPoints, nullptr, &aControlPoints, aT);
+
+ Point cp21 = aControlPoints.mCP2 - aControlPoints.mCP1;
+ Point cp41 = aControlPoints.mCP4 - aControlPoints.mCP1;
+
+ if (cp21.x == 0.f && cp21.y == 0.f) {
+ // In this case s3 becomes lim[n->0] (cp41.x * n) / n - (cp41.y * n) / n = cp41.x - cp41.y.
+
+ // Use the absolute value so that Min and Max will correspond with the
+ // minimum and maximum of the range.
+ *aMin = aT - CubicRoot(std::abs(aTolerance / (cp41.x - cp41.y)));
+ *aMax = aT + CubicRoot(std::abs(aTolerance / (cp41.x - cp41.y)));
+ return;
+ }
+
+ Float s3 = (cp41.x * cp21.y - cp41.y * cp21.x) / hypotf(cp21.x, cp21.y);
+
+ if (s3 == 0) {
+ // This means within the precision we have it can be approximated
+ // infinitely by a linear segment. Deal with this by specifying the
+ // approximation range as extending beyond the entire curve.
+ *aMin = -1.0f;
+ *aMax = 2.0f;
+ return;
+ }
+
+ Float tf = CubicRoot(std::abs(aTolerance / s3));
+
+ *aMin = aT - tf * (1 - aT);
+ *aMax = aT + tf * (1 - aT);
+}
+
+/* Find the inflection points of a bezier curve. Will return false if the
+ * curve is degenerate in such a way that it is best approximated by a straight
+ * line.
+ *
+ * The below algorithm was written by Jeff Muizelaar <jmuizelaar@mozilla.com>, explanation follows:
+ *
+ * The lower inflection point is returned in aT1, the higher one in aT2. In the
+ * case of a single inflection point this will be in aT1.
+ *
+ * The method is inspired by the algorithm in "analysis of in?ection points for planar cubic bezier curve"
+ *
+ * Here are some differences between this algorithm and versions discussed elsewhere in the literature:
+ *
+ * zhang et. al compute a0, d0 and e0 incrementally using the follow formula:
+ *
+ * Point a0 = CP2 - CP1
+ * Point a1 = CP3 - CP2
+ * Point a2 = CP4 - CP1
+ *
+ * Point d0 = a1 - a0
+ * Point d1 = a2 - a1
+
+ * Point e0 = d1 - d0
+ *
+ * this avoids any multiplications and may or may not be faster than the approach take below.
+ *
+ * "fast, precise flattening of cubic bezier path and ofset curves" by hain et. al
+ * Point a = CP1 + 3 * CP2 - 3 * CP3 + CP4
+ * Point b = 3 * CP1 - 6 * CP2 + 3 * CP3
+ * Point c = -3 * CP1 + 3 * CP2
+ * Point d = CP1
+ * the a, b, c, d can be expressed in terms of a0, d0 and e0 defined above as:
+ * c = 3 * a0
+ * b = 3 * d0
+ * a = e0
+ *
+ *
+ * a = 3a = a.y * b.x - a.x * b.y
+ * b = 3b = a.y * c.x - a.x * c.y
+ * c = 9c = b.y * c.x - b.x * c.y
+ *
+ * The additional multiples of 3 cancel each other out as show below:
+ *
+ * x = (-b + sqrt(b * b - 4 * a * c)) / (2 * a)
+ * x = (-3 * b + sqrt(3 * b * 3 * b - 4 * a * 3 * 9 * c / 3)) / (2 * 3 * a)
+ * x = 3 * (-b + sqrt(b * b - 4 * a * c)) / (2 * 3 * a)
+ * x = (-b + sqrt(b * b - 4 * a * c)) / (2 * a)
+ *
+ * I haven't looked into whether the formulation of the quadratic formula in
+ * hain has any numerical advantages over the one used below.
+ */
+static inline void
+FindInflectionPoints(const BezierControlPoints &aControlPoints,
+ Float *aT1, Float *aT2, uint32_t *aCount)
+{
+ // Find inflection points.
+ // See www.faculty.idc.ac.il/arik/quality/appendixa.html for an explanation
+ // of this approach.
+ Point A = aControlPoints.mCP2 - aControlPoints.mCP1;
+ Point B = aControlPoints.mCP3 - (aControlPoints.mCP2 * 2) + aControlPoints.mCP1;
+ Point C = aControlPoints.mCP4 - (aControlPoints.mCP3 * 3) + (aControlPoints.mCP2 * 3) - aControlPoints.mCP1;
+
+ Float a = Float(B.x) * C.y - Float(B.y) * C.x;
+ Float b = Float(A.x) * C.y - Float(A.y) * C.x;
+ Float c = Float(A.x) * B.y - Float(A.y) * B.x;
+
+ if (a == 0) {
+ // Not a quadratic equation.
+ if (b == 0) {
+ // Instead of a linear acceleration change we have a constant
+ // acceleration change. This means the equation has no solution
+ // and there are no inflection points, unless the constant is 0.
+ // In that case the curve is a straight line, essentially that means
+ // the easiest way to deal with is is by saying there's an inflection
+ // point at t == 0. The inflection point approximation range found will
+ // automatically extend into infinity.
+ if (c == 0) {
+ *aCount = 1;
+ *aT1 = 0;
+ return;
+ }
+ *aCount = 0;
+ return;
+ }
+ *aT1 = -c / b;
+ *aCount = 1;
+ return;
+ } else {
+ Float discriminant = b * b - 4 * a * c;
+
+ if (discriminant < 0) {
+ // No inflection points.
+ *aCount = 0;
+ } else if (discriminant == 0) {
+ *aCount = 1;
+ *aT1 = -b / (2 * a);
+ } else {
+ /* Use the following formula for computing the roots:
+ *
+ * q = -1/2 * (b + sign(b) * sqrt(b^2 - 4ac))
+ * t1 = q / a
+ * t2 = c / q
+ */
+ Float q = sqrtf(discriminant);
+ if (b < 0) {
+ q = b - q;
+ } else {
+ q = b + q;
+ }
+ q *= Float(-1./2);
+
+ *aT1 = q / a;
+ *aT2 = c / q;
+ if (*aT1 > *aT2) {
+ std::swap(*aT1, *aT2);
+ }
+ *aCount = 2;
+ }
+ }
+
+ return;
+}
+
+void
+FlattenBezier(const BezierControlPoints &aControlPoints,
+ PathSink *aSink, Float aTolerance)
+{
+ Float t1;
+ Float t2;
+ uint32_t count;
+
+ FindInflectionPoints(aControlPoints, &t1, &t2, &count);
+
+ // Check that at least one of the inflection points is inside [0..1]
+ if (count == 0 || ((t1 < 0 || t1 > 1.0) && ((t2 < 0 || t2 > 1.0) || count == 1)) ) {
+ FlattenBezierCurveSegment(aControlPoints, aSink, aTolerance);
+ return;
+ }
+
+ Float t1min = t1, t1max = t1, t2min = t2, t2max = t2;
+
+ BezierControlPoints remainingCP = aControlPoints;
+
+ // For both inflection points, calulate the range where they can be linearly
+ // approximated if they are positioned within [0,1]
+ if (count > 0 && t1 >= 0 && t1 < 1.0) {
+ FindInflectionApproximationRange(aControlPoints, &t1min, &t1max, t1, aTolerance);
+ }
+ if (count > 1 && t2 >= 0 && t2 < 1.0) {
+ FindInflectionApproximationRange(aControlPoints, &t2min, &t2max, t2, aTolerance);
+ }
+ BezierControlPoints nextCPs = aControlPoints;
+ BezierControlPoints prevCPs;
+
+ // Process ranges. [t1min, t1max] and [t2min, t2max] are approximated by line
+ // segments.
+ if (count == 1 && t1min <= 0 && t1max >= 1.0) {
+ // The whole range can be approximated by a line segment.
+ aSink->LineTo(aControlPoints.mCP4);
+ return;
+ }
+
+ if (t1min > 0) {
+ // Flatten the Bezier up until the first inflection point's approximation
+ // point.
+ SplitBezier(aControlPoints, &prevCPs,
+ &remainingCP, t1min);
+ FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
+ }
+ if (t1max >= 0 && t1max < 1.0 && (count == 1 || t2min > t1max)) {
+ // The second inflection point's approximation range begins after the end
+ // of the first, approximate the first inflection point by a line and
+ // subsequently flatten up until the end or the next inflection point.
+ SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
+
+ aSink->LineTo(nextCPs.mCP1);
+
+ if (count == 1 || (count > 1 && t2min >= 1.0)) {
+ // No more inflection points to deal with, flatten the rest of the curve.
+ FlattenBezierCurveSegment(nextCPs, aSink, aTolerance);
+ }
+ } else if (count > 1 && t2min > 1.0) {
+ // We've already concluded t2min <= t1max, so if this is true the
+ // approximation range for the first inflection point runs past the
+ // end of the curve, draw a line to the end and we're done.
+ aSink->LineTo(aControlPoints.mCP4);
+ return;
+ }
+
+ if (count > 1 && t2min < 1.0 && t2max > 0) {
+ if (t2min > 0 && t2min < t1max) {
+ // In this case the t2 approximation range starts inside the t1
+ // approximation range.
+ SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
+ aSink->LineTo(nextCPs.mCP1);
+ } else if (t2min > 0 && t1max > 0) {
+ SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
+
+ // Find a control points describing the portion of the curve between t1max and t2min.
+ Float t2mina = (t2min - t1max) / (1 - t1max);
+ SplitBezier(nextCPs, &prevCPs, &nextCPs, t2mina);
+ FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
+ } else if (t2min > 0) {
+ // We have nothing interesting before t2min, find that bit and flatten it.
+ SplitBezier(aControlPoints, &prevCPs, &nextCPs, t2min);
+ FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
+ }
+ if (t2max < 1.0) {
+ // Flatten the portion of the curve after t2max
+ SplitBezier(aControlPoints, nullptr, &nextCPs, t2max);
+
+ // Draw a line to the start, this is the approximation between t2min and
+ // t2max.
+ aSink->LineTo(nextCPs.mCP1);
+ FlattenBezierCurveSegment(nextCPs, aSink, aTolerance);
+ } else {
+ // Our approximation range extends beyond the end of the curve.
+ aSink->LineTo(aControlPoints.mCP4);
+ return;
+ }
+ }
+}
+
+}
+}
diff --git a/gfx/2d/PathAnalysis.h b/gfx/2d/PathAnalysis.h
new file mode 100644
index 000000000..6b9b33f89
--- /dev/null
+++ b/gfx/2d/PathAnalysis.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "2D.h"
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+struct FlatPathOp
+{
+ enum OpType {
+ OP_MOVETO,
+ OP_LINETO,
+ };
+
+ OpType mType;
+ Point mPoint;
+};
+
+class FlattenedPath : public PathSink
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FlattenedPath)
+ FlattenedPath() : mCachedLength(0)
+ , mCalculatedLength(false)
+ {
+ }
+
+ virtual void MoveTo(const Point &aPoint);
+ virtual void LineTo(const Point &aPoint);
+ virtual void BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3);
+ virtual void QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2);
+ virtual void Close();
+ virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise = false);
+
+ virtual Point CurrentPoint() const { return mPathOps.empty() ? Point() : mPathOps[mPathOps.size() - 1].mPoint; }
+
+ Float ComputeLength();
+ Point ComputePointAtLength(Float aLength, Point *aTangent);
+
+private:
+ Float mCachedLength;
+ bool mCalculatedLength;
+ Point mLastMove;
+
+ std::vector<FlatPathOp> mPathOps;
+};
+
+}
+}
diff --git a/gfx/2d/PathCG.cpp b/gfx/2d/PathCG.cpp
index 191a4039b..0798a6ee3 100644
--- a/gfx/2d/PathCG.cpp
+++ b/gfx/2d/PathCG.cpp
@@ -7,6 +7,7 @@
#include <math.h>
#include "DrawTargetCG.h"
#include "Logging.h"
+#include "PathHelpers.h"
namespace mozilla {
namespace gfx {
@@ -19,12 +20,19 @@ PathBuilderCG::~PathBuilderCG()
void
PathBuilderCG::MoveTo(const Point &aPoint)
{
+ if (!aPoint.IsFinite()) {
+ return;
+ }
CGPathMoveToPoint(mCGPath, nullptr, aPoint.x, aPoint.y);
}
void
PathBuilderCG::LineTo(const Point &aPoint)
{
+ if (!aPoint.IsFinite()) {
+ return;
+ }
+
if (CGPathIsEmpty(mCGPath))
MoveTo(aPoint);
else
@@ -36,6 +44,9 @@ PathBuilderCG::BezierTo(const Point &aCP1,
const Point &aCP2,
const Point &aCP3)
{
+ if (!aCP1.IsFinite() || !aCP2.IsFinite() || !aCP3.IsFinite()) {
+ return;
+ }
if (CGPathIsEmpty(mCGPath))
MoveTo(aCP1);
@@ -48,13 +59,17 @@ PathBuilderCG::BezierTo(const Point &aCP1,
void
PathBuilderCG::QuadraticBezierTo(const Point &aCP1,
- const Point &aCP2)
+ const Point &aCP2)
{
+ if (!aCP1.IsFinite() || !aCP2.IsFinite()) {
+ return;
+ }
+
if (CGPathIsEmpty(mCGPath))
MoveTo(aCP1);
CGPathAddQuadCurveToPoint(mCGPath, nullptr,
- aCP1.x, aCP1.y,
- aCP2.x, aCP2.y);
+ aCP1.x, aCP1.y,
+ aCP2.x, aCP2.y);
}
void
@@ -68,13 +83,42 @@ void
PathBuilderCG::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
Float aEndAngle, bool aAntiClockwise)
{
+ if (!aOrigin.IsFinite() || !IsFinite(aRadius) ||
+ !IsFinite(aStartAngle) || !IsFinite(aEndAngle)) {
+ return;
+ }
+
+ // Disabled for now due to a CG bug when using CGPathAddArc with stroke
+ // dashing and rotation transforms that are multiples of 90 degrees. See:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=949661#c8
+#if 0
+ // Core Graphic's initial coordinate system is y-axis up, whereas Moz2D's is
+ // y-axis down. Core Graphics therefore considers "clockwise" to mean "sweep
+ // in the direction of decreasing angle" whereas Moz2D considers it to mean
+ // "sweep in the direction of increasing angle". In other words if this
+ // Moz2D method is instructed to sweep anti-clockwise we need to tell
+ // CGPathAddArc to sweep clockwise, and vice versa. Hence why we pass the
+ // value of aAntiClockwise directly to CGPathAddArc's "clockwise" bool
+ // parameter.
+ CGPathAddArc(mCGPath, nullptr,
+ aOrigin.x, aOrigin.y,
+ aRadius,
+ aStartAngle,
+ aEndAngle,
+ aAntiClockwise);
+#endif
+ ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle,
+ aAntiClockwise);
}
Point
PathBuilderCG::CurrentPoint() const
{
- CGPoint pt = CGPathGetCurrentPoint(mCGPath);
- Point ret(pt.x, pt.y);
+ Point ret;
+ if (!CGPathIsEmpty(mCGPath)) {
+ CGPoint pt = CGPathGetCurrentPoint(mCGPath);
+ ret.MoveTo(pt.x, pt.y);
+ }
return ret;
}
@@ -86,16 +130,14 @@ PathBuilderCG::EnsureActive(const Point &aPoint)
TemporaryRef<Path>
PathBuilderCG::Finish()
{
- RefPtr<PathCG> path = new PathCG(mCGPath, mFillRule);
- return path;
+ return new PathCG(mCGPath, mFillRule);
}
TemporaryRef<PathBuilder>
PathCG::CopyToBuilder(FillRule aFillRule) const
{
CGMutablePathRef path = CGPathCreateMutableCopy(mPath);
- RefPtr<PathBuilderCG> builder = new PathBuilderCG(path, aFillRule);
- return builder;
+ return new PathBuilderCG(path, aFillRule);
}
@@ -155,10 +197,57 @@ PathCG::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) c
ta.transform = GfxMatrixToCGAffineTransform(aTransform);
CGPathApply(mPath, &ta, TransformApplier::TranformCGPathApplierFunc);
- RefPtr<PathBuilderCG> builder = new PathBuilderCG(ta.path, aFillRule);
- return builder;
+ return new PathBuilderCG(ta.path, aFillRule);
}
+static void
+StreamPathToSinkApplierFunc(void *vinfo, const CGPathElement *element)
+{
+ PathSink *sink = reinterpret_cast<PathSink*>(vinfo);
+ switch (element->type) {
+ case kCGPathElementMoveToPoint:
+ {
+ CGPoint pt = element->points[0];
+ sink->MoveTo(CGPointToPoint(pt));
+ break;
+ }
+ case kCGPathElementAddLineToPoint:
+ {
+ CGPoint pt = element->points[0];
+ sink->LineTo(CGPointToPoint(pt));
+ break;
+ }
+ case kCGPathElementAddQuadCurveToPoint:
+ {
+ CGPoint cpt = element->points[0];
+ CGPoint pt = element->points[1];
+ sink->QuadraticBezierTo(CGPointToPoint(cpt),
+ CGPointToPoint(pt));
+ break;
+ }
+ case kCGPathElementAddCurveToPoint:
+ {
+ CGPoint cpt1 = element->points[0];
+ CGPoint cpt2 = element->points[1];
+ CGPoint pt = element->points[2];
+ sink->BezierTo(CGPointToPoint(cpt1),
+ CGPointToPoint(cpt2),
+ CGPointToPoint(pt));
+ break;
+ }
+ case kCGPathElementCloseSubpath:
+ {
+ sink->Close();
+ break;
+ }
+ }
+}
+
+void
+PathCG::StreamToSink(PathSink *aSink) const
+{
+ CGPathApply(mPath, aSink, StreamPathToSinkApplierFunc);
+}
bool
PathCG::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
@@ -171,7 +260,7 @@ PathCG::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
// The transform parameter of CGPathContainsPoint doesn't seem to work properly on OS X 10.5
// so we transform aPoint ourselves.
- return CGPathContainsPoint(mPath, nullptr, point, mFillRule == FILL_EVEN_ODD);
+ return CGPathContainsPoint(mPath, nullptr, point, mFillRule == FillRule::FILL_EVEN_ODD);
}
static size_t
@@ -235,7 +324,8 @@ PathCG::GetBounds(const Matrix &aTransform) const
{
//XXX: are these bounds tight enough
Rect bounds = CGRectToRect(CGPathGetBoundingBox(mPath));
- //XXX: curretnly this returns the bounds of the transformed bounds
+
+ //XXX: currently this returns the bounds of the transformed bounds
// this is strictly looser than the bounds of the transformed path
return aTransform.TransformBounds(bounds);
}
@@ -260,6 +350,10 @@ PathCG::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
CGContextRestoreGState(cg);
+ if (!bounds.IsFinite()) {
+ return Rect();
+ }
+
return aTransform.TransformBounds(bounds);
}
diff --git a/gfx/2d/PathCG.h b/gfx/2d/PathCG.h
index 660e91dc0..9598edb87 100644
--- a/gfx/2d/PathCG.h
+++ b/gfx/2d/PathCG.h
@@ -17,6 +17,7 @@ class PathCG;
class PathBuilderCG : public PathBuilder
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderCG)
// absorbs a reference of aPath
PathBuilderCG(CGMutablePathRef aPath, FillRule aFillRule)
: mFillRule(aFillRule)
@@ -24,7 +25,7 @@ public:
mCGPath = aPath;
}
- PathBuilderCG(FillRule aFillRule)
+ explicit PathBuilderCG(FillRule aFillRule)
: mFillRule(aFillRule)
{
mCGPath = CGPathCreateMutable();
@@ -46,8 +47,11 @@ public:
virtual TemporaryRef<Path> Finish();
+ virtual BackendType GetBackendType() const { return BackendType::COREGRAPHICS; }
+
private:
friend class PathCG;
+ friend class ScaledFontMac;
void EnsureActive(const Point &aPoint);
@@ -60,6 +64,7 @@ private:
class PathCG : public Path
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathCG)
PathCG(CGMutablePathRef aPath, FillRule aFillRule)
: mPath(aPath)
, mFillRule(aFillRule)
@@ -68,13 +73,13 @@ public:
}
virtual ~PathCG() { CGPathRelease(mPath); }
- // Paths will always return BACKEND_COREGRAPHICS, but note that they
- // are compatible with BACKEND_COREGRAPHICS_ACCELERATED backend.
- virtual BackendType GetBackendType() const { return BACKEND_COREGRAPHICS; }
+ // Paths will always return BackendType::COREGRAPHICS, but note that they
+ // are compatible with BackendType::COREGRAPHICS_ACCELERATED backend.
+ virtual BackendType GetBackendType() const { return BackendType::COREGRAPHICS; }
- virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const;
+ virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const;
virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
- FillRule aFillRule = FILL_WINDING) const;
+ FillRule aFillRule = FillRule::FILL_WINDING) const;
virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
@@ -84,6 +89,8 @@ public:
virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
const Matrix &aTransform = Matrix()) const;
+ virtual void StreamToSink(PathSink *aSink) const;
+
virtual FillRule GetFillRule() const { return mFillRule; }
CGMutablePathRef GetPath() const { return mPath; }
@@ -92,7 +99,6 @@ private:
friend class DrawTargetCG;
CGMutablePathRef mPath;
- bool mEndedActive;
Point mEndPoint;
FillRule mFillRule;
};
diff --git a/gfx/2d/PathCairo.cpp b/gfx/2d/PathCairo.cpp
index c2c370b56..d0c9d056d 100644
--- a/gfx/2d/PathCairo.cpp
+++ b/gfx/2d/PathCairo.cpp
@@ -13,138 +13,37 @@
namespace mozilla {
namespace gfx {
-CairoPathContext::CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget)
- : mContext(aCtx)
- , mDrawTarget(aDrawTarget)
+PathBuilderCairo::PathBuilderCairo(FillRule aFillRule)
+ : mFillRule(aFillRule)
{
- cairo_reference(mContext);
-
- // A new path in the DrawTarget's context.
- aDrawTarget->SetPathObserver(this);
- cairo_new_path(mContext);
-}
-
-CairoPathContext::CairoPathContext(CairoPathContext& aPathContext)
- : mContext(aPathContext.mContext)
- , mDrawTarget(nullptr)
-{
- cairo_reference(mContext);
- DuplicateContextAndPath();
-}
-
-CairoPathContext::~CairoPathContext()
-{
- if (mDrawTarget) {
- DrawTargetCairo* drawTarget = mDrawTarget;
- ForgetDrawTarget();
-
- // We need to set mDrawTarget to nullptr before we tell DrawTarget otherwise
- // we will think we need to make a defensive copy of the path.
- drawTarget->SetPathObserver(nullptr);
- }
- cairo_destroy(mContext);
-}
-
-void
-CairoPathContext::DuplicateContextAndPath()
-{
- // Duplicate the path.
- cairo_path_t* path = cairo_copy_path(mContext);
-
- // Duplicate the context.
- cairo_surface_t* surf = cairo_get_target(mContext);
- cairo_matrix_t matrix;
- cairo_get_matrix(mContext, &matrix);
- cairo_destroy(mContext);
-
- mContext = cairo_create(surf);
-
- // Set the matrix to match the source context so that the path is copied in
- // device space. After this point it doesn't matter what the transform is
- // set to because it's always swapped out before use.
- cairo_set_matrix(mContext, &matrix);
-
- // Add the path, and throw away our duplicate.
- cairo_append_path(mContext, path);
- cairo_path_destroy(path);
}
void
-CairoPathContext::ForgetDrawTarget()
-{
- // We don't need to set the path observer back to nullptr in this case
- // because ForgetDrawTarget() is trigged when the target has been
- // grabbed by another path observer.
- mDrawTarget = nullptr;
-}
-
-void
-CairoPathContext::PathWillChange()
-{
- // Once we've copied out the context's path, there's no use to holding on to
- // the draw target. Thus, there's nothing for us to do if we're independent
- // of the draw target, since we'll have already copied out the context's
- // path.
- if (mDrawTarget) {
- // The context we point to is going to change from under us. To continue
- // using this path, we need to copy it to a new context.
- DuplicateContextAndPath();
- ForgetDrawTarget();
- }
-}
-
-void
-CairoPathContext::CopyPathTo(cairo_t* aToContext, Matrix& aTransform)
-{
- if (aToContext != mContext) {
- CairoTempMatrix tempMatrix(mContext, aTransform);
- cairo_path_t* path = cairo_copy_path(mContext);
- cairo_new_path(aToContext);
- cairo_append_path(aToContext, path);
- cairo_path_destroy(path);
- }
-}
-
-bool
-CairoPathContext::ContainsPath(const Path* aPath)
-{
- if (aPath->GetBackendType() != BACKEND_CAIRO) {
- return false;
- }
-
- const PathCairo* path = static_cast<const PathCairo*>(aPath);
- RefPtr<CairoPathContext> ctx = const_cast<PathCairo*>(path)->GetPathContext();
- return ctx == this;
-}
-
-PathBuilderCairo::PathBuilderCairo(CairoPathContext* aPathContext,
- FillRule aFillRule,
- const Matrix& aTransform /* = Matrix() */)
- : mPathContext(aPathContext)
- , mTransform(aTransform)
- , mFillRule(aFillRule)
-{}
-
-PathBuilderCairo::PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule)
- : mPathContext(new CairoPathContext(aCtx, aDrawTarget))
- , mTransform(aDrawTarget->GetTransform())
- , mFillRule(aFillRule)
-{}
-
-void
PathBuilderCairo::MoveTo(const Point &aPoint)
{
- PrepareForWrite();
- CairoTempMatrix tempMatrix(*mPathContext, mTransform);
- cairo_move_to(*mPathContext, aPoint.x, aPoint.y);
+ cairo_path_data_t data;
+ data.header.type = CAIRO_PATH_MOVE_TO;
+ data.header.length = 2;
+ mPathData.push_back(data);
+ data.point.x = aPoint.x;
+ data.point.y = aPoint.y;
+ mPathData.push_back(data);
+
+ mBeginPoint = mCurrentPoint = aPoint;
}
void
PathBuilderCairo::LineTo(const Point &aPoint)
{
- PrepareForWrite();
- CairoTempMatrix tempMatrix(*mPathContext, mTransform);
- cairo_line_to(*mPathContext, aPoint.x, aPoint.y);
+ cairo_path_data_t data;
+ data.header.type = CAIRO_PATH_LINE_TO;
+ data.header.length = 2;
+ mPathData.push_back(data);
+ data.point.x = aPoint.x;
+ data.point.y = aPoint.y;
+ mPathData.push_back(data);
+
+ mCurrentPoint = aPoint;
}
void
@@ -152,18 +51,27 @@ PathBuilderCairo::BezierTo(const Point &aCP1,
const Point &aCP2,
const Point &aCP3)
{
- PrepareForWrite();
- CairoTempMatrix tempMatrix(*mPathContext, mTransform);
- cairo_curve_to(*mPathContext, aCP1.x, aCP1.y, aCP2.x, aCP2.y, aCP3.x, aCP3.y);
+ cairo_path_data_t data;
+ data.header.type = CAIRO_PATH_CURVE_TO;
+ data.header.length = 4;
+ mPathData.push_back(data);
+ data.point.x = aCP1.x;
+ data.point.y = aCP1.y;
+ mPathData.push_back(data);
+ data.point.x = aCP2.x;
+ data.point.y = aCP2.y;
+ mPathData.push_back(data);
+ data.point.x = aCP3.x;
+ data.point.y = aCP3.y;
+ mPathData.push_back(data);
+
+ mCurrentPoint = aCP3;
}
void
PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
const Point &aCP2)
{
- PrepareForWrite();
- CairoTempMatrix tempMatrix(*mPathContext, mTransform);
-
// We need to elevate the degree of this quadratic Bézier to cubic, so we're
// going to add an intermediate control point, and recompute control point 1.
// The first and last control points remain the same.
@@ -173,94 +81,116 @@ PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
Point CP3 = aCP2;
- cairo_curve_to(*mPathContext, CP1.x, CP1.y, CP2.x, CP2.y, CP3.x, CP3.y);
+ cairo_path_data_t data;
+ data.header.type = CAIRO_PATH_CURVE_TO;
+ data.header.length = 4;
+ mPathData.push_back(data);
+ data.point.x = CP1.x;
+ data.point.y = CP1.y;
+ mPathData.push_back(data);
+ data.point.x = CP2.x;
+ data.point.y = CP2.y;
+ mPathData.push_back(data);
+ data.point.x = CP3.x;
+ data.point.y = CP3.y;
+ mPathData.push_back(data);
+
+ mCurrentPoint = aCP2;
}
void
PathBuilderCairo::Close()
{
- PrepareForWrite();
- cairo_close_path(*mPathContext);
+ cairo_path_data_t data;
+ data.header.type = CAIRO_PATH_CLOSE_PATH;
+ data.header.length = 1;
+ mPathData.push_back(data);
+
+ mCurrentPoint = mBeginPoint;
}
void
PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
float aEndAngle, bool aAntiClockwise)
{
- ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise);
+ ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
}
Point
PathBuilderCairo::CurrentPoint() const
{
- CairoTempMatrix tempMatrix(*mPathContext, mTransform);
- double x, y;
- cairo_get_current_point(*mPathContext, &x, &y);
- return Point((Float)x, (Float)y);
+ return mCurrentPoint;
}
TemporaryRef<Path>
PathBuilderCairo::Finish()
{
- return new PathCairo(mPathContext, mTransform, mFillRule);
+ return new PathCairo(mFillRule, mPathData, mCurrentPoint);
}
-TemporaryRef<CairoPathContext>
-PathBuilderCairo::GetPathContext()
+PathCairo::PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint)
+ : mFillRule(aFillRule)
+ , mContainingContext(nullptr)
+ , mCurrentPoint(aCurrentPoint)
{
- return mPathContext;
+ mPathData.swap(aPathData);
}
-void
-PathBuilderCairo::PrepareForWrite()
+PathCairo::PathCairo(cairo_t *aContext)
+ : mFillRule(FillRule::FILL_WINDING)
+ , mContainingContext(nullptr)
{
- // Only PathBuilder and PathCairo maintain references to CairoPathContext.
- // DrawTarget does not. If we're sharing a reference to the context then we
- // need to create a copy that we can modify. This provides copy on write
- // behaviour.
- if (mPathContext->refCount() != 1) {
- mPathContext = new CairoPathContext(*mPathContext);
+ cairo_path_t *path = cairo_copy_path(aContext);
+
+ // XXX - mCurrentPoint is not properly set here, the same is true for the
+ // D2D Path code, we never require current point when hitting this codepath
+ // but this should be fixed.
+ for (int i = 0; i < path->num_data; i++) {
+ mPathData.push_back(path->data[i]);
}
+
+ cairo_path_destroy(path);
}
-PathCairo::PathCairo(CairoPathContext* aPathContext, Matrix& aTransform,
- FillRule aFillRule)
- : mPathContext(aPathContext)
- , mTransform(aTransform)
- , mFillRule(aFillRule)
-{}
+PathCairo::~PathCairo()
+{
+ if (mContainingContext) {
+ cairo_destroy(mContainingContext);
+ }
+}
TemporaryRef<PathBuilder>
PathCairo::CopyToBuilder(FillRule aFillRule) const
{
- return new PathBuilderCairo(mPathContext, aFillRule, mTransform);
+ RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
+
+ builder->mPathData = mPathData;
+ builder->mCurrentPoint = mCurrentPoint;
+
+ return builder.forget();
}
TemporaryRef<PathBuilder>
PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
{
- // We are given the transform we would apply from device space to user space.
- // However in cairo our path is in device space so we view the transform as
- // being the other way round. We therefore need to apply the inverse transform
- // to our current cairo transform.
- Matrix inverse = aTransform;
- inverse.Invert();
+ RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
+
+ AppendPathToBuilder(builder, &aTransform);
+ builder->mCurrentPoint = aTransform * mCurrentPoint;
- return new PathBuilderCairo(mPathContext, aFillRule, mTransform * inverse);
+ return builder.forget();
}
bool
PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
{
- CairoTempMatrix(*mPathContext, mTransform);
-
Matrix inverse = aTransform;
inverse.Invert();
Point transformed = inverse * aPoint;
- // Needs the correct fill rule set.
- cairo_set_fill_rule(*mPathContext, GfxFillRuleToCairoFillRule(mFillRule));
- return cairo_in_fill(*mPathContext, transformed.x, transformed.y);
+ EnsureContainingContext();
+
+ return cairo_in_fill(mContainingContext, transformed.x, transformed.y);
}
bool
@@ -268,24 +198,25 @@ PathCairo::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
const Point &aPoint,
const Matrix &aTransform) const
{
- CairoTempMatrix(*mPathContext, mTransform);
-
Matrix inverse = aTransform;
inverse.Invert();
Point transformed = inverse * aPoint;
- SetCairoStrokeOptions(*mPathContext, aStrokeOptions);
- return cairo_in_stroke(*mPathContext, transformed.x, transformed.y);
+ EnsureContainingContext();
+
+ SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
+
+ return cairo_in_stroke(mContainingContext, transformed.x, transformed.y);
}
Rect
PathCairo::GetBounds(const Matrix &aTransform) const
{
- CairoTempMatrix(*mPathContext, mTransform);
+ EnsureContainingContext();
double x1, y1, x2, y2;
- cairo_path_extents(*mPathContext, &x1, &y1, &x2, &y2);
+ cairo_path_extents(mContainingContext, &x1, &y1, &x2, &y2);
Rect bounds(Float(x1), Float(y1), Float(x2 - x1), Float(y2 - y1));
return aTransform.TransformBounds(bounds);
}
@@ -294,28 +225,98 @@ Rect
PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
const Matrix &aTransform) const
{
- CairoTempMatrix(*mPathContext, mTransform);
+ EnsureContainingContext();
double x1, y1, x2, y2;
- SetCairoStrokeOptions(*mPathContext, aStrokeOptions);
+ SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
- cairo_stroke_extents(*mPathContext, &x1, &y1, &x2, &y2);
+ cairo_stroke_extents(mContainingContext, &x1, &y1, &x2, &y2);
Rect bounds((Float)x1, (Float)y1, (Float)(x2 - x1), (Float)(y2 - y1));
return aTransform.TransformBounds(bounds);
}
-TemporaryRef<CairoPathContext>
-PathCairo::GetPathContext()
+void
+PathCairo::StreamToSink(PathSink *aSink) const
+{
+ for (size_t i = 0; i < mPathData.size(); i++) {
+ switch (mPathData[i].header.type) {
+ case CAIRO_PATH_MOVE_TO:
+ i++;
+ aSink->MoveTo(Point(mPathData[i].point.x, mPathData[i].point.y));
+ break;
+ case CAIRO_PATH_LINE_TO:
+ i++;
+ aSink->LineTo(Point(mPathData[i].point.x, mPathData[i].point.y));
+ break;
+ case CAIRO_PATH_CURVE_TO:
+ aSink->BezierTo(Point(mPathData[i + 1].point.x, mPathData[i + 1].point.y),
+ Point(mPathData[i + 2].point.x, mPathData[i + 2].point.y),
+ Point(mPathData[i + 3].point.x, mPathData[i + 3].point.y));
+ i += 3;
+ break;
+ case CAIRO_PATH_CLOSE_PATH:
+ aSink->Close();
+ break;
+ default:
+ // Corrupt path data!
+ MOZ_ASSERT(false);
+ }
+ }
+}
+
+void
+PathCairo::EnsureContainingContext() const
{
- return mPathContext;
+ if (mContainingContext) {
+ return;
+ }
+
+ mContainingContext = cairo_create(DrawTargetCairo::GetDummySurface());
+
+ SetPathOnContext(mContainingContext);
}
void
-PathCairo::CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget)
+PathCairo::SetPathOnContext(cairo_t *aContext) const
{
- mPathContext->CopyPathTo(aContext, mTransform);
+ // Needs the correct fill rule set.
cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule));
+
+ cairo_new_path(aContext);
+
+ if (mPathData.size()) {
+ cairo_path_t path;
+ path.data = const_cast<cairo_path_data_t*>(&mPathData.front());
+ path.num_data = mPathData.size();
+ path.status = CAIRO_STATUS_SUCCESS;
+ cairo_append_path(aContext, &path);
+ }
+}
+
+void
+PathCairo::AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform) const
+{
+ if (aTransform) {
+ size_t i = 0;
+ while (i < mPathData.size()) {
+ uint32_t pointCount = mPathData[i].header.length - 1;
+ aBuilder->mPathData.push_back(mPathData[i]);
+ i++;
+ for (uint32_t c = 0; c < pointCount; c++) {
+ cairo_path_data_t data;
+ Point newPoint = *aTransform * Point(mPathData[i].point.x, mPathData[i].point.y);
+ data.point.x = newPoint.x;
+ data.point.y = newPoint.y;
+ aBuilder->mPathData.push_back(data);
+ i++;
+ }
+ }
+ } else {
+ for (size_t i = 0; i < mPathData.size(); i++) {
+ aBuilder->mPathData.push_back(mPathData[i]);
+ }
+ }
}
}
diff --git a/gfx/2d/PathCairo.h b/gfx/2d/PathCairo.h
index 50cb366cf..898181242 100644
--- a/gfx/2d/PathCairo.h
+++ b/gfx/2d/PathCairo.h
@@ -8,86 +8,19 @@
#include "2D.h"
#include "cairo.h"
+#include <vector>
namespace mozilla {
namespace gfx {
class DrawTargetCairo;
-
-// A reference to a cairo context that can maintain and set a path.
-//
-// This class exists to make it possible for us to not construct paths manually
-// using cairo_path_t, which in the common case is a speed and memory
-// optimization (as the cairo_t maintains the path for us, and we don't have to
-// use cairo_append_path). Instead, we can share a cairo_t with a DrawTarget,
-// and have it inform us when we need to make a copy of the path.
-//
-// Exactly one Path* object represents the current path on a given DrawTarget's
-// context. That Path* object registers its CairoPathContext with the
-// DrawTarget it's associated with. If that DrawTarget is going to change its
-// path, it has to tell the CairoPathContext beforehand so the path can be
-// saved off.
-// The path ownership is transferred to every new instance of CairoPathContext
-// in the constructor. We inform the draw target of the new context object,
-// which causes us to save off a copy of the path, as we're not going to be
-// informed upon changes any more.
-// Any transformation on aCtx is not applied to this path, though a path can be
-// transformed separately from its context by passing a matrix to the
-// constructor.
-class CairoPathContext : public RefCounted<CairoPathContext>
-{
-public:
- // Construct a new empty CairoPathContext that uses the given draw target and
- // its cairo context. Using the existing context may save having to copy the
- // path later.
- CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget);
-
- // Copy the path.
- CairoPathContext(CairoPathContext& aPathContext);
-
- ~CairoPathContext();
-
- // Copy the path on mContext to be the path on aToContext, if they aren't the
- // same. At this point we set the fill rule for the destination context as
- // there is little point in doing this earlier.
- void CopyPathTo(cairo_t* aToContext, Matrix& aTransform);
-
- // This method must be called by the draw target before it changes the path
- // currently on the cairo context.
- void PathWillChange();
-
- // This method must be called as the draw target is dying. In this case, we
- // forget our reference to the draw target, and become the only reference to
- // our context.
- void ForgetDrawTarget();
-
- // Create a duplicate context, and copy this path to that context.
- void DuplicateContextAndPath();
-
- // Returns true if this CairoPathContext represents path.
- bool ContainsPath(const Path* path);
-
- cairo_t* GetContext() const { return mContext; }
- DrawTargetCairo* GetDrawTarget() const { return mDrawTarget; }
- operator cairo_t* () const { return mContext; }
-
-private: // data
- cairo_t* mContext;
- // Not a RefPtr to avoid cycles.
- DrawTargetCairo* mDrawTarget;
-};
+class PathCairo;
class PathBuilderCairo : public PathBuilder
{
public:
- // Creates a new empty path. It also implicitly takes ownership of aCtx by
- // calling aDrawTarget->SetPathObserver(). Therefore, if the draw target has a
- // path observer, this constructor will cause it to copy out its path.
- PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule);
-
- // Creates a path builder out of an existing CairoPathContext with a new fill
- // rule and transform.
- PathBuilderCairo(CairoPathContext* aContext, FillRule aFillRule, const Matrix& aTransform = Matrix());
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderCairo)
+ explicit PathBuilderCairo(FillRule aFillRule);
virtual void MoveTo(const Point &aPoint);
virtual void LineTo(const Point &aPoint);
@@ -102,26 +35,32 @@ public:
virtual Point CurrentPoint() const;
virtual TemporaryRef<Path> Finish();
- TemporaryRef<CairoPathContext> GetPathContext();
+ virtual BackendType GetBackendType() const { return BackendType::CAIRO; }
private: // data
- void PrepareForWrite();
+ friend class PathCairo;
- RefPtr<CairoPathContext> mPathContext;
- Matrix mTransform;
FillRule mFillRule;
+ std::vector<cairo_path_data_t> mPathData;
+ // It's easiest to track this here, parsing the path data to find the current
+ // point is a little tricky.
+ Point mCurrentPoint;
+ Point mBeginPoint;
};
class PathCairo : public Path
{
public:
- PathCairo(CairoPathContext* aPathContex, Matrix& aTransform, FillRule aFillRule);
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathCairo)
+ PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint);
+ explicit PathCairo(cairo_t *aContext);
+ ~PathCairo();
- virtual BackendType GetBackendType() const { return BACKEND_CAIRO; }
+ virtual BackendType GetBackendType() const { return BackendType::CAIRO; }
- virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const;
+ virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const;
virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
- FillRule aFillRule = FILL_WINDING) const;
+ FillRule aFillRule = FillRule::FILL_WINDING) const;
virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
@@ -134,19 +73,20 @@ public:
virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
const Matrix &aTransform = Matrix()) const;
- virtual FillRule GetFillRule() const { return mFillRule; }
+ virtual void StreamToSink(PathSink *aSink) const;
- TemporaryRef<CairoPathContext> GetPathContext();
+ virtual FillRule GetFillRule() const { return mFillRule; }
- // Set this path to be the current path for aContext (if it's not already
- // aContext's path). You must pass the draw target associated with the
- // context as aDrawTarget.
- void CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget);
+ void SetPathOnContext(cairo_t *aContext) const;
+ void AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform = nullptr) const;
private:
- RefPtr<CairoPathContext> mPathContext;
- Matrix mTransform;
+ void EnsureContainingContext() const;
+
FillRule mFillRule;
+ std::vector<cairo_path_data_t> mPathData;
+ mutable cairo_t *mContainingContext;
+ Point mCurrentPoint;
};
}
diff --git a/gfx/2d/PathD2D.cpp b/gfx/2d/PathD2D.cpp
index cd4d25471..e18dae8a5 100644
--- a/gfx/2d/PathD2D.cpp
+++ b/gfx/2d/PathD2D.cpp
@@ -91,6 +91,70 @@ private:
bool mNeedsFigureEnded;
};
+class StreamingGeometrySink : public ID2D1SimplifiedGeometrySink
+{
+public:
+ StreamingGeometrySink(PathSink *aSink)
+ : mSink(aSink)
+ {
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
+ {
+ if (!aPtr) {
+ return E_POINTER;
+ }
+
+ if (aIID == IID_IUnknown) {
+ *aPtr = static_cast<IUnknown*>(this);
+ return S_OK;
+ } else if (aIID == IID_ID2D1SimplifiedGeometrySink) {
+ *aPtr = static_cast<ID2D1SimplifiedGeometrySink*>(this);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return 1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return 1;
+ }
+
+ // We ignore SetFillMode, this depends on the destination sink.
+ STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE aMode)
+ { return; }
+ STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F aPoint, D2D1_FIGURE_BEGIN aBegin)
+ { mSink->MoveTo(ToPoint(aPoint)); }
+ STDMETHOD_(void, AddLines)(const D2D1_POINT_2F *aLines, UINT aCount)
+ { for (UINT i = 0; i < aCount; i++) { mSink->LineTo(ToPoint(aLines[i])); } }
+ STDMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *aSegments, UINT aCount)
+ {
+ for (UINT i = 0; i < aCount; i++) {
+ mSink->BezierTo(ToPoint(aSegments[i].point1), ToPoint(aSegments[i].point2), ToPoint(aSegments[i].point3));
+ }
+ }
+ STDMETHOD(Close)()
+ { /* Should never be called! */ return S_OK; }
+ STDMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT aFlags)
+ { /* Should never be called! */ }
+
+ STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END aEnd)
+ {
+ if (aEnd == D2D1_FIGURE_END_CLOSED) {
+ return mSink->Close();
+ }
+ }
+private:
+
+ PathSink *mSink;
+};
+
PathBuilderD2D::~PathBuilderD2D()
{
}
@@ -235,11 +299,11 @@ PathBuilderD2D::Finish()
HRESULT hr = mSink->Close();
if (FAILED(hr)) {
- gfxDebug() << "Failed to close PathSink. Code: " << hr;
+ gfxDebug() << "Failed to close PathSink. Code: " << hexa(hr);
return nullptr;
}
- return new PathD2D(mGeometry, mFigureActive, mCurrentPoint, mFillRule);
+ return new PathD2D(mGeometry, mFigureActive, mCurrentPoint, mFillRule, mBackendType);
}
TemporaryRef<PathBuilder>
@@ -255,18 +319,18 @@ PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule)
HRESULT hr = DrawTargetD2D::factory()->CreatePathGeometry(byRef(path));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create PathGeometry. Code: " << hr;
+ gfxWarning() << "Failed to create PathGeometry. Code: " << hexa(hr);
return nullptr;
}
RefPtr<ID2D1GeometrySink> sink;
hr = path->Open(byRef(sink));
if (FAILED(hr)) {
- gfxWarning() << "Failed to open Geometry for writing. Code: " << hr;
+ gfxWarning() << "Failed to open Geometry for writing. Code: " << hexa(hr);
return nullptr;
}
- if (aFillRule == FILL_WINDING) {
+ if (aFillRule == FillRule::FILL_WINDING) {
sink->SetFillMode(D2D1_FILL_MODE_WINDING);
}
@@ -281,7 +345,7 @@ PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule)
sink);
}
- RefPtr<PathBuilderD2D> pathBuilder = new PathBuilderD2D(sink, path, mFillRule);
+ RefPtr<PathBuilderD2D> pathBuilder = new PathBuilderD2D(sink, path, aFillRule, mBackendType);
pathBuilder->mCurrentPoint = aTransform * mEndPoint;
@@ -289,9 +353,25 @@ PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule)
pathBuilder->mFigureActive = true;
}
- return pathBuilder;
+ return pathBuilder.forget();
}
+void
+PathD2D::StreamToSink(PathSink *aSink) const
+{
+ HRESULT hr;
+
+ StreamingGeometrySink sink(aSink);
+
+ hr = mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+ D2D1::IdentityMatrix(), &sink);
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to stream D2D path to sink. Code: " << hexa(hr);
+ return;
+ }
+}
+
bool
PathD2D::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
{
@@ -332,35 +412,37 @@ PathD2D::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
Rect
PathD2D::GetBounds(const Matrix &aTransform) const
{
- D2D1_RECT_F bounds;
+ D2D1_RECT_F d2dBounds;
- HRESULT hr = mGeometry->GetBounds(D2DMatrix(aTransform), &bounds);
+ HRESULT hr = mGeometry->GetBounds(D2DMatrix(aTransform), &d2dBounds);
- if (FAILED(hr)) {
- gfxWarning() << "Failed to get stroked bounds for path. Code: " << hr;
- bounds.bottom = bounds.left = bounds.right = bounds.top = 0;
+ Rect bounds = ToRect(d2dBounds);
+ if (FAILED(hr) || !bounds.IsFinite()) {
+ gfxWarning() << "Failed to get stroked bounds for path. Code: " << hexa(hr);
+ return Rect();
}
- return ToRect(bounds);
+ return bounds;
}
Rect
PathD2D::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
const Matrix &aTransform) const
{
- D2D1_RECT_F bounds;
+ D2D1_RECT_F d2dBounds;
RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
HRESULT hr =
mGeometry->GetWidenedBounds(aStrokeOptions.mLineWidth, strokeStyle,
- D2DMatrix(aTransform), &bounds);
+ D2DMatrix(aTransform), &d2dBounds);
- if (FAILED(hr)) {
- gfxWarning() << "Failed to get stroked bounds for path. Code: " << hr;
- bounds.bottom = bounds.left = bounds.right = bounds.top = 0;
+ Rect bounds = ToRect(d2dBounds);
+ if (FAILED(hr) || !bounds.IsFinite()) {
+ gfxWarning() << "Failed to get stroked bounds for path. Code: " << hexa(hr);
+ return Rect();
}
- return ToRect(bounds);
+ return bounds;
}
}
diff --git a/gfx/2d/PathD2D.h b/gfx/2d/PathD2D.h
index 9b72b6cdb..3afd82783 100644
--- a/gfx/2d/PathD2D.h
+++ b/gfx/2d/PathD2D.h
@@ -6,8 +6,9 @@
#ifndef MOZILLA_GFX_PATHD2D_H_
#define MOZILLA_GFX_PATHD2D_H_
+#include <d2d1.h>
+
#include "2D.h"
-#include "moz-d2d1-1.h"
namespace mozilla {
namespace gfx {
@@ -17,11 +18,13 @@ class PathD2D;
class PathBuilderD2D : public PathBuilder
{
public:
- PathBuilderD2D(ID2D1GeometrySink *aSink, ID2D1PathGeometry *aGeom, FillRule aFillRule)
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderD2D)
+ PathBuilderD2D(ID2D1GeometrySink *aSink, ID2D1PathGeometry *aGeom, FillRule aFillRule, BackendType aBackendType)
: mSink(aSink)
, mGeometry(aGeom)
, mFigureActive(false)
, mFillRule(aFillRule)
+ , mBackendType(aBackendType)
{
}
virtual ~PathBuilderD2D();
@@ -40,6 +43,8 @@ public:
virtual TemporaryRef<Path> Finish();
+ virtual BackendType GetBackendType() const { return mBackendType; }
+
ID2D1GeometrySink *GetSink() { return mSink; }
private:
@@ -54,24 +59,27 @@ private:
Point mCurrentPoint;
Point mBeginPoint;
FillRule mFillRule;
+ BackendType mBackendType;
};
class PathD2D : public Path
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathD2D)
PathD2D(ID2D1PathGeometry *aGeometry, bool aEndedActive,
- const Point &aEndPoint, FillRule aFillRule)
+ const Point &aEndPoint, FillRule aFillRule, BackendType aBackendType)
: mGeometry(aGeometry)
, mEndedActive(aEndedActive)
, mEndPoint(aEndPoint)
, mFillRule(aFillRule)
+ , mBackendType(aBackendType)
{}
- virtual BackendType GetBackendType() const { return BACKEND_DIRECT2D; }
+ virtual BackendType GetBackendType() const { return mBackendType; }
- virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const;
+ virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const;
virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
- FillRule aFillRule = FILL_WINDING) const;
+ FillRule aFillRule = FillRule::FILL_WINDING) const;
virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
@@ -84,17 +92,21 @@ public:
virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
const Matrix &aTransform = Matrix()) const;
+ virtual void StreamToSink(PathSink *aSink) const;
+
virtual FillRule GetFillRule() const { return mFillRule; }
ID2D1Geometry *GetGeometry() { return mGeometry; }
private:
friend class DrawTargetD2D;
+ friend class DrawTargetD2D1;
mutable RefPtr<ID2D1PathGeometry> mGeometry;
bool mEndedActive;
Point mEndPoint;
FillRule mFillRule;
+ BackendType mBackendType;
};
}
diff --git a/gfx/2d/PathHelpers.cpp b/gfx/2d/PathHelpers.cpp
new file mode 100644
index 000000000..b9c0a3c1f
--- /dev/null
+++ b/gfx/2d/PathHelpers.cpp
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+UserDataKey sDisablePixelSnapping;
+
+void
+AppendRectToPath(PathBuilder* aPathBuilder,
+ const Rect& aRect,
+ bool aDrawClockwise)
+{
+ if (aDrawClockwise) {
+ aPathBuilder->MoveTo(aRect.TopLeft());
+ aPathBuilder->LineTo(aRect.TopRight());
+ aPathBuilder->LineTo(aRect.BottomRight());
+ aPathBuilder->LineTo(aRect.BottomLeft());
+ } else {
+ aPathBuilder->MoveTo(aRect.TopRight());
+ aPathBuilder->LineTo(aRect.TopLeft());
+ aPathBuilder->LineTo(aRect.BottomLeft());
+ aPathBuilder->LineTo(aRect.BottomRight());
+ }
+ aPathBuilder->Close();
+}
+
+void
+AppendRoundedRectToPath(PathBuilder* aPathBuilder,
+ const Rect& aRect,
+ const RectCornerRadii& aRadii,
+ bool aDrawClockwise)
+{
+ // For CW drawing, this looks like:
+ //
+ // ...******0** 1 C
+ // ****
+ // *** 2
+ // **
+ // *
+ // *
+ // 3
+ // *
+ // *
+ //
+ // Where 0, 1, 2, 3 are the control points of the Bezier curve for
+ // the corner, and C is the actual corner point.
+ //
+ // At the start of the loop, the current point is assumed to be
+ // the point adjacent to the top left corner on the top
+ // horizontal. Note that corner indices start at the top left and
+ // continue clockwise, whereas in our loop i = 0 refers to the top
+ // right corner.
+ //
+ // When going CCW, the control points are swapped, and the first
+ // corner that's drawn is the top left (along with the top segment).
+ //
+ // There is considerable latitude in how one chooses the four
+ // control points for a Bezier curve approximation to an ellipse.
+ // For the overall path to be continuous and show no corner at the
+ // endpoints of the arc, points 0 and 3 must be at the ends of the
+ // straight segments of the rectangle; points 0, 1, and C must be
+ // collinear; and points 3, 2, and C must also be collinear. This
+ // leaves only two free parameters: the ratio of the line segments
+ // 01 and 0C, and the ratio of the line segments 32 and 3C. See
+ // the following papers for extensive discussion of how to choose
+ // these ratios:
+ //
+ // Dokken, Tor, et al. "Good approximation of circles by
+ // curvature-continuous Bezier curves." Computer-Aided
+ // Geometric Design 7(1990) 33--41.
+ // Goldapp, Michael. "Approximation of circular arcs by cubic
+ // polynomials." Computer-Aided Geometric Design 8(1991) 227--238.
+ // Maisonobe, Luc. "Drawing an elliptical arc using polylines,
+ // quadratic, or cubic Bezier curves."
+ // http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
+ //
+ // We follow the approach in section 2 of Goldapp (least-error,
+ // Hermite-type approximation) and make both ratios equal to
+ //
+ // 2 2 + n - sqrt(2n + 28)
+ // alpha = - * ---------------------
+ // 3 n - 4
+ //
+ // where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ).
+ //
+ // This is the result of Goldapp's equation (10b) when the angle
+ // swept out by the arc is pi/2, and the parameter "a-bar" is the
+ // expression given immediately below equation (21).
+ //
+ // Using this value, the maximum radial error for a circle, as a
+ // fraction of the radius, is on the order of 0.2 x 10^-3.
+ // Neither Dokken nor Goldapp discusses error for a general
+ // ellipse; Maisonobe does, but his choice of control points
+ // follows different constraints, and Goldapp's expression for
+ // 'alpha' gives much smaller radial error, even for very flat
+ // ellipses, than Maisonobe's equivalent.
+ //
+ // For the various corners and for each axis, the sign of this
+ // constant changes, or it might be 0 -- it's multiplied by the
+ // appropriate multiplier from the list before using.
+
+ const Float alpha = Float(0.55191497064665766025);
+
+ typedef struct { Float a, b; } twoFloats;
+
+ twoFloats cwCornerMults[4] = { { -1, 0 }, // cc == clockwise
+ { 0, -1 },
+ { +1, 0 },
+ { 0, +1 } };
+ twoFloats ccwCornerMults[4] = { { +1, 0 }, // ccw == counter-clockwise
+ { 0, -1 },
+ { -1, 0 },
+ { 0, +1 } };
+
+ twoFloats *cornerMults = aDrawClockwise ? cwCornerMults : ccwCornerMults;
+
+ Point cornerCoords[] = { aRect.TopLeft(), aRect.TopRight(),
+ aRect.BottomRight(), aRect.BottomLeft() };
+
+ Point pc, p0, p1, p2, p3;
+
+ if (aDrawClockwise) {
+ aPathBuilder->MoveTo(Point(aRect.X() + aRadii[RectCorner::TopLeft].width,
+ aRect.Y()));
+ } else {
+ aPathBuilder->MoveTo(Point(aRect.X() + aRect.Width() - aRadii[RectCorner::TopRight].width,
+ aRect.Y()));
+ }
+
+ for (int i = 0; i < 4; ++i) {
+ // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
+ int c = aDrawClockwise ? ((i+1) % 4) : ((4-i) % 4);
+
+ // i+2 and i+3 respectively. These are used to index into the corner
+ // multiplier table, and were deduced by calculating out the long form
+ // of each corner and finding a pattern in the signs and values.
+ int i2 = (i+2) % 4;
+ int i3 = (i+3) % 4;
+
+ pc = cornerCoords[c];
+
+ if (aRadii[c].width > 0.0 && aRadii[c].height > 0.0) {
+ p0.x = pc.x + cornerMults[i].a * aRadii[c].width;
+ p0.y = pc.y + cornerMults[i].b * aRadii[c].height;
+
+ p3.x = pc.x + cornerMults[i3].a * aRadii[c].width;
+ p3.y = pc.y + cornerMults[i3].b * aRadii[c].height;
+
+ p1.x = p0.x + alpha * cornerMults[i2].a * aRadii[c].width;
+ p1.y = p0.y + alpha * cornerMults[i2].b * aRadii[c].height;
+
+ p2.x = p3.x - alpha * cornerMults[i3].a * aRadii[c].width;
+ p2.y = p3.y - alpha * cornerMults[i3].b * aRadii[c].height;
+
+ aPathBuilder->LineTo(p0);
+ aPathBuilder->BezierTo(p1, p2, p3);
+ } else {
+ aPathBuilder->LineTo(pc);
+ }
+ }
+
+ aPathBuilder->Close();
+}
+
+void
+AppendEllipseToPath(PathBuilder* aPathBuilder,
+ const Point& aCenter,
+ const Size& aDimensions)
+{
+ Size halfDim = aDimensions / 2.f;
+ Rect rect(aCenter - Point(halfDim.width, halfDim.height), aDimensions);
+ RectCornerRadii radii(halfDim.width, halfDim.height);
+
+ AppendRoundedRectToPath(aPathBuilder, rect, radii);
+}
+
+bool
+SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
+ const DrawTarget& aDrawTarget)
+{
+ Matrix mat = aDrawTarget.GetTransform();
+ if (mat.HasNonTranslation()) {
+ return false;
+ }
+ if (aP1.x != aP2.x && aP1.y != aP2.y) {
+ return false; // not a horizontal or vertical line
+ }
+ Point p1 = aP1 + mat.GetTranslation(); // into device space
+ Point p2 = aP2 + mat.GetTranslation();
+ p1.Round();
+ p2.Round();
+ p1 -= mat.GetTranslation(); // back into user space
+ p2 -= mat.GetTranslation();
+ if (aP1.x == aP2.x) {
+ // snap vertical line, adding 0.5 to align it to be mid-pixel:
+ aP1 = p1 + Point(0.5, 0);
+ aP2 = p2 + Point(0.5, 0);
+ } else {
+ // snap horizontal line, adding 0.5 to align it to be mid-pixel:
+ aP1 = p1 + Point(0, 0.5);
+ aP2 = p2 + Point(0, 0.5);
+ }
+ return true;
+}
+
+void
+StrokeSnappedEdgesOfRect(const Rect& aRect, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions)
+{
+ if (aRect.IsEmpty()) {
+ return;
+ }
+
+ Point p1 = aRect.TopLeft();
+ Point p2 = aRect.BottomLeft();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+ p1 = aRect.BottomLeft();
+ p2 = aRect.BottomRight();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+ p1 = aRect.TopLeft();
+ p2 = aRect.TopRight();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+ p1 = aRect.TopRight();
+ p2 = aRect.BottomRight();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+}
+
+} // namespace gfx
+} // namespace mozilla
+
diff --git a/gfx/2d/PathHelpers.h b/gfx/2d/PathHelpers.h
index 9cecae830..d7a732648 100644
--- a/gfx/2d/PathHelpers.h
+++ b/gfx/2d/PathHelpers.h
@@ -8,16 +8,17 @@
#include "2D.h"
#include "mozilla/Constants.h"
+#include "UserData.h"
namespace mozilla {
namespace gfx {
template <typename T>
-void ArcToBezier(T* aSink, const Point &aOrigin, float aRadius, float aStartAngle,
- float aEndAngle, bool aAntiClockwise)
+void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius,
+ float aStartAngle, float aEndAngle, bool aAntiClockwise)
{
- Point startPoint(aOrigin.x + cos(aStartAngle) * aRadius,
- aOrigin.y + sin(aStartAngle) * aRadius);
+ Point startPoint(aOrigin.x + cosf(aStartAngle) * aRadius.width,
+ aOrigin.y + sinf(aStartAngle) * aRadius.height);
aSink->LineTo(startPoint);
@@ -56,23 +57,25 @@ void ArcToBezier(T* aSink, const Point &aOrigin, float aRadius, float aStartAngl
currentEndAngle = currentStartAngle + arcSweepLeft * sweepDirection;
}
- Point currentStartPoint(aOrigin.x + cos(currentStartAngle) * aRadius,
- aOrigin.y + sin(currentStartAngle) * aRadius);
- Point currentEndPoint(aOrigin.x + cos(currentEndAngle) * aRadius,
- aOrigin.y + sin(currentEndAngle) * aRadius);
+ Point currentStartPoint(aOrigin.x + cosf(currentStartAngle) * aRadius.width,
+ aOrigin.y + sinf(currentStartAngle) * aRadius.height);
+ Point currentEndPoint(aOrigin.x + cosf(currentEndAngle) * aRadius.width,
+ aOrigin.y + sinf(currentEndAngle) * aRadius.height);
// Calculate kappa constant for partial curve. The sign of angle in the
// tangent will actually ensure this is negative for a counter clockwise
// sweep, so changing signs later isn't needed.
- Float kappa = (4.0f / 3.0f) * tan((currentEndAngle - currentStartAngle) / 4.0f) * aRadius;
+ Float kappaFactor = (4.0f / 3.0f) * tan((currentEndAngle - currentStartAngle) / 4.0f);
+ Float kappaX = kappaFactor * aRadius.width;
+ Float kappaY = kappaFactor * aRadius.height;
Point tangentStart(-sin(currentStartAngle), cos(currentStartAngle));
Point cp1 = currentStartPoint;
- cp1 += tangentStart * kappa;
+ cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY);
Point revTangentEnd(sin(currentEndAngle), -cos(currentEndAngle));
Point cp2 = currentEndPoint;
- cp2 += revTangentEnd * kappa;
+ cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY);
aSink->BezierTo(cp1, cp2, currentEndPoint);
@@ -81,7 +84,279 @@ void ArcToBezier(T* aSink, const Point &aOrigin, float aRadius, float aStartAngl
}
}
+/* This is basically the ArcToBezier with the parameters for drawing a circle
+ * inlined which vastly simplifies it and avoids a bunch of transcedental function
+ * calls which should make it faster. */
+template <typename T>
+void EllipseToBezier(T* aSink, const Point &aOrigin, const Size &aRadius)
+{
+ Point startPoint(aOrigin.x + aRadius.width,
+ aOrigin.y);
+
+ aSink->LineTo(startPoint);
+
+ // Calculate kappa constant for partial curve. The sign of angle in the
+ // tangent will actually ensure this is negative for a counter clockwise
+ // sweep, so changing signs later isn't needed.
+ Float kappaFactor = (4.0f / 3.0f) * tan((M_PI/2.0f) / 4.0f);
+ Float kappaX = kappaFactor * aRadius.width;
+ Float kappaY = kappaFactor * aRadius.height;
+ Float cosStartAngle = 1;
+ Float sinStartAngle = 0;
+ for (int i = 0; i < 4; i++) {
+ // We guarantee here the current point is the start point of the next
+ // curve segment.
+ Point currentStartPoint(aOrigin.x + cosStartAngle * aRadius.width,
+ aOrigin.y + sinStartAngle * aRadius.height);
+ Point currentEndPoint(aOrigin.x + -sinStartAngle * aRadius.width,
+ aOrigin.y + cosStartAngle * aRadius.height);
+
+ Point tangentStart(-sinStartAngle, cosStartAngle);
+ Point cp1 = currentStartPoint;
+ cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY);
+
+ Point revTangentEnd(cosStartAngle, sinStartAngle);
+ Point cp2 = currentEndPoint;
+ cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY);
+
+ aSink->BezierTo(cp1, cp2, currentEndPoint);
+
+ // cos(x+pi/2) == -sin(x)
+ // sin(x+pi/2) == cos(x)
+ Float tmp = cosStartAngle;
+ cosStartAngle = -sinStartAngle;
+ sinStartAngle = tmp;
+ }
}
+
+/**
+ * Appends a path represending a rectangle to the path being built by
+ * aPathBuilder.
+ *
+ * aRect The rectangle to append.
+ * aDrawClockwise If set to true, the path will start at the left of the top
+ * left edge and draw clockwise. If set to false the path will
+ * start at the right of the top left edge and draw counter-
+ * clockwise.
+ */
+GFX2D_API void AppendRectToPath(PathBuilder* aPathBuilder,
+ const Rect& aRect,
+ bool aDrawClockwise = true);
+
+inline TemporaryRef<Path> MakePathForRect(const DrawTarget& aDrawTarget,
+ const Rect& aRect,
+ bool aDrawClockwise = true)
+{
+ RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
+ AppendRectToPath(builder, aRect, aDrawClockwise);
+ return builder->Finish();
}
+struct RectCornerRadii {
+ Size radii[RectCorner::Count];
+
+ RectCornerRadii() {}
+
+ explicit RectCornerRadii(Float radius) {
+ for (int i = 0; i < RectCorner::Count; i++) {
+ radii[i].SizeTo(radius, radius);
+ }
+ }
+
+ explicit RectCornerRadii(Float radiusX, Float radiusY) {
+ for (int i = 0; i < RectCorner::Count; i++) {
+ radii[i].SizeTo(radiusX, radiusY);
+ }
+ }
+
+ RectCornerRadii(Float tl, Float tr, Float br, Float bl) {
+ radii[RectCorner::TopLeft].SizeTo(tl, tl);
+ radii[RectCorner::TopRight].SizeTo(tr, tr);
+ radii[RectCorner::BottomRight].SizeTo(br, br);
+ radii[RectCorner::BottomLeft].SizeTo(bl, bl);
+ }
+
+ RectCornerRadii(const Size& tl, const Size& tr,
+ const Size& br, const Size& bl) {
+ radii[RectCorner::TopLeft] = tl;
+ radii[RectCorner::TopRight] = tr;
+ radii[RectCorner::BottomRight] = br;
+ radii[RectCorner::BottomLeft] = bl;
+ }
+
+ const Size& operator[](size_t aCorner) const {
+ return radii[aCorner];
+ }
+
+ Size& operator[](size_t aCorner) {
+ return radii[aCorner];
+ }
+
+ void Scale(Float aXScale, Float aYScale) {
+ for (int i = 0; i < RectCorner::Count; i++) {
+ radii[i].Scale(aXScale, aYScale);
+ }
+ }
+
+ const Size TopLeft() const { return radii[RectCorner::TopLeft]; }
+ Size& TopLeft() { return radii[RectCorner::TopLeft]; }
+
+ const Size TopRight() const { return radii[RectCorner::TopRight]; }
+ Size& TopRight() { return radii[RectCorner::TopRight]; }
+
+ const Size BottomRight() const { return radii[RectCorner::BottomRight]; }
+ Size& BottomRight() { return radii[RectCorner::BottomRight]; }
+
+ const Size BottomLeft() const { return radii[RectCorner::BottomLeft]; }
+ Size& BottomLeft() { return radii[RectCorner::BottomLeft]; }
+};
+
+/**
+ * Appends a path represending a rounded rectangle to the path being built by
+ * aPathBuilder.
+ *
+ * aRect The rectangle to append.
+ * aCornerRadii Contains the radii of the top-left, top-right, bottom-right
+ * and bottom-left corners, in that order.
+ * aDrawClockwise If set to true, the path will start at the left of the top
+ * left edge and draw clockwise. If set to false the path will
+ * start at the right of the top left edge and draw counter-
+ * clockwise.
+ */
+GFX2D_API void AppendRoundedRectToPath(PathBuilder* aPathBuilder,
+ const Rect& aRect,
+ const RectCornerRadii& aRadii,
+ bool aDrawClockwise = true);
+
+inline TemporaryRef<Path> MakePathForRoundedRect(const DrawTarget& aDrawTarget,
+ const Rect& aRect,
+ const RectCornerRadii& aRadii,
+ bool aDrawClockwise = true)
+{
+ RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
+ AppendRoundedRectToPath(builder, aRect, aRadii, aDrawClockwise);
+ return builder->Finish();
+}
+
+/**
+ * Appends a path represending an ellipse to the path being built by
+ * aPathBuilder.
+ *
+ * The ellipse extends aDimensions.width / 2.0 in the horizontal direction
+ * from aCenter, and aDimensions.height / 2.0 in the vertical direction.
+ */
+GFX2D_API void AppendEllipseToPath(PathBuilder* aPathBuilder,
+ const Point& aCenter,
+ const Size& aDimensions);
+
+inline TemporaryRef<Path> MakePathForEllipse(const DrawTarget& aDrawTarget,
+ const Point& aCenter,
+ const Size& aDimensions)
+{
+ RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
+ AppendEllipseToPath(builder, aCenter, aDimensions);
+ return builder->Finish();
+}
+
+/**
+ * If aDrawTarget's transform only contains a translation, and if this line is
+ * a horizontal or vertical line, this function will snap the line's vertices
+ * to align with the device pixel grid so that stroking the line with a one
+ * pixel wide stroke will result in a crisp line that is not antialiased over
+ * two pixels across its width.
+ *
+ * @return Returns true if this function snaps aRect's vertices, else returns
+ * false.
+ */
+GFX2D_API bool SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
+ const DrawTarget& aDrawTarget);
+
+/**
+ * This function paints each edge of aRect separately, snapping the edges using
+ * SnapLineToDevicePixelsForStroking. Stroking the edges as separate paths
+ * helps ensure not only that the stroke spans a single row of device pixels if
+ * possible, but also that the ends of stroke dashes start and end on device
+ * pixels too.
+ */
+GFX2D_API void StrokeSnappedEdgesOfRect(const Rect& aRect,
+ DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions);
+
+extern UserDataKey sDisablePixelSnapping;
+
+/**
+ * If aDrawTarget's transform only contains a translation or, if
+ * aAllowScaleOr90DegreeRotate is true, and/or a scale/90 degree rotation, this
+ * function will convert aRect to device space and snap it to device pixels.
+ * This function returns true if aRect is modified, otherwise it returns false.
+ *
+ * Note that the snapping is such that filling the rect using a DrawTarget
+ * which has the identity matrix as its transform will result in crisp edges.
+ * (That is, aRect will have integer values, aligning its edges between pixel
+ * boundaries.) If on the other hand you stroking the rect with an odd valued
+ * stroke width then the edges of the stroke will be antialiased (assuming an
+ * AntialiasMode that does antialiasing).
+ */
+inline bool UserToDevicePixelSnapped(Rect& aRect, const DrawTarget& aDrawTarget,
+ bool aAllowScaleOr90DegreeRotate = false)
+{
+ if (aDrawTarget.GetUserData(&sDisablePixelSnapping)) {
+ return false;
+ }
+
+ Matrix mat = aDrawTarget.GetTransform();
+
+ const Float epsilon = 0.0000001f;
+#define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
+ if (!aAllowScaleOr90DegreeRotate &&
+ (!WITHIN_E(mat._11, 1.f) || !WITHIN_E(mat._22, 1.f) ||
+ !WITHIN_E(mat._12, 0.f) || !WITHIN_E(mat._21, 0.f))) {
+ // We have non-translation, but only translation is allowed.
+ return false;
+ }
+#undef WITHIN_E
+
+ Point p1 = mat * aRect.TopLeft();
+ Point p2 = mat * aRect.TopRight();
+ Point p3 = mat * aRect.BottomRight();
+
+ // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
+ // two opposite corners define the entire rectangle. So check if
+ // the axis-aligned rectangle with opposite corners p1 and p3
+ // define an axis-aligned rectangle whose other corners are p2 and p4.
+ // We actually only need to check one of p2 and p4, since an affine
+ // transform maps parallelograms to parallelograms.
+ if (p2 == Point(p1.x, p3.y) || p2 == Point(p3.x, p1.y)) {
+ p1.Round();
+ p3.Round();
+
+ aRect.MoveTo(Point(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
+ aRect.SizeTo(Size(std::max(p1.x, p3.x) - aRect.X(),
+ std::max(p1.y, p3.y) - aRect.Y()));
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * This function has the same behavior as UserToDevicePixelSnapped except that
+ * aRect is not transformed to device space.
+ */
+inline void MaybeSnapToDevicePixels(Rect& aRect, const DrawTarget& aDrawTarget,
+ bool aIgnoreScale = false)
+{
+ if (UserToDevicePixelSnapped(aRect, aDrawTarget, aIgnoreScale)) {
+ // Since UserToDevicePixelSnapped returned true we know there is no
+ // rotation/skew in 'mat', so we can just use TransformBounds() here.
+ Matrix mat = aDrawTarget.GetTransform();
+ mat.Invert();
+ aRect = mat.TransformBounds(aRect);
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
+
#endif /* MOZILLA_GFX_PATHHELPERS_H_ */
diff --git a/gfx/2d/PathRecording.cpp b/gfx/2d/PathRecording.cpp
index 647649b1a..e43871290 100644
--- a/gfx/2d/PathRecording.cpp
+++ b/gfx/2d/PathRecording.cpp
@@ -90,7 +90,7 @@ PathRecording::CopyToBuilder(FillRule aFillRule) const
RefPtr<PathBuilder> pathBuilder = mPath->CopyToBuilder(aFillRule);
RefPtr<PathBuilderRecording> recording = new PathBuilderRecording(pathBuilder, aFillRule);
recording->mPathOps = mPathOps;
- return recording;
+ return recording.forget();
}
TemporaryRef<PathBuilder>
@@ -113,7 +113,7 @@ PathRecording::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFill
}
recording->mPathOps.push_back(newPathOp);
}
- return recording;
+ return recording.forget();
}
}
diff --git a/gfx/2d/PathRecording.h b/gfx/2d/PathRecording.h
index 8c89cb7e3..7fde43ea9 100644
--- a/gfx/2d/PathRecording.h
+++ b/gfx/2d/PathRecording.h
@@ -37,6 +37,7 @@ class DrawEventRecorderPrivate;
class PathBuilderRecording : public PathBuilder
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderRecording)
PathBuilderRecording(PathBuilder *aBuilder, FillRule aFillRule)
: mPathBuilder(aBuilder), mFillRule(aFillRule)
{
@@ -71,6 +72,8 @@ public:
virtual TemporaryRef<Path> Finish();
+ virtual BackendType GetBackendType() const { return BackendType::RECORDING; }
+
private:
friend class PathRecording;
@@ -82,6 +85,7 @@ private:
class PathRecording : public Path
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathRecording)
PathRecording(Path *aPath, const std::vector<PathOp> aOps, FillRule aFillRule)
: mPath(aPath), mPathOps(aOps), mFillRule(aFillRule)
{
@@ -89,10 +93,10 @@ public:
~PathRecording();
- virtual BackendType GetBackendType() const { return BACKEND_RECORDING; }
- virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const;
+ virtual BackendType GetBackendType() const { return BackendType::RECORDING; }
+ virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const;
virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
- FillRule aFillRule = FILL_WINDING) const;
+ FillRule aFillRule = FillRule::FILL_WINDING) const;
virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
{ return mPath->ContainsPoint(aPoint, aTransform); }
virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
@@ -106,7 +110,9 @@ public:
virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
const Matrix &aTransform = Matrix()) const
{ return mPath->GetStrokedBounds(aStrokeOptions, aTransform); }
-
+
+ virtual void StreamToSink(PathSink *aSink) const { mPath->StreamToSink(aSink); }
+
virtual FillRule GetFillRule() const { return mFillRule; }
void StorePath(std::ostream &aStream) const;
diff --git a/gfx/2d/PathSkia.cpp b/gfx/2d/PathSkia.cpp
index 132e5dc5a..efa0c6f56 100644
--- a/gfx/2d/PathSkia.cpp
+++ b/gfx/2d/PathSkia.cpp
@@ -31,7 +31,7 @@ void
PathBuilderSkia::SetFillRule(FillRule aFillRule)
{
mFillRule = aFillRule;
- if (mFillRule == FILL_WINDING) {
+ if (mFillRule == FillRule::FILL_WINDING) {
mPath.setFillType(SkPath::kWinding_FillType);
} else {
mPath.setFillType(SkPath::kEvenOdd_FillType);
@@ -88,7 +88,7 @@ void
PathBuilderSkia::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
float aEndAngle, bool aAntiClockwise)
{
- ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise);
+ ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
}
Point
@@ -105,8 +105,13 @@ PathBuilderSkia::CurrentPoint() const
TemporaryRef<Path>
PathBuilderSkia::Finish()
{
- RefPtr<PathSkia> path = new PathSkia(mPath, mFillRule);
- return path;
+ return new PathSkia(mPath, mFillRule);
+}
+
+void
+PathBuilderSkia::AppendPath(const SkPath &aPath)
+{
+ mPath.addPath(aPath);
}
TemporaryRef<PathBuilder>
@@ -118,8 +123,7 @@ PathSkia::CopyToBuilder(FillRule aFillRule) const
TemporaryRef<PathBuilder>
PathSkia::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
{
- RefPtr<PathBuilderSkia> builder = new PathBuilderSkia(aTransform, mPath, aFillRule);
- return builder;
+ return new PathBuilderSkia(aTransform, mPath, aFillRule);
}
bool
@@ -137,24 +141,16 @@ PathSkia::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
}
SkRegion pointRect;
- pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1)),
- int32_t(SkFloatToScalar(transformed.y - 1)),
- int32_t(SkFloatToScalar(transformed.x + 1)),
- int32_t(SkFloatToScalar(transformed.y + 1)));
+ pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1.f)),
+ int32_t(SkFloatToScalar(transformed.y - 1.f)),
+ int32_t(SkFloatToScalar(transformed.x + 1.f)),
+ int32_t(SkFloatToScalar(transformed.y + 1.f)));
SkRegion pathRegion;
return pathRegion.setPath(mPath, pointRect);
}
-static Rect SkRectToRect(const SkRect& aBounds)
-{
- return Rect(SkScalarToFloat(aBounds.fLeft),
- SkScalarToFloat(aBounds.fTop),
- SkScalarToFloat(aBounds.fRight - aBounds.fLeft),
- SkScalarToFloat(aBounds.fBottom - aBounds.fTop));
-}
-
bool
PathSkia::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
const Point &aPoint,
@@ -178,10 +174,10 @@ PathSkia::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
}
SkRegion pointRect;
- pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1)),
- int32_t(SkFloatToScalar(transformed.y - 1)),
- int32_t(SkFloatToScalar(transformed.x + 1)),
- int32_t(SkFloatToScalar(transformed.y + 1)));
+ pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1.f)),
+ int32_t(SkFloatToScalar(transformed.y - 1.f)),
+ int32_t(SkFloatToScalar(transformed.x + 1.f)),
+ int32_t(SkFloatToScalar(transformed.y + 1.f)));
SkRegion pathRegion;
@@ -209,5 +205,39 @@ PathSkia::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
return aTransform.TransformBounds(bounds);
}
+void
+PathSkia::StreamToSink(PathSink *aSink) const
+{
+ SkPath::RawIter iter(mPath);
+
+ SkPoint points[4];
+ SkPath::Verb currentVerb;
+ while ((currentVerb = iter.next(points)) != SkPath::kDone_Verb) {
+ switch (currentVerb) {
+ case SkPath::kMove_Verb:
+ aSink->MoveTo(SkPointToPoint(points[0]));
+ break;
+ case SkPath::kLine_Verb:
+ aSink->LineTo(SkPointToPoint(points[1]));
+ break;
+ case SkPath::kCubic_Verb:
+ aSink->BezierTo(SkPointToPoint(points[1]),
+ SkPointToPoint(points[2]),
+ SkPointToPoint(points[3]));
+ break;
+ case SkPath::kQuad_Verb:
+ aSink->QuadraticBezierTo(SkPointToPoint(points[1]),
+ SkPointToPoint(points[2]));
+ break;
+ case SkPath::kClose_Verb:
+ aSink->Close();
+ break;
+ default:
+ MOZ_ASSERT(false);
+ // Unexpected verb found in path!
+ }
+ }
+}
+
}
}
diff --git a/gfx/2d/PathSkia.h b/gfx/2d/PathSkia.h
index df6f5718a..57fd709b0 100644
--- a/gfx/2d/PathSkia.h
+++ b/gfx/2d/PathSkia.h
@@ -17,8 +17,9 @@ class PathSkia;
class PathBuilderSkia : public PathBuilder
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderSkia)
PathBuilderSkia(const Matrix& aTransform, const SkPath& aPath, FillRule aFillRule);
- PathBuilderSkia(FillRule aFillRule);
+ explicit PathBuilderSkia(FillRule aFillRule);
virtual void MoveTo(const Point &aPoint);
virtual void LineTo(const Point &aPoint);
@@ -33,7 +34,12 @@ public:
virtual Point CurrentPoint() const;
virtual TemporaryRef<Path> Finish();
+ void AppendPath(const SkPath &aPath);
+
+ virtual BackendType GetBackendType() const { return BackendType::SKIA; }
+
private:
+
void SetFillRule(FillRule aFillRule);
SkPath mPath;
@@ -43,17 +49,18 @@ private:
class PathSkia : public Path
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathSkia)
PathSkia(SkPath& aPath, FillRule aFillRule)
: mFillRule(aFillRule)
{
mPath.swap(aPath);
}
- virtual BackendType GetBackendType() const { return BACKEND_SKIA; }
+ virtual BackendType GetBackendType() const { return BackendType::SKIA; }
- virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const;
+ virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const;
virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
- FillRule aFillRule = FILL_WINDING) const;
+ FillRule aFillRule = FillRule::FILL_WINDING) const;
virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
@@ -66,6 +73,8 @@ public:
virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
const Matrix &aTransform = Matrix()) const;
+ virtual void StreamToSink(PathSink *aSink) const;
+
virtual FillRule GetFillRule() const { return mFillRule; }
const SkPath& GetPath() const { return mPath; }
diff --git a/gfx/2d/PatternHelpers.h b/gfx/2d/PatternHelpers.h
new file mode 100644
index 000000000..79c2502b9
--- /dev/null
+++ b/gfx/2d/PatternHelpers.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 20; 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 _MOZILLA_GFX_PATTERNHELPERS_H
+#define _MOZILLA_GFX_PATTERNHELPERS_H
+
+#include "mozilla/Alignment.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * This class is used to allow general pattern creation functions to return
+ * any type of pattern via an out-paramater without allocating a pattern
+ * instance on the free-store (an instance of this class being created on the
+ * stack before passing it in to the creation function). Without this class
+ * writing pattern creation functions would be a pain since Pattern objects are
+ * not reference counted, making lifetime management of instances created on
+ * the free-store and returned from a creation function hazardous. Besides
+ * that, in the case that ColorPattern's are expected to be common, it is
+ * particularly desirable to avoid the overhead of allocating on the
+ * free-store.
+ */
+class GeneralPattern
+{
+public:
+ explicit GeneralPattern()
+ : mPattern(nullptr)
+ {}
+
+ GeneralPattern(const GeneralPattern& aOther)
+ : mPattern(nullptr)
+ {}
+
+ ~GeneralPattern() {
+ if (mPattern) {
+ mPattern->~Pattern();
+ }
+ }
+
+ Pattern* Init(const Pattern& aPattern) {
+ MOZ_ASSERT(!mPattern);
+ switch (aPattern.GetType()) {
+ case PatternType::COLOR:
+ mPattern = new (mColorPattern.addr())
+ ColorPattern(static_cast<const ColorPattern&>(aPattern));
+ break;
+ case PatternType::LINEAR_GRADIENT:
+ mPattern = new (mLinearGradientPattern.addr())
+ LinearGradientPattern(static_cast<const LinearGradientPattern&>(aPattern));
+ break;
+ case PatternType::RADIAL_GRADIENT:
+ mPattern = new (mRadialGradientPattern.addr())
+ RadialGradientPattern(static_cast<const RadialGradientPattern&>(aPattern));
+ break;
+ case PatternType::SURFACE:
+ mPattern = new (mSurfacePattern.addr())
+ SurfacePattern(static_cast<const SurfacePattern&>(aPattern));
+ break;
+ default:
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown pattern type");
+ }
+ return mPattern;
+ }
+
+ ColorPattern* InitColorPattern(const Color &aColor) {
+ MOZ_ASSERT(!mPattern);
+ mPattern = new (mColorPattern.addr()) ColorPattern(aColor);
+ return mColorPattern.addr();
+ }
+
+ LinearGradientPattern* InitLinearGradientPattern(const Point &aBegin,
+ const Point &aEnd,
+ GradientStops *aStops,
+ const Matrix &aMatrix = Matrix()) {
+ MOZ_ASSERT(!mPattern);
+ mPattern = new (mLinearGradientPattern.addr())
+ LinearGradientPattern(aBegin, aEnd, aStops, aMatrix);
+ return mLinearGradientPattern.addr();
+ }
+
+ RadialGradientPattern* InitRadialGradientPattern(const Point &aCenter1,
+ const Point &aCenter2,
+ Float aRadius1,
+ Float aRadius2,
+ GradientStops *aStops,
+ const Matrix &aMatrix = Matrix()) {
+ MOZ_ASSERT(!mPattern);
+ mPattern = new (mRadialGradientPattern.addr())
+ RadialGradientPattern(aCenter1, aCenter2, aRadius1, aRadius2, aStops, aMatrix);
+ return mRadialGradientPattern.addr();
+ }
+
+ SurfacePattern* InitSurfacePattern(SourceSurface *aSourceSurface,
+ ExtendMode aExtendMode,
+ const Matrix &aMatrix = Matrix(),
+ Filter aFilter = Filter::GOOD,
+ const IntRect &aSamplingRect = IntRect()) {
+ MOZ_ASSERT(!mPattern);
+ mPattern = new (mSurfacePattern.addr())
+ SurfacePattern(aSourceSurface, aExtendMode, aMatrix, aFilter, aSamplingRect);
+ return mSurfacePattern.addr();
+ }
+
+ Pattern* GetPattern() {
+ return mPattern;
+ }
+
+ const Pattern* GetPattern() const {
+ return mPattern;
+ }
+
+ operator Pattern&() {
+ if (!mPattern) {
+ MOZ_CRASH("GeneralPattern not initialized");
+ }
+ return *mPattern;
+ }
+
+private:
+ union {
+ AlignedStorage2<ColorPattern> mColorPattern;
+ AlignedStorage2<LinearGradientPattern> mLinearGradientPattern;
+ AlignedStorage2<RadialGradientPattern> mRadialGradientPattern;
+ AlignedStorage2<SurfacePattern> mSurfacePattern;
+ };
+ Pattern *mPattern;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_PATTERNHELPERS_H
+
diff --git a/gfx/2d/Point.h b/gfx/2d/Point.h
index 026b44604..0e271ccc2 100644
--- a/gfx/2d/Point.h
+++ b/gfx/2d/Point.h
@@ -6,24 +6,50 @@
#ifndef MOZILLA_GFX_POINT_H_
#define MOZILLA_GFX_POINT_H_
+#include "mozilla/Attributes.h"
#include "Types.h"
+#include "Coord.h"
+#include "BaseCoord.h"
#include "BasePoint.h"
+#include "BasePoint3D.h"
+#include "BasePoint4D.h"
#include "BaseSize.h"
+#include "mozilla/TypeTraits.h"
+
+#include <cmath>
namespace mozilla {
+
+template <typename> struct IsPixel;
+
namespace gfx {
// This should only be used by the typedefs below.
struct UnknownUnits {};
+} // close namespace 'gfx' because IsPixel specialization must be in 'mozilla'
+
+template<> struct IsPixel<gfx::UnknownUnits> : TrueType {};
+
+namespace gfx {
+
template<class units>
struct IntPointTyped :
- public BasePoint< int32_t, IntPointTyped<units> >,
+ public BasePoint< int32_t, IntPointTyped<units>, IntCoordTyped<units> >,
public units {
- typedef BasePoint< int32_t, IntPointTyped<units> > Super;
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef IntCoordTyped<units> Coord;
+ typedef BasePoint< int32_t, IntPointTyped<units>, IntCoordTyped<units> > Super;
- IntPointTyped() : Super() {}
- IntPointTyped(int32_t aX, int32_t aY) : Super(aX, aY) {}
+ MOZ_CONSTEXPR IntPointTyped() : Super() {}
+ MOZ_CONSTEXPR IntPointTyped(int32_t aX, int32_t aY) : Super(Coord(aX), Coord(aY)) {}
+ // The mixed-type constructors (int, Coord) and (Coord, int) are needed to
+ // avoid ambiguities because Coord is implicitly convertible to int.
+ MOZ_CONSTEXPR IntPointTyped(int32_t aX, Coord aY) : Super(Coord(aX), aY) {}
+ MOZ_CONSTEXPR IntPointTyped(Coord aX, int32_t aY) : Super(aX, Coord(aY)) {}
+ MOZ_CONSTEXPR IntPointTyped(Coord aX, Coord aY) : Super(aX, aY) {}
// XXX When all of the code is ported, the following functions to convert to and from
// unknown types should be removed.
@@ -40,13 +66,22 @@ typedef IntPointTyped<UnknownUnits> IntPoint;
template<class units>
struct PointTyped :
- public BasePoint< Float, PointTyped<units> >,
+ public BasePoint< Float, PointTyped<units>, CoordTyped<units> >,
public units {
- typedef BasePoint< Float, PointTyped<units> > Super;
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef CoordTyped<units> Coord;
+ typedef BasePoint< Float, PointTyped<units>, CoordTyped<units> > Super;
- PointTyped() : Super() {}
- PointTyped(Float aX, Float aY) : Super(aX, aY) {}
- PointTyped(const IntPointTyped<units>& point) : Super(float(point.x), float(point.y)) {}
+ MOZ_CONSTEXPR PointTyped() : Super() {}
+ MOZ_CONSTEXPR PointTyped(Float aX, Float aY) : Super(Coord(aX), Coord(aY)) {}
+ // The mixed-type constructors (Float, Coord) and (Coord, Float) are needed to
+ // avoid ambiguities because Coord is implicitly convertible to Float.
+ MOZ_CONSTEXPR PointTyped(Float aX, Coord aY) : Super(Coord(aX), aY) {}
+ MOZ_CONSTEXPR PointTyped(Coord aX, Float aY) : Super(aX, Coord(aY)) {}
+ MOZ_CONSTEXPR PointTyped(Coord aX, Coord aY) : Super(aX.value, aY.value) {}
+ MOZ_CONSTEXPR MOZ_IMPLICIT PointTyped(const IntPointTyped<units>& point) : Super(float(point.x), float(point.y)) {}
// XXX When all of the code is ported, the following functions to convert to and from
// unknown types should be removed.
@@ -63,18 +98,79 @@ typedef PointTyped<UnknownUnits> Point;
template<class units>
IntPointTyped<units> RoundedToInt(const PointTyped<units>& aPoint) {
- return IntPointTyped<units>(NS_lround(aPoint.x),
- NS_lround(aPoint.y));
+ return IntPointTyped<units>(int32_t(floorf(aPoint.x + 0.5f)),
+ int32_t(floorf(aPoint.y + 0.5f)));
+}
+
+template<class units>
+IntPointTyped<units> TruncatedToInt(const PointTyped<units>& aPoint) {
+ return IntPointTyped<units>(int32_t(aPoint.x),
+ int32_t(aPoint.y));
}
template<class units>
+struct Point3DTyped :
+ public BasePoint3D< Float, Point3DTyped<units> > {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BasePoint3D< Float, Point3DTyped<units> > Super;
+
+ Point3DTyped() : Super() {}
+ Point3DTyped(Float aX, Float aY, Float aZ) : Super(aX, aY, aZ) {}
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static Point3DTyped<units> FromUnknownPoint(const Point3DTyped<UnknownUnits>& aPoint) {
+ return Point3DTyped<units>(aPoint.x, aPoint.y, aPoint.z);
+ }
+
+ Point3DTyped<UnknownUnits> ToUnknownPoint() const {
+ return Point3DTyped<UnknownUnits>(this->x, this->y, this->z);
+ }
+};
+typedef Point3DTyped<UnknownUnits> Point3D;
+
+template<class units>
+struct Point4DTyped :
+ public BasePoint4D< Float, Point4DTyped<units> > {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BasePoint4D< Float, Point4DTyped<units> > Super;
+
+ Point4DTyped() : Super() {}
+ Point4DTyped(Float aX, Float aY, Float aZ, Float aW) : Super(aX, aY, aZ, aW) {}
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static Point4DTyped<units> FromUnknownPoint(const Point4DTyped<UnknownUnits>& aPoint) {
+ return Point4DTyped<units>(aPoint.x, aPoint.y, aPoint.z, aPoint.w);
+ }
+
+ Point4DTyped<UnknownUnits> ToUnknownPoint() const {
+ return Point4DTyped<UnknownUnits>(this->x, this->y, this->z, this->w);
+ }
+
+ PointTyped<units> As2DPoint() {
+ return PointTyped<units>(this->x / this->w, this->y / this->w);
+ }
+};
+typedef Point4DTyped<UnknownUnits> Point4D;
+
+template<class units>
struct IntSizeTyped :
public BaseSize< int32_t, IntSizeTyped<units> >,
public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
typedef BaseSize< int32_t, IntSizeTyped<units> > Super;
- IntSizeTyped() : Super() {}
- IntSizeTyped(int32_t aWidth, int32_t aHeight) : Super(aWidth, aHeight) {}
+ MOZ_CONSTEXPR IntSizeTyped() : Super() {}
+ MOZ_CONSTEXPR IntSizeTyped(int32_t aWidth, int32_t aHeight) : Super(aWidth, aHeight) {}
// XXX When all of the code is ported, the following functions to convert to and from
// unknown types should be removed.
@@ -93,10 +189,13 @@ template<class units>
struct SizeTyped :
public BaseSize< Float, SizeTyped<units> >,
public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
typedef BaseSize< Float, SizeTyped<units> > Super;
- SizeTyped() : Super() {}
- SizeTyped(Float aWidth, Float aHeight) : Super(aWidth, aHeight) {}
+ MOZ_CONSTEXPR SizeTyped() : Super() {}
+ MOZ_CONSTEXPR SizeTyped(Float aWidth, Float aHeight) : Super(aWidth, aHeight) {}
explicit SizeTyped(const IntSizeTyped<units>& size) :
Super(float(size.width), float(size.height)) {}
@@ -113,6 +212,12 @@ struct SizeTyped :
};
typedef SizeTyped<UnknownUnits> Size;
+template<class units>
+IntSizeTyped<units> RoundedToInt(const SizeTyped<units>& aSize) {
+ return IntSizeTyped<units>(int32_t(floorf(aSize.width + 0.5f)),
+ int32_t(floorf(aSize.height + 0.5f)));
+}
+
}
}
diff --git a/gfx/2d/QuartzSupport.h b/gfx/2d/QuartzSupport.h
index d02db8738..a85c8e2d9 100644
--- a/gfx/2d/QuartzSupport.h
+++ b/gfx/2d/QuartzSupport.h
@@ -14,6 +14,7 @@
#include "gfxTypes.h"
#include "mozilla/RefPtr.h"
#include "mozilla/gfx/MacIOSurface.h"
+#include "nsError.h"
// Get the system color space.
CGColorSpaceRef CreateSystemColorSpace();
@@ -26,6 +27,7 @@ enum AllowOfflineRendererEnum { ALLOW_OFFLINE_RENDERER, DISALLOW_OFFLINE_RENDERE
class nsCARenderer : public mozilla::RefCounted<nsCARenderer> {
public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(nsCARenderer)
nsCARenderer() : mCARenderer(nullptr), mWrapperCALayer(nullptr), mFBOTexture(0),
mOpenGLContext(nullptr), mCGImage(nullptr), mCGData(nullptr),
mIOSurface(nullptr), mFBO(0), mIOTexture(0),
@@ -51,7 +53,7 @@ public:
* is attached then an internal pixel buffer will be
* used.
*/
- void AttachIOSurface(mozilla::RefPtr<MacIOSurface> aSurface);
+ void AttachIOSurface(MacIOSurface *aSurface);
IOSurfaceID GetIOSurfaceID();
// aX, aY, aWidth and aHeight are in "display pixels". Multiply by
// surf->GetContentsScaleFactor() to get device pixels.
@@ -86,21 +88,12 @@ private:
mozilla::RefPtr<MacIOSurface> mIOSurface;
uint32_t mFBO;
uint32_t mIOTexture;
- uint32_t mUnsupportedWidth;
- uint32_t mUnsupportedHeight;
+ int mUnsupportedWidth;
+ int mUnsupportedHeight;
AllowOfflineRendererEnum mAllowOfflineRenderer;
double mContentsScaleFactor;
};
-enum CGContextType {
- CG_CONTEXT_TYPE_UNKNOWN = 0,
- // These are found by inspection, it's possible they could be changed
- CG_CONTEXT_TYPE_BITMAP = 4,
- CG_CONTEXT_TYPE_IOSURFACE = 8
-};
-
-CGContextType GetContextType(CGContextRef ref);
-
#endif // XP_MACOSX
#endif // nsCoreAnimationSupport_h__
diff --git a/gfx/2d/QuartzSupport.mm b/gfx/2d/QuartzSupport.mm
index 90e380037..cdefa0557 100644
--- a/gfx/2d/QuartzSupport.mm
+++ b/gfx/2d/QuartzSupport.mm
@@ -6,10 +6,12 @@
#include "QuartzSupport.h"
#include "nsDebug.h"
+#include "MacIOSurface.h"
#import <QuartzCore/QuartzCore.h>
#import <AppKit/NSOpenGL.h>
#include <dlfcn.h>
+#include "GLDefs.h"
#define IOSURFACE_FRAMEWORK_PATH \
"/System/Library/Frameworks/IOSurface.framework/IOSurface"
@@ -26,453 +28,12 @@
using mozilla::RefPtr;
using mozilla::TemporaryRef;
-// IOSurface signatures
-typedef CFTypeRef IOSurfacePtr;
-typedef IOSurfacePtr (*IOSurfaceCreateFunc) (CFDictionaryRef properties);
-typedef IOSurfacePtr (*IOSurfaceLookupFunc) (uint32_t io_surface_id);
-typedef IOSurfaceID (*IOSurfaceGetIDFunc) (CFTypeRef io_surface);
-typedef IOReturn (*IOSurfaceLockFunc) (CFTypeRef io_surface,
- uint32_t options,
- uint32_t *seed);
-typedef IOReturn (*IOSurfaceUnlockFunc) (CFTypeRef io_surface,
- uint32_t options,
- uint32_t *seed);
-typedef void* (*IOSurfaceGetBaseAddressFunc) (CFTypeRef io_surface);
-typedef size_t (*IOSurfaceGetWidthFunc) (IOSurfacePtr io_surface);
-typedef size_t (*IOSurfaceGetHeightFunc) (IOSurfacePtr io_surface);
-typedef size_t (*IOSurfaceGetBytesPerRowFunc) (IOSurfacePtr io_surface);
-typedef CGLError (*CGLTexImageIOSurface2DFunc) (CGLContextObj ctxt,
- GLenum target, GLenum internalFormat,
- GLsizei width, GLsizei height,
- GLenum format, GLenum type,
- IOSurfacePtr ioSurface, GLuint plane);
-typedef CGContextRef (*IOSurfaceContextCreateFunc)(CFTypeRef io_surface,
- unsigned width, unsigned height,
- unsigned bitsPerComponent, unsigned bytes,
- CGColorSpaceRef colorSpace, CGBitmapInfo bitmapInfo);
-typedef CGImageRef (*IOSurfaceContextCreateImageFunc)(CGContextRef ref);
-typedef IOSurfacePtr (*IOSurfaceContextGetSurfaceFunc)(CGContextRef ref);
-
-#define GET_CONST(const_name) \
- ((CFStringRef*) dlsym(sIOSurfaceFramework, const_name))
-#define GET_IOSYM(dest,sym_name) \
- (typeof(dest)) dlsym(sIOSurfaceFramework, sym_name)
-#define GET_CGLSYM(dest,sym_name) \
- (typeof(dest)) dlsym(sOpenGLFramework, sym_name)
-#define GET_CGSYM(dest,sym_name) \
- (typeof(dest)) dlsym(sCoreGraphicsFramework, sym_name)
-
-class MacIOSurfaceLib: public MacIOSurface {
-public:
- static void *sIOSurfaceFramework;
- static void *sOpenGLFramework;
- static void *sCoreGraphicsFramework;
- static bool isLoaded;
- static IOSurfaceCreateFunc sCreate;
- static IOSurfaceGetIDFunc sGetID;
- static IOSurfaceLookupFunc sLookup;
- static IOSurfaceGetBaseAddressFunc sGetBaseAddress;
- static IOSurfaceLockFunc sLock;
- static IOSurfaceUnlockFunc sUnlock;
- static IOSurfaceGetWidthFunc sWidth;
- static IOSurfaceGetHeightFunc sHeight;
- static IOSurfaceGetBytesPerRowFunc sBytesPerRow;
- static CGLTexImageIOSurface2DFunc sTexImage;
- static IOSurfaceContextCreateFunc sIOSurfaceContextCreate;
- static IOSurfaceContextCreateImageFunc sIOSurfaceContextCreateImage;
- static IOSurfaceContextGetSurfaceFunc sIOSurfaceContextGetSurface;
- static CFStringRef kPropWidth;
- static CFStringRef kPropHeight;
- static CFStringRef kPropBytesPerElem;
- static CFStringRef kPropBytesPerRow;
- static CFStringRef kPropIsGlobal;
-
- static bool isInit();
- static CFStringRef GetIOConst(const char* symbole);
- static IOSurfacePtr IOSurfaceCreate(CFDictionaryRef properties);
- static IOSurfacePtr IOSurfaceLookup(IOSurfaceID aIOSurfaceID);
- static IOSurfaceID IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr);
- static void *IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr);
- static size_t IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr);
- static size_t IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr);
- static size_t IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr);
- static IOReturn IOSurfaceLock(IOSurfacePtr aIOSurfacePtr,
- uint32_t options, uint32_t *seed);
- static IOReturn IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr,
- uint32_t options, uint32_t *seed);
- static CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt,
- GLenum target, GLenum internalFormat,
- GLsizei width, GLsizei height,
- GLenum format, GLenum type,
- IOSurfacePtr ioSurface, GLuint plane);
- static CGContextRef IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr,
- unsigned aWidth, unsigned aHeight,
- unsigned aBitsPerCompoent, unsigned aBytes,
- CGColorSpaceRef aColorSpace, CGBitmapInfo bitmapInfo);
- static CGImageRef IOSurfaceContextCreateImage(CGContextRef ref);
- static IOSurfacePtr IOSurfaceContextGetSurface(CGContextRef ref);
- static unsigned int (*sCGContextGetTypePtr) (CGContextRef);
- static void LoadLibrary();
- static void CloseLibrary();
-
- // Static deconstructor
- static class LibraryUnloader {
- public:
- ~LibraryUnloader() {
- CloseLibrary();
- }
- } sLibraryUnloader;
-};
-
-MacIOSurfaceLib::LibraryUnloader MacIOSurfaceLib::sLibraryUnloader;
-bool MacIOSurfaceLib::isLoaded = false;
-void* MacIOSurfaceLib::sIOSurfaceFramework;
-void* MacIOSurfaceLib::sOpenGLFramework;
-void* MacIOSurfaceLib::sCoreGraphicsFramework;
-IOSurfaceCreateFunc MacIOSurfaceLib::sCreate;
-IOSurfaceGetIDFunc MacIOSurfaceLib::sGetID;
-IOSurfaceLookupFunc MacIOSurfaceLib::sLookup;
-IOSurfaceGetBaseAddressFunc MacIOSurfaceLib::sGetBaseAddress;
-IOSurfaceGetWidthFunc MacIOSurfaceLib::sWidth;
-IOSurfaceGetHeightFunc MacIOSurfaceLib::sHeight;
-IOSurfaceGetBytesPerRowFunc MacIOSurfaceLib::sBytesPerRow;
-IOSurfaceLockFunc MacIOSurfaceLib::sLock;
-IOSurfaceUnlockFunc MacIOSurfaceLib::sUnlock;
-CGLTexImageIOSurface2DFunc MacIOSurfaceLib::sTexImage;
-IOSurfaceContextCreateFunc MacIOSurfaceLib::sIOSurfaceContextCreate;
-IOSurfaceContextCreateImageFunc MacIOSurfaceLib::sIOSurfaceContextCreateImage;
-IOSurfaceContextGetSurfaceFunc MacIOSurfaceLib::sIOSurfaceContextGetSurface;
-unsigned int (*MacIOSurfaceLib::sCGContextGetTypePtr) (CGContextRef) = nullptr;
-
-CFStringRef MacIOSurfaceLib::kPropWidth;
-CFStringRef MacIOSurfaceLib::kPropHeight;
-CFStringRef MacIOSurfaceLib::kPropBytesPerElem;
-CFStringRef MacIOSurfaceLib::kPropBytesPerRow;
-CFStringRef MacIOSurfaceLib::kPropIsGlobal;
-
-bool MacIOSurfaceLib::isInit() {
- // Guard against trying to reload the library
- // if it is not available.
- if (!isLoaded)
- LoadLibrary();
- if (!sIOSurfaceFramework) {
- NS_ERROR("MacIOSurfaceLib failed to initialize");
- }
- return sIOSurfaceFramework;
-}
-
-IOSurfacePtr MacIOSurfaceLib::IOSurfaceCreate(CFDictionaryRef properties) {
- return sCreate(properties);
-}
-
-IOSurfacePtr MacIOSurfaceLib::IOSurfaceLookup(IOSurfaceID aIOSurfaceID) {
- return sLookup(aIOSurfaceID);
-}
-
-IOSurfaceID MacIOSurfaceLib::IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr) {
- return sGetID(aIOSurfacePtr);
-}
-
-void* MacIOSurfaceLib::IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr) {
- return sGetBaseAddress(aIOSurfacePtr);
-}
-
-size_t MacIOSurfaceLib::IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr) {
- return sWidth(aIOSurfacePtr);
-}
-
-size_t MacIOSurfaceLib::IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr) {
- return sHeight(aIOSurfacePtr);
-}
-
-size_t MacIOSurfaceLib::IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr) {
- return sBytesPerRow(aIOSurfacePtr);
-}
-
-IOReturn MacIOSurfaceLib::IOSurfaceLock(IOSurfacePtr aIOSurfacePtr,
- uint32_t options, uint32_t *seed) {
- return sLock(aIOSurfacePtr, options, seed);
-}
-
-IOReturn MacIOSurfaceLib::IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr,
- uint32_t options, uint32_t *seed) {
- return sUnlock(aIOSurfacePtr, options, seed);
-}
-
-CGLError MacIOSurfaceLib::CGLTexImageIOSurface2D(CGLContextObj ctxt,
- GLenum target, GLenum internalFormat,
- GLsizei width, GLsizei height,
- GLenum format, GLenum type,
- IOSurfacePtr ioSurface, GLuint plane) {
- return sTexImage(ctxt, target, internalFormat, width, height,
- format, type, ioSurface, plane);
-}
-
-CGContextRef MacIOSurfaceLib::IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr,
- unsigned aWidth, unsigned aHeight,
- unsigned aBitsPerComponent, unsigned aBytes,
- CGColorSpaceRef aColorSpace, CGBitmapInfo bitmapInfo) {
- if (!sIOSurfaceContextCreate)
- return nullptr;
- return sIOSurfaceContextCreate(aIOSurfacePtr, aWidth, aHeight, aBitsPerComponent, aBytes, aColorSpace, bitmapInfo);
-}
-
-CGImageRef MacIOSurfaceLib::IOSurfaceContextCreateImage(CGContextRef aContext) {
- if (!sIOSurfaceContextCreateImage)
- return nullptr;
- return sIOSurfaceContextCreateImage(aContext);
-}
-
-IOSurfacePtr MacIOSurfaceLib::IOSurfaceContextGetSurface(CGContextRef aContext) {
- if (!sIOSurfaceContextGetSurface)
- return nullptr;
- return sIOSurfaceContextGetSurface(aContext);
-}
-
-CFStringRef MacIOSurfaceLib::GetIOConst(const char* symbole) {
- CFStringRef *address = (CFStringRef*)dlsym(sIOSurfaceFramework, symbole);
- if (!address)
- return nullptr;
-
- return *address;
-}
-
-void MacIOSurfaceLib::LoadLibrary() {
- if (isLoaded) {
- return;
- }
- isLoaded = true;
- sIOSurfaceFramework = dlopen(IOSURFACE_FRAMEWORK_PATH,
- RTLD_LAZY | RTLD_LOCAL);
- sOpenGLFramework = dlopen(OPENGL_FRAMEWORK_PATH,
- RTLD_LAZY | RTLD_LOCAL);
-
- sCoreGraphicsFramework = dlopen(COREGRAPHICS_FRAMEWORK_PATH,
- RTLD_LAZY | RTLD_LOCAL);
- if (!sIOSurfaceFramework || !sOpenGLFramework || !sCoreGraphicsFramework) {
- if (sIOSurfaceFramework)
- dlclose(sIOSurfaceFramework);
- if (sOpenGLFramework)
- dlclose(sOpenGLFramework);
- if (sCoreGraphicsFramework)
- dlclose(sCoreGraphicsFramework);
- sIOSurfaceFramework = nullptr;
- sOpenGLFramework = nullptr;
- sCoreGraphicsFramework = nullptr;
- return;
- }
-
- kPropWidth = GetIOConst("kIOSurfaceWidth");
- kPropHeight = GetIOConst("kIOSurfaceHeight");
- kPropBytesPerElem = GetIOConst("kIOSurfaceBytesPerElement");
- kPropBytesPerRow = GetIOConst("kIOSurfaceBytesPerRow");
- kPropIsGlobal = GetIOConst("kIOSurfaceIsGlobal");
- sCreate = GET_IOSYM(sCreate, "IOSurfaceCreate");
- sGetID = GET_IOSYM(sGetID, "IOSurfaceGetID");
- sWidth = GET_IOSYM(sWidth, "IOSurfaceGetWidth");
- sHeight = GET_IOSYM(sHeight, "IOSurfaceGetHeight");
- sBytesPerRow = GET_IOSYM(sBytesPerRow, "IOSurfaceGetBytesPerRow");
- sLookup = GET_IOSYM(sLookup, "IOSurfaceLookup");
- sLock = GET_IOSYM(sLock, "IOSurfaceLock");
- sUnlock = GET_IOSYM(sUnlock, "IOSurfaceUnlock");
- sGetBaseAddress = GET_IOSYM(sGetBaseAddress, "IOSurfaceGetBaseAddress");
- sTexImage = GET_CGLSYM(sTexImage, "CGLTexImageIOSurface2D");
- sCGContextGetTypePtr = (unsigned int (*)(CGContext*))dlsym(RTLD_DEFAULT, "CGContextGetType");
-
- // Optional symbols
- sIOSurfaceContextCreate = GET_CGSYM(sIOSurfaceContextCreate, "CGIOSurfaceContextCreate");
- sIOSurfaceContextCreateImage = GET_CGSYM(sIOSurfaceContextCreateImage, "CGIOSurfaceContextCreateImage");
- sIOSurfaceContextGetSurface = GET_CGSYM(sIOSurfaceContextGetSurface, "CGIOSurfaceContextGetSurface");
-
- if (!sCreate || !sGetID || !sLookup || !sTexImage || !sGetBaseAddress ||
- !kPropWidth || !kPropHeight || !kPropBytesPerElem || !kPropIsGlobal ||
- !sLock || !sUnlock || !sWidth || !sHeight || !kPropBytesPerRow ||
- !sBytesPerRow) {
- CloseLibrary();
- }
-}
-
-void MacIOSurfaceLib::CloseLibrary() {
- if (sIOSurfaceFramework) {
- dlclose(sIOSurfaceFramework);
- }
- if (sOpenGLFramework) {
- dlclose(sOpenGLFramework);
- }
- sIOSurfaceFramework = nullptr;
- sOpenGLFramework = nullptr;
-}
-
-MacIOSurface::~MacIOSurface() {
- CFRelease(mIOSurfacePtr);
-}
-
-TemporaryRef<MacIOSurface> MacIOSurface::CreateIOSurface(int aWidth, int aHeight,
- double aContentsScaleFactor) {
- if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0)
- return nullptr;
-
- CFMutableDictionaryRef props = ::CFDictionaryCreateMutable(
- kCFAllocatorDefault, 4,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- if (!props)
- return nullptr;
-
- int32_t bytesPerElem = 4;
- size_t intScaleFactor = ceil(aContentsScaleFactor);
- aWidth *= intScaleFactor;
- aHeight *= intScaleFactor;
- CFNumberRef cfWidth = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aWidth);
- CFNumberRef cfHeight = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aHeight);
- CFNumberRef cfBytesPerElem = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &bytesPerElem);
- ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropWidth,
- cfWidth);
- ::CFRelease(cfWidth);
- ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropHeight,
- cfHeight);
- ::CFRelease(cfHeight);
- ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropBytesPerElem,
- cfBytesPerElem);
- ::CFRelease(cfBytesPerElem);
- ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropIsGlobal,
- kCFBooleanTrue);
-
- IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceCreate(props);
- ::CFRelease(props);
-
- if (!surfaceRef)
- return nullptr;
-
- RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor);
- if (!ioSurface) {
- ::CFRelease(surfaceRef);
- return nullptr;
- }
-
- return ioSurface.forget();
-}
-
-TemporaryRef<MacIOSurface> MacIOSurface::LookupSurface(IOSurfaceID aIOSurfaceID,
- double aContentsScaleFactor) {
- if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0)
- return nullptr;
-
- IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceLookup(aIOSurfaceID);
- if (!surfaceRef)
- return nullptr;
-
- RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor);
- if (!ioSurface) {
- ::CFRelease(surfaceRef);
- return nullptr;
- }
- return ioSurface.forget();
-}
-
-IOSurfaceID MacIOSurface::GetIOSurfaceID() {
- return MacIOSurfaceLib::IOSurfaceGetID(mIOSurfacePtr);
-}
-
-void* MacIOSurface::GetBaseAddress() {
- return MacIOSurfaceLib::IOSurfaceGetBaseAddress(mIOSurfacePtr);
-}
-
-size_t MacIOSurface::GetWidth() {
- size_t intScaleFactor = ceil(mContentsScaleFactor);
- return GetDevicePixelWidth() / intScaleFactor;
-}
-
-size_t MacIOSurface::GetHeight() {
- size_t intScaleFactor = ceil(mContentsScaleFactor);
- return GetDevicePixelHeight() / intScaleFactor;
-}
-
-size_t MacIOSurface::GetDevicePixelWidth() {
- return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr);
-}
-
-size_t MacIOSurface::GetDevicePixelHeight() {
- return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr);
-}
-
-size_t MacIOSurface::GetBytesPerRow() {
- return MacIOSurfaceLib::IOSurfaceGetBytesPerRow(mIOSurfacePtr);
-}
-
-#define READ_ONLY 0x1
-void MacIOSurface::Lock() {
- MacIOSurfaceLib::IOSurfaceLock(mIOSurfacePtr, READ_ONLY, nullptr);
-}
-
-void MacIOSurface::Unlock() {
- MacIOSurfaceLib::IOSurfaceUnlock(mIOSurfacePtr, READ_ONLY, nullptr);
-}
-
-#include "SourceSurfaceRawData.h"
-using mozilla::gfx::SourceSurface;
-using mozilla::gfx::SourceSurfaceRawData;
-using mozilla::gfx::IntSize;
-
-TemporaryRef<SourceSurface>
-MacIOSurface::GetAsSurface() {
- Lock();
- size_t bytesPerRow = GetBytesPerRow();
- size_t ioWidth = GetDevicePixelWidth();
- size_t ioHeight = GetDevicePixelHeight();
-
- unsigned char* ioData = (unsigned char*)GetBaseAddress();
- unsigned char* dataCpy = (unsigned char*)malloc(bytesPerRow*ioHeight);
- for (size_t i = 0; i < ioHeight; i++) {
- memcpy(dataCpy + i * bytesPerRow,
- ioData + i * bytesPerRow, ioWidth * 4);
- }
-
- Unlock();
-
- RefPtr<SourceSurfaceRawData> surf = new SourceSurfaceRawData();
- surf->InitWrappingData(dataCpy, IntSize(ioWidth, ioHeight), bytesPerRow, mozilla::gfx::FORMAT_B8G8R8A8, true);
-
- return surf.forget();
-}
-
-CGLError
-MacIOSurface::CGLTexImageIOSurface2D(void *c,
- GLenum internalFormat, GLenum format,
- GLenum type, GLuint plane)
-{
- NSOpenGLContext *ctxt = static_cast<NSOpenGLContext*>(c);
- return MacIOSurfaceLib::CGLTexImageIOSurface2D((CGLContextObj)[ctxt CGLContextObj],
- GL_TEXTURE_RECTANGLE_ARB,
- internalFormat,
- GetDevicePixelWidth(),
- GetDevicePixelHeight(),
- format, type,
- mIOSurfacePtr, plane);
-}
-
CGColorSpaceRef CreateSystemColorSpace() {
- CMProfileRef system_profile = nullptr;
- CGColorSpaceRef cspace = nullptr;
-
- if (::CMGetSystemProfile(&system_profile) == noErr) {
- // Create a colorspace with the systems profile
- cspace = ::CGColorSpaceCreateWithPlatformColorSpace(system_profile);
- ::CMCloseProfile(system_profile);
- } else {
- // Default to generic
- cspace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
- }
-
- return cspace;
-}
-
-CGContextRef MacIOSurface::CreateIOSurfaceContext() {
- CGContextRef ref = MacIOSurfaceLib::IOSurfaceContextCreate(mIOSurfacePtr,
- GetDevicePixelWidth(),
- GetDevicePixelHeight(),
- 8, 32, CreateSystemColorSpace(), 0x2002);
- return ref;
+ CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID());
+ if (!cspace) {
+ cspace = ::CGColorSpaceCreateDeviceRGB();
+ }
+ return cspace;
}
nsCARenderer::~nsCARenderer() {
@@ -633,9 +194,7 @@ nsresult nsCARenderer::SetupRenderer(void *aCALayer, int aWidth, int aHeight,
dataProvider, nullptr, true, kCGRenderingIntentDefault);
::CGDataProviderRelease(dataProvider);
- if (colorSpace) {
- ::CGColorSpaceRelease(colorSpace);
- }
+ ::CGColorSpaceRelease(colorSpace);
if (!mCGImage) {
mUnsupportedWidth = aWidth;
mUnsupportedHeight = aHeight;
@@ -778,12 +337,9 @@ void nsCARenderer::SetViewport(int aWidth, int aHeight) {
::glScalef(1.0, -1.0, 1.0);
}
-void nsCARenderer::AttachIOSurface(RefPtr<MacIOSurface> aSurface) {
+void nsCARenderer::AttachIOSurface(MacIOSurface *aSurface) {
if (mIOSurface &&
aSurface->GetIOSurfaceID() == mIOSurface->GetIOSurfaceID()) {
- // This object isn't needed since we already have a
- // handle to the same io surface.
- aSurface = nullptr;
return;
}
@@ -809,7 +365,8 @@ void nsCARenderer::AttachIOSurface(RefPtr<MacIOSurface> aSurface) {
// Rebind the FBO to make it live
::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
- if (mIOSurface->GetWidth() != width || mIOSurface->GetHeight() != height) {
+ if (static_cast<int>(mIOSurface->GetWidth()) != width ||
+ static_cast<int>(mIOSurface->GetHeight()) != height) {
width = mIOSurface->GetWidth();
height = mIOSurface->GetHeight();
SetBounds(width, height);
@@ -1068,48 +625,3 @@ void nsCARenderer::SaveToDisk(MacIOSurface *surf) {
}
#endif
-
-CGImageRef MacIOSurface::CreateImageFromIOSurfaceContext(CGContextRef aContext) {
- if (!MacIOSurfaceLib::isInit())
- return nullptr;
-
- return MacIOSurfaceLib::IOSurfaceContextCreateImage(aContext);
-}
-
-TemporaryRef<MacIOSurface> MacIOSurface::IOSurfaceContextGetSurface(CGContextRef aContext,
- double aContentsScaleFactor) {
- if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0)
- return nullptr;
-
- IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceContextGetSurface(aContext);
- if (!surfaceRef)
- return nullptr;
-
- // Retain the IOSurface because MacIOSurface will release it
- CFRetain(surfaceRef);
-
- RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor);
- if (!ioSurface) {
- ::CFRelease(surfaceRef);
- return nullptr;
- }
- return ioSurface.forget();
-}
-
-
-CGContextType GetContextType(CGContextRef ref)
-{
- if (!MacIOSurfaceLib::isInit() || !MacIOSurfaceLib::sCGContextGetTypePtr)
- return CG_CONTEXT_TYPE_UNKNOWN;
-
- unsigned int type = MacIOSurfaceLib::sCGContextGetTypePtr(ref);
- if (type == CG_CONTEXT_TYPE_BITMAP) {
- return CG_CONTEXT_TYPE_BITMAP;
- } else if (type == CG_CONTEXT_TYPE_IOSURFACE) {
- return CG_CONTEXT_TYPE_IOSURFACE;
- } else {
- return CG_CONTEXT_TYPE_UNKNOWN;
- }
-}
-
-
diff --git a/gfx/2d/RadialGradientEffectD2D1.cpp b/gfx/2d/RadialGradientEffectD2D1.cpp
index 42bdf7027..e53a3f855 100644
--- a/gfx/2d/RadialGradientEffectD2D1.cpp
+++ b/gfx/2d/RadialGradientEffectD2D1.cpp
@@ -50,11 +50,11 @@ static const PCWSTR kXmlDescription =
);
// {FB947CDA-718E-40CC-AE7B-D255830D7D14}
-DEFINE_GUID(GUID_SampleRadialGradientPS,
-0xfb947cda, 0x718e, 0x40cc, 0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14);
+static const GUID GUID_SampleRadialGradientPS =
+ {0xfb947cda, 0x718e, 0x40cc, {0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14}};
// {2C468128-6546-453C-8E25-F2DF0DE10A0F}
-DEFINE_GUID(GUID_SampleRadialGradientA0PS,
-0x2c468128, 0x6546, 0x453c, 0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf);
+static const GUID GUID_SampleRadialGradientA0PS =
+ {0x2c468128, 0x6546, 0x453c, {0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf}};
namespace mozilla {
namespace gfx {
@@ -116,7 +116,7 @@ RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
return S_OK;
}
- D2D1_POINT_2F dc = D2D1::Point2F(mCenter2.x - mCenter1.x, mCenter2.y - mCenter2.y);
+ D2D1_POINT_2F dc = D2D1::Point2F(mCenter2.x - mCenter1.x, mCenter2.y - mCenter1.y);
float dr = mRadius2 - mRadius1;
float A = dc.x * dc.x + dc.y * dc.y - dr * dr;
@@ -147,14 +147,18 @@ RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
float A;
float radius1;
float sq_radius1;
- float padding2[3];
+ float repeat_correct;
+ float allow_odd;
+ float padding2[1];
float transform[8];
};
PSConstantBuffer buffer = { { dc.x, dc.y, dr }, 0,
{ mCenter1.x, mCenter1.y },
A, mRadius1, mRadius1 * mRadius1,
- { 0, 0, 0 }, { mat._11, mat._21, mat._31, 0,
+ mStopCollection->GetExtendMode() != D2D1_EXTEND_MODE_CLAMP ? 1 : 0,
+ mStopCollection->GetExtendMode() == D2D1_EXTEND_MODE_MIRROR ? 1 : 0,
+ { 0 }, { mat._11, mat._21, mat._31, 0,
mat._12, mat._22, mat._32, 0 } };
hr = mDrawInfo->SetPixelShaderConstantBuffer((BYTE*)&buffer, sizeof(buffer));
@@ -264,12 +268,13 @@ HRESULT
RadialGradientEffectD2D1::Register(ID2D1Factory1 *aFactory)
{
D2D1_PROPERTY_BINDING bindings[] = {
- D2D1_VALUE_TYPE_BINDING(L"StopCollection", &SetStopCollection, &GetStopCollection),
- D2D1_VALUE_TYPE_BINDING(L"Center1", &SetCenter1, &GetCenter1),
- D2D1_VALUE_TYPE_BINDING(L"Center2", &SetCenter2, &GetCenter2),
- D2D1_VALUE_TYPE_BINDING(L"Radius1", &SetRadius1, &GetRadius1),
- D2D1_VALUE_TYPE_BINDING(L"Radius2", &SetRadius2, &GetRadius2),
- D2D1_VALUE_TYPE_BINDING(L"Transform", &SetTransform, &GetTransform)
+ D2D1_VALUE_TYPE_BINDING(L"StopCollection", &RadialGradientEffectD2D1::SetStopCollection,
+ &RadialGradientEffectD2D1::GetStopCollection),
+ D2D1_VALUE_TYPE_BINDING(L"Center1", &RadialGradientEffectD2D1::SetCenter1, &RadialGradientEffectD2D1::GetCenter1),
+ D2D1_VALUE_TYPE_BINDING(L"Center2", &RadialGradientEffectD2D1::SetCenter2, &RadialGradientEffectD2D1::GetCenter2),
+ D2D1_VALUE_TYPE_BINDING(L"Radius1", &RadialGradientEffectD2D1::SetRadius1, &RadialGradientEffectD2D1::GetRadius1),
+ D2D1_VALUE_TYPE_BINDING(L"Radius2", &RadialGradientEffectD2D1::SetRadius2, &RadialGradientEffectD2D1::GetRadius2),
+ D2D1_VALUE_TYPE_BINDING(L"Transform", &RadialGradientEffectD2D1::SetTransform, &RadialGradientEffectD2D1::GetTransform)
};
HRESULT hr = aFactory->RegisterEffectFromString(CLSID_RadialGradientEffect, kXmlDescription, bindings, ARRAYSIZE(bindings), CreateEffect);
@@ -279,6 +284,12 @@ RadialGradientEffectD2D1::Register(ID2D1Factory1 *aFactory)
return hr;
}
+void
+RadialGradientEffectD2D1::Unregister(ID2D1Factory1 *aFactory)
+{
+ aFactory->UnregisterEffect(CLSID_RadialGradientEffect);
+}
+
HRESULT __stdcall
RadialGradientEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
{
@@ -364,21 +375,23 @@ RadialGradientEffectD2D1::CreateGradientTexture()
UINT32 width = 4096;
UINT32 stride = 4096 * 4;
D2D1_RESOURCE_TEXTURE_PROPERTIES props;
- props.dimensions = 1;
- props.extents = &width;
+ // Older shader models do not support 1D textures. So just use a width x 1 texture.
+ props.dimensions = 2;
+ UINT32 dims[] = { width, 1 };
+ props.extents = dims;
props.channelDepth = D2D1_CHANNEL_DEPTH_4;
props.bufferPrecision = D2D1_BUFFER_PRECISION_8BPC_UNORM;
props.filter = D2D1_FILTER_MIN_MAG_MIP_LINEAR;
- D2D1_EXTEND_MODE extendMode = mStopCollection->GetExtendMode();
- props.extendModes = &extendMode;
+ D2D1_EXTEND_MODE extendMode[] = { mStopCollection->GetExtendMode(), mStopCollection->GetExtendMode() };
+ props.extendModes = extendMode;
HRESULT hr = mEffectContext->CreateResourceTexture(nullptr, &props, &textureData.front(), &stride, 4096 * 4, byRef(tex));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create resource texture: " << hr;
+ gfxWarning() << "Failed to create resource texture: " << hexa(hr);
}
- return tex;
+ return tex.forget();
}
}
diff --git a/gfx/2d/RadialGradientEffectD2D1.h b/gfx/2d/RadialGradientEffectD2D1.h
index 6218f7384..ac8d433dc 100644
--- a/gfx/2d/RadialGradientEffectD2D1.h
+++ b/gfx/2d/RadialGradientEffectD2D1.h
@@ -11,6 +11,7 @@
#include <d2d1effecthelpers.h>
#include "2D.h"
+#include "mozilla/Attributes.h"
// {97143DC6-CBC4-4DD4-A8BA-13342B0BA46D}
DEFINE_GUID(CLSID_RadialGradientEffect,
@@ -37,8 +38,8 @@ enum {
RADIAL_PROP_TRANSFORM
};
-class RadialGradientEffectD2D1 : public ID2D1EffectImpl
- , public ID2D1DrawTransform
+class RadialGradientEffectD2D1 final : public ID2D1EffectImpl
+ , public ID2D1DrawTransform
{
public:
// ID2D1EffectImpl
@@ -71,6 +72,7 @@ public:
IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo *pDrawInfo);
static HRESULT Register(ID2D1Factory1* aFactory);
+ static void Unregister(ID2D1Factory1* aFactory);
static HRESULT __stdcall CreateEffect(IUnknown** aEffectImpl);
HRESULT SetStopCollection(IUnknown *aStopCollection);
diff --git a/gfx/2d/RecordedEvent.cpp b/gfx/2d/RecordedEvent.cpp
index c70fd5899..8506029a2 100644
--- a/gfx/2d/RecordedEvent.cpp
+++ b/gfx/2d/RecordedEvent.cpp
@@ -7,6 +7,8 @@
#include "PathRecording.h"
#include "Tools.h"
+#include "Filters.h"
+#include "Logging.h"
namespace mozilla {
namespace gfx {
@@ -16,9 +18,9 @@ using namespace std;
static std::string NameFromBackend(BackendType aType)
{
switch (aType) {
- case BACKEND_NONE:
+ case BackendType::NONE:
return "None";
- case BACKEND_DIRECT2D:
+ case BackendType::DIRECT2D:
return "Direct2D";
default:
return "Unknown";
@@ -49,18 +51,96 @@ RecordedEvent::LoadEventFromStream(std::istream &aStream, EventType aType)
LOAD_EVENT_TYPE(STROKE, RecordedStroke);
LOAD_EVENT_TYPE(DRAWSURFACE, RecordedDrawSurface);
LOAD_EVENT_TYPE(DRAWSURFACEWITHSHADOW, RecordedDrawSurfaceWithShadow);
+ LOAD_EVENT_TYPE(DRAWFILTER, RecordedDrawFilter);
LOAD_EVENT_TYPE(PATHCREATION, RecordedPathCreation);
LOAD_EVENT_TYPE(PATHDESTRUCTION, RecordedPathDestruction);
LOAD_EVENT_TYPE(SOURCESURFACECREATION, RecordedSourceSurfaceCreation);
LOAD_EVENT_TYPE(SOURCESURFACEDESTRUCTION, RecordedSourceSurfaceDestruction);
+ LOAD_EVENT_TYPE(FILTERNODECREATION, RecordedFilterNodeCreation);
+ LOAD_EVENT_TYPE(FILTERNODEDESTRUCTION, RecordedFilterNodeDestruction);
LOAD_EVENT_TYPE(GRADIENTSTOPSCREATION, RecordedGradientStopsCreation);
LOAD_EVENT_TYPE(GRADIENTSTOPSDESTRUCTION, RecordedGradientStopsDestruction);
LOAD_EVENT_TYPE(SNAPSHOT, RecordedSnapshot);
LOAD_EVENT_TYPE(SCALEDFONTCREATION, RecordedScaledFontCreation);
LOAD_EVENT_TYPE(SCALEDFONTDESTRUCTION, RecordedScaledFontDestruction);
LOAD_EVENT_TYPE(MASKSURFACE, RecordedMaskSurface);
+ LOAD_EVENT_TYPE(FILTERNODESETATTRIBUTE, RecordedFilterNodeSetAttribute);
+ LOAD_EVENT_TYPE(FILTERNODESETINPUT, RecordedFilterNodeSetInput);
default:
- return NULL;
+ return nullptr;
+ }
+}
+
+string
+RecordedEvent::GetEventName(EventType aType)
+{
+ switch (aType) {
+ case DRAWTARGETCREATION:
+ return "DrawTarget Creation";
+ case DRAWTARGETDESTRUCTION:
+ return "DrawTarget Destruction";
+ case FILLRECT:
+ return "FillRect";
+ case STROKERECT:
+ return "StrokeRect";
+ case STROKELINE:
+ return "StrokeLine";
+ case CLEARRECT:
+ return "ClearRect";
+ case COPYSURFACE:
+ return "CopySurface";
+ case SETTRANSFORM:
+ return "SetTransform";
+ case PUSHCLIP:
+ return "PushClip";
+ case PUSHCLIPRECT:
+ return "PushClipRect";
+ case POPCLIP:
+ return "PopClip";
+ case FILL:
+ return "Fill";
+ case FILLGLYPHS:
+ return "FillGlyphs";
+ case MASK:
+ return "Mask";
+ case STROKE:
+ return "Stroke";
+ case DRAWSURFACE:
+ return "DrawSurface";
+ case DRAWSURFACEWITHSHADOW:
+ return "DrawSurfaceWithShadow";
+ case DRAWFILTER:
+ return "DrawFilter";
+ case PATHCREATION:
+ return "PathCreation";
+ case PATHDESTRUCTION:
+ return "PathDestruction";
+ case SOURCESURFACECREATION:
+ return "SourceSurfaceCreation";
+ case SOURCESURFACEDESTRUCTION:
+ return "SourceSurfaceDestruction";
+ case FILTERNODECREATION:
+ return "FilterNodeCreation";
+ case FILTERNODEDESTRUCTION:
+ return "FilterNodeDestruction";
+ case GRADIENTSTOPSCREATION:
+ return "GradientStopsCreation";
+ case GRADIENTSTOPSDESTRUCTION:
+ return "GradientStopsDestruction";
+ case SNAPSHOT:
+ return "Snapshot";
+ case SCALEDFONTCREATION:
+ return "ScaledFontCreation";
+ case SCALEDFONTDESTRUCTION:
+ return "ScaledFontDestruction";
+ case MASKSURFACE:
+ return "MaskSurface";
+ case FILTERNODESETATTRIBUTE:
+ return "SetAttribute";
+ case FILTERNODESETINPUT:
+ return "SetInput";
+ default:
+ return "Unknown";
}
}
@@ -70,22 +150,22 @@ RecordedEvent::RecordPatternData(std::ostream &aStream, const PatternStorage &aP
WriteElement(aStream, aPattern.mType);
switch (aPattern.mType) {
- case PATTERN_COLOR:
+ case PatternType::COLOR:
{
WriteElement(aStream, *reinterpret_cast<const ColorPatternStorage*>(&aPattern.mStorage));
return;
}
- case PATTERN_LINEAR_GRADIENT:
+ case PatternType::LINEAR_GRADIENT:
{
WriteElement(aStream, *reinterpret_cast<const LinearGradientPatternStorage*>(&aPattern.mStorage));
return;
}
- case PATTERN_RADIAL_GRADIENT:
+ case PatternType::RADIAL_GRADIENT:
{
WriteElement(aStream, *reinterpret_cast<const RadialGradientPatternStorage*>(&aPattern.mStorage));
return;
}
- case PATTERN_SURFACE:
+ case PatternType::SURFACE:
{
WriteElement(aStream, *reinterpret_cast<const SurfacePatternStorage*>(&aPattern.mStorage));
return;
@@ -101,22 +181,22 @@ RecordedEvent::ReadPatternData(std::istream &aStream, PatternStorage &aPattern)
ReadElement(aStream, aPattern.mType);
switch (aPattern.mType) {
- case PATTERN_COLOR:
+ case PatternType::COLOR:
{
ReadElement(aStream, *reinterpret_cast<ColorPatternStorage*>(&aPattern.mStorage));
return;
}
- case PATTERN_LINEAR_GRADIENT:
+ case PatternType::LINEAR_GRADIENT:
{
ReadElement(aStream, *reinterpret_cast<LinearGradientPatternStorage*>(&aPattern.mStorage));
return;
}
- case PATTERN_RADIAL_GRADIENT:
+ case PatternType::RADIAL_GRADIENT:
{
ReadElement(aStream, *reinterpret_cast<RadialGradientPatternStorage*>(&aPattern.mStorage));
return;
}
- case PATTERN_SURFACE:
+ case PatternType::SURFACE:
{
ReadElement(aStream, *reinterpret_cast<SurfacePatternStorage*>(&aPattern.mStorage));
return;
@@ -132,13 +212,13 @@ RecordedEvent::StorePattern(PatternStorage &aDestination, const Pattern &aSource
aDestination.mType = aSource.GetType();
switch (aSource.GetType()) {
- case PATTERN_COLOR:
+ case PatternType::COLOR:
{
reinterpret_cast<ColorPatternStorage*>(&aDestination.mStorage)->mColor =
static_cast<const ColorPattern*>(&aSource)->mColor;
return;
}
- case PATTERN_LINEAR_GRADIENT:
+ case PatternType::LINEAR_GRADIENT:
{
LinearGradientPatternStorage *store =
reinterpret_cast<LinearGradientPatternStorage*>(&aDestination.mStorage);
@@ -150,7 +230,7 @@ RecordedEvent::StorePattern(PatternStorage &aDestination, const Pattern &aSource
store->mStops = pat->mStops.get();
return;
}
- case PATTERN_RADIAL_GRADIENT:
+ case PatternType::RADIAL_GRADIENT:
{
RadialGradientPatternStorage *store =
reinterpret_cast<RadialGradientPatternStorage*>(&aDestination.mStorage);
@@ -164,7 +244,7 @@ RecordedEvent::StorePattern(PatternStorage &aDestination, const Pattern &aSource
store->mStops = pat->mStops.get();
return;
}
- case PATTERN_SURFACE:
+ case PatternType::SURFACE:
{
SurfacePatternStorage *store =
reinterpret_cast<SurfacePatternStorage*>(&aDestination.mStorage);
@@ -231,13 +311,13 @@ void
RecordedEvent::OutputSimplePatternInfo(const PatternStorage &aStorage, std::stringstream &aOutput) const
{
switch (aStorage.mType) {
- case PATTERN_COLOR:
+ case PatternType::COLOR:
{
const Color color = reinterpret_cast<const ColorPatternStorage*>(&aStorage.mStorage)->mColor;
aOutput << "Color: (" << color.r << ", " << color.g << ", " << color.b << ", " << color.a << ")";
return;
}
- case PATTERN_LINEAR_GRADIENT:
+ case PatternType::LINEAR_GRADIENT:
{
const LinearGradientPatternStorage *store =
reinterpret_cast<const LinearGradientPatternStorage*>(&aStorage.mStorage);
@@ -246,7 +326,7 @@ RecordedEvent::OutputSimplePatternInfo(const PatternStorage &aStorage, std::stri
") - (" << store->mEnd.x << ", " << store->mEnd.y << ") Stops: " << store->mStops;
return;
}
- case PATTERN_RADIAL_GRADIENT:
+ case PatternType::RADIAL_GRADIENT:
{
const RadialGradientPatternStorage *store =
reinterpret_cast<const RadialGradientPatternStorage*>(&aStorage.mStorage);
@@ -254,7 +334,7 @@ RecordedEvent::OutputSimplePatternInfo(const PatternStorage &aStorage, std::stri
store->mCenter2.y << ") Radius 2: " << store->mRadius2;
return;
}
- case PATTERN_SURFACE:
+ case PatternType::SURFACE:
{
const SurfacePatternStorage *store =
reinterpret_cast<const SurfacePatternStorage*>(&aStorage.mStorage);
@@ -277,7 +357,7 @@ RecordedDrawingEvent::RecordToStream(ostream &aStream) const
}
ReferencePtr
-RecordedDrawingEvent::GetObject() const
+RecordedDrawingEvent::GetObjectRef() const
{
return mDT;
}
@@ -288,6 +368,11 @@ RecordedDrawTargetCreation::PlayEvent(Translator *aTranslator) const
RefPtr<DrawTarget> newDT =
aTranslator->GetReferenceDrawTarget()->CreateSimilarDrawTarget(mSize, mFormat);
aTranslator->AddDrawTarget(mRefPtr, newDT);
+
+ if (mHasExistingData) {
+ Rect dataRect(0, 0, mExistingData->GetSize().width, mExistingData->GetSize().height);
+ newDT->DrawSurface(mExistingData, dataRect, dataRect);
+ }
}
void
@@ -297,15 +382,43 @@ RecordedDrawTargetCreation::RecordToStream(ostream &aStream) const
WriteElement(aStream, mBackendType);
WriteElement(aStream, mSize);
WriteElement(aStream, mFormat);
+ WriteElement(aStream, mHasExistingData);
+
+ if (mHasExistingData) {
+ MOZ_ASSERT(mExistingData);
+ MOZ_ASSERT(mExistingData->GetSize() == mSize);
+ RefPtr<DataSourceSurface> dataSurf = mExistingData->GetDataSurface();
+ for (int y = 0; y < mSize.height; y++) {
+ aStream.write((const char*)dataSurf->GetData() + y * dataSurf->Stride(),
+ BytesPerPixel(mFormat) * mSize.width);
+ }
+ }
}
RecordedDrawTargetCreation::RecordedDrawTargetCreation(istream &aStream)
: RecordedEvent(DRAWTARGETCREATION)
+ , mExistingData(nullptr)
{
ReadElement(aStream, mRefPtr);
ReadElement(aStream, mBackendType);
ReadElement(aStream, mSize);
ReadElement(aStream, mFormat);
+ ReadElement(aStream, mHasExistingData);
+
+ if (mHasExistingData) {
+ RefPtr<DataSourceSurface> dataSurf = Factory::CreateDataSourceSurface(mSize, mFormat);
+ if (!dataSurf) {
+ gfxWarning() << "RecordedDrawTargetCreation had to reset mHasExistingData";
+ mHasExistingData = false;
+ return;
+ }
+
+ for (int y = 0; y < mSize.height; y++) {
+ aStream.read((char*)dataSurf->GetData() + y * dataSurf->Stride(),
+ BytesPerPixel(mFormat) * mSize.width);
+ }
+ mExistingData = dataSurf;
+ }
}
void
@@ -342,7 +455,7 @@ RecordedDrawTargetDestruction::OutputSimpleEventInfo(stringstream &aStringStream
struct GenericPattern
{
GenericPattern(const PatternStorage &aStorage, Translator *aTranslator)
- : mPattern(NULL), mTranslator(aTranslator)
+ : mPattern(nullptr), mTranslator(aTranslator)
{
mStorage = const_cast<PatternStorage*>(&aStorage);
}
@@ -356,9 +469,9 @@ struct GenericPattern
operator Pattern*()
{
switch(mStorage->mType) {
- case PATTERN_COLOR:
+ case PatternType::COLOR:
return new (mColPat) ColorPattern(reinterpret_cast<ColorPatternStorage*>(&mStorage->mStorage)->mColor);
- case PATTERN_SURFACE:
+ case PatternType::SURFACE:
{
SurfacePatternStorage *storage = reinterpret_cast<SurfacePatternStorage*>(&mStorage->mStorage);
mPattern =
@@ -366,7 +479,7 @@ struct GenericPattern
storage->mExtend, storage->mMatrix, storage->mFilter);
return mPattern;
}
- case PATTERN_LINEAR_GRADIENT:
+ case PatternType::LINEAR_GRADIENT:
{
LinearGradientPatternStorage *storage = reinterpret_cast<LinearGradientPatternStorage*>(&mStorage->mStorage);
mPattern =
@@ -375,7 +488,7 @@ struct GenericPattern
storage->mMatrix);
return mPattern;
}
- case PATTERN_RADIAL_GRADIENT:
+ case PatternType::RADIAL_GRADIENT:
{
RadialGradientPatternStorage *storage = reinterpret_cast<RadialGradientPatternStorage*>(&mStorage->mStorage);
mPattern =
@@ -829,6 +942,39 @@ RecordedDrawSurface::OutputSimpleEventInfo(stringstream &aStringStream) const
}
void
+RecordedDrawFilter::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->
+ DrawFilter(aTranslator->LookupFilterNode(mNode), mSourceRect,
+ mDestPoint, mOptions);
+}
+
+void
+RecordedDrawFilter::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mNode);
+ WriteElement(aStream, mSourceRect);
+ WriteElement(aStream, mDestPoint);
+ WriteElement(aStream, mOptions);
+}
+
+RecordedDrawFilter::RecordedDrawFilter(istream &aStream)
+ : RecordedDrawingEvent(DRAWFILTER, aStream)
+{
+ ReadElement(aStream, mNode);
+ ReadElement(aStream, mSourceRect);
+ ReadElement(aStream, mDestPoint);
+ ReadElement(aStream, mOptions);
+}
+
+void
+RecordedDrawFilter::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] DrawFilter (" << mNode << ")";
+}
+
+void
RecordedDrawSurfaceWithShadow::PlayEvent(Translator *aTranslator) const
{
aTranslator->LookupDrawTarget(mDT)->
@@ -1050,6 +1196,62 @@ RecordedSourceSurfaceDestruction::OutputSimpleEventInfo(stringstream &aStringStr
aStringStream << "[" << mRefPtr << "] SourceSurface Destroyed";
}
+RecordedFilterNodeCreation::~RecordedFilterNodeCreation()
+{
+}
+
+void
+RecordedFilterNodeCreation::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<FilterNode> node = aTranslator->GetReferenceDrawTarget()->
+ CreateFilter(mType);
+ aTranslator->AddFilterNode(mRefPtr, node);
+}
+
+void
+RecordedFilterNodeCreation::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mType);
+}
+
+RecordedFilterNodeCreation::RecordedFilterNodeCreation(istream &aStream)
+ : RecordedEvent(FILTERNODECREATION)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mType);
+}
+
+void
+RecordedFilterNodeCreation::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] FilterNode created (Type: " << int(mType) << ")";
+}
+
+void
+RecordedFilterNodeDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemoveFilterNode(mRefPtr);
+}
+
+void
+RecordedFilterNodeDestruction::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+}
+
+RecordedFilterNodeDestruction::RecordedFilterNodeDestruction(istream &aStream)
+ : RecordedEvent(FILTERNODEDESTRUCTION)
+{
+ ReadElement(aStream, mRefPtr);
+}
+
+void
+RecordedFilterNodeDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] FilterNode Destroyed";
+}
+
RecordedGradientStopsCreation::~RecordedGradientStopsCreation()
{
if (mDataOwned) {
@@ -1252,5 +1454,116 @@ RecordedMaskSurface::OutputSimpleEventInfo(stringstream &aStringStream) const
OutputSimplePatternInfo(mPattern, aStringStream);
}
+template<typename T>
+void
+ReplaySetAttribute(FilterNode *aNode, uint32_t aIndex, T aValue)
+{
+ aNode->SetAttribute(aIndex, aValue);
+}
+
+void
+RecordedFilterNodeSetAttribute::PlayEvent(Translator *aTranslator) const
+{
+#define REPLAY_SET_ATTRIBUTE(type, argtype) \
+ case ARGTYPE_##argtype: \
+ ReplaySetAttribute(aTranslator->LookupFilterNode(mNode), mIndex, *(type*)&mPayload.front()); \
+ break
+
+ switch (mArgType) {
+ REPLAY_SET_ATTRIBUTE(bool, BOOL);
+ REPLAY_SET_ATTRIBUTE(uint32_t, UINT32);
+ REPLAY_SET_ATTRIBUTE(Float, FLOAT);
+ REPLAY_SET_ATTRIBUTE(Size, SIZE);
+ REPLAY_SET_ATTRIBUTE(IntSize, INTSIZE);
+ REPLAY_SET_ATTRIBUTE(IntPoint, INTPOINT);
+ REPLAY_SET_ATTRIBUTE(Rect, RECT);
+ REPLAY_SET_ATTRIBUTE(IntRect, INTRECT);
+ REPLAY_SET_ATTRIBUTE(Point, POINT);
+ REPLAY_SET_ATTRIBUTE(Matrix5x4, MATRIX5X4);
+ REPLAY_SET_ATTRIBUTE(Point3D, POINT3D);
+ REPLAY_SET_ATTRIBUTE(Color, COLOR);
+ case ARGTYPE_FLOAT_ARRAY:
+ aTranslator->LookupFilterNode(mNode)->SetAttribute(
+ mIndex,
+ reinterpret_cast<const Float*>(&mPayload.front()),
+ mPayload.size() / sizeof(Float));
+ break;
+ }
+}
+
+void
+RecordedFilterNodeSetAttribute::RecordToStream(ostream &aStream) const
+{
+ RecordedEvent::RecordToStream(aStream);
+ WriteElement(aStream, mNode);
+ WriteElement(aStream, mIndex);
+ WriteElement(aStream, mArgType);
+ WriteElement(aStream, uint64_t(mPayload.size()));
+ aStream.write((const char*)&mPayload.front(), mPayload.size());
+}
+
+RecordedFilterNodeSetAttribute::RecordedFilterNodeSetAttribute(istream &aStream)
+ : RecordedEvent(FILTERNODESETATTRIBUTE)
+{
+ ReadElement(aStream, mNode);
+ ReadElement(aStream, mIndex);
+ ReadElement(aStream, mArgType);
+ uint64_t size;
+ ReadElement(aStream, size);
+ mPayload.resize(size_t(size));
+ aStream.read((char*)&mPayload.front(), size);
+}
+
+void
+RecordedFilterNodeSetAttribute::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mNode << "] SetAttribute (" << mIndex << ")";
+}
+
+void
+RecordedFilterNodeSetInput::PlayEvent(Translator *aTranslator) const
+{
+ if (mInputFilter) {
+ aTranslator->LookupFilterNode(mNode)->SetInput(
+ mIndex, aTranslator->LookupFilterNode(mInputFilter));
+ } else {
+ aTranslator->LookupFilterNode(mNode)->SetInput(
+ mIndex, aTranslator->LookupSourceSurface(mInputSurface));
+ }
+}
+
+void
+RecordedFilterNodeSetInput::RecordToStream(ostream &aStream) const
+{
+ RecordedEvent::RecordToStream(aStream);
+ WriteElement(aStream, mNode);
+ WriteElement(aStream, mIndex);
+ WriteElement(aStream, mInputFilter);
+ WriteElement(aStream, mInputSurface);
+}
+
+RecordedFilterNodeSetInput::RecordedFilterNodeSetInput(istream &aStream)
+ : RecordedEvent(FILTERNODESETINPUT)
+{
+ ReadElement(aStream, mNode);
+ ReadElement(aStream, mIndex);
+ ReadElement(aStream, mInputFilter);
+ ReadElement(aStream, mInputSurface);
+}
+
+void
+RecordedFilterNodeSetInput::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mNode << "] SetAttribute (" << mIndex << ", ";
+
+ if (mInputFilter) {
+ aStringStream << "Filter: " << mInputFilter;
+ } else {
+ aStringStream << "Surface: " << mInputSurface;
+ }
+
+ aStringStream << ")";
+}
+
}
}
diff --git a/gfx/2d/RecordedEvent.h b/gfx/2d/RecordedEvent.h
index 27e2b1838..fca007650 100644
--- a/gfx/2d/RecordedEvent.h
+++ b/gfx/2d/RecordedEvent.h
@@ -20,10 +20,10 @@ namespace gfx {
// loss of backwards compatibility. Old streams will not work in a player
// using a newer major revision. And new streams will not work in a player
// using an older major revision.
-const uint16_t kMajorRevision = 2;
+const uint16_t kMajorRevision = 3;
// A change in minor revision means additions of new events. New streams will
// not play in older players.
-const uint16_t kMinorRevision = 1;
+const uint16_t kMinorRevision = 2;
struct ReferencePtr
{
@@ -31,7 +31,7 @@ struct ReferencePtr
: mLongPtr(0)
{}
- ReferencePtr(const void* aLongPtr)
+ MOZ_IMPLICIT ReferencePtr(const void* aLongPtr)
: mLongPtr(uint64_t(aLongPtr))
{}
@@ -69,9 +69,12 @@ inline std::string StringFromPtr(ReferencePtr aPtr)
class Translator
{
public:
+ virtual ~Translator() {}
+
virtual DrawTarget *LookupDrawTarget(ReferencePtr aRefPtr) = 0;
virtual Path *LookupPath(ReferencePtr aRefPtr) = 0;
virtual SourceSurface *LookupSourceSurface(ReferencePtr aRefPtr) = 0;
+ virtual FilterNode *LookupFilterNode(ReferencePtr aRefPtr) = 0;
virtual GradientStops *LookupGradientStops(ReferencePtr aRefPtr) = 0;
virtual ScaledFont *LookupScaledFont(ReferencePtr aRefPtr) = 0;
virtual void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget *aDT) = 0;
@@ -80,6 +83,8 @@ public:
virtual void RemovePath(ReferencePtr aRefPtr) = 0;
virtual void AddSourceSurface(ReferencePtr aRefPtr, SourceSurface *aPath) = 0;
virtual void RemoveSourceSurface(ReferencePtr aRefPtr) = 0;
+ virtual void AddFilterNode(mozilla::gfx::ReferencePtr aRefPtr, FilterNode *aSurface) = 0;
+ virtual void RemoveFilterNode(mozilla::gfx::ReferencePtr aRefPtr) = 0;
virtual void AddGradientStops(ReferencePtr aRefPtr, GradientStops *aPath) = 0;
virtual void RemoveGradientStops(ReferencePtr aRefPtr) = 0;
virtual void AddScaledFont(ReferencePtr aRefPtr, ScaledFont *aScaledFont) = 0;
@@ -161,8 +166,16 @@ public:
SNAPSHOT,
SCALEDFONTCREATION,
SCALEDFONTDESTRUCTION,
- MASKSURFACE
+ MASKSURFACE,
+ FILTERNODECREATION,
+ FILTERNODEDESTRUCTION,
+ DRAWFILTER,
+ FILTERNODESETATTRIBUTE,
+ FILTERNODESETINPUT
};
+ static const uint32_t kTotalEventTypes = RecordedEvent::FILTERNODESETINPUT + 1;
+
+ static std::string GetEventName(EventType aType);
virtual void PlayEvent(Translator *aTranslator) const {}
@@ -178,7 +191,7 @@ public:
virtual std::string GetName() const = 0;
- virtual ReferencePtr GetObject() const = 0;
+ virtual ReferencePtr GetObjectRef() const = 0;
virtual ReferencePtr GetDestinedDT() { return nullptr; }
@@ -190,7 +203,7 @@ public:
protected:
friend class DrawEventRecorderPrivate;
- RecordedEvent(int32_t aType) : mType(aType)
+ MOZ_IMPLICIT RecordedEvent(int32_t aType) : mType(aType)
{}
int32_t mType;
@@ -211,15 +224,17 @@ protected:
RecordedDrawingEvent(EventType aType, std::istream &aStream);
virtual void RecordToStream(std::ostream &aStream) const;
- virtual ReferencePtr GetObject() const;
+ virtual ReferencePtr GetObjectRef() const;
ReferencePtr mDT;
};
class RecordedDrawTargetCreation : public RecordedEvent {
public:
- RecordedDrawTargetCreation(ReferencePtr aRefPtr, BackendType aType, const IntSize &aSize, SurfaceFormat aFormat)
+ RecordedDrawTargetCreation(ReferencePtr aRefPtr, BackendType aType, const IntSize &aSize, SurfaceFormat aFormat,
+ bool aHasExistingData = false, SourceSurface *aExistingData = nullptr)
: RecordedEvent(DRAWTARGETCREATION), mRefPtr(aRefPtr), mBackendType(aType), mSize(aSize), mFormat(aFormat)
+ , mHasExistingData(aHasExistingData), mExistingData(aExistingData)
{}
virtual void PlayEvent(Translator *aTranslator) const;
@@ -228,22 +243,24 @@ public:
virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
virtual std::string GetName() const { return "DrawTarget Creation"; }
- virtual ReferencePtr GetObject() const { return mRefPtr; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
ReferencePtr mRefPtr;
BackendType mBackendType;
IntSize mSize;
SurfaceFormat mFormat;
+ bool mHasExistingData;
+ RefPtr<SourceSurface> mExistingData;
private:
friend class RecordedEvent;
- RecordedDrawTargetCreation(std::istream &aStream);
+ MOZ_IMPLICIT RecordedDrawTargetCreation(std::istream &aStream);
};
class RecordedDrawTargetDestruction : public RecordedEvent {
public:
- RecordedDrawTargetDestruction(ReferencePtr aRefPtr)
+ MOZ_IMPLICIT RecordedDrawTargetDestruction(ReferencePtr aRefPtr)
: RecordedEvent(DRAWTARGETDESTRUCTION), mRefPtr(aRefPtr)
{}
@@ -253,7 +270,7 @@ public:
virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
virtual std::string GetName() const { return "DrawTarget Destruction"; }
- virtual ReferencePtr GetObject() const { return mRefPtr; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
ReferencePtr mRefPtr;
@@ -261,7 +278,7 @@ public:
private:
friend class RecordedEvent;
- RecordedDrawTargetDestruction(std::istream &aStream);
+ MOZ_IMPLICIT RecordedDrawTargetDestruction(std::istream &aStream);
};
class RecordedFillRect : public RecordedDrawingEvent {
@@ -281,7 +298,7 @@ public:
private:
friend class RecordedEvent;
- RecordedFillRect(std::istream &aStream);
+ MOZ_IMPLICIT RecordedFillRect(std::istream &aStream);
Rect mRect;
PatternStorage mPattern;
@@ -307,7 +324,7 @@ public:
private:
friend class RecordedEvent;
- RecordedStrokeRect(std::istream &aStream);
+ MOZ_IMPLICIT RecordedStrokeRect(std::istream &aStream);
Rect mRect;
PatternStorage mPattern;
@@ -335,7 +352,7 @@ public:
private:
friend class RecordedEvent;
- RecordedStrokeLine(std::istream &aStream);
+ MOZ_IMPLICIT RecordedStrokeLine(std::istream &aStream);
Point mBegin;
Point mEnd;
@@ -361,7 +378,7 @@ public:
private:
friend class RecordedEvent;
- RecordedFill(std::istream &aStream);
+ MOZ_IMPLICIT RecordedFill(std::istream &aStream);
ReferencePtr mPath;
PatternStorage mPattern;
@@ -390,7 +407,7 @@ public:
private:
friend class RecordedEvent;
- RecordedFillGlyphs(std::istream &aStream);
+ MOZ_IMPLICIT RecordedFillGlyphs(std::istream &aStream);
ReferencePtr mScaledFont;
PatternStorage mPattern;
@@ -417,7 +434,7 @@ public:
private:
friend class RecordedEvent;
- RecordedMask(std::istream &aStream);
+ MOZ_IMPLICIT RecordedMask(std::istream &aStream);
PatternStorage mSource;
PatternStorage mMask;
@@ -443,7 +460,7 @@ public:
private:
friend class RecordedEvent;
- RecordedStroke(std::istream &aStream);
+ MOZ_IMPLICIT RecordedStroke(std::istream &aStream);
ReferencePtr mPath;
PatternStorage mPattern;
@@ -467,7 +484,7 @@ public:
private:
friend class RecordedEvent;
- RecordedClearRect(std::istream &aStream);
+ MOZ_IMPLICIT RecordedClearRect(std::istream &aStream);
Rect mRect;
};
@@ -490,7 +507,7 @@ public:
private:
friend class RecordedEvent;
- RecordedCopySurface(std::istream &aStream);
+ MOZ_IMPLICIT RecordedCopySurface(std::istream &aStream);
ReferencePtr mSourceSurface;
IntRect mSourceRect;
@@ -513,7 +530,7 @@ public:
private:
friend class RecordedEvent;
- RecordedPushClip(std::istream &aStream);
+ MOZ_IMPLICIT RecordedPushClip(std::istream &aStream);
ReferencePtr mPath;
};
@@ -534,14 +551,14 @@ public:
private:
friend class RecordedEvent;
- RecordedPushClipRect(std::istream &aStream);
+ MOZ_IMPLICIT RecordedPushClipRect(std::istream &aStream);
Rect mRect;
};
class RecordedPopClip : public RecordedDrawingEvent {
public:
- RecordedPopClip(DrawTarget *aDT)
+ MOZ_IMPLICIT RecordedPopClip(DrawTarget *aDT)
: RecordedDrawingEvent(POPCLIP, aDT)
{}
@@ -554,7 +571,7 @@ public:
private:
friend class RecordedEvent;
- RecordedPopClip(std::istream &aStream);
+ MOZ_IMPLICIT RecordedPopClip(std::istream &aStream);
};
class RecordedSetTransform : public RecordedDrawingEvent {
@@ -573,7 +590,7 @@ public:
private:
friend class RecordedEvent;
- RecordedSetTransform(std::istream &aStream);
+ MOZ_IMPLICIT RecordedSetTransform(std::istream &aStream);
Matrix mTransform;
};
@@ -597,7 +614,7 @@ public:
private:
friend class RecordedEvent;
- RecordedDrawSurface(std::istream &aStream);
+ MOZ_IMPLICIT RecordedDrawSurface(std::istream &aStream);
ReferencePtr mRefSource;
Rect mDest;
@@ -625,7 +642,7 @@ public:
private:
friend class RecordedEvent;
- RecordedDrawSurfaceWithShadow(std::istream &aStream);
+ MOZ_IMPLICIT RecordedDrawSurfaceWithShadow(std::istream &aStream);
ReferencePtr mRefSource;
Point mDest;
@@ -635,9 +652,37 @@ private:
CompositionOp mOp;
};
+class RecordedDrawFilter : public RecordedDrawingEvent {
+public:
+ RecordedDrawFilter(DrawTarget *aDT, ReferencePtr aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+ : RecordedDrawingEvent(DRAWFILTER, aDT), mNode(aNode), mSourceRect(aSourceRect)
+ , mDestPoint(aDestPoint), mOptions(aOptions)
+ {
+ }
+
+ virtual void PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "DrawFilter"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedDrawFilter(std::istream &aStream);
+
+ ReferencePtr mNode;
+ Rect mSourceRect;
+ Point mDestPoint;
+ DrawOptions mOptions;
+};
+
class RecordedPathCreation : public RecordedEvent {
public:
- RecordedPathCreation(PathRecording *aPath);
+ MOZ_IMPLICIT RecordedPathCreation(PathRecording *aPath);
~RecordedPathCreation();
virtual void PlayEvent(Translator *aTranslator) const;
@@ -646,7 +691,7 @@ public:
virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
virtual std::string GetName() const { return "Path Creation"; }
- virtual ReferencePtr GetObject() const { return mRefPtr; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
private:
friend class RecordedEvent;
@@ -654,12 +699,12 @@ private:
FillRule mFillRule;
std::vector<PathOp> mPathOps;
- RecordedPathCreation(std::istream &aStream);
+ MOZ_IMPLICIT RecordedPathCreation(std::istream &aStream);
};
class RecordedPathDestruction : public RecordedEvent {
public:
- RecordedPathDestruction(PathRecording *aPath)
+ MOZ_IMPLICIT RecordedPathDestruction(PathRecording *aPath)
: RecordedEvent(PATHDESTRUCTION), mRefPtr(aPath)
{
}
@@ -670,13 +715,13 @@ public:
virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
virtual std::string GetName() const { return "Path Destruction"; }
- virtual ReferencePtr GetObject() const { return mRefPtr; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
private:
friend class RecordedEvent;
ReferencePtr mRefPtr;
- RecordedPathDestruction(std::istream &aStream);
+ MOZ_IMPLICIT RecordedPathDestruction(std::istream &aStream);
};
class RecordedSourceSurfaceCreation : public RecordedEvent {
@@ -696,7 +741,7 @@ public:
virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
virtual std::string GetName() const { return "SourceSurface Creation"; }
- virtual ReferencePtr GetObject() const { return mRefPtr; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
private:
friend class RecordedEvent;
@@ -707,12 +752,12 @@ private:
SurfaceFormat mFormat;
bool mDataOwned;
- RecordedSourceSurfaceCreation(std::istream &aStream);
+ MOZ_IMPLICIT RecordedSourceSurfaceCreation(std::istream &aStream);
};
class RecordedSourceSurfaceDestruction : public RecordedEvent {
public:
- RecordedSourceSurfaceDestruction(ReferencePtr aRefPtr)
+ MOZ_IMPLICIT RecordedSourceSurfaceDestruction(ReferencePtr aRefPtr)
: RecordedEvent(SOURCESURFACEDESTRUCTION), mRefPtr(aRefPtr)
{
}
@@ -723,13 +768,60 @@ public:
virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
virtual std::string GetName() const { return "SourceSurface Destruction"; }
- virtual ReferencePtr GetObject() const { return mRefPtr; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
private:
friend class RecordedEvent;
ReferencePtr mRefPtr;
- RecordedSourceSurfaceDestruction(std::istream &aStream);
+ MOZ_IMPLICIT RecordedSourceSurfaceDestruction(std::istream &aStream);
+};
+
+class RecordedFilterNodeCreation : public RecordedEvent {
+public:
+ RecordedFilterNodeCreation(ReferencePtr aRefPtr, FilterType aType)
+ : RecordedEvent(FILTERNODECREATION), mRefPtr(aRefPtr), mType(aType)
+ {
+ }
+
+ ~RecordedFilterNodeCreation();
+
+ virtual void PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "FilterNode Creation"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+ FilterType mType;
+
+ MOZ_IMPLICIT RecordedFilterNodeCreation(std::istream &aStream);
+};
+
+class RecordedFilterNodeDestruction : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedFilterNodeDestruction(ReferencePtr aRefPtr)
+ : RecordedEvent(FILTERNODEDESTRUCTION), mRefPtr(aRefPtr)
+ {
+ }
+
+ virtual void PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "FilterNode Destruction"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+
+ MOZ_IMPLICIT RecordedFilterNodeDestruction(std::istream &aStream);
};
class RecordedGradientStopsCreation : public RecordedEvent {
@@ -749,7 +841,7 @@ public:
virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
virtual std::string GetName() const { return "GradientStops Creation"; }
- virtual ReferencePtr GetObject() const { return mRefPtr; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
private:
friend class RecordedEvent;
@@ -759,12 +851,12 @@ private:
ExtendMode mExtendMode;
bool mDataOwned;
- RecordedGradientStopsCreation(std::istream &aStream);
+ MOZ_IMPLICIT RecordedGradientStopsCreation(std::istream &aStream);
};
class RecordedGradientStopsDestruction : public RecordedEvent {
public:
- RecordedGradientStopsDestruction(ReferencePtr aRefPtr)
+ MOZ_IMPLICIT RecordedGradientStopsDestruction(ReferencePtr aRefPtr)
: RecordedEvent(GRADIENTSTOPSDESTRUCTION), mRefPtr(aRefPtr)
{
}
@@ -775,13 +867,13 @@ public:
virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
virtual std::string GetName() const { return "GradientStops Destruction"; }
- virtual ReferencePtr GetObject() const { return mRefPtr; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
private:
friend class RecordedEvent;
ReferencePtr mRefPtr;
- RecordedGradientStopsDestruction(std::istream &aStream);
+ MOZ_IMPLICIT RecordedGradientStopsDestruction(std::istream &aStream);
};
class RecordedSnapshot : public RecordedEvent {
@@ -797,14 +889,14 @@ public:
virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
virtual std::string GetName() const { return "Snapshot"; }
- virtual ReferencePtr GetObject() const { return mRefPtr; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
private:
friend class RecordedEvent;
ReferencePtr mRefPtr;
ReferencePtr mDT;
- RecordedSnapshot(std::istream &aStream);
+ MOZ_IMPLICIT RecordedSnapshot(std::istream &aStream);
};
class RecordedScaledFontCreation : public RecordedEvent {
@@ -815,7 +907,7 @@ public:
}
RecordedScaledFontCreation(ReferencePtr aRefPtr, ScaledFont *aScaledFont)
- : RecordedEvent(SCALEDFONTCREATION), mRefPtr(aRefPtr), mData(NULL)
+ : RecordedEvent(SCALEDFONTCREATION), mRefPtr(aRefPtr), mData(nullptr)
{
aScaledFont->GetFontFileData(&FontDataProc, this);
}
@@ -828,7 +920,7 @@ public:
virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
virtual std::string GetName() const { return "ScaledFont Creation"; }
- virtual ReferencePtr GetObject() const { return mRefPtr; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
void SetFontData(const uint8_t *aData, uint32_t aSize, uint32_t aIndex, Float aGlyphSize);
@@ -841,12 +933,12 @@ private:
Float mGlyphSize;
uint32_t mIndex;
- RecordedScaledFontCreation(std::istream &aStream);
+ MOZ_IMPLICIT RecordedScaledFontCreation(std::istream &aStream);
};
class RecordedScaledFontDestruction : public RecordedEvent {
public:
- RecordedScaledFontDestruction(ReferencePtr aRefPtr)
+ MOZ_IMPLICIT RecordedScaledFontDestruction(ReferencePtr aRefPtr)
: RecordedEvent(SCALEDFONTDESTRUCTION), mRefPtr(aRefPtr)
{
}
@@ -857,13 +949,13 @@ public:
virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
virtual std::string GetName() const { return "ScaledFont Destruction"; }
- virtual ReferencePtr GetObject() const { return mRefPtr; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
private:
friend class RecordedEvent;
ReferencePtr mRefPtr;
- RecordedScaledFontDestruction(std::istream &aStream);
+ MOZ_IMPLICIT RecordedScaledFontDestruction(std::istream &aStream);
};
class RecordedMaskSurface : public RecordedDrawingEvent {
@@ -885,7 +977,7 @@ public:
private:
friend class RecordedEvent;
- RecordedMaskSurface(std::istream &aStream);
+ MOZ_IMPLICIT RecordedMaskSurface(std::istream &aStream);
PatternStorage mPattern;
ReferencePtr mRefMask;
@@ -893,6 +985,94 @@ private:
DrawOptions mOptions;
};
+class RecordedFilterNodeSetAttribute : public RecordedEvent
+{
+public:
+ enum ArgType {
+ ARGTYPE_UINT32,
+ ARGTYPE_BOOL,
+ ARGTYPE_FLOAT,
+ ARGTYPE_SIZE,
+ ARGTYPE_INTSIZE,
+ ARGTYPE_INTPOINT,
+ ARGTYPE_RECT,
+ ARGTYPE_INTRECT,
+ ARGTYPE_POINT,
+ ARGTYPE_MATRIX5X4,
+ ARGTYPE_POINT3D,
+ ARGTYPE_COLOR,
+ ARGTYPE_FLOAT_ARRAY
+ };
+
+ template<typename T>
+ RecordedFilterNodeSetAttribute(FilterNode *aNode, uint32_t aIndex, T aArgument, ArgType aArgType)
+ : RecordedEvent(FILTERNODESETATTRIBUTE), mNode(aNode), mIndex(aIndex), mArgType(aArgType)
+ {
+ mPayload.resize(sizeof(T));
+ memcpy(&mPayload.front(), &aArgument, sizeof(T));
+ }
+
+ RecordedFilterNodeSetAttribute(FilterNode *aNode, uint32_t aIndex, const Float *aFloat, uint32_t aSize)
+ : RecordedEvent(FILTERNODESETATTRIBUTE), mNode(aNode), mIndex(aIndex), mArgType(ARGTYPE_FLOAT_ARRAY)
+ {
+ mPayload.resize(sizeof(Float) * aSize);
+ memcpy(&mPayload.front(), aFloat, sizeof(Float) * aSize);
+ }
+
+ virtual void PlayEvent(Translator *aTranslator) const;
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "SetAttribute"; }
+
+ virtual ReferencePtr GetObjectRef() const { return mNode; }
+
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mNode;
+
+ uint32_t mIndex;
+ ArgType mArgType;
+ std::vector<uint8_t> mPayload;
+
+ MOZ_IMPLICIT RecordedFilterNodeSetAttribute(std::istream &aStream);
+};
+
+class RecordedFilterNodeSetInput : public RecordedEvent
+{
+public:
+ RecordedFilterNodeSetInput(FilterNode* aNode, uint32_t aIndex, FilterNode* aInputNode)
+ : RecordedEvent(FILTERNODESETINPUT), mNode(aNode), mIndex(aIndex)
+ , mInputFilter(aInputNode), mInputSurface(nullptr)
+ {
+ }
+
+ RecordedFilterNodeSetInput(FilterNode *aNode, uint32_t aIndex, SourceSurface *aInputSurface)
+ : RecordedEvent(FILTERNODESETINPUT), mNode(aNode), mIndex(aIndex)
+ , mInputFilter(nullptr), mInputSurface(aInputSurface)
+ {
+ }
+
+ virtual void PlayEvent(Translator *aTranslator) const;
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "SetInput"; }
+
+ virtual ReferencePtr GetObjectRef() const { return mNode; }
+
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mNode;
+ uint32_t mIndex;
+ ReferencePtr mInputFilter;
+ ReferencePtr mInputSurface;
+
+ MOZ_IMPLICIT RecordedFilterNodeSetInput(std::istream &aStream);
+};
+
}
}
diff --git a/gfx/2d/Rect.h b/gfx/2d/Rect.h
index 2084b394e..db0f45d37 100644
--- a/gfx/2d/Rect.h
+++ b/gfx/2d/Rect.h
@@ -11,28 +11,67 @@
#include "Point.h"
#include "Tools.h"
+#include <cmath>
+
namespace mozilla {
+
+template <typename> struct IsPixel;
+
namespace gfx {
-struct Margin :
- public BaseMargin<Float, Margin> {
- typedef BaseMargin<Float, Margin> Super;
+template<class units>
+struct IntMarginTyped:
+ public BaseMargin<int32_t, IntMarginTyped<units> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseMargin<int32_t, IntMarginTyped<units> > Super;
+
+ IntMarginTyped() : Super() {}
+ IntMarginTyped(int32_t aTop, int32_t aRight, int32_t aBottom, int32_t aLeft) :
+ Super(aTop, aRight, aBottom, aLeft) {}
+};
+typedef IntMarginTyped<UnknownUnits> IntMargin;
+
+template<class units>
+struct MarginTyped:
+ public BaseMargin<Float, MarginTyped<units> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseMargin<Float, MarginTyped<units> > Super;
- // Constructors
- Margin() : Super(0, 0, 0, 0) {}
- Margin(const Margin& aMargin) : Super(aMargin) {}
- Margin(Float aTop, Float aRight, Float aBottom, Float aLeft)
- : Super(aTop, aRight, aBottom, aLeft) {}
+ MarginTyped() : Super() {}
+ MarginTyped(Float aTop, Float aRight, Float aBottom, Float aLeft) :
+ Super(aTop, aRight, aBottom, aLeft) {}
+ explicit MarginTyped(const IntMarginTyped<units>& aMargin) :
+ Super(float(aMargin.top), float(aMargin.right),
+ float(aMargin.bottom), float(aMargin.left)) {}
};
+typedef MarginTyped<UnknownUnits> Margin;
+
+template<class units>
+IntMarginTyped<units> RoundedToInt(const MarginTyped<units>& aMargin)
+{
+ return IntMarginTyped<units>(int32_t(floorf(aMargin.top + 0.5f)),
+ int32_t(floorf(aMargin.right + 0.5f)),
+ int32_t(floorf(aMargin.bottom + 0.5f)),
+ int32_t(floorf(aMargin.left + 0.5f)));
+}
template<class units>
struct IntRectTyped :
- public BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>, IntSizeTyped<units>, Margin>,
+ public BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>, IntSizeTyped<units>, IntMarginTyped<units> >,
public units {
- typedef BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>, IntSizeTyped<units>, Margin> Super;
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>, IntSizeTyped<units>, IntMarginTyped<units> > Super;
IntRectTyped() : Super() {}
- IntRectTyped(IntPointTyped<units> aPos, IntSizeTyped<units> aSize) :
+ IntRectTyped(const IntPointTyped<units>& aPos, const IntSizeTyped<units>& aSize) :
Super(aPos, aSize) {}
IntRectTyped(int32_t _x, int32_t _y, int32_t _width, int32_t _height) :
Super(_x, _y, _width, _height) {}
@@ -52,17 +91,28 @@ struct IntRectTyped :
IntRectTyped<UnknownUnits> ToUnknownRect() const {
return IntRectTyped<UnknownUnits>(this->x, this->y, this->width, this->height);
}
+
+ bool Overflows() const {
+ CheckedInt<int32_t> xMost = this->x;
+ xMost += this->width;
+ CheckedInt<int32_t> yMost = this->y;
+ yMost += this->height;
+ return !xMost.isValid() || !yMost.isValid();
+ }
};
typedef IntRectTyped<UnknownUnits> IntRect;
template<class units>
struct RectTyped :
- public BaseRect<Float, RectTyped<units>, PointTyped<units>, SizeTyped<units>, Margin>,
+ public BaseRect<Float, RectTyped<units>, PointTyped<units>, SizeTyped<units>, MarginTyped<units> >,
public units {
- typedef BaseRect<Float, RectTyped<units>, PointTyped<units>, SizeTyped<units>, Margin> Super;
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseRect<Float, RectTyped<units>, PointTyped<units>, SizeTyped<units>, MarginTyped<units> > Super;
RectTyped() : Super() {}
- RectTyped(PointTyped<units> aPos, SizeTyped<units> aSize) :
+ RectTyped(const PointTyped<units>& aPos, const SizeTyped<units>& aSize) :
Super(aPos, aSize) {}
RectTyped(Float _x, Float _y, Float _width, Float _height) :
Super(_x, _y, _width, _height) {}
@@ -70,7 +120,7 @@ struct RectTyped :
Super(float(rect.x), float(rect.y),
float(rect.width), float(rect.height)) {}
- GFX2D_API void NudgeToIntegers()
+ void NudgeToIntegers()
{
NudgeToInteger(&(this->x));
NudgeToInteger(&(this->y));
@@ -97,16 +147,22 @@ struct RectTyped :
RectTyped<UnknownUnits> ToUnknownRect() const {
return RectTyped<UnknownUnits>(this->x, this->y, this->width, this->height);
}
+
+ // This is here only to keep IPDL-generated code happy. DO NOT USE.
+ bool operator==(const RectTyped<units>& aRect) const
+ {
+ return RectTyped<units>::IsEqualEdges(aRect);
+ }
};
typedef RectTyped<UnknownUnits> Rect;
template<class units>
IntRectTyped<units> RoundedToInt(const RectTyped<units>& aRect)
{
- return IntRectTyped<units>(NS_lround(aRect.x),
- NS_lround(aRect.y),
- NS_lround(aRect.width),
- NS_lround(aRect.height));
+ return IntRectTyped<units>(int32_t(floorf(aRect.x + 0.5f)),
+ int32_t(floorf(aRect.y + 0.5f)),
+ int32_t(floorf(aRect.width + 0.5f)),
+ int32_t(floorf(aRect.height + 0.5f)));
}
template<class units>
@@ -120,6 +176,17 @@ IntRectTyped<units> RoundedIn(const RectTyped<units>& aRect)
int32_t(copy.height));
}
+template<class units>
+IntRectTyped<units> RoundedOut(const RectTyped<units>& aRect)
+{
+ RectTyped<units> copy(aRect);
+ copy.RoundOut();
+ return IntRectTyped<units>(int32_t(copy.x),
+ int32_t(copy.y),
+ int32_t(copy.width),
+ int32_t(copy.height));
+}
+
}
}
diff --git a/gfx/2d/SIMD.h b/gfx/2d/SIMD.h
new file mode 100644
index 000000000..6bf53a38e
--- /dev/null
+++ b/gfx/2d/SIMD.h
@@ -0,0 +1,1180 @@
+/* -*- Mode: C++; tab-width: 20; 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 _MOZILLA_GFX_SIMD_H_
+#define _MOZILLA_GFX_SIMD_H_
+
+/**
+ * Consumers of this file need to #define SIMD_COMPILE_SSE2 before including it
+ * if they want access to the SSE2 functions.
+ */
+
+#ifdef SIMD_COMPILE_SSE2
+#include <xmmintrin.h>
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+namespace simd {
+
+template<typename u8x16_t>
+u8x16_t Load8(const uint8_t* aSource);
+
+template<typename u8x16_t>
+u8x16_t From8(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h,
+ uint8_t i, uint8_t j, uint8_t k, uint8_t l, uint8_t m, uint8_t n, uint8_t o, uint8_t p);
+
+template<typename u8x16_t>
+u8x16_t FromZero8();
+
+template<typename i16x8_t>
+i16x8_t FromI16(int16_t a, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g, int16_t h);
+
+template<typename u16x8_t>
+u16x8_t FromU16(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h);
+
+template<typename i16x8_t>
+i16x8_t FromI16(int16_t a);
+
+template<typename u16x8_t>
+u16x8_t FromU16(uint16_t a);
+
+template<typename i32x4_t>
+i32x4_t From32(int32_t a, int32_t b, int32_t c, int32_t d);
+
+template<typename i32x4_t>
+i32x4_t From32(int32_t a);
+
+template<typename f32x4_t>
+f32x4_t FromF32(float a, float b, float c, float d);
+
+template<typename f32x4_t>
+f32x4_t FromF32(float a);
+
+// All SIMD backends overload these functions for their SIMD types:
+
+#if 0
+
+// Store 16 bytes to a 16-byte aligned address
+void Store8(uint8_t* aTarget, u8x16_t aM);
+
+// Fixed shifts
+template<int32_t aNumberOfBits> i16x8_t ShiftRight16(i16x8_t aM);
+template<int32_t aNumberOfBits> i32x4_t ShiftRight32(i32x4_t aM);
+
+i16x8_t Add16(i16x8_t aM1, i16x8_t aM2);
+i32x4_t Add32(i32x4_t aM1, i32x4_t aM2);
+i16x8_t Sub16(i16x8_t aM1, i16x8_t aM2);
+i32x4_t Sub32(i32x4_t aM1, i32x4_t aM2);
+u8x16_t Min8(u8x16_t aM1, iu8x16_t aM2);
+u8x16_t Max8(u8x16_t aM1, iu8x16_t aM2);
+i32x4_t Min32(i32x4_t aM1, i32x4_t aM2);
+i32x4_t Max32(i32x4_t aM1, i32x4_t aM2);
+
+// Truncating i16 -> i16 multiplication
+i16x8_t Mul16(i16x8_t aM1, i16x8_t aM2);
+
+// Long multiplication i16 -> i32
+// aFactorsA1B1 = (a1[4] b1[4])
+// aFactorsA2B2 = (a2[4] b2[4])
+// aProductA = a1 * a2, aProductB = b1 * b2
+void Mul16x4x2x2To32x4x2(i16x8_t aFactorsA1B1, i16x8_t aFactorsA2B2,
+ i32x4_t& aProductA, i32x4_t& aProductB);
+
+// Long multiplication + pairwise addition i16 -> i32
+// See the scalar implementation for specifics.
+i32x4_t MulAdd16x8x2To32x4(i16x8_t aFactorsA, i16x8_t aFactorsB);
+i32x4_t MulAdd16x8x2To32x4(u16x8_t aFactorsA, u16x8_t aFactorsB);
+
+// Set all four 32-bit components to the value of the component at aIndex.
+template<int8_t aIndex>
+i32x4_t Splat32(i32x4_t aM);
+
+// Interpret the input as four 32-bit values, apply Splat32<aIndex> on them,
+// re-interpret the result as sixteen 8-bit values.
+template<int8_t aIndex>
+u8x16_t Splat32On8(u8x16_t aM);
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> i32x4 Shuffle32(i32x4 aM);
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> i16x8 ShuffleLo16(i16x8 aM);
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> i16x8 ShuffleHi16(i16x8 aM);
+
+u8x16_t InterleaveLo8(u8x16_t m1, u8x16_t m2);
+u8x16_t InterleaveHi8(u8x16_t m1, u8x16_t m2);
+i16x8_t InterleaveLo16(i16x8_t m1, i16x8_t m2);
+i16x8_t InterleaveHi16(i16x8_t m1, i16x8_t m2);
+i32x4_t InterleaveLo32(i32x4_t m1, i32x4_t m2);
+
+i16x8_t UnpackLo8x8ToI16x8(u8x16_t m);
+i16x8_t UnpackHi8x8ToI16x8(u8x16_t m);
+u16x8_t UnpackLo8x8ToU16x8(u8x16_t m);
+u16x8_t UnpackHi8x8ToU16x8(u8x16_t m);
+
+i16x8_t PackAndSaturate32To16(i32x4_t m1, i32x4_t m2);
+u8x16_t PackAndSaturate16To8(i16x8_t m1, i16x8_t m2);
+u8x16_t PackAndSaturate32To8(i32x4_t m1, i32x4_t m2, i32x4_t m3, const i32x4_t& m4);
+
+i32x4 FastDivideBy255(i32x4 m);
+i16x8 FastDivideBy255_16(i16x8 m);
+
+#endif
+
+// Scalar
+
+struct Scalaru8x16_t {
+ uint8_t u8[16];
+};
+
+union Scalari16x8_t {
+ int16_t i16[8];
+ uint16_t u16[8];
+};
+
+typedef Scalari16x8_t Scalaru16x8_t;
+
+struct Scalari32x4_t {
+ int32_t i32[4];
+};
+
+struct Scalarf32x4_t {
+ float f32[4];
+};
+
+template<>
+inline Scalaru8x16_t
+Load8<Scalaru8x16_t>(const uint8_t* aSource)
+{
+ return *(Scalaru8x16_t*)aSource;
+}
+
+inline void Store8(uint8_t* aTarget, Scalaru8x16_t aM)
+{
+ *(Scalaru8x16_t*)aTarget = aM;
+}
+
+template<>
+inline Scalaru8x16_t From8<Scalaru8x16_t>(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h,
+ uint8_t i, uint8_t j, uint8_t k, uint8_t l, uint8_t m, uint8_t n, uint8_t o, uint8_t p)
+{
+ Scalaru8x16_t _m;
+ _m.u8[0] = a;
+ _m.u8[1] = b;
+ _m.u8[2] = c;
+ _m.u8[3] = d;
+ _m.u8[4] = e;
+ _m.u8[5] = f;
+ _m.u8[6] = g;
+ _m.u8[7] = h;
+ _m.u8[8+0] = i;
+ _m.u8[8+1] = j;
+ _m.u8[8+2] = k;
+ _m.u8[8+3] = l;
+ _m.u8[8+4] = m;
+ _m.u8[8+5] = n;
+ _m.u8[8+6] = o;
+ _m.u8[8+7] = p;
+ return _m;
+}
+
+template<>
+inline Scalaru8x16_t FromZero8<Scalaru8x16_t>()
+{
+ return From8<Scalaru8x16_t>(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
+}
+
+template<>
+inline Scalari16x8_t FromI16<Scalari16x8_t>(int16_t a, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g, int16_t h)
+{
+ Scalari16x8_t m;
+ m.i16[0] = a;
+ m.i16[1] = b;
+ m.i16[2] = c;
+ m.i16[3] = d;
+ m.i16[4] = e;
+ m.i16[5] = f;
+ m.i16[6] = g;
+ m.i16[7] = h;
+ return m;
+}
+
+template<>
+inline Scalaru16x8_t FromU16<Scalaru16x8_t>(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h)
+{
+ Scalaru16x8_t m;
+ m.u16[0] = a;
+ m.u16[1] = b;
+ m.u16[2] = c;
+ m.u16[3] = d;
+ m.u16[4] = e;
+ m.u16[5] = f;
+ m.u16[6] = g;
+ m.u16[7] = h;
+ return m;
+}
+
+template<>
+inline Scalari16x8_t FromI16<Scalari16x8_t>(int16_t a)
+{
+ return FromI16<Scalari16x8_t>(a, a, a, a, a, a, a, a);
+}
+
+template<>
+inline Scalaru16x8_t FromU16<Scalaru16x8_t>(uint16_t a)
+{
+ return FromU16<Scalaru16x8_t>(a, a, a, a, a, a, a, a);
+}
+
+template<>
+inline Scalari32x4_t From32<Scalari32x4_t>(int32_t a, int32_t b, int32_t c, int32_t d)
+{
+ Scalari32x4_t m;
+ m.i32[0] = a;
+ m.i32[1] = b;
+ m.i32[2] = c;
+ m.i32[3] = d;
+ return m;
+}
+
+template<>
+inline Scalarf32x4_t FromF32<Scalarf32x4_t>(float a, float b, float c, float d)
+{
+ Scalarf32x4_t m;
+ m.f32[0] = a;
+ m.f32[1] = b;
+ m.f32[2] = c;
+ m.f32[3] = d;
+ return m;
+}
+
+template<>
+inline Scalarf32x4_t FromF32<Scalarf32x4_t>(float a)
+{
+ return FromF32<Scalarf32x4_t>(a, a, a, a);
+}
+
+template<>
+inline Scalari32x4_t From32<Scalari32x4_t>(int32_t a)
+{
+ return From32<Scalari32x4_t>(a, a, a, a);
+}
+
+template<int32_t aNumberOfBits>
+inline Scalari16x8_t ShiftRight16(Scalari16x8_t aM)
+{
+ return FromI16<Scalari16x8_t>(uint16_t(aM.i16[0]) >> aNumberOfBits, uint16_t(aM.i16[1]) >> aNumberOfBits,
+ uint16_t(aM.i16[2]) >> aNumberOfBits, uint16_t(aM.i16[3]) >> aNumberOfBits,
+ uint16_t(aM.i16[4]) >> aNumberOfBits, uint16_t(aM.i16[5]) >> aNumberOfBits,
+ uint16_t(aM.i16[6]) >> aNumberOfBits, uint16_t(aM.i16[7]) >> aNumberOfBits);
+}
+
+template<int32_t aNumberOfBits>
+inline Scalari32x4_t ShiftRight32(Scalari32x4_t aM)
+{
+ return From32<Scalari32x4_t>(aM.i32[0] >> aNumberOfBits, aM.i32[1] >> aNumberOfBits,
+ aM.i32[2] >> aNumberOfBits, aM.i32[3] >> aNumberOfBits);
+}
+
+inline Scalaru16x8_t Add16(Scalaru16x8_t aM1, Scalaru16x8_t aM2)
+{
+ return FromU16<Scalaru16x8_t>(aM1.u16[0] + aM2.u16[0], aM1.u16[1] + aM2.u16[1],
+ aM1.u16[2] + aM2.u16[2], aM1.u16[3] + aM2.u16[3],
+ aM1.u16[4] + aM2.u16[4], aM1.u16[5] + aM2.u16[5],
+ aM1.u16[6] + aM2.u16[6], aM1.u16[7] + aM2.u16[7]);
+}
+
+inline Scalari32x4_t Add32(Scalari32x4_t aM1, Scalari32x4_t aM2)
+{
+ return From32<Scalari32x4_t>(aM1.i32[0] + aM2.i32[0], aM1.i32[1] + aM2.i32[1],
+ aM1.i32[2] + aM2.i32[2], aM1.i32[3] + aM2.i32[3]);
+}
+
+inline Scalaru16x8_t Sub16(Scalaru16x8_t aM1, Scalaru16x8_t aM2)
+{
+ return FromU16<Scalaru16x8_t>(aM1.u16[0] - aM2.u16[0], aM1.u16[1] - aM2.u16[1],
+ aM1.u16[2] - aM2.u16[2], aM1.u16[3] - aM2.u16[3],
+ aM1.u16[4] - aM2.u16[4], aM1.u16[5] - aM2.u16[5],
+ aM1.u16[6] - aM2.u16[6], aM1.u16[7] - aM2.u16[7]);
+}
+
+inline Scalari32x4_t Sub32(Scalari32x4_t aM1, Scalari32x4_t aM2)
+{
+ return From32<Scalari32x4_t>(aM1.i32[0] - aM2.i32[0], aM1.i32[1] - aM2.i32[1],
+ aM1.i32[2] - aM2.i32[2], aM1.i32[3] - aM2.i32[3]);
+}
+
+inline int32_t
+umin(int32_t a, int32_t b)
+{
+ return a - ((a - b) & -(a > b));
+}
+
+inline int32_t
+umax(int32_t a, int32_t b)
+{
+ return a - ((a - b) & -(a < b));
+}
+
+inline Scalaru8x16_t Min8(Scalaru8x16_t aM1, Scalaru8x16_t aM2)
+{
+ return From8<Scalaru8x16_t>(umin(aM1.u8[0], aM2.u8[0]), umin(aM1.u8[1], aM2.u8[1]),
+ umin(aM1.u8[2], aM2.u8[2]), umin(aM1.u8[3], aM2.u8[3]),
+ umin(aM1.u8[4], aM2.u8[4]), umin(aM1.u8[5], aM2.u8[5]),
+ umin(aM1.u8[6], aM2.u8[6]), umin(aM1.u8[7], aM2.u8[7]),
+ umin(aM1.u8[8+0], aM2.u8[8+0]), umin(aM1.u8[8+1], aM2.u8[8+1]),
+ umin(aM1.u8[8+2], aM2.u8[8+2]), umin(aM1.u8[8+3], aM2.u8[8+3]),
+ umin(aM1.u8[8+4], aM2.u8[8+4]), umin(aM1.u8[8+5], aM2.u8[8+5]),
+ umin(aM1.u8[8+6], aM2.u8[8+6]), umin(aM1.u8[8+7], aM2.u8[8+7]));
+}
+
+inline Scalaru8x16_t Max8(Scalaru8x16_t aM1, Scalaru8x16_t aM2)
+{
+ return From8<Scalaru8x16_t>(umax(aM1.u8[0], aM2.u8[0]), umax(aM1.u8[1], aM2.u8[1]),
+ umax(aM1.u8[2], aM2.u8[2]), umax(aM1.u8[3], aM2.u8[3]),
+ umax(aM1.u8[4], aM2.u8[4]), umax(aM1.u8[5], aM2.u8[5]),
+ umax(aM1.u8[6], aM2.u8[6]), umax(aM1.u8[7], aM2.u8[7]),
+ umax(aM1.u8[8+0], aM2.u8[8+0]), umax(aM1.u8[8+1], aM2.u8[8+1]),
+ umax(aM1.u8[8+2], aM2.u8[8+2]), umax(aM1.u8[8+3], aM2.u8[8+3]),
+ umax(aM1.u8[8+4], aM2.u8[8+4]), umax(aM1.u8[8+5], aM2.u8[8+5]),
+ umax(aM1.u8[8+6], aM2.u8[8+6]), umax(aM1.u8[8+7], aM2.u8[8+7]));
+}
+
+inline Scalari32x4_t Min32(Scalari32x4_t aM1, Scalari32x4_t aM2)
+{
+ return From32<Scalari32x4_t>(umin(aM1.i32[0], aM2.i32[0]), umin(aM1.i32[1], aM2.i32[1]),
+ umin(aM1.i32[2], aM2.i32[2]), umin(aM1.i32[3], aM2.i32[3]));
+}
+
+inline Scalari32x4_t Max32(Scalari32x4_t aM1, Scalari32x4_t aM2)
+{
+ return From32<Scalari32x4_t>(umax(aM1.i32[0], aM2.i32[0]), umax(aM1.i32[1], aM2.i32[1]),
+ umax(aM1.i32[2], aM2.i32[2]), umax(aM1.i32[3], aM2.i32[3]));
+}
+
+inline Scalaru16x8_t Mul16(Scalaru16x8_t aM1, Scalaru16x8_t aM2)
+{
+ return FromU16<Scalaru16x8_t>(uint16_t(int32_t(aM1.u16[0]) * int32_t(aM2.u16[0])), uint16_t(int32_t(aM1.u16[1]) * int32_t(aM2.u16[1])),
+ uint16_t(int32_t(aM1.u16[2]) * int32_t(aM2.u16[2])), uint16_t(int32_t(aM1.u16[3]) * int32_t(aM2.u16[3])),
+ uint16_t(int32_t(aM1.u16[4]) * int32_t(aM2.u16[4])), uint16_t(int32_t(aM1.u16[5]) * int32_t(aM2.u16[5])),
+ uint16_t(int32_t(aM1.u16[6]) * int32_t(aM2.u16[6])), uint16_t(int32_t(aM1.u16[7]) * int32_t(aM2.u16[7])));
+}
+
+inline void Mul16x4x2x2To32x4x2(Scalari16x8_t aFactorsA1B1,
+ Scalari16x8_t aFactorsA2B2,
+ Scalari32x4_t& aProductA,
+ Scalari32x4_t& aProductB)
+{
+ aProductA = From32<Scalari32x4_t>(aFactorsA1B1.i16[0] * aFactorsA2B2.i16[0],
+ aFactorsA1B1.i16[1] * aFactorsA2B2.i16[1],
+ aFactorsA1B1.i16[2] * aFactorsA2B2.i16[2],
+ aFactorsA1B1.i16[3] * aFactorsA2B2.i16[3]);
+ aProductB = From32<Scalari32x4_t>(aFactorsA1B1.i16[4] * aFactorsA2B2.i16[4],
+ aFactorsA1B1.i16[5] * aFactorsA2B2.i16[5],
+ aFactorsA1B1.i16[6] * aFactorsA2B2.i16[6],
+ aFactorsA1B1.i16[7] * aFactorsA2B2.i16[7]);
+}
+
+inline Scalari32x4_t MulAdd16x8x2To32x4(Scalari16x8_t aFactorsA,
+ Scalari16x8_t aFactorsB)
+{
+ return From32<Scalari32x4_t>(aFactorsA.i16[0] * aFactorsB.i16[0] + aFactorsA.i16[1] * aFactorsB.i16[1],
+ aFactorsA.i16[2] * aFactorsB.i16[2] + aFactorsA.i16[3] * aFactorsB.i16[3],
+ aFactorsA.i16[4] * aFactorsB.i16[4] + aFactorsA.i16[5] * aFactorsB.i16[5],
+ aFactorsA.i16[6] * aFactorsB.i16[6] + aFactorsA.i16[7] * aFactorsB.i16[7]);
+}
+
+template<int8_t aIndex>
+inline void AssertIndex()
+{
+ static_assert(aIndex == 0 || aIndex == 1 || aIndex == 2 || aIndex == 3,
+ "Invalid splat index");
+}
+
+template<int8_t aIndex>
+inline Scalari32x4_t Splat32(Scalari32x4_t aM)
+{
+ AssertIndex<aIndex>();
+ return From32<Scalari32x4_t>(aM.i32[aIndex], aM.i32[aIndex],
+ aM.i32[aIndex], aM.i32[aIndex]);
+}
+
+template<int8_t i>
+inline Scalaru8x16_t Splat32On8(Scalaru8x16_t aM)
+{
+ AssertIndex<i>();
+ return From8<Scalaru8x16_t>(aM.u8[i*4], aM.u8[i*4+1], aM.u8[i*4+2], aM.u8[i*4+3],
+ aM.u8[i*4], aM.u8[i*4+1], aM.u8[i*4+2], aM.u8[i*4+3],
+ aM.u8[i*4], aM.u8[i*4+1], aM.u8[i*4+2], aM.u8[i*4+3],
+ aM.u8[i*4], aM.u8[i*4+1], aM.u8[i*4+2], aM.u8[i*4+3]);
+}
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3>
+inline Scalari32x4_t Shuffle32(Scalari32x4_t aM)
+{
+ AssertIndex<i0>();
+ AssertIndex<i1>();
+ AssertIndex<i2>();
+ AssertIndex<i3>();
+ Scalari32x4_t m = aM;
+ m.i32[0] = aM.i32[i3];
+ m.i32[1] = aM.i32[i2];
+ m.i32[2] = aM.i32[i1];
+ m.i32[3] = aM.i32[i0];
+ return m;
+}
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3>
+inline Scalari16x8_t ShuffleLo16(Scalari16x8_t aM)
+{
+ AssertIndex<i0>();
+ AssertIndex<i1>();
+ AssertIndex<i2>();
+ AssertIndex<i3>();
+ Scalari16x8_t m = aM;
+ m.i16[0] = aM.i16[i3];
+ m.i16[1] = aM.i16[i2];
+ m.i16[2] = aM.i16[i1];
+ m.i16[3] = aM.i16[i0];
+ return m;
+}
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3>
+inline Scalari16x8_t ShuffleHi16(Scalari16x8_t aM)
+{
+ AssertIndex<i0>();
+ AssertIndex<i1>();
+ AssertIndex<i2>();
+ AssertIndex<i3>();
+ Scalari16x8_t m = aM;
+ m.i16[4 + 0] = aM.i16[4 + i3];
+ m.i16[4 + 1] = aM.i16[4 + i2];
+ m.i16[4 + 2] = aM.i16[4 + i1];
+ m.i16[4 + 3] = aM.i16[4 + i0];
+ return m;
+}
+
+template<int8_t aIndexLo, int8_t aIndexHi>
+inline Scalaru16x8_t Splat16(Scalaru16x8_t aM)
+{
+ AssertIndex<aIndexLo>();
+ AssertIndex<aIndexHi>();
+ Scalaru16x8_t m;
+ int16_t chosenValueLo = aM.u16[aIndexLo];
+ m.u16[0] = chosenValueLo;
+ m.u16[1] = chosenValueLo;
+ m.u16[2] = chosenValueLo;
+ m.u16[3] = chosenValueLo;
+ int16_t chosenValueHi = aM.u16[4 + aIndexHi];
+ m.u16[4] = chosenValueHi;
+ m.u16[5] = chosenValueHi;
+ m.u16[6] = chosenValueHi;
+ m.u16[7] = chosenValueHi;
+ return m;
+}
+
+inline Scalaru8x16_t
+InterleaveLo8(Scalaru8x16_t m1, Scalaru8x16_t m2)
+{
+ return From8<Scalaru8x16_t>(m1.u8[0], m2.u8[0], m1.u8[1], m2.u8[1],
+ m1.u8[2], m2.u8[2], m1.u8[3], m2.u8[3],
+ m1.u8[4], m2.u8[4], m1.u8[5], m2.u8[5],
+ m1.u8[6], m2.u8[6], m1.u8[7], m2.u8[7]);
+}
+
+inline Scalaru8x16_t
+InterleaveHi8(Scalaru8x16_t m1, Scalaru8x16_t m2)
+{
+ return From8<Scalaru8x16_t>(m1.u8[8+0], m2.u8[8+0], m1.u8[8+1], m2.u8[8+1],
+ m1.u8[8+2], m2.u8[8+2], m1.u8[8+3], m2.u8[8+3],
+ m1.u8[8+4], m2.u8[8+4], m1.u8[8+5], m2.u8[8+5],
+ m1.u8[8+6], m2.u8[8+6], m1.u8[8+7], m2.u8[8+7]);
+}
+
+inline Scalaru16x8_t
+InterleaveLo16(Scalaru16x8_t m1, Scalaru16x8_t m2)
+{
+ return FromU16<Scalaru16x8_t>(m1.u16[0], m2.u16[0], m1.u16[1], m2.u16[1],
+ m1.u16[2], m2.u16[2], m1.u16[3], m2.u16[3]);
+}
+
+inline Scalaru16x8_t
+InterleaveHi16(Scalaru16x8_t m1, Scalaru16x8_t m2)
+{
+ return FromU16<Scalaru16x8_t>(m1.u16[4], m2.u16[4], m1.u16[5], m2.u16[5],
+ m1.u16[6], m2.u16[6], m1.u16[7], m2.u16[7]);
+}
+
+inline Scalari32x4_t
+InterleaveLo32(Scalari32x4_t m1, Scalari32x4_t m2)
+{
+ return From32<Scalari32x4_t>(m1.i32[0], m2.i32[0], m1.i32[1], m2.i32[1]);
+}
+
+inline Scalari16x8_t
+UnpackLo8x8ToI16x8(Scalaru8x16_t aM)
+{
+ Scalari16x8_t m;
+ m.i16[0] = aM.u8[0];
+ m.i16[1] = aM.u8[1];
+ m.i16[2] = aM.u8[2];
+ m.i16[3] = aM.u8[3];
+ m.i16[4] = aM.u8[4];
+ m.i16[5] = aM.u8[5];
+ m.i16[6] = aM.u8[6];
+ m.i16[7] = aM.u8[7];
+ return m;
+}
+
+inline Scalari16x8_t
+UnpackHi8x8ToI16x8(Scalaru8x16_t aM)
+{
+ Scalari16x8_t m;
+ m.i16[0] = aM.u8[8+0];
+ m.i16[1] = aM.u8[8+1];
+ m.i16[2] = aM.u8[8+2];
+ m.i16[3] = aM.u8[8+3];
+ m.i16[4] = aM.u8[8+4];
+ m.i16[5] = aM.u8[8+5];
+ m.i16[6] = aM.u8[8+6];
+ m.i16[7] = aM.u8[8+7];
+ return m;
+}
+
+inline Scalaru16x8_t
+UnpackLo8x8ToU16x8(Scalaru8x16_t aM)
+{
+ return FromU16<Scalaru16x8_t>(uint16_t(aM.u8[0]), uint16_t(aM.u8[1]), uint16_t(aM.u8[2]), uint16_t(aM.u8[3]),
+ uint16_t(aM.u8[4]), uint16_t(aM.u8[5]), uint16_t(aM.u8[6]), uint16_t(aM.u8[7]));
+}
+
+inline Scalaru16x8_t
+UnpackHi8x8ToU16x8(Scalaru8x16_t aM)
+{
+ return FromU16<Scalaru16x8_t>(aM.u8[8+0], aM.u8[8+1], aM.u8[8+2], aM.u8[8+3],
+ aM.u8[8+4], aM.u8[8+5], aM.u8[8+6], aM.u8[8+7]);
+}
+
+template<uint8_t aNumBytes>
+inline Scalaru8x16_t
+Rotate8(Scalaru8x16_t a1234, Scalaru8x16_t a5678)
+{
+ Scalaru8x16_t m;
+ for (uint8_t i = 0; i < 16; i++) {
+ uint8_t sourceByte = i + aNumBytes;
+ m.u8[i] = sourceByte < 16 ? a1234.u8[sourceByte] : a5678.u8[sourceByte - 16];
+ }
+ return m;
+}
+
+template<typename T>
+inline int16_t
+SaturateTo16(T a)
+{
+ return int16_t(a >= INT16_MIN ? (a <= INT16_MAX ? a : INT16_MAX) : INT16_MIN);
+}
+
+inline Scalari16x8_t
+PackAndSaturate32To16(Scalari32x4_t m1, Scalari32x4_t m2)
+{
+ Scalari16x8_t m;
+ m.i16[0] = SaturateTo16(m1.i32[0]);
+ m.i16[1] = SaturateTo16(m1.i32[1]);
+ m.i16[2] = SaturateTo16(m1.i32[2]);
+ m.i16[3] = SaturateTo16(m1.i32[3]);
+ m.i16[4] = SaturateTo16(m2.i32[0]);
+ m.i16[5] = SaturateTo16(m2.i32[1]);
+ m.i16[6] = SaturateTo16(m2.i32[2]);
+ m.i16[7] = SaturateTo16(m2.i32[3]);
+ return m;
+}
+
+template<typename T>
+inline uint16_t
+SaturateToU16(T a)
+{
+ return uint16_t(umin(a & -(a >= 0), INT16_MAX));
+}
+
+inline Scalaru16x8_t
+PackAndSaturate32ToU16(Scalari32x4_t m1, Scalari32x4_t m2)
+{
+ Scalaru16x8_t m;
+ m.u16[0] = SaturateToU16(m1.i32[0]);
+ m.u16[1] = SaturateToU16(m1.i32[1]);
+ m.u16[2] = SaturateToU16(m1.i32[2]);
+ m.u16[3] = SaturateToU16(m1.i32[3]);
+ m.u16[4] = SaturateToU16(m2.i32[0]);
+ m.u16[5] = SaturateToU16(m2.i32[1]);
+ m.u16[6] = SaturateToU16(m2.i32[2]);
+ m.u16[7] = SaturateToU16(m2.i32[3]);
+ return m;
+}
+
+template<typename T>
+inline uint8_t
+SaturateTo8(T a)
+{
+ return uint8_t(umin(a & -(a >= 0), 255));
+}
+
+inline Scalaru8x16_t
+PackAndSaturate32To8(Scalari32x4_t m1, Scalari32x4_t m2, Scalari32x4_t m3, const Scalari32x4_t& m4)
+{
+ Scalaru8x16_t m;
+ m.u8[0] = SaturateTo8(m1.i32[0]);
+ m.u8[1] = SaturateTo8(m1.i32[1]);
+ m.u8[2] = SaturateTo8(m1.i32[2]);
+ m.u8[3] = SaturateTo8(m1.i32[3]);
+ m.u8[4] = SaturateTo8(m2.i32[0]);
+ m.u8[5] = SaturateTo8(m2.i32[1]);
+ m.u8[6] = SaturateTo8(m2.i32[2]);
+ m.u8[7] = SaturateTo8(m2.i32[3]);
+ m.u8[8] = SaturateTo8(m3.i32[0]);
+ m.u8[9] = SaturateTo8(m3.i32[1]);
+ m.u8[10] = SaturateTo8(m3.i32[2]);
+ m.u8[11] = SaturateTo8(m3.i32[3]);
+ m.u8[12] = SaturateTo8(m4.i32[0]);
+ m.u8[13] = SaturateTo8(m4.i32[1]);
+ m.u8[14] = SaturateTo8(m4.i32[2]);
+ m.u8[15] = SaturateTo8(m4.i32[3]);
+ return m;
+}
+
+inline Scalaru8x16_t
+PackAndSaturate16To8(Scalari16x8_t m1, Scalari16x8_t m2)
+{
+ Scalaru8x16_t m;
+ m.u8[0] = SaturateTo8(m1.i16[0]);
+ m.u8[1] = SaturateTo8(m1.i16[1]);
+ m.u8[2] = SaturateTo8(m1.i16[2]);
+ m.u8[3] = SaturateTo8(m1.i16[3]);
+ m.u8[4] = SaturateTo8(m1.i16[4]);
+ m.u8[5] = SaturateTo8(m1.i16[5]);
+ m.u8[6] = SaturateTo8(m1.i16[6]);
+ m.u8[7] = SaturateTo8(m1.i16[7]);
+ m.u8[8] = SaturateTo8(m2.i16[0]);
+ m.u8[9] = SaturateTo8(m2.i16[1]);
+ m.u8[10] = SaturateTo8(m2.i16[2]);
+ m.u8[11] = SaturateTo8(m2.i16[3]);
+ m.u8[12] = SaturateTo8(m2.i16[4]);
+ m.u8[13] = SaturateTo8(m2.i16[5]);
+ m.u8[14] = SaturateTo8(m2.i16[6]);
+ m.u8[15] = SaturateTo8(m2.i16[7]);
+ return m;
+}
+
+// Fast approximate division by 255. It has the property that
+// for all 0 <= n <= 255*255, FAST_DIVIDE_BY_255(n) == n/255.
+// But it only uses two adds and two shifts instead of an
+// integer division (which is expensive on many processors).
+//
+// equivalent to v/255
+template<class B, class A>
+inline B FastDivideBy255(A v)
+{
+ return ((v << 8) + v + 255) >> 16;
+}
+
+inline Scalaru16x8_t
+FastDivideBy255_16(Scalaru16x8_t m)
+{
+ return FromU16<Scalaru16x8_t>(FastDivideBy255<uint16_t>(int32_t(m.u16[0])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[1])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[2])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[3])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[4])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[5])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[6])),
+ FastDivideBy255<uint16_t>(int32_t(m.u16[7])));
+}
+
+inline Scalari32x4_t
+FastDivideBy255(Scalari32x4_t m)
+{
+ return From32<Scalari32x4_t>(FastDivideBy255<int32_t>(m.i32[0]),
+ FastDivideBy255<int32_t>(m.i32[1]),
+ FastDivideBy255<int32_t>(m.i32[2]),
+ FastDivideBy255<int32_t>(m.i32[3]));
+}
+
+inline Scalaru8x16_t
+Pick(Scalaru8x16_t mask, Scalaru8x16_t a, Scalaru8x16_t b)
+{
+ return From8<Scalaru8x16_t>((a.u8[0] & (~mask.u8[0])) | (b.u8[0] & mask.u8[0]),
+ (a.u8[1] & (~mask.u8[1])) | (b.u8[1] & mask.u8[1]),
+ (a.u8[2] & (~mask.u8[2])) | (b.u8[2] & mask.u8[2]),
+ (a.u8[3] & (~mask.u8[3])) | (b.u8[3] & mask.u8[3]),
+ (a.u8[4] & (~mask.u8[4])) | (b.u8[4] & mask.u8[4]),
+ (a.u8[5] & (~mask.u8[5])) | (b.u8[5] & mask.u8[5]),
+ (a.u8[6] & (~mask.u8[6])) | (b.u8[6] & mask.u8[6]),
+ (a.u8[7] & (~mask.u8[7])) | (b.u8[7] & mask.u8[7]),
+ (a.u8[8+0] & (~mask.u8[8+0])) | (b.u8[8+0] & mask.u8[8+0]),
+ (a.u8[8+1] & (~mask.u8[8+1])) | (b.u8[8+1] & mask.u8[8+1]),
+ (a.u8[8+2] & (~mask.u8[8+2])) | (b.u8[8+2] & mask.u8[8+2]),
+ (a.u8[8+3] & (~mask.u8[8+3])) | (b.u8[8+3] & mask.u8[8+3]),
+ (a.u8[8+4] & (~mask.u8[8+4])) | (b.u8[8+4] & mask.u8[8+4]),
+ (a.u8[8+5] & (~mask.u8[8+5])) | (b.u8[8+5] & mask.u8[8+5]),
+ (a.u8[8+6] & (~mask.u8[8+6])) | (b.u8[8+6] & mask.u8[8+6]),
+ (a.u8[8+7] & (~mask.u8[8+7])) | (b.u8[8+7] & mask.u8[8+7]));
+}
+
+inline Scalari32x4_t
+Pick(Scalari32x4_t mask, Scalari32x4_t a, Scalari32x4_t b)
+{
+ return From32<Scalari32x4_t>((a.i32[0] & (~mask.i32[0])) | (b.i32[0] & mask.i32[0]),
+ (a.i32[1] & (~mask.i32[1])) | (b.i32[1] & mask.i32[1]),
+ (a.i32[2] & (~mask.i32[2])) | (b.i32[2] & mask.i32[2]),
+ (a.i32[3] & (~mask.i32[3])) | (b.i32[3] & mask.i32[3]));
+}
+
+inline Scalarf32x4_t MixF32(Scalarf32x4_t a, Scalarf32x4_t b, float t)
+{
+ return FromF32<Scalarf32x4_t>(a.f32[0] + (b.f32[0] - a.f32[0]) * t,
+ a.f32[1] + (b.f32[1] - a.f32[1]) * t,
+ a.f32[2] + (b.f32[2] - a.f32[2]) * t,
+ a.f32[3] + (b.f32[3] - a.f32[3]) * t);
+}
+
+inline Scalarf32x4_t WSumF32(Scalarf32x4_t a, Scalarf32x4_t b, float wa, float wb)
+{
+ return FromF32<Scalarf32x4_t>(a.f32[0] * wa + b.f32[0] * wb,
+ a.f32[1] * wa + b.f32[1] * wb,
+ a.f32[2] * wa + b.f32[2] * wb,
+ a.f32[3] * wa + b.f32[3] * wb);
+}
+
+inline Scalarf32x4_t AbsF32(Scalarf32x4_t a)
+{
+ return FromF32<Scalarf32x4_t>(fabs(a.f32[0]),
+ fabs(a.f32[1]),
+ fabs(a.f32[2]),
+ fabs(a.f32[3]));
+}
+
+inline Scalarf32x4_t AddF32(Scalarf32x4_t a, Scalarf32x4_t b)
+{
+ return FromF32<Scalarf32x4_t>(a.f32[0] + b.f32[0],
+ a.f32[1] + b.f32[1],
+ a.f32[2] + b.f32[2],
+ a.f32[3] + b.f32[3]);
+}
+
+inline Scalarf32x4_t MulF32(Scalarf32x4_t a, Scalarf32x4_t b)
+{
+ return FromF32<Scalarf32x4_t>(a.f32[0] * b.f32[0],
+ a.f32[1] * b.f32[1],
+ a.f32[2] * b.f32[2],
+ a.f32[3] * b.f32[3]);
+}
+
+inline Scalarf32x4_t DivF32(Scalarf32x4_t a, Scalarf32x4_t b)
+{
+ return FromF32<Scalarf32x4_t>(a.f32[0] / b.f32[0],
+ a.f32[1] / b.f32[1],
+ a.f32[2] / b.f32[2],
+ a.f32[3] / b.f32[3]);
+}
+
+template<uint8_t aIndex>
+inline Scalarf32x4_t SplatF32(Scalarf32x4_t m)
+{
+ AssertIndex<aIndex>();
+ return FromF32<Scalarf32x4_t>(m.f32[aIndex],
+ m.f32[aIndex],
+ m.f32[aIndex],
+ m.f32[aIndex]);
+}
+
+inline Scalari32x4_t F32ToI32(Scalarf32x4_t m)
+{
+ return From32<Scalari32x4_t>(int32_t(floor(m.f32[0] + 0.5f)),
+ int32_t(floor(m.f32[1] + 0.5f)),
+ int32_t(floor(m.f32[2] + 0.5f)),
+ int32_t(floor(m.f32[3] + 0.5f)));
+}
+
+#ifdef SIMD_COMPILE_SSE2
+
+// SSE2
+
+template<>
+inline __m128i
+Load8<__m128i>(const uint8_t* aSource)
+{
+ return _mm_load_si128((const __m128i*)aSource);
+}
+
+inline void Store8(uint8_t* aTarget, __m128i aM)
+{
+ _mm_store_si128((__m128i*)aTarget, aM);
+}
+
+template<>
+inline __m128i FromZero8<__m128i>()
+{
+ return _mm_setzero_si128();
+}
+
+template<>
+inline __m128i From8<__m128i>(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h,
+ uint8_t i, uint8_t j, uint8_t k, uint8_t l, uint8_t m, uint8_t n, uint8_t o, uint8_t p)
+{
+ return _mm_setr_epi16((b << 8) + a, (d << 8) + c, (e << 8) + f, (h << 8) + g,
+ (j << 8) + i, (l << 8) + k, (m << 8) + n, (p << 8) + o);
+}
+
+template<>
+inline __m128i FromI16<__m128i>(int16_t a, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g, int16_t h)
+{
+ return _mm_setr_epi16(a, b, c, d, e, f, g, h);
+}
+
+template<>
+inline __m128i FromU16<__m128i>(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h)
+{
+ return _mm_setr_epi16(a, b, c, d, e, f, g, h);
+}
+
+template<>
+inline __m128i FromI16<__m128i>(int16_t a)
+{
+ return _mm_set1_epi16(a);
+}
+
+template<>
+inline __m128i FromU16<__m128i>(uint16_t a)
+{
+ return _mm_set1_epi16((int16_t)a);
+}
+
+template<>
+inline __m128i From32<__m128i>(int32_t a, int32_t b, int32_t c, int32_t d)
+{
+ return _mm_setr_epi32(a, b, c, d);
+}
+
+template<>
+inline __m128i From32<__m128i>(int32_t a)
+{
+ return _mm_set1_epi32(a);
+}
+
+template<>
+inline __m128 FromF32<__m128>(float a, float b, float c, float d)
+{
+ return _mm_setr_ps(a, b, c, d);
+}
+
+template<>
+inline __m128 FromF32<__m128>(float a)
+{
+ return _mm_set1_ps(a);
+}
+
+template<int32_t aNumberOfBits>
+inline __m128i ShiftRight16(__m128i aM)
+{
+ return _mm_srli_epi16(aM, aNumberOfBits);
+}
+
+template<int32_t aNumberOfBits>
+inline __m128i ShiftRight32(__m128i aM)
+{
+ return _mm_srai_epi32(aM, aNumberOfBits);
+}
+
+inline __m128i Add16(__m128i aM1, __m128i aM2)
+{
+ return _mm_add_epi16(aM1, aM2);
+}
+
+inline __m128i Add32(__m128i aM1, __m128i aM2)
+{
+ return _mm_add_epi32(aM1, aM2);
+}
+
+inline __m128i Sub16(__m128i aM1, __m128i aM2)
+{
+ return _mm_sub_epi16(aM1, aM2);
+}
+
+inline __m128i Sub32(__m128i aM1, __m128i aM2)
+{
+ return _mm_sub_epi32(aM1, aM2);
+}
+
+inline __m128i Min8(__m128i aM1, __m128i aM2)
+{
+ return _mm_min_epu8(aM1, aM2);
+}
+
+inline __m128i Max8(__m128i aM1, __m128i aM2)
+{
+ return _mm_max_epu8(aM1, aM2);
+}
+
+inline __m128i Min32(__m128i aM1, __m128i aM2)
+{
+ __m128i m1_minus_m2 = _mm_sub_epi32(aM1, aM2);
+ __m128i m1_greater_than_m2 = _mm_cmpgt_epi32(aM1, aM2);
+ return _mm_sub_epi32(aM1, _mm_and_si128(m1_minus_m2, m1_greater_than_m2));
+}
+
+inline __m128i Max32(__m128i aM1, __m128i aM2)
+{
+ __m128i m1_minus_m2 = _mm_sub_epi32(aM1, aM2);
+ __m128i m2_greater_than_m1 = _mm_cmpgt_epi32(aM2, aM1);
+ return _mm_sub_epi32(aM1, _mm_and_si128(m1_minus_m2, m2_greater_than_m1));
+}
+
+inline __m128i Mul16(__m128i aM1, __m128i aM2)
+{
+ return _mm_mullo_epi16(aM1, aM2);
+}
+
+inline __m128i MulU16(__m128i aM1, __m128i aM2)
+{
+ return _mm_mullo_epi16(aM1, aM2);
+}
+
+inline void Mul16x4x2x2To32x4x2(__m128i aFactorsA1B1,
+ __m128i aFactorsA2B2,
+ __m128i& aProductA,
+ __m128i& aProductB)
+{
+ __m128i prodAB_lo = _mm_mullo_epi16(aFactorsA1B1, aFactorsA2B2);
+ __m128i prodAB_hi = _mm_mulhi_epi16(aFactorsA1B1, aFactorsA2B2);
+ aProductA = _mm_unpacklo_epi16(prodAB_lo, prodAB_hi);
+ aProductB = _mm_unpackhi_epi16(prodAB_lo, prodAB_hi);
+}
+
+inline __m128i MulAdd16x8x2To32x4(__m128i aFactorsA,
+ __m128i aFactorsB)
+{
+ return _mm_madd_epi16(aFactorsA, aFactorsB);
+}
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3>
+inline __m128i Shuffle32(__m128i aM)
+{
+ AssertIndex<i0>();
+ AssertIndex<i1>();
+ AssertIndex<i2>();
+ AssertIndex<i3>();
+ return _mm_shuffle_epi32(aM, _MM_SHUFFLE(i0, i1, i2, i3));
+}
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3>
+inline __m128i ShuffleLo16(__m128i aM)
+{
+ AssertIndex<i0>();
+ AssertIndex<i1>();
+ AssertIndex<i2>();
+ AssertIndex<i3>();
+ return _mm_shufflelo_epi16(aM, _MM_SHUFFLE(i0, i1, i2, i3));
+}
+
+template<int8_t i0, int8_t i1, int8_t i2, int8_t i3>
+inline __m128i ShuffleHi16(__m128i aM)
+{
+ AssertIndex<i0>();
+ AssertIndex<i1>();
+ AssertIndex<i2>();
+ AssertIndex<i3>();
+ return _mm_shufflehi_epi16(aM, _MM_SHUFFLE(i0, i1, i2, i3));
+}
+
+template<int8_t aIndex>
+inline __m128i Splat32(__m128i aM)
+{
+ return Shuffle32<aIndex,aIndex,aIndex,aIndex>(aM);
+}
+
+template<int8_t aIndex>
+inline __m128i Splat32On8(__m128i aM)
+{
+ return Shuffle32<aIndex,aIndex,aIndex,aIndex>(aM);
+}
+
+template<int8_t aIndexLo, int8_t aIndexHi>
+inline __m128i Splat16(__m128i aM)
+{
+ AssertIndex<aIndexLo>();
+ AssertIndex<aIndexHi>();
+ return ShuffleHi16<aIndexHi,aIndexHi,aIndexHi,aIndexHi>(
+ ShuffleLo16<aIndexLo,aIndexLo,aIndexLo,aIndexLo>(aM));
+}
+
+inline __m128i
+UnpackLo8x8ToI16x8(__m128i m)
+{
+ __m128i zero = _mm_set1_epi8(0);
+ return _mm_unpacklo_epi8(m, zero);
+}
+
+inline __m128i
+UnpackHi8x8ToI16x8(__m128i m)
+{
+ __m128i zero = _mm_set1_epi8(0);
+ return _mm_unpackhi_epi8(m, zero);
+}
+
+inline __m128i
+UnpackLo8x8ToU16x8(__m128i m)
+{
+ __m128i zero = _mm_set1_epi8(0);
+ return _mm_unpacklo_epi8(m, zero);
+}
+
+inline __m128i
+UnpackHi8x8ToU16x8(__m128i m)
+{
+ __m128i zero = _mm_set1_epi8(0);
+ return _mm_unpackhi_epi8(m, zero);
+}
+
+inline __m128i
+InterleaveLo8(__m128i m1, __m128i m2)
+{
+ return _mm_unpacklo_epi8(m1, m2);
+}
+
+inline __m128i
+InterleaveHi8(__m128i m1, __m128i m2)
+{
+ return _mm_unpackhi_epi8(m1, m2);
+}
+
+inline __m128i
+InterleaveLo16(__m128i m1, __m128i m2)
+{
+ return _mm_unpacklo_epi16(m1, m2);
+}
+
+inline __m128i
+InterleaveHi16(__m128i m1, __m128i m2)
+{
+ return _mm_unpackhi_epi16(m1, m2);
+}
+
+inline __m128i
+InterleaveLo32(__m128i m1, __m128i m2)
+{
+ return _mm_unpacklo_epi32(m1, m2);
+}
+
+template<uint8_t aNumBytes>
+inline __m128i
+Rotate8(__m128i a1234, __m128i a5678)
+{
+ return _mm_or_si128(_mm_srli_si128(a1234, aNumBytes), _mm_slli_si128(a5678, 16 - aNumBytes));
+}
+
+inline __m128i
+PackAndSaturate32To16(__m128i m1, __m128i m2)
+{
+ return _mm_packs_epi32(m1, m2);
+}
+
+inline __m128i
+PackAndSaturate32ToU16(__m128i m1, __m128i m2)
+{
+ return _mm_packs_epi32(m1, m2);
+}
+
+inline __m128i
+PackAndSaturate32To8(__m128i m1, __m128i m2, __m128i m3, const __m128i& m4)
+{
+ // Pack into 8 16bit signed integers (saturating).
+ __m128i m12 = _mm_packs_epi32(m1, m2);
+ __m128i m34 = _mm_packs_epi32(m3, m4);
+
+ // Pack into 16 8bit unsigned integers (saturating).
+ return _mm_packus_epi16(m12, m34);
+}
+
+inline __m128i
+PackAndSaturate16To8(__m128i m1, __m128i m2)
+{
+ // Pack into 16 8bit unsigned integers (saturating).
+ return _mm_packus_epi16(m1, m2);
+}
+
+inline __m128i
+FastDivideBy255(__m128i m)
+{
+ // v = m << 8
+ __m128i v = _mm_slli_epi32(m, 8);
+ // v = v + (m + (255,255,255,255))
+ v = _mm_add_epi32(v, _mm_add_epi32(m, _mm_set1_epi32(255)));
+ // v = v >> 16
+ return _mm_srai_epi32(v, 16);
+}
+
+inline __m128i
+FastDivideBy255_16(__m128i m)
+{
+ __m128i zero = _mm_set1_epi16(0);
+ __m128i lo = _mm_unpacklo_epi16(m, zero);
+ __m128i hi = _mm_unpackhi_epi16(m, zero);
+ return _mm_packs_epi32(FastDivideBy255(lo), FastDivideBy255(hi));
+}
+
+inline __m128i
+Pick(__m128i mask, __m128i a, __m128i b)
+{
+ return _mm_or_si128(_mm_andnot_si128(mask, a), _mm_and_si128(mask, b));
+}
+
+inline __m128 MixF32(__m128 a, __m128 b, float t)
+{
+ return _mm_add_ps(a, _mm_mul_ps(_mm_sub_ps(b, a), _mm_set1_ps(t)));
+}
+
+inline __m128 WSumF32(__m128 a, __m128 b, float wa, float wb)
+{
+ return _mm_add_ps(_mm_mul_ps(a, _mm_set1_ps(wa)), _mm_mul_ps(b, _mm_set1_ps(wb)));
+}
+
+inline __m128 AbsF32(__m128 a)
+{
+ return _mm_max_ps(_mm_sub_ps(_mm_setzero_ps(), a), a);
+}
+
+inline __m128 AddF32(__m128 a, __m128 b)
+{
+ return _mm_add_ps(a, b);
+}
+
+inline __m128 MulF32(__m128 a, __m128 b)
+{
+ return _mm_mul_ps(a, b);
+}
+
+inline __m128 DivF32(__m128 a, __m128 b)
+{
+ return _mm_div_ps(a, b);
+}
+
+template<uint8_t aIndex>
+inline __m128 SplatF32(__m128 m)
+{
+ AssertIndex<aIndex>();
+ return _mm_shuffle_ps(m, m, _MM_SHUFFLE(aIndex, aIndex, aIndex, aIndex));
+}
+
+inline __m128i F32ToI32(__m128 m)
+{
+ return _mm_cvtps_epi32(m);
+}
+
+#endif // SIMD_COMPILE_SSE2
+
+} // namespace simd
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_SIMD_H_
diff --git a/gfx/2d/SVGTurbulenceRenderer-inl.h b/gfx/2d/SVGTurbulenceRenderer-inl.h
new file mode 100644
index 000000000..006f842e3
--- /dev/null
+++ b/gfx/2d/SVGTurbulenceRenderer-inl.h
@@ -0,0 +1,360 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "2D.h"
+#include "Filters.h"
+#include "SIMD.h"
+
+namespace mozilla {
+namespace gfx {
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+class SVGTurbulenceRenderer
+{
+public:
+ SVGTurbulenceRenderer(const Size &aBaseFrequency, int32_t aSeed,
+ int aNumOctaves, const Rect &aTileRect);
+
+ TemporaryRef<DataSourceSurface> Render(const IntSize &aSize, const Point &aOffset) const;
+
+private:
+ /* The turbulence calculation code is an adapted version of what
+ appears in the SVG 1.1 specification:
+ http://www.w3.org/TR/SVG11/filters.html#feTurbulence
+ */
+
+ struct StitchInfo {
+ int32_t width; // How much to subtract to wrap for stitching.
+ int32_t height;
+ int32_t wrapX; // Minimum value to wrap.
+ int32_t wrapY;
+ };
+
+ const static int sBSize = 0x100;
+ const static int sBM = 0xff;
+ void InitFromSeed(int32_t aSeed);
+ void AdjustBaseFrequencyForStitch(const Rect &aTileRect);
+ IntPoint AdjustForStitch(IntPoint aLatticePoint, const StitchInfo& aStitchInfo) const;
+ StitchInfo CreateStitchInfo(const Rect &aTileRect) const;
+ f32x4_t Noise2(Point aVec, const StitchInfo& aStitchInfo) const;
+ i32x4_t Turbulence(const Point &aPoint) const;
+ Point EquivalentNonNegativeOffset(const Point &aOffset) const;
+
+ Size mBaseFrequency;
+ int32_t mNumOctaves;
+ StitchInfo mStitchInfo;
+ bool mStitchable;
+ TurbulenceType mType;
+ uint8_t mLatticeSelector[sBSize];
+ f32x4_t mGradient[sBSize][2];
+};
+
+namespace {
+
+struct RandomNumberSource
+{
+ explicit RandomNumberSource(int32_t aSeed) : mLast(SetupSeed(aSeed)) {}
+ int32_t Next() { mLast = Random(mLast); return mLast; }
+
+private:
+ static const int32_t RAND_M = 2147483647; /* 2**31 - 1 */
+ static const int32_t RAND_A = 16807; /* 7**5; primitive root of m */
+ static const int32_t RAND_Q = 127773; /* m / a */
+ static const int32_t RAND_R = 2836; /* m % a */
+
+ /* Produces results in the range [1, 2**31 - 2].
+ Algorithm is: r = (a * r) mod m
+ where a = 16807 and m = 2**31 - 1 = 2147483647
+ See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988
+ To test: the algorithm should produce the result 1043618065
+ as the 10,000th generated number if the original seed is 1.
+ */
+
+ static int32_t
+ SetupSeed(int32_t aSeed) {
+ if (aSeed <= 0)
+ aSeed = -(aSeed % (RAND_M - 1)) + 1;
+ if (aSeed > RAND_M - 1)
+ aSeed = RAND_M - 1;
+ return aSeed;
+ }
+
+ static int32_t
+ Random(int32_t aSeed)
+ {
+ int32_t result = RAND_A * (aSeed % RAND_Q) - RAND_R * (aSeed / RAND_Q);
+ if (result <= 0)
+ result += RAND_M;
+ return result;
+ }
+
+ int32_t mLast;
+};
+
+} // unnamed namespace
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::SVGTurbulenceRenderer(const Size &aBaseFrequency, int32_t aSeed,
+ int aNumOctaves, const Rect &aTileRect)
+ : mBaseFrequency(aBaseFrequency)
+ , mNumOctaves(aNumOctaves)
+{
+ InitFromSeed(aSeed);
+ if (Stitch) {
+ AdjustBaseFrequencyForStitch(aTileRect);
+ mStitchInfo = CreateStitchInfo(aTileRect);
+ }
+}
+
+template<typename T>
+static void
+Swap(T& a, T& b) {
+ T c = a;
+ a = b;
+ b = c;
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+void
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::InitFromSeed(int32_t aSeed)
+{
+ RandomNumberSource rand(aSeed);
+
+ float gradient[4][sBSize][2];
+ for (int32_t k = 0; k < 4; k++) {
+ for (int32_t i = 0; i < sBSize; i++) {
+ float a, b;
+ do {
+ a = float((rand.Next() % (sBSize + sBSize)) - sBSize) / sBSize;
+ b = float((rand.Next() % (sBSize + sBSize)) - sBSize) / sBSize;
+ } while (a == 0 && b == 0);
+ float s = sqrt(a * a + b * b);
+ gradient[k][i][0] = a / s;
+ gradient[k][i][1] = b / s;
+ }
+ }
+
+ for (int32_t i = 0; i < sBSize; i++) {
+ mLatticeSelector[i] = i;
+ }
+ for (int32_t i1 = sBSize - 1; i1 > 0; i1--) {
+ int32_t i2 = rand.Next() % sBSize;
+ Swap(mLatticeSelector[i1], mLatticeSelector[i2]);
+ }
+
+ for (int32_t i = 0; i < sBSize; i++) {
+ // Contrary to the code in the spec, we build the first lattice selector
+ // lookup into mGradient so that we don't need to do it again for every
+ // pixel.
+ // We also change the order of the gradient indexing so that we can process
+ // all four color channels at the same time.
+ uint8_t j = mLatticeSelector[i];
+ mGradient[i][0] = simd::FromF32<f32x4_t>(gradient[2][j][0], gradient[1][j][0],
+ gradient[0][j][0], gradient[3][j][0]);
+ mGradient[i][1] = simd::FromF32<f32x4_t>(gradient[2][j][1], gradient[1][j][1],
+ gradient[0][j][1], gradient[3][j][1]);
+ }
+}
+
+// Adjust aFreq such that aLength * AdjustForLength(aFreq, aLength) is integer
+// and as close to aLength * aFreq as possible.
+static inline float
+AdjustForLength(float aFreq, float aLength)
+{
+ float lowFreq = floor(aLength * aFreq) / aLength;
+ float hiFreq = ceil(aLength * aFreq) / aLength;
+ if (aFreq / lowFreq < hiFreq / aFreq) {
+ return lowFreq;
+ }
+ return hiFreq;
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+void
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::AdjustBaseFrequencyForStitch(const Rect &aTileRect)
+{
+ mBaseFrequency = Size(AdjustForLength(mBaseFrequency.width, aTileRect.width),
+ AdjustForLength(mBaseFrequency.height, aTileRect.height));
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+typename SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::StitchInfo
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::CreateStitchInfo(const Rect &aTileRect) const
+{
+ StitchInfo stitch;
+ stitch.width = int32_t(floorf(aTileRect.width * mBaseFrequency.width + 0.5f));
+ stitch.height = int32_t(floorf(aTileRect.height * mBaseFrequency.height + 0.5f));
+ stitch.wrapX = int32_t(aTileRect.x * mBaseFrequency.width) + stitch.width;
+ stitch.wrapY = int32_t(aTileRect.y * mBaseFrequency.height) + stitch.height;
+ return stitch;
+}
+
+static MOZ_ALWAYS_INLINE Float
+SCurve(Float t)
+{
+ return t * t * (3 - 2 * t);
+}
+
+static MOZ_ALWAYS_INLINE Point
+SCurve(Point t)
+{
+ return Point(SCurve(t.x), SCurve(t.y));
+}
+
+template<typename f32x4_t>
+static MOZ_ALWAYS_INLINE f32x4_t
+BiMix(const f32x4_t& aa, const f32x4_t& ab,
+ const f32x4_t& ba, const f32x4_t& bb, Point s)
+{
+ return simd::MixF32(simd::MixF32(aa, ab, s.x),
+ simd::MixF32(ba, bb, s.x), s.y);
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+IntPoint
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::AdjustForStitch(IntPoint aLatticePoint,
+ const StitchInfo& aStitchInfo) const
+{
+ if (Stitch) {
+ if (aLatticePoint.x >= aStitchInfo.wrapX) {
+ aLatticePoint.x -= aStitchInfo.width;
+ }
+ if (aLatticePoint.y >= aStitchInfo.wrapY) {
+ aLatticePoint.y -= aStitchInfo.height;
+ }
+ }
+ return aLatticePoint;
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+f32x4_t
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Noise2(Point aVec, const StitchInfo& aStitchInfo) const
+{
+ // aVec is guaranteed to be non-negative, so casting to int32_t always
+ // rounds towards negative infinity.
+ IntPoint topLeftLatticePoint(int32_t(aVec.x), int32_t(aVec.y));
+ Point r = aVec - topLeftLatticePoint; // fractional offset
+
+ IntPoint b0 = AdjustForStitch(topLeftLatticePoint, aStitchInfo);
+ IntPoint b1 = AdjustForStitch(b0 + IntPoint(1, 1), aStitchInfo);
+
+ uint8_t i = mLatticeSelector[b0.x & sBM];
+ uint8_t j = mLatticeSelector[b1.x & sBM];
+
+ const f32x4_t* qua = mGradient[(i + b0.y) & sBM];
+ const f32x4_t* qub = mGradient[(i + b1.y) & sBM];
+ const f32x4_t* qva = mGradient[(j + b0.y) & sBM];
+ const f32x4_t* qvb = mGradient[(j + b1.y) & sBM];
+
+ return BiMix(simd::WSumF32(qua[0], qua[1], r.x, r.y),
+ simd::WSumF32(qva[0], qva[1], r.x - 1.f, r.y),
+ simd::WSumF32(qub[0], qub[1], r.x, r.y - 1.f),
+ simd::WSumF32(qvb[0], qvb[1], r.x - 1.f, r.y - 1.f),
+ SCurve(r));
+}
+
+template<typename f32x4_t, typename i32x4_t, typename u8x16_t>
+static inline i32x4_t
+ColorToBGRA(f32x4_t aUnscaledUnpreFloat)
+{
+ // Color is an unpremultiplied float vector where 1.0f means white. We will
+ // convert it into an integer vector where 255 means white.
+ f32x4_t alpha = simd::SplatF32<3>(aUnscaledUnpreFloat);
+ f32x4_t scaledUnpreFloat = simd::MulF32(aUnscaledUnpreFloat, simd::FromF32<f32x4_t>(255));
+ i32x4_t scaledUnpreInt = simd::F32ToI32(scaledUnpreFloat);
+
+ // Multiply all channels with alpha.
+ i32x4_t scaledPreInt = simd::F32ToI32(simd::MulF32(scaledUnpreFloat, alpha));
+
+ // Use the premultiplied color channels and the unpremultiplied alpha channel.
+ i32x4_t alphaMask = simd::From32<i32x4_t>(0, 0, 0, -1);
+ return simd::Pick(alphaMask, scaledPreInt, scaledUnpreInt);
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+i32x4_t
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Turbulence(const Point &aPoint) const
+{
+ StitchInfo stitchInfo = mStitchInfo;
+ f32x4_t sum = simd::FromF32<f32x4_t>(0);
+ Point vec(aPoint.x * mBaseFrequency.width, aPoint.y * mBaseFrequency.height);
+ f32x4_t ratio = simd::FromF32<f32x4_t>(1);
+
+ for (int octave = 0; octave < mNumOctaves; octave++) {
+ f32x4_t thisOctave = Noise2(vec, stitchInfo);
+ if (Type == TURBULENCE_TYPE_TURBULENCE) {
+ thisOctave = simd::AbsF32(thisOctave);
+ }
+ sum = simd::AddF32(sum, simd::DivF32(thisOctave, ratio));
+ vec = vec * 2;
+ ratio = simd::MulF32(ratio, simd::FromF32<f32x4_t>(2));
+
+ if (Stitch) {
+ stitchInfo.width *= 2;
+ stitchInfo.wrapX *= 2;
+ stitchInfo.height *= 2;
+ stitchInfo.wrapY *= 2;
+ }
+ }
+
+ if (Type == TURBULENCE_TYPE_FRACTAL_NOISE) {
+ sum = simd::DivF32(simd::AddF32(sum, simd::FromF32<f32x4_t>(1)), simd::FromF32<f32x4_t>(2));
+ }
+ return ColorToBGRA<f32x4_t,i32x4_t,u8x16_t>(sum);
+}
+
+static inline Float
+MakeNonNegative(Float aValue, Float aIncrementSize)
+{
+ if (aValue >= 0) {
+ return aValue;
+ }
+ return aValue + ceilf(-aValue / aIncrementSize) * aIncrementSize;
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+Point
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::EquivalentNonNegativeOffset(const Point &aOffset) const
+{
+ Size basePeriod = Stitch ? Size(mStitchInfo.width, mStitchInfo.height) :
+ Size(sBSize, sBSize);
+ Size repeatingSize(basePeriod.width / mBaseFrequency.width,
+ basePeriod.height / mBaseFrequency.height);
+ return Point(MakeNonNegative(aOffset.x, repeatingSize.width),
+ MakeNonNegative(aOffset.y, repeatingSize.height));
+}
+
+template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
+TemporaryRef<DataSourceSurface>
+SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Render(const IntSize &aSize, const Point &aOffset) const
+{
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aSize, SurfaceFormat::B8G8R8A8);
+ if (!target) {
+ return nullptr;
+ }
+
+ uint8_t* targetData = target->GetData();
+ uint32_t stride = target->Stride();
+
+ Point startOffset = EquivalentNonNegativeOffset(aOffset);
+
+ for (int32_t y = 0; y < aSize.height; y++) {
+ for (int32_t x = 0; x < aSize.width; x += 4) {
+ int32_t targIndex = y * stride + x * 4;
+ i32x4_t a = Turbulence(startOffset + Point(x, y));
+ i32x4_t b = Turbulence(startOffset + Point(x + 1, y));
+ i32x4_t c = Turbulence(startOffset + Point(x + 2, y));
+ i32x4_t d = Turbulence(startOffset + Point(x + 3, y));
+ u8x16_t result1234 = simd::PackAndSaturate32To8(a, b, c, d);
+ simd::Store8(&targetData[targIndex], result1234);
+ }
+ }
+
+ return target;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/Scale.cpp b/gfx/2d/Scale.cpp
index 71f6f6d84..cc4710435 100644
--- a/gfx/2d/Scale.cpp
+++ b/gfx/2d/Scale.cpp
@@ -18,23 +18,24 @@ bool Scale(uint8_t* srcData, int32_t srcWidth, int32_t srcHeight, int32_t srcStr
SurfaceFormat format)
{
#ifdef USE_SKIA
- bool opaque;
- if (format == FORMAT_B8G8R8A8) {
- opaque = false;
+ SkAlphaType alphaType;
+ if (format == SurfaceFormat::B8G8R8A8) {
+ alphaType = kPremul_SkAlphaType;
} else {
- opaque = true;
+ alphaType = kOpaque_SkAlphaType;
}
- SkBitmap::Config config = GfxFormatToSkiaConfig(format);
+ SkImageInfo info = SkImageInfo::Make(srcWidth,
+ srcHeight,
+ GfxFormatToSkiaColorType(format),
+ alphaType);
SkBitmap imgSrc;
- imgSrc.setConfig(config, srcWidth, srcHeight, srcStride);
- imgSrc.setPixels(srcData);
- imgSrc.setIsOpaque(opaque);
+ imgSrc.installPixels(info, srcData, srcStride);
// Rescaler is compatible with 32 bpp only. Convert to RGB32 if needed.
- if (config != SkBitmap::kARGB_8888_Config) {
- imgSrc.copyTo(&imgSrc, SkBitmap::kARGB_8888_Config);
+ if (format != SurfaceFormat::B8G8R8A8) {
+ imgSrc.copyTo(&imgSrc, kBGRA_8888_SkColorType);
}
// This returns an SkBitmap backed by dstData; since it also wrote to dstData,
diff --git a/gfx/2d/ScaleFactor.h b/gfx/2d/ScaleFactor.h
index d7eec8275..c67cb9e04 100644
--- a/gfx/2d/ScaleFactor.h
+++ b/gfx/2d/ScaleFactor.h
@@ -6,6 +6,8 @@
#ifndef MOZILLA_GFX_SCALEFACTOR_H_
#define MOZILLA_GFX_SCALEFACTOR_H_
+#include "mozilla/Attributes.h"
+
#include "gfxPoint.h"
namespace mozilla {
@@ -26,18 +28,14 @@ template<class src, class dst>
struct ScaleFactor {
float scale;
- ScaleFactor() : scale(1.0) {}
- ScaleFactor(const ScaleFactor<src, dst>& aCopy) : scale(aCopy.scale) {}
- explicit ScaleFactor(float aScale) : scale(aScale) {}
+ MOZ_CONSTEXPR ScaleFactor() : scale(1.0) {}
+ MOZ_CONSTEXPR ScaleFactor(const ScaleFactor<src, dst>& aCopy) : scale(aCopy.scale) {}
+ explicit MOZ_CONSTEXPR ScaleFactor(float aScale) : scale(aScale) {}
explicit ScaleFactor(float aX, float aY) : scale(aX) {
MOZ_ASSERT(fabs(aX - aY) < 1e-6);
}
- explicit ScaleFactor(gfxSize aScale) : scale(aScale.width) {
- MOZ_ASSERT(fabs(aScale.width - aScale.height) < 1e-6);
- }
-
ScaleFactor<dst, src> Inverse() {
return ScaleFactor<dst, src>(1 / scale);
}
@@ -50,6 +48,22 @@ struct ScaleFactor {
return !(*this == aOther);
}
+ bool operator<(const ScaleFactor<src, dst>& aOther) const {
+ return scale < aOther.scale;
+ }
+
+ bool operator<=(const ScaleFactor<src, dst>& aOther) const {
+ return scale <= aOther.scale;
+ }
+
+ bool operator>(const ScaleFactor<src, dst>& aOther) const {
+ return scale > aOther.scale;
+ }
+
+ bool operator>=(const ScaleFactor<src, dst>& aOther) const {
+ return scale >= aOther.scale;
+ }
+
template<class other>
ScaleFactor<other, dst> operator/(const ScaleFactor<src, other>& aOther) const {
return ScaleFactor<other, dst>(scale / aOther.scale);
diff --git a/gfx/2d/ScaledFontBase.cpp b/gfx/2d/ScaledFontBase.cpp
index 1fd0e7af6..f0eb74d39 100644
--- a/gfx/2d/ScaledFontBase.cpp
+++ b/gfx/2d/ScaledFontBase.cpp
@@ -8,11 +8,12 @@
#ifdef USE_SKIA
#include "PathSkia.h"
#include "skia/SkPaint.h"
-#include "skia/SkPath.h"
#endif
#ifdef USE_CAIRO
#include "PathCairo.h"
+#include "DrawTargetCairo.h"
+#include "HelpersCairo.h"
#endif
#include <vector>
@@ -28,7 +29,7 @@ ScaledFontBase::~ScaledFontBase()
#ifdef USE_SKIA
SkSafeUnref(mTypeface);
#endif
-#ifdef USE_CAIRO
+#ifdef USE_CAIRO_SCALED_FONT
cairo_scaled_font_destroy(mScaledFont);
#endif
}
@@ -39,48 +40,65 @@ ScaledFontBase::ScaledFontBase(Float aSize)
#ifdef USE_SKIA
mTypeface = nullptr;
#endif
-#ifdef USE_CAIRO
+#ifdef USE_CAIRO_SCALED_FONT
mScaledFont = nullptr;
#endif
}
+#ifdef USE_SKIA
+SkPath
+ScaledFontBase::GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer)
+{
+ SkTypeface *typeFace = GetSkTypeface();
+ MOZ_ASSERT(typeFace);
+
+ SkPaint paint;
+ paint.setTypeface(typeFace);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint.setTextSize(SkFloatToScalar(mSize));
+
+ std::vector<uint16_t> indices;
+ std::vector<SkPoint> offsets;
+ indices.resize(aBuffer.mNumGlyphs);
+ offsets.resize(aBuffer.mNumGlyphs);
+
+ for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+ indices[i] = aBuffer.mGlyphs[i].mIndex;
+ offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x);
+ offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y);
+ }
+
+ SkPath path;
+ paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path);
+ return path;
+}
+#endif
+
TemporaryRef<Path>
ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
{
#ifdef USE_SKIA
- if (aTarget->GetType() == BACKEND_SKIA) {
- SkPaint paint;
- paint.setTypeface(GetSkTypeface());
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- paint.setTextSize(SkFloatToScalar(mSize));
-
- std::vector<uint16_t> indices;
- std::vector<SkPoint> offsets;
- indices.resize(aBuffer.mNumGlyphs);
- offsets.resize(aBuffer.mNumGlyphs);
-
- for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
- indices[i] = aBuffer.mGlyphs[i].mIndex;
- offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x);
- offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y);
- }
-
- SkPath path;
- paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path);
- return new PathSkia(path, FILL_WINDING);
+ if (aTarget->GetBackendType() == BackendType::SKIA) {
+ SkPath path = GetSkiaPathForGlyphs(aBuffer);
+ return new PathSkia(path, FillRule::FILL_WINDING);
}
#endif
#ifdef USE_CAIRO
- if (aTarget->GetType() == BACKEND_CAIRO) {
+ if (aTarget->GetBackendType() == BackendType::CAIRO) {
MOZ_ASSERT(mScaledFont);
- RefPtr<PathBuilder> builder_iface = aTarget->CreatePathBuilder();
- PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(builder_iface.get());
+ DrawTarget *dt = const_cast<DrawTarget*>(aTarget);
+ cairo_t *ctx = static_cast<cairo_t*>(dt->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
- // Manually build the path for the PathBuilder.
- RefPtr<CairoPathContext> context = builder->GetPathContext();
+ bool isNewContext = !ctx;
+ if (!ctx) {
+ ctx = cairo_create(DrawTargetCairo::GetDummySurface());
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(aTarget->GetTransform(), mat);
+ cairo_set_matrix(ctx, &mat);
+ }
- cairo_set_scaled_font(*context, mScaledFont);
+ cairo_set_scaled_font(ctx, mScaledFont);
// Convert our GlyphBuffer into an array of Cairo glyphs.
std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
@@ -90,28 +108,78 @@ ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *a
glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
}
- cairo_glyph_path(*context, &glyphs[0], aBuffer.mNumGlyphs);
+ cairo_new_path(ctx);
+
+ cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
+
+ RefPtr<PathCairo> newPath = new PathCairo(ctx);
+ if (isNewContext) {
+ cairo_destroy(ctx);
+ }
- return builder->Finish();
+ return newPath.forget();
}
#endif
return nullptr;
}
void
-ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder)
+ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint)
{
- // XXX - implement me
- MOZ_ASSERT(false);
- return;
+#ifdef USE_SKIA
+ if (aBackendType == BackendType::SKIA) {
+ PathBuilderSkia *builder = static_cast<PathBuilderSkia*>(aBuilder);
+ builder->AppendPath(GetSkiaPathForGlyphs(aBuffer));
+ return;
+ }
+#endif
+#ifdef USE_CAIRO
+ if (aBackendType == BackendType::CAIRO) {
+ MOZ_ASSERT(mScaledFont);
+
+ PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(aBuilder);
+ cairo_t *ctx = cairo_create(DrawTargetCairo::GetDummySurface());
+
+ if (aTransformHint) {
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(*aTransformHint, mat);
+ cairo_set_matrix(ctx, &mat);
+ }
+
+ // Convert our GlyphBuffer into an array of Cairo glyphs.
+ std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
+ for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
+ glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
+ glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
+ glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
+ }
+
+ cairo_set_scaled_font(ctx, mScaledFont);
+ cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
+
+ RefPtr<PathCairo> cairoPath = new PathCairo(ctx);
+ cairo_destroy(ctx);
+
+ cairoPath->AppendPathToBuilder(builder);
+ return;
+ }
+#endif
+
+ MOZ_CRASH("The specified backend type is not supported by CopyGlyphsToBuilder");
}
-#ifdef USE_CAIRO
+#ifdef USE_CAIRO_SCALED_FONT
void
ScaledFontBase::SetCairoScaledFont(cairo_scaled_font_t* font)
{
MOZ_ASSERT(!mScaledFont);
+ if (font == mScaledFont)
+ return;
+
+ if (mScaledFont)
+ cairo_scaled_font_destroy(mScaledFont);
+
mScaledFont = font;
cairo_scaled_font_reference(mScaledFont);
}
diff --git a/gfx/2d/ScaledFontBase.h b/gfx/2d/ScaledFontBase.h
index ff5e90a8c..93f4441be 100644
--- a/gfx/2d/ScaledFontBase.h
+++ b/gfx/2d/ScaledFontBase.h
@@ -8,10 +8,16 @@
#include "2D.h"
+// Skia uses cairo_scaled_font_t as the internal font type in ScaledFont
+#if defined(USE_SKIA) || defined(USE_CAIRO)
+#define USE_CAIRO_SCALED_FONT
+#endif
+
#ifdef USE_SKIA
+#include "skia/SkPath.h"
#include "skia/SkTypeface.h"
#endif
-#ifdef USE_CAIRO
+#ifdef USE_CAIRO_SCALED_FONT
#include "cairo.h"
#endif
@@ -23,12 +29,13 @@ namespace gfx {
class ScaledFontBase : public ScaledFont
{
public:
- ScaledFontBase(Float aSize);
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontBase)
+ explicit ScaledFontBase(Float aSize);
virtual ~ScaledFontBase();
virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
- virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder);
+ virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint);
float GetSize() { return mSize; }
@@ -37,9 +44,9 @@ public:
#endif
// Not true, but required to instantiate a ScaledFontBase.
- virtual FontType GetType() const { return FONT_SKIA; }
+ virtual FontType GetType() const { return FontType::SKIA; }
-#ifdef USE_CAIRO
+#ifdef USE_CAIRO_SCALED_FONT
cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; }
void SetCairoScaledFont(cairo_scaled_font_t* font);
#endif
@@ -48,8 +55,9 @@ protected:
friend class DrawTargetSkia;
#ifdef USE_SKIA
SkTypeface* mTypeface;
+ SkPath GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer);
#endif
-#ifdef USE_CAIRO
+#ifdef USE_CAIRO_SCALED_FONT
cairo_scaled_font_t* mScaledFont;
#endif
Float mSize;
diff --git a/gfx/2d/ScaledFontCairo.cpp b/gfx/2d/ScaledFontCairo.cpp
new file mode 100644
index 000000000..12ab27e26
--- /dev/null
+++ b/gfx/2d/ScaledFontCairo.cpp
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "ScaledFontCairo.h"
+#include "Logging.h"
+
+#ifdef MOZ_ENABLE_FREETYPE
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include "cairo-ft.h"
+#endif
+
+#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
+#include "skia/SkTypeface.h"
+#include "skia/SkTypeface_cairo.h"
+#endif
+
+#include <string>
+
+typedef struct FT_FaceRec_* FT_Face;
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+// On Linux and Android our "platform" font is a cairo_scaled_font_t and we use
+// an SkFontHost implementation that allows Skia to render using this.
+// This is mainly because FT_Face is not good for sharing between libraries, which
+// is a requirement when we consider runtime switchable backends and so on
+ScaledFontCairo::ScaledFontCairo(cairo_scaled_font_t* aScaledFont, Float aSize)
+ : ScaledFontBase(aSize)
+{
+ SetCairoScaledFont(aScaledFont);
+}
+
+#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
+SkTypeface* ScaledFontCairo::GetSkTypeface()
+{
+ if (!mTypeface) {
+ cairo_font_face_t* fontFace = cairo_scaled_font_get_font_face(mScaledFont);
+ FT_Face face = cairo_ft_scaled_font_lock_face(mScaledFont);
+
+ int style = SkTypeface::kNormal;
+
+ if (face->style_flags & FT_STYLE_FLAG_ITALIC)
+ style |= SkTypeface::kItalic;
+
+ if (face->style_flags & FT_STYLE_FLAG_BOLD)
+ style |= SkTypeface::kBold;
+
+ bool isFixedWidth = face->face_flags & FT_FACE_FLAG_FIXED_WIDTH;
+ cairo_ft_scaled_font_unlock_face(mScaledFont);
+
+ mTypeface = SkCreateTypefaceFromCairoFont(fontFace, (SkTypeface::Style)style, isFixedWidth);
+ }
+
+ return mTypeface;
+}
+#endif
+
+}
+}
diff --git a/gfx/2d/ScaledFontCairo.h b/gfx/2d/ScaledFontCairo.h
new file mode 100644
index 000000000..9f4d227ae
--- /dev/null
+++ b/gfx/2d/ScaledFontCairo.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_SCALEDFONTCAIRO_H_
+#define MOZILLA_GFX_SCALEDFONTCAIRO_H_
+
+#include "ScaledFontBase.h"
+
+#include "cairo.h"
+
+namespace mozilla {
+namespace gfx {
+
+class ScaledFontCairo : public ScaledFontBase
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontCairo)
+
+ ScaledFontCairo(cairo_scaled_font_t* aScaledFont, Float aSize);
+
+#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
+ virtual SkTypeface* GetSkTypeface();
+#endif
+};
+
+// We need to be able to tell Skia whether or not to use
+// hinting when rendering text, so that the glyphs it renders
+// are the same as what layout is expecting. At the moment, only
+// Skia uses this class when rendering with FreeType, as gfxFT2Font
+// is the only gfxFont that honours gfxPlatform::FontHintingEnabled().
+class GlyphRenderingOptionsCairo : public GlyphRenderingOptions
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptionsCairo)
+ GlyphRenderingOptionsCairo()
+ : mHinting(FontHinting::NORMAL)
+ , mAutoHinting(false)
+ {
+ }
+
+ void SetHinting(FontHinting aHinting) { mHinting = aHinting; }
+ void SetAutoHinting(bool aAutoHinting) { mAutoHinting = aAutoHinting; }
+ FontHinting GetHinting() const { return mHinting; }
+ bool GetAutoHinting() const { return mAutoHinting; }
+ virtual FontType GetType() const { return FontType::CAIRO; }
+private:
+ FontHinting mHinting;
+ bool mAutoHinting;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_SCALEDFONTCAIRO_H_ */
diff --git a/gfx/2d/ScaledFontDWrite.cpp b/gfx/2d/ScaledFontDWrite.cpp
index 47a0d5093..e1e1b7137 100644
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -213,7 +213,7 @@ DoGrayscale(IDWriteFontFace *aDWFace, Float ppem)
return true;
}
-IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = NULL;
+IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr;
HRESULT STDMETHODCALLTYPE
DWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey,
@@ -275,7 +275,7 @@ DWriteFontFileStream::ReadFileFragment(const void **fragmentStart,
// We should be alive for the duration of this.
*fragmentStart = &mData[index];
- *fragmentContext = NULL;
+ *fragmentContext = nullptr;
return S_OK;
}
@@ -309,7 +309,7 @@ ScaledFontDWrite::ScaledFontDWrite(uint8_t *aData, uint32_t aSize,
TemporaryRef<Path>
ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
{
- if (aTarget->GetType() != BACKEND_DIRECT2D) {
+ if (aTarget->GetBackendType() != BackendType::DIRECT2D && aTarget->GetBackendType() != BackendType::DIRECT2D1_1) {
return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
}
@@ -324,9 +324,13 @@ ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget
}
void
-ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder)
+ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint)
{
- // XXX - Check path builder type!
+ if (aBackendType != BackendType::DIRECT2D && aBackendType != BackendType::DIRECT2D1_1) {
+ ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint);
+ return;
+ }
+
PathBuilderD2D *pathBuilderD2D =
static_cast<PathBuilderD2D*>(aBuilder);
@@ -405,23 +409,23 @@ ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton
AntialiasMode
ScaledFontDWrite::GetDefaultAAMode()
{
- AntialiasMode defaultMode = AA_SUBPIXEL;
+ AntialiasMode defaultMode = AntialiasMode::SUBPIXEL;
switch (GetSystemTextQuality()) {
case CLEARTYPE_QUALITY:
- defaultMode = AA_SUBPIXEL;
+ defaultMode = AntialiasMode::SUBPIXEL;
break;
case ANTIALIASED_QUALITY:
- defaultMode = AA_GRAY;
+ defaultMode = AntialiasMode::GRAY;
break;
case DEFAULT_QUALITY:
- defaultMode = AA_NONE;
+ defaultMode = AntialiasMode::NONE;
break;
}
- if (defaultMode == AA_GRAY) {
+ if (defaultMode == AntialiasMode::GRAY) {
if (!DoGrayscale(mFontFace, mSize)) {
- defaultMode = AA_NONE;
+ defaultMode = AntialiasMode::NONE;
}
}
return defaultMode;
diff --git a/gfx/2d/ScaledFontDWrite.h b/gfx/2d/ScaledFontDWrite.h
index 2f5cc94f5..941babed1 100644
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -14,19 +14,20 @@ struct ID2D1GeometrySink;
namespace mozilla {
namespace gfx {
-class ScaledFontDWrite MOZ_FINAL : public ScaledFontBase
+class ScaledFontDWrite final : public ScaledFontBase
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontDwrite)
ScaledFontDWrite(IDWriteFontFace *aFont, Float aSize)
: mFontFace(aFont)
, ScaledFontBase(aSize)
{}
ScaledFontDWrite(uint8_t *aData, uint32_t aSize, uint32_t aIndex, Float aGlyphSize);
- virtual FontType GetType() const { return FONT_DWRITE; }
+ virtual FontType GetType() const { return FontType::DWRITE; }
virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
- virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder);
+ virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint);
void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
@@ -48,15 +49,17 @@ public:
class GlyphRenderingOptionsDWrite : public GlyphRenderingOptions
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptionsDWrite)
GlyphRenderingOptionsDWrite(IDWriteRenderingParams *aParams)
: mParams(aParams)
{
}
- virtual FontType GetType() const { return FONT_DWRITE; }
+ virtual FontType GetType() const { return FontType::DWRITE; }
private:
friend class DrawTargetD2D;
+ friend class DrawTargetD2D1;
RefPtr<IDWriteRenderingParams> mParams;
};
diff --git a/gfx/2d/ScaledFontFreetype.cpp b/gfx/2d/ScaledFontFreetype.cpp
deleted file mode 100644
index 980548446..000000000
--- a/gfx/2d/ScaledFontFreetype.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; 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/. */
-
-#include "ScaledFontFreetype.h"
-#include "Logging.h"
-
-#include "gfxFont.h"
-
-#ifdef USE_SKIA
-#include "skia/SkTypeface.h"
-#endif
-
-#include <string>
-
-using namespace std;
-
-namespace mozilla {
-namespace gfx {
-
-#ifdef USE_SKIA
-static SkTypeface::Style
-fontStyleToSkia(FontStyle aStyle)
-{
- switch (aStyle) {
- case FONT_STYLE_NORMAL:
- return SkTypeface::kNormal;
- case FONT_STYLE_ITALIC:
- return SkTypeface::kItalic;
- case FONT_STYLE_BOLD:
- return SkTypeface::kBold;
- case FONT_STYLE_BOLD_ITALIC:
- return SkTypeface::kBoldItalic;
- }
-
- gfxWarning() << "Unknown font style";
- return SkTypeface::kNormal;
-}
-#endif
-
-// Ideally we want to use FT_Face here but as there is currently no way to get
-// an SkTypeface from an FT_Face we do this.
-ScaledFontFreetype::ScaledFontFreetype(FontOptions* aFont, Float aSize)
- : ScaledFontBase(aSize)
-{
-#ifdef USE_SKIA
- mTypeface = SkTypeface::CreateFromName(aFont->mName.c_str(), fontStyleToSkia(aFont->mStyle));
-#endif
-}
-
-}
-}
diff --git a/gfx/2d/ScaledFontMac.cpp b/gfx/2d/ScaledFontMac.cpp
index 6add2e3a3..5babcec54 100644
--- a/gfx/2d/ScaledFontMac.cpp
+++ b/gfx/2d/ScaledFontMac.cpp
@@ -79,7 +79,8 @@ SkTypeface* ScaledFontMac::GetSkTypeface()
TemporaryRef<Path>
ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
{
- if (aTarget->GetType() == BACKEND_COREGRAPHICS || aTarget->GetType() == BACKEND_COREGRAPHICS_ACCELERATED) {
+ if (aTarget->GetBackendType() == BackendType::COREGRAPHICS ||
+ aTarget->GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED) {
CGMutablePathRef path = CGPathCreateMutable();
for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
@@ -93,12 +94,180 @@ ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aT
CGPathAddPath(path, &matrix, glyphPath);
CGPathRelease(glyphPath);
}
- TemporaryRef<Path> ret = new PathCG(path, FILL_WINDING);
+ TemporaryRef<Path> ret = new PathCG(path, FillRule::FILL_WINDING);
CGPathRelease(path);
return ret;
- } else {
- return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
}
+ return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
+}
+
+void
+ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint)
+{
+ if (!(aBackendType == BackendType::COREGRAPHICS || aBackendType == BackendType::COREGRAPHICS_ACCELERATED)) {
+ ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint);
+ return;
+ }
+
+ PathBuilderCG *pathBuilderCG =
+ static_cast<PathBuilderCG*>(aBuilder);
+ // XXX: check builder type
+ for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+ // XXX: we could probably fold both of these transforms together to avoid extra work
+ CGAffineTransform flip = CGAffineTransformMakeScale(1, -1);
+ CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex);
+
+ CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize,
+ aBuffer.mGlyphs[i].mPosition.x,
+ aBuffer.mGlyphs[i].mPosition.y);
+ CGPathAddPath(pathBuilderCG->mCGPath, &matrix, glyphPath);
+ CGPathRelease(glyphPath);
+ }
+}
+
+uint32_t
+CalcTableChecksum(const uint32_t *tableStart, uint32_t length, bool skipChecksumAdjust = false)
+{
+ uint32_t sum = 0L;
+ const uint32_t *table = tableStart;
+ const uint32_t *end = table+((length+3) & ~3) / sizeof(uint32_t);
+ while (table < end) {
+ if (skipChecksumAdjust && (table - tableStart) == 2) {
+ table++;
+ } else {
+ sum += CFSwapInt32BigToHost(*table++);
+ }
+ }
+ return sum;
+}
+
+struct TableRecord {
+ uint32_t tag;
+ uint32_t checkSum;
+ uint32_t offset;
+ uint32_t length;
+ CFDataRef data;
+};
+
+int maxPow2LessThan(int a)
+{
+ int x = 1;
+ int shift = 0;
+ while ((x<<(shift+1)) < a) {
+ shift++;
+ }
+ return shift;
+}
+
+struct writeBuf
+{
+ explicit writeBuf(int size)
+ {
+ this->data = new unsigned char [size];
+ this->offset = 0;
+ }
+ ~writeBuf() {
+ delete this->data;
+ }
+
+ template <class T>
+ void writeElement(T a)
+ {
+ *reinterpret_cast<T*>(&this->data[this->offset]) = a;
+ this->offset += sizeof(T);
+ }
+
+ void writeMem(const void *data, unsigned long length)
+ {
+ memcpy(&this->data[this->offset], data, length);
+ this->offset += length;
+ }
+
+ void align()
+ {
+ while (this->offset & 3) {
+ this->data[this->offset] = 0;
+ this->offset++;
+ }
+ }
+
+ unsigned char *data;
+ int offset;
+};
+
+bool
+ScaledFontMac::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
+{
+ // We'll reconstruct a TTF font from the tables we can get from the CGFont
+ CFArrayRef tags = CGFontCopyTableTags(mFont);
+ CFIndex count = CFArrayGetCount(tags);
+
+ TableRecord *records = new TableRecord[count];
+ uint32_t offset = 0;
+ offset += sizeof(uint32_t)*3;
+ offset += sizeof(uint32_t)*4*count;
+ bool CFF = false;
+ for (CFIndex i = 0; i<count; i++) {
+ uint32_t tag = (uint32_t)(uintptr_t)CFArrayGetValueAtIndex(tags, i);
+ if (tag == 0x43464620) // 'CFF '
+ CFF = true;
+ CFDataRef data = CGFontCopyTableForTag(mFont, tag);
+ records[i].tag = tag;
+ records[i].offset = offset;
+ records[i].data = data;
+ records[i].length = CFDataGetLength(data);
+ bool skipChecksumAdjust = (tag == 0x68656164); // 'head'
+ records[i].checkSum = CalcTableChecksum(reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(data)),
+ records[i].length, skipChecksumAdjust);
+ offset += records[i].length;
+ // 32 bit align the tables
+ offset = (offset + 3) & ~3;
+ }
+ CFRelease(tags);
+
+ struct writeBuf buf(offset);
+ // write header/offset table
+ if (CFF) {
+ buf.writeElement(CFSwapInt32HostToBig(0x4f54544f));
+ } else {
+ buf.writeElement(CFSwapInt32HostToBig(0x00010000));
+ }
+ buf.writeElement(CFSwapInt16HostToBig(count));
+ buf.writeElement(CFSwapInt16HostToBig((1<<maxPow2LessThan(count))*16));
+ buf.writeElement(CFSwapInt16HostToBig(maxPow2LessThan(count)));
+ buf.writeElement(CFSwapInt16HostToBig(count*16-((1<<maxPow2LessThan(count))*16)));
+
+ // write table record entries
+ for (CFIndex i = 0; i<count; i++) {
+ buf.writeElement(CFSwapInt32HostToBig(records[i].tag));
+ buf.writeElement(CFSwapInt32HostToBig(records[i].checkSum));
+ buf.writeElement(CFSwapInt32HostToBig(records[i].offset));
+ buf.writeElement(CFSwapInt32HostToBig(records[i].length));
+ }
+
+ // write tables
+ int checkSumAdjustmentOffset = 0;
+ for (CFIndex i = 0; i<count; i++) {
+ if (records[i].tag == 0x68656164) {
+ checkSumAdjustmentOffset = buf.offset + 2*4;
+ }
+ buf.writeMem(CFDataGetBytePtr(records[i].data), CFDataGetLength(records[i].data));
+ buf.align();
+ CFRelease(records[i].data);
+ }
+ delete[] records;
+
+ // clear the checksumAdjust field before checksumming the whole font
+ memset(&buf.data[checkSumAdjustmentOffset], 0, sizeof(uint32_t));
+ uint32_t fontChecksum = CFSwapInt32HostToBig(0xb1b0afba - CalcTableChecksum(reinterpret_cast<const uint32_t*>(buf.data), offset));
+ // set checkSumAdjust to the computed checksum
+ memcpy(&buf.data[checkSumAdjustmentOffset], &fontChecksum, sizeof(fontChecksum));
+
+ // we always use an index of 0
+ aDataCallback(buf.data, buf.offset, 0, mSize, aBaton);
+
+ return true;
+
}
}
diff --git a/gfx/2d/ScaledFontMac.h b/gfx/2d/ScaledFontMac.h
index 456919b38..03c68add6 100644
--- a/gfx/2d/ScaledFontMac.h
+++ b/gfx/2d/ScaledFontMac.h
@@ -17,14 +17,17 @@ namespace gfx {
class ScaledFontMac : public ScaledFontBase
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontMac)
ScaledFontMac(CGFontRef aFont, Float aSize);
virtual ~ScaledFontMac();
- virtual FontType GetType() const { return FONT_MAC; }
+ virtual FontType GetType() const { return FontType::MAC; }
#ifdef USE_SKIA
virtual SkTypeface* GetSkTypeface();
#endif
virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
+ virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint);
+ virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
private:
friend class DrawTargetCG;
diff --git a/gfx/2d/ScaledFontWin.h b/gfx/2d/ScaledFontWin.h
index 7a99bf587..20c95ed3d 100644
--- a/gfx/2d/ScaledFontWin.h
+++ b/gfx/2d/ScaledFontWin.h
@@ -15,9 +15,10 @@ namespace gfx {
class ScaledFontWin : public ScaledFontBase
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontWin)
ScaledFontWin(LOGFONT* aFont, Float aSize);
- virtual FontType GetType() const { return FONT_GDI; }
+ virtual FontType GetType() const { return FontType::GDI; }
#ifdef USE_SKIA
virtual SkTypeface* GetSkTypeface();
#endif
diff --git a/gfx/2d/ShadersD2D.fx b/gfx/2d/ShadersD2D.fx
index cef1b69f9..df4ced845 100644
--- a/gfx/2d/ShadersD2D.fx
+++ b/gfx/2d/ShadersD2D.fx
@@ -300,18 +300,27 @@ float4 SampleBlendTextureSeparablePS_1( VS_OUTPUT In) : SV_Target
} else if(blendop == 5) {
retval.rgb = max(output.rgb, background.rgb);
} else {
- if(output.r > 0)
- retval.r = 1 - min(1, (1 - background.r) / output.r);
- else
- retval.r = background.r ? 1 : 0;
- if(output.g > 0)
- retval.g = 1 - min(1, (1 - background.g) / output.g);
- else
- retval.g = background.g ? 1 : 0;
- if(output.b > 0)
- retval.b = 1 - min(1, (1 - background.b) / output.b);
- else
- retval.b = background.b ? 1 : 0;
+ if(background.r == 0)
+ retval.r = 0;
+ else
+ if(output.r == 1)
+ retval.r = 1;
+ else
+ retval.r = min(1, background.r / (1 - output.r));
+ if(background.g == 0)
+ retval.g = 0;
+ else
+ if(output.g == 1)
+ retval.g = 1;
+ else
+ retval.g = min(1, background.g / (1 - output.g));
+ if(background.b == 0)
+ retval.b = 0;
+ else
+ if(output.b == 1)
+ retval.b = 1;
+ else
+ retval.b = min(1, background.b / (1 - output.b));
}
output.rgb = ((1 - background.a) * output.rgb + background.a * retval.rgb) * output.a;
@@ -331,18 +340,27 @@ float4 SampleBlendTextureSeparablePS_2( VS_OUTPUT In) : SV_Target
float4 retval = output;
if(blendop == 7) {
- if(output.r > 0)
+ if(background.r == 1)
+ retval.r = 1;
+ else
+ if(output.r == 0)
+ retval.r = 0;
+ else
retval.r = 1 - min(1, (1 - background.r) / output.r);
+ if(background.g == 1)
+ retval.g = 1;
+ else
+ if(output.g == 0)
+ retval.g = 0;
else
- retval.r = background.r ? 1 : 0;
- if(output.g > 0)
retval.g = 1 - min(1, (1 - background.g) / output.g);
+ if(background.b == 1)
+ retval.b = 1;
+ else
+ if(output.b == 0)
+ retval.b = 0;
else
- retval.g = background.g ? 1 : 0;
- if(output.b > 0)
retval.b = 1 - min(1, (1 - background.b) / output.b);
- else
- retval.b = background.b ? 1 : 0;
} else if(blendop == 8) {
if(output.r <= 0.5)
retval.r = 2 * output.r * background.r;
diff --git a/gfx/2d/ShadersD2D.h b/gfx/2d/ShadersD2D.h
index 7b8832c7d..9cc65a09b 100644
--- a/gfx/2d/ShadersD2D.h
+++ b/gfx/2d/ShadersD2D.h
@@ -130,7 +130,7 @@ technique10 SampleTexture
RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -213,7 +213,7 @@ technique10 SampleTexture
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Resource Bindings:
@@ -278,7 +278,7 @@ technique10 SampleTextureForSeparableBlending_1
RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -361,7 +361,7 @@ technique10 SampleTextureForSeparableBlending_1
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -429,58 +429,61 @@ technique10 SampleTextureForSeparableBlending_1
mov r0.w, c0.x
add r0.x, r0.w, c3.x
mul r0.x, r0.x, r0.x
- texld r1, t0, s0
- texld r2, t0, s1
+ texld r1, t0, s1
+ texld r2, t0, s0
rcp r0.y, r2.w
+ mad r3.xyz, r2, r0.y, -c2.x
+ mul r3.xyz, r3, r3
+ mad r4.xyz, r2, -r0.y, c2.x
+ rcp r3.w, r4.x
+ rcp r4.w, r1.w
+ mul r5.xyz, r1, r4.w
+ mad r1.xyz, r1, -r4.w, c2.z
+ mul r3.w, r3.w, r5.x
+ min r4.w, r3.w, c2.x
+ cmp r4.w, -r3.x, c2.x, r4.w
+ mul r6.xyz, r5, r5
+ cmp r7.x, -r6.x, c2.y, r4.w
+ rcp r4.w, r4.y
+ mul r4.w, r4.w, r5.y
+ min r5.w, r4.w, c2.x
+ cmp r4.w, -r3.y, c2.x, r5.w
+ cmp r7.y, -r6.y, c2.y, r4.w
+ rcp r4.w, r4.z
+ mul r4.w, r4.w, r5.z
+ min r5.w, r4.w, c2.x
+ cmp r4.w, -r3.z, c2.x, r5.w
+ cmp r7.z, -r6.z, c2.y, r4.w
mul r3.xyz, r0.y, r2
- mul r4.xyz, r3, r3
- cmp r4.xyz, -r4, c2.y, c2.x
- mad r5, r2.xyzx, -r0.y, c2.zzzx
- mad r0.yz, r2, -r0.y, c2.x
- rcp r3.w, r1.w
- mul r2.xyz, r1, r3.w
- rcp r4.w, r2.x
- mad r4.w, r5.w, -r4.w, c2.x
- max r5.w, r4.w, c2.y
- cmp r6.x, -r2.x, r4.x, r5.w
- rcp r5.w, r2.y
- mad r5.w, r0.y, -r5.w, c2.x
- max r6.w, r5.w, c2.y
- cmp r6.y, -r2.y, r4.y, r6.w
- rcp r5.w, r2.z
- mad r5.w, r0.z, -r5.w, c2.x
- max r6.w, r5.w, c2.y
- cmp r6.z, -r2.z, r4.z, r6.w
- max r4.xyz, r2, r3
- cmp r0.xyz, -r0.x, r4, r6
- add r4, r0.w, c1
- mul r4, r4, r4
- min r6.xyz, r3, r2
- cmp r0.xyz, -r4.w, r6, r0
- mad r6.xyz, r3, -c2.w, -c2.x
- add r6.xyz, -r6, c2.x
- mad r7.xyz, r1, -r3.w, c2.x
- mad r8.xyz, r1, r3.w, r3
- mad r8.xyz, r2, -r3, r8
- mad r6.xyz, r7, -r6, c2.x
- add r7.xyz, r3, r3
- mul r3.xyz, r3, r2
- mul r7.xyz, r2, r7
- cmp r5.xyz, r5, r7, r6
- cmp r0.xyz, -r4.z, r5, r0
- cmp r0.xyz, -r4.y, r8, r0
- cmp r0.xyz, -r4.x, r3, r0
- lrp r3.xyz, r2.w, r0, r2
- mul r3.w, r2.w, r2.w
- cmp r3.w, -r3.w, c2.x, c2.y
- mul r0.xyz, r1.w, r3
- mul r0.w, r1.w, r1.w
+ mad r6.xyz, r2, r0.y, r5
+ mad r6.xyz, r3, -r5, r6
+ max r8.xyz, r3, r5
+ cmp r0.xyz, -r0.x, r8, r7
+ add r7, r0.w, c1
+ mul r7, r7, r7
+ min r8.xyz, r5, r3
+ cmp r0.xyz, -r7.w, r8, r0
+ mad r8.xyz, r5, -c2.w, -c2.x
+ add r8.xyz, -r8, c2.x
+ mad r4.xyz, r4, -r8, c2.x
+ add r8.xyz, r5, r5
+ mul r5.xyz, r5, r3
+ mul r8.xyz, r3, r8
+ cmp r1.xyz, r1, r8, r4
+ cmp r0.xyz, -r7.z, r1, r0
+ cmp r0.xyz, -r7.y, r6, r0
+ cmp r0.xyz, -r7.x, r5, r0
+ lrp r4.xyz, r1.w, r0, r3
+ mul r4.w, r1.w, r1.w
+ cmp r4.w, -r4.w, c2.x, c2.y
+ mul r0.xyz, r2.w, r4
+ mul r0.w, r2.w, r2.w
cmp r0.w, -r0.w, c2.x, c2.y
- add r0.w, r3.w, r0.w
- cmp r1.xyz, -r0.w, r0, r1
- mov oC0, r1
+ add r0.w, r4.w, r0.w
+ cmp r2.xyz, -r0.w, r0, r2
+ mov oC0, r2
- // approximately 53 instruction slots used (2 texture, 51 arithmetic)
+ // approximately 56 instruction slots used (2 texture, 54 arithmetic)
ps_4_0
dcl_constantbuffer cb0[1], immediateIndexed
dcl_sampler s0, mode_default
@@ -526,16 +529,17 @@ technique10 SampleTextureForSeparableBlending_1
min r2.xyz, r0.xyzx, r1.xyzx
else
ieq r2.w, cb0[0].x, l(5)
- max r3.xyz, r0.xyzx, r1.xyzx
- lt r4.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r0.xyzx
- add r5.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
- div r5.xyz, r5.xyzx, r0.xyzx
- min r5.xyz, r5.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
- add r5.xyz, -r5.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
- ne r1.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r1.xyzx
- and r1.xyz, r1.xyzx, l(0x3f800000, 0x3f800000, 0x3f800000, 0)
- movc r1.xyz, r4.xyzx, r5.xyzx, r1.xyzx
- movc r2.xyz, r2.wwww, r3.xyzx, r1.xyzx
+ if_nz r2.w
+ max r2.xyz, r0.xyzx, r1.xyzx
+ else
+ eq r3.xyz, r1.xyzx, l(0.000000, 0.000000, 0.000000, 0.000000)
+ eq r4.xyz, r0.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ add r5.xyz, -r0.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ div r1.xyz, r1.xyzx, r5.xyzx
+ min r1.xyz, r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ movc r1.xyz, r4.xyzx, l(1.000000,1.000000,1.000000,0), r1.xyzx
+ movc r2.xyz, r3.xyzx, l(0,0,0,0), r1.xyzx
+ endif
endif
endif
endif
@@ -546,7 +550,7 @@ technique10 SampleTextureForSeparableBlending_1
mul o0.xyz, r0.wwww, r0.xyzx
mov o0.w, r0.w
ret
- // Approximately 56 instruction slots used
+ // Approximately 57 instruction slots used
};
}
@@ -560,7 +564,7 @@ technique10 SampleTextureForSeparableBlending_2
RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -643,7 +647,7 @@ technique10 SampleTextureForSeparableBlending_2
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -703,9 +707,9 @@ technique10 SampleTextureForSeparableBlending_2
//
ps_2_x
def c1, -7, -8, -9, -10
- def c2, 1, 0, 0.25, 0.5
- def c3, 2, -1, 16, -12
- def c4, 4, 2, 1, 0
+ def c2, 1, 0, -1, 0.25
+ def c3, 0.5, 2, -1, 4
+ def c4, 16, -12, 2, 1
dcl t0
dcl_2d s0
dcl_2d s1
@@ -715,10 +719,10 @@ technique10 SampleTextureForSeparableBlending_2
texld r1, t0, s0
texld r2, t0, s1
rcp r3.w, r2.w
- mad r3.xy, r2.yzzw, -r3.w, c2.z
+ mad r3.xy, r2.yzzw, -r3.w, c2.w
mul r4.xyz, r2, r3.w
- mad r5.xyz, r4, c3.z, c3.w
- mad r5.xyz, r5, r4, c4.x
+ mad r5.xyz, r4, c4.x, c4.y
+ mad r5.xyz, r5, r4, c3.w
mul r5.xyz, r4, r5
rsq r4.w, r4.y
rcp r4.w, r4.w
@@ -726,11 +730,11 @@ technique10 SampleTextureForSeparableBlending_2
mad r4.w, r2.y, -r3.w, r4.w
rcp r3.x, r1.w
mul r6.xyz, r1, r3.x
- mad r7.xyz, r6, c3.x, c3.y
+ mad r7.xyz, r6, c3.y, c3.z
mad r4.w, r7.y, r4.w, r4.y
- mad r8.xyz, r1, -r3.x, c2.w
- mad r9, r2.xyzx, -r3.w, c2.xxxz
- mad r10.xyz, r6, -c4.y, c4.z
+ mad r8.xyz, r1, -r3.x, c3.x
+ mad r9, r2.xyzx, -r3.w, c2.xxxw
+ mad r10.xyz, r6, -c4.z, c4.w
mul r10.xyz, r4, r10
mad r10.xyz, r10, -r9, r4
cmp r11.y, r8.y, r10.y, r4.w
@@ -744,36 +748,40 @@ technique10 SampleTextureForSeparableBlending_2
rcp r4.w, r4.w
cmp r4.w, r9.w, r5.x, r4.w
mad r4.w, r2.x, -r3.w, r4.w
+ mad r2.xyz, r2, r3.w, c2.z
+ mul r2.xyz, r2, r2
mad r4.w, r7.x, r4.w, r4.x
- add r2.xyz, -r7, c2.x
- mad r2.xyz, r9, -r2, c2.x
+ add r3.yzw, -r7.xxyz, c2.x
+ mad r3.yzw, r9.xxyz, -r3, c2.x
cmp r11.x, r8.x, r10.x, r4.w
- mad r3.yzw, r1.xxyz, r3.x, -r4.xxyz
- mad r5.xyz, r1, r3.x, r4
- abs r3.xyz, r3.yzww
- mul r7.xyz, r4, r6
- mad r5.xyz, r7, -c3.x, r5
- cmp r3.xyz, -r0.w, r3, r5
- cmp r3.xyz, -r0.z, r11, r3
- add r5.xyz, r6, r6
- mul r5.xyz, r4, r5
- mul r4.xyz, r4, r4
- cmp r4.xyz, -r4, c2.y, c2.x
- cmp r2.xyz, r8, r5, r2
- cmp r0.yzw, -r0.y, r2.xxyz, r3.xxyz
- rcp r4.w, r6.x
- mad r4.w, r9.x, -r4.w, c2.x
- max r6.w, r4.w, c2.y
- cmp r2.x, -r6.x, r4.x, r6.w
- rcp r6.w, r6.y
- mad r6.w, r9.y, -r6.w, c2.x
- max r3.x, r6.w, c2.y
- cmp r2.y, -r6.y, r4.y, r3.x
- rcp r6.w, r6.z
- mad r6.w, r9.z, -r6.w, c2.x
+ mad r5.xyz, r1, r3.x, -r4
+ mad r7.xyz, r1, r3.x, r4
+ abs r5.xyz, r5
+ mul r10.xyz, r4, r6
+ mad r7.xyz, r10, -c3.y, r7
+ cmp r5.xyz, -r0.w, r5, r7
+ cmp r5.xyz, -r0.z, r11, r5
+ add r7.xyz, r6, r6
+ mul r4.xyz, r4, r7
+ cmp r3.xyz, r8, r4, r3.yzww
+ cmp r0.yzw, -r0.y, r3.xxyz, r5.xxyz
+ rcp r6.w, r6.x
+ mad r6.w, r9.x, -r6.w, c2.x
max r3.x, r6.w, c2.y
- cmp r2.z, -r6.z, r4.z, r3.x
- cmp r0.xyz, -r0.x, r2, r0.yzww
+ mul r3.yzw, r6.xxyz, r6.xxyz
+ cmp r6.w, -r3.y, c2.y, r3.x
+ cmp r4.x, -r2.x, c2.x, r6.w
+ rcp r4.w, r6.y
+ mad r4.w, r9.y, -r4.w, c2.x
+ max r6.w, r4.w, c2.y
+ cmp r4.w, -r3.z, c2.y, r6.w
+ cmp r4.y, -r2.y, c2.x, r4.w
+ rcp r4.w, r6.z
+ mad r4.w, r9.z, -r4.w, c2.x
+ max r6.w, r4.w, c2.y
+ cmp r4.w, -r3.w, c2.y, r6.w
+ cmp r4.z, -r2.z, c2.x, r4.w
+ cmp r0.xyz, -r0.x, r4, r0.yzww
lrp r3.xyz, r2.w, r0, r6
mul r3.w, r2.w, r2.w
cmp r3.w, -r3.w, c2.x, c2.y
@@ -784,7 +792,7 @@ technique10 SampleTextureForSeparableBlending_2
cmp r1.xyz, -r0.w, r0, r1
mov oC0, r1
- // approximately 74 instruction slots used (2 texture, 72 arithmetic)
+ // approximately 78 instruction slots used (2 texture, 76 arithmetic)
ps_4_0
dcl_constantbuffer cb0[1], immediateIndexed
dcl_sampler s0, mode_default
@@ -807,14 +815,14 @@ technique10 SampleTextureForSeparableBlending_2
div r1.xyz, r1.xyzx, r1.wwww
ieq r2.x, cb0[0].x, l(7)
if_nz r2.x
- lt r2.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r0.xyzx
- add r3.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
- div r3.xyz, r3.xyzx, r0.xyzx
- min r3.xyz, r3.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
- add r3.xyz, -r3.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
- ne r4.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r1.xyzx
- and r4.xyz, r4.xyzx, l(0x3f800000, 0x3f800000, 0x3f800000, 0)
- movc r2.xyz, r2.xyzx, r3.xyzx, r4.xyzx
+ eq r2.xyz, r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ eq r3.xyz, r0.xyzx, l(0.000000, 0.000000, 0.000000, 0.000000)
+ add r4.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ div r4.xyz, r4.xyzx, r0.xyzx
+ min r4.xyz, r4.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ add r4.xyz, -r4.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ movc r3.xyz, r3.xyzx, l(0,0,0,0), r4.xyzx
+ movc r2.xyz, r2.xyzx, l(1.000000,1.000000,1.000000,0), r3.xyzx
else
ieq r2.w, cb0[0].x, l(8)
if_nz r2.w
@@ -874,7 +882,7 @@ technique10 SampleTextureForNonSeparableBlending
RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -957,7 +965,7 @@ technique10 SampleTextureForNonSeparableBlending
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -1435,7 +1443,7 @@ technique10 SampleRadialGradient
RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -1552,7 +1560,7 @@ technique10 SampleRadialGradient
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -1704,7 +1712,7 @@ technique10 SampleRadialGradient
RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -1821,7 +1829,7 @@ technique10 SampleRadialGradient
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -1948,7 +1956,7 @@ technique10 SampleRadialGradient
RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2065,7 +2073,7 @@ technique10 SampleRadialGradient
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2217,7 +2225,7 @@ technique10 SampleRadialGradient
RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2334,7 +2342,7 @@ technique10 SampleRadialGradient
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2461,7 +2469,7 @@ technique10 SampleRadialGradient
RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2578,7 +2586,7 @@ technique10 SampleRadialGradient
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2730,7 +2738,7 @@ technique10 SampleRadialGradient
RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2847,7 +2855,7 @@ technique10 SampleRadialGradient
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2978,7 +2986,7 @@ technique10 SampleMaskedTexture
RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3061,7 +3069,7 @@ technique10 SampleMaskedTexture
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Resource Bindings:
@@ -3142,7 +3150,7 @@ technique10 SampleTextureWithShadow
BlendState = ShadowBlendH;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3225,7 +3233,7 @@ technique10 SampleTextureWithShadow
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3377,7 +3385,7 @@ technique10 SampleTextureWithShadow
BlendState = ShadowBlendV;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3460,7 +3468,7 @@ technique10 SampleTextureWithShadow
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3609,7 +3617,7 @@ technique10 SampleTextureWithShadow
BlendState = ShadowBlendV;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3692,7 +3700,7 @@ technique10 SampleTextureWithShadow
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3857,7 +3865,7 @@ technique10 SampleTextTexture
BlendState = bTextBlend;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3940,7 +3948,7 @@ technique10 SampleTextTexture
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -4037,7 +4045,7 @@ technique10 SampleTextTexture
BlendState = bTextBlend;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -4120,7 +4128,7 @@ technique10 SampleTextTexture
GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -4227,19 +4235,19 @@ technique10 SampleTextTexture
const BYTE d2deffect[] =
{
- 68, 88, 66, 67, 161, 21,
- 0, 226, 106, 112, 70, 241,
- 114, 10, 21, 220, 141, 49,
- 31, 193, 1, 0, 0, 0,
- 190, 19, 1, 0, 1, 0,
+ 68, 88, 66, 67, 116, 210,
+ 237, 43, 26, 169, 147, 99,
+ 62, 90, 128, 241, 238, 193,
+ 236, 181, 1, 0, 0, 0,
+ 242, 19, 1, 0, 1, 0,
0, 0, 36, 0, 0, 0,
- 70, 88, 49, 48, 146, 19,
+ 70, 88, 49, 48, 198, 19,
1, 0, 1, 16, 255, 254,
4, 0, 0, 0, 16, 0,
0, 0, 13, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 8, 0, 0, 0, 10, 7,
+ 8, 0, 0, 0, 62, 7,
1, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
0, 0, 3, 0, 0, 0,
@@ -4456,16 +4464,16 @@ const BYTE d2deffect[] =
0, 0, 0, 83, 97, 109,
112, 108, 101, 84, 101, 120,
116, 117, 114, 101, 0, 80,
- 48, 0, 72, 4, 0, 0,
- 68, 88, 66, 67, 28, 248,
- 40, 83, 2, 166, 203, 194,
- 228, 163, 91, 123, 149, 165,
- 41, 212, 1, 0, 0, 0,
- 72, 4, 0, 0, 6, 0,
+ 48, 0, 68, 4, 0, 0,
+ 68, 88, 66, 67, 77, 85,
+ 167, 240, 56, 56, 155, 78,
+ 125, 96, 49, 253, 103, 100,
+ 22, 62, 1, 0, 0, 0,
+ 68, 4, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
248, 0, 0, 0, 244, 1,
0, 0, 112, 2, 0, 0,
- 164, 3, 0, 0, 216, 3,
+ 160, 3, 0, 0, 212, 3,
0, 0, 65, 111, 110, 57,
184, 0, 0, 0, 184, 0,
0, 0, 0, 2, 254, 255,
@@ -4544,7 +4552,7 @@ const BYTE d2deffect[] =
116, 0, 0, 0, 5, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 4, 0,
- 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -4554,14 +4562,14 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 82, 68, 69, 70, 44, 1,
+ 82, 68, 69, 70, 40, 1,
0, 0, 1, 0, 0, 0,
64, 0, 0, 0, 1, 0,
0, 0, 28, 0, 0, 0,
@@ -4609,97 +4617,93 @@ const BYTE d2deffect[] =
83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
- 171, 171, 73, 83, 71, 78,
- 44, 0, 0, 0, 1, 0,
- 0, 0, 8, 0, 0, 0,
- 32, 0, 0, 0, 0, 0,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 73, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 7, 3, 0, 0,
- 80, 79, 83, 73, 84, 73,
- 79, 78, 0, 171, 171, 171,
- 79, 83, 71, 78, 104, 0,
0, 0, 3, 0, 0, 0,
- 8, 0, 0, 0, 80, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 3, 0,
- 0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 92, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 12, 0, 0, 92, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 7, 3,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 12, 3, 0, 0, 83, 86,
- 95, 80, 111, 115, 105, 116,
- 105, 111, 110, 0, 84, 69,
- 88, 67, 79, 79, 82, 68,
- 0, 171, 171, 171, 232, 4,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 2, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 12, 0, 0,
+ 92, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 212, 2, 0, 0, 68, 88,
- 66, 67, 211, 98, 170, 114,
- 188, 232, 19, 214, 0, 60,
- 65, 88, 34, 156, 69, 131,
- 1, 0, 0, 0, 212, 2,
- 0, 0, 6, 0, 0, 0,
- 56, 0, 0, 0, 164, 0,
- 0, 0, 16, 1, 0, 0,
- 140, 1, 0, 0, 48, 2,
- 0, 0, 160, 2, 0, 0,
- 65, 111, 110, 57, 100, 0,
- 0, 0, 100, 0, 0, 0,
- 0, 2, 255, 255, 60, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 3, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 232, 4, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 212, 2, 0, 0,
+ 68, 88, 66, 67, 17, 106,
+ 69, 218, 119, 68, 79, 85,
+ 211, 176, 27, 183, 77, 210,
+ 131, 41, 1, 0, 0, 0,
+ 212, 2, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 164, 0, 0, 0, 16, 1,
+ 0, 0, 140, 1, 0, 0,
+ 48, 2, 0, 0, 160, 2,
+ 0, 0, 65, 111, 110, 57,
+ 100, 0, 0, 0, 100, 0,
+ 0, 0, 0, 2, 255, 255,
+ 60, 0, 0, 0, 40, 0,
+ 0, 0, 0, 0, 40, 0,
0, 0, 40, 0, 0, 0,
+ 40, 0, 1, 0, 36, 0,
0, 0, 40, 0, 0, 0,
- 40, 0, 0, 0, 40, 0,
- 1, 0, 36, 0, 0, 0,
- 40, 0, 0, 0, 0, 0,
- 1, 2, 255, 255, 31, 0,
- 0, 2, 0, 0, 0, 128,
- 0, 0, 15, 176, 31, 0,
- 0, 2, 0, 0, 0, 144,
- 0, 8, 15, 160, 66, 0,
- 0, 3, 0, 0, 15, 128,
- 0, 0, 228, 176, 0, 8,
- 228, 160, 1, 0, 0, 2,
- 0, 8, 15, 128, 0, 0,
- 228, 128, 255, 255, 0, 0,
- 83, 72, 68, 82, 100, 0,
- 0, 0, 64, 0, 0, 0,
- 25, 0, 0, 0, 90, 0,
- 0, 3, 0, 96, 16, 0,
- 0, 0, 0, 0, 88, 24,
- 0, 4, 0, 112, 16, 0,
- 0, 0, 0, 0, 85, 85,
- 0, 0, 98, 16, 0, 3,
- 50, 16, 16, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 69, 0, 0, 9,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 70, 16, 16, 0,
- 1, 0, 0, 0, 70, 126,
+ 0, 0, 1, 2, 255, 255,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 100, 0, 0, 0, 64, 0,
+ 0, 0, 25, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
16, 0, 0, 0, 0, 0,
- 0, 96, 16, 0, 0, 0,
- 0, 0, 62, 0, 0, 1,
- 83, 84, 65, 84, 116, 0,
- 0, 0, 2, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 69, 0,
+ 0, 9, 242, 32, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -4709,451 +4713,461 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 82, 68,
- 69, 70, 156, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 28, 0, 0, 0, 0, 4,
- 255, 255, 0, 1, 0, 0,
- 105, 0, 0, 0, 92, 0,
- 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 101, 0, 0, 0, 2, 0,
- 0, 0, 5, 0, 0, 0,
- 4, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 1, 0, 0, 0, 12, 0,
- 0, 0, 115, 83, 97, 109,
- 112, 108, 101, 114, 0, 116,
- 101, 120, 0, 77, 105, 99,
- 114, 111, 115, 111, 102, 116,
- 32, 40, 82, 41, 32, 72,
- 76, 83, 76, 32, 83, 104,
- 97, 100, 101, 114, 32, 67,
- 111, 109, 112, 105, 108, 101,
- 114, 32, 57, 46, 51, 48,
- 46, 57, 50, 48, 48, 46,
- 49, 54, 51, 56, 52, 0,
- 73, 83, 71, 78, 104, 0,
- 0, 0, 3, 0, 0, 0,
- 8, 0, 0, 0, 80, 0,
+ 82, 68, 69, 70, 156, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 105, 0, 0, 0,
+ 92, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 92, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 101, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
0, 0, 1, 0, 0, 0,
- 3, 3, 0, 0, 92, 0,
+ 12, 0, 0, 0, 115, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 116, 101, 120, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 73, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
0, 0, 1, 0, 0, 0,
- 12, 0, 0, 0, 83, 86,
- 95, 80, 111, 115, 105, 116,
- 105, 111, 110, 0, 84, 69,
- 88, 67, 79, 79, 82, 68,
- 0, 171, 171, 171, 79, 83,
- 71, 78, 44, 0, 0, 0,
- 1, 0, 0, 0, 8, 0,
- 0, 0, 32, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 15, 0,
- 0, 0, 83, 86, 95, 84,
- 97, 114, 103, 101, 116, 0,
- 171, 171, 72, 9, 0, 0,
- 0, 0, 0, 0, 83, 97,
- 109, 112, 108, 101, 84, 101,
- 120, 116, 117, 114, 101, 70,
- 111, 114, 83, 101, 112, 97,
- 114, 97, 98, 108, 101, 66,
- 108, 101, 110, 100, 105, 110,
- 103, 95, 49, 0, 72, 4,
- 0, 0, 68, 88, 66, 67,
- 28, 248, 40, 83, 2, 166,
- 203, 194, 228, 163, 91, 123,
- 149, 165, 41, 212, 1, 0,
- 0, 0, 72, 4, 0, 0,
- 6, 0, 0, 0, 56, 0,
- 0, 0, 248, 0, 0, 0,
- 244, 1, 0, 0, 112, 2,
- 0, 0, 164, 3, 0, 0,
- 216, 3, 0, 0, 65, 111,
- 110, 57, 184, 0, 0, 0,
- 184, 0, 0, 0, 0, 2,
- 254, 255, 132, 0, 0, 0,
- 52, 0, 0, 0, 1, 0,
- 36, 0, 0, 0, 48, 0,
- 0, 0, 48, 0, 0, 0,
- 36, 0, 1, 0, 48, 0,
0, 0, 0, 0, 3, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 2,
- 254, 255, 81, 0, 0, 5,
- 4, 0, 15, 160, 0, 0,
- 0, 0, 0, 0, 128, 63,
0, 0, 0, 0, 0, 0,
- 0, 0, 31, 0, 0, 2,
- 5, 0, 0, 128, 0, 0,
- 15, 144, 4, 0, 0, 4,
- 0, 0, 3, 224, 0, 0,
- 228, 144, 2, 0, 238, 160,
- 2, 0, 228, 160, 4, 0,
- 0, 4, 0, 0, 12, 224,
- 0, 0, 20, 144, 3, 0,
- 180, 160, 3, 0, 20, 160,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171, 68, 9,
+ 0, 0, 0, 0, 0, 0,
+ 83, 97, 109, 112, 108, 101,
+ 84, 101, 120, 116, 117, 114,
+ 101, 70, 111, 114, 83, 101,
+ 112, 97, 114, 97, 98, 108,
+ 101, 66, 108, 101, 110, 100,
+ 105, 110, 103, 95, 49, 0,
+ 68, 4, 0, 0, 68, 88,
+ 66, 67, 77, 85, 167, 240,
+ 56, 56, 155, 78, 125, 96,
+ 49, 253, 103, 100, 22, 62,
+ 1, 0, 0, 0, 68, 4,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 248, 0,
+ 0, 0, 244, 1, 0, 0,
+ 112, 2, 0, 0, 160, 3,
+ 0, 0, 212, 3, 0, 0,
+ 65, 111, 110, 57, 184, 0,
+ 0, 0, 184, 0, 0, 0,
+ 0, 2, 254, 255, 132, 0,
+ 0, 0, 52, 0, 0, 0,
+ 1, 0, 36, 0, 0, 0,
+ 48, 0, 0, 0, 48, 0,
+ 0, 0, 36, 0, 1, 0,
+ 48, 0, 0, 0, 0, 0,
+ 3, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 254, 255, 81, 0,
+ 0, 5, 4, 0, 15, 160,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 5, 0, 0, 128,
+ 0, 0, 15, 144, 4, 0,
+ 0, 4, 0, 0, 3, 224,
+ 0, 0, 228, 144, 2, 0,
+ 238, 160, 2, 0, 228, 160,
4, 0, 0, 4, 0, 0,
- 3, 128, 0, 0, 228, 144,
- 1, 0, 238, 160, 1, 0,
- 228, 160, 2, 0, 0, 3,
- 0, 0, 3, 192, 0, 0,
- 228, 128, 0, 0, 228, 160,
- 1, 0, 0, 2, 0, 0,
- 12, 192, 4, 0, 68, 160,
- 255, 255, 0, 0, 83, 72,
- 68, 82, 244, 0, 0, 0,
- 64, 0, 1, 0, 61, 0,
- 0, 0, 89, 0, 0, 4,
- 70, 142, 32, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 95, 0, 0, 3, 50, 16,
- 16, 0, 0, 0, 0, 0,
- 103, 0, 0, 4, 242, 32,
- 16, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 101, 0,
- 0, 3, 50, 32, 16, 0,
- 1, 0, 0, 0, 101, 0,
- 0, 3, 194, 32, 16, 0,
- 1, 0, 0, 0, 50, 0,
- 0, 11, 50, 32, 16, 0,
- 0, 0, 0, 0, 70, 16,
+ 12, 224, 0, 0, 20, 144,
+ 3, 0, 180, 160, 3, 0,
+ 20, 160, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 1, 0, 238, 160,
+ 1, 0, 228, 160, 2, 0,
+ 0, 3, 0, 0, 3, 192,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 12, 192, 4, 0,
+ 68, 160, 255, 255, 0, 0,
+ 83, 72, 68, 82, 244, 0,
+ 0, 0, 64, 0, 1, 0,
+ 61, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 95, 0, 0, 3,
+ 50, 16, 16, 0, 0, 0,
+ 0, 0, 103, 0, 0, 4,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 50, 32,
16, 0, 0, 0, 0, 0,
- 230, 138, 32, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
0, 0, 0, 0, 0, 0,
- 70, 128, 32, 0, 0, 0,
+ 0, 0, 70, 128, 32, 0,
0, 0, 0, 0, 0, 0,
- 54, 0, 0, 8, 194, 32,
- 16, 0, 0, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 194, 32, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 128, 63, 50, 0, 0, 11,
- 50, 32, 16, 0, 1, 0,
- 0, 0, 70, 16, 16, 0,
- 0, 0, 0, 0, 230, 138,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 70, 128,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 50, 0,
- 0, 11, 194, 32, 16, 0,
- 1, 0, 0, 0, 6, 20,
+ 0, 0, 128, 63, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
16, 0, 0, 0, 0, 0,
- 166, 142, 32, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 6, 132, 32, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 62, 0, 0, 1, 83, 84,
- 65, 84, 116, 0, 0, 0,
- 5, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 6, 20, 16, 0, 0, 0,
+ 0, 0, 166, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 6, 132, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 82, 68, 69, 70,
- 44, 1, 0, 0, 1, 0,
- 0, 0, 64, 0, 0, 0,
- 1, 0, 0, 0, 28, 0,
- 0, 0, 0, 4, 254, 255,
- 0, 1, 0, 0, 246, 0,
- 0, 0, 60, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 40, 1, 0, 0,
+ 1, 0, 0, 0, 64, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 246, 0, 0, 0, 60, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 99, 98,
- 48, 0, 60, 0, 0, 0,
- 4, 0, 0, 0, 88, 0,
- 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 184, 0, 0, 0,
- 0, 0, 0, 0, 16, 0,
- 0, 0, 2, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 212, 0, 0, 0,
- 16, 0, 0, 0, 16, 0,
- 0, 0, 2, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 222, 0, 0, 0,
- 32, 0, 0, 0, 16, 0,
- 0, 0, 2, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 236, 0, 0, 0,
- 48, 0, 0, 0, 16, 0,
+ 99, 98, 48, 0, 60, 0,
+ 0, 0, 4, 0, 0, 0,
+ 88, 0, 0, 0, 64, 0,
0, 0, 0, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 81, 117, 97, 100,
- 68, 101, 115, 99, 0, 171,
- 171, 171, 1, 0, 3, 0,
- 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
0, 0, 0, 0, 0, 0,
- 84, 101, 120, 67, 111, 111,
- 114, 100, 115, 0, 77, 97,
- 115, 107, 84, 101, 120, 67,
- 111, 111, 114, 100, 115, 0,
- 84, 101, 120, 116, 67, 111,
- 108, 111, 114, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 171, 171, 73, 83,
- 71, 78, 44, 0, 0, 0,
- 1, 0, 0, 0, 8, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 212, 0,
+ 0, 0, 16, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 222, 0,
0, 0, 32, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 236, 0,
+ 0, 0, 48, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 81, 117,
+ 97, 100, 68, 101, 115, 99,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 7, 3,
- 0, 0, 80, 79, 83, 73,
- 84, 73, 79, 78, 0, 171,
- 171, 171, 79, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
+ 0, 0, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 77, 97, 115, 107, 84, 101,
+ 120, 67, 111, 111, 114, 100,
+ 115, 0, 84, 101, 120, 116,
+ 67, 111, 108, 111, 114, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 54,
+ 46, 51, 46, 57, 54, 48,
+ 48, 46, 49, 54, 51, 56,
+ 52, 0, 73, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 0, 0, 7, 3, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 171, 171, 171,
+ 79, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 12, 0, 0,
- 92, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 3, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 76, 12, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 1, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
- 0, 0, 68, 13, 0, 0,
- 68, 88, 66, 67, 245, 249,
- 228, 50, 151, 61, 21, 152,
- 221, 49, 83, 177, 245, 217,
- 117, 158, 1, 0, 0, 0,
- 68, 13, 0, 0, 6, 0,
- 0, 0, 56, 0, 0, 0,
- 128, 4, 0, 0, 184, 10,
- 0, 0, 52, 11, 0, 0,
- 160, 12, 0, 0, 16, 13,
- 0, 0, 65, 111, 110, 57,
- 64, 4, 0, 0, 64, 4,
- 0, 0, 0, 2, 255, 255,
- 8, 4, 0, 0, 56, 0,
- 0, 0, 1, 0, 44, 0,
- 0, 0, 56, 0, 0, 0,
- 56, 0, 2, 0, 36, 0,
+ 3, 12, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 72, 12,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 72, 13, 0, 0, 68, 88,
+ 66, 67, 193, 65, 249, 15,
+ 188, 209, 36, 123, 179, 111,
+ 3, 63, 40, 10, 7, 98,
+ 1, 0, 0, 0, 72, 13,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 172, 4,
+ 0, 0, 188, 10, 0, 0,
+ 56, 11, 0, 0, 164, 12,
+ 0, 0, 20, 13, 0, 0,
+ 65, 111, 110, 57, 108, 4,
+ 0, 0, 108, 4, 0, 0,
+ 0, 2, 255, 255, 52, 4,
0, 0, 56, 0, 0, 0,
- 0, 0, 1, 1, 1, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 3, 0, 0, 0,
- 1, 2, 255, 255, 81, 0,
- 0, 5, 1, 0, 15, 160,
- 0, 0, 128, 191, 0, 0,
- 0, 192, 0, 0, 64, 192,
- 0, 0, 128, 192, 81, 0,
- 0, 5, 2, 0, 15, 160,
- 0, 0, 128, 63, 0, 0,
- 0, 0, 0, 0, 0, 63,
- 0, 0, 0, 192, 81, 0,
- 0, 5, 3, 0, 15, 160,
- 0, 0, 160, 192, 0, 0,
+ 1, 0, 44, 0, 0, 0,
+ 56, 0, 0, 0, 56, 0,
+ 2, 0, 36, 0, 0, 0,
+ 56, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 1, 0, 15, 160, 0, 0,
+ 128, 191, 0, 0, 0, 192,
+ 0, 0, 64, 192, 0, 0,
+ 128, 192, 81, 0, 0, 5,
+ 2, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 192, 81, 0, 0, 5,
+ 3, 0, 15, 160, 0, 0,
+ 160, 192, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 31, 0,
- 0, 2, 0, 0, 0, 128,
- 0, 0, 15, 176, 31, 0,
- 0, 2, 0, 0, 0, 144,
- 0, 8, 15, 160, 31, 0,
- 0, 2, 0, 0, 0, 144,
- 1, 8, 15, 160, 1, 0,
- 0, 2, 0, 0, 8, 128,
- 0, 0, 0, 160, 2, 0,
- 0, 3, 0, 0, 1, 128,
- 0, 0, 255, 128, 3, 0,
- 0, 160, 5, 0, 0, 3,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 1, 0, 0, 2,
+ 0, 0, 8, 128, 0, 0,
+ 0, 160, 2, 0, 0, 3,
0, 0, 1, 128, 0, 0,
- 0, 128, 0, 0, 0, 128,
- 66, 0, 0, 3, 1, 0,
- 15, 128, 0, 0, 228, 176,
- 0, 8, 228, 160, 66, 0,
- 0, 3, 2, 0, 15, 128,
+ 255, 128, 3, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 0, 0, 0, 128, 66, 0,
+ 0, 3, 1, 0, 15, 128,
0, 0, 228, 176, 1, 8,
- 228, 160, 6, 0, 0, 2,
- 0, 0, 2, 128, 2, 0,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 6, 0, 0, 2, 0, 0,
+ 2, 128, 2, 0, 255, 128,
+ 4, 0, 0, 4, 3, 0,
+ 7, 128, 2, 0, 228, 128,
+ 0, 0, 85, 128, 2, 0,
+ 0, 161, 5, 0, 0, 3,
+ 3, 0, 7, 128, 3, 0,
+ 228, 128, 3, 0, 228, 128,
+ 4, 0, 0, 4, 4, 0,
+ 7, 128, 2, 0, 228, 128,
+ 0, 0, 85, 129, 2, 0,
+ 0, 160, 6, 0, 0, 2,
+ 3, 0, 8, 128, 4, 0,
+ 0, 128, 6, 0, 0, 2,
+ 4, 0, 8, 128, 1, 0,
255, 128, 5, 0, 0, 3,
- 3, 0, 7, 128, 0, 0,
- 85, 128, 2, 0, 228, 128,
+ 5, 0, 7, 128, 1, 0,
+ 228, 128, 4, 0, 255, 128,
+ 4, 0, 0, 4, 1, 0,
+ 7, 128, 1, 0, 228, 128,
+ 4, 0, 255, 129, 2, 0,
+ 170, 160, 5, 0, 0, 3,
+ 3, 0, 8, 128, 3, 0,
+ 255, 128, 5, 0, 0, 128,
+ 10, 0, 0, 3, 4, 0,
+ 8, 128, 3, 0, 255, 128,
+ 2, 0, 0, 160, 88, 0,
+ 0, 4, 4, 0, 8, 128,
+ 3, 0, 0, 129, 2, 0,
+ 0, 160, 4, 0, 255, 128,
+ 5, 0, 0, 3, 6, 0,
+ 7, 128, 5, 0, 228, 128,
+ 5, 0, 228, 128, 88, 0,
+ 0, 4, 7, 0, 1, 128,
+ 6, 0, 0, 129, 2, 0,
+ 85, 160, 4, 0, 255, 128,
+ 6, 0, 0, 2, 4, 0,
+ 8, 128, 4, 0, 85, 128,
5, 0, 0, 3, 4, 0,
- 7, 128, 3, 0, 228, 128,
- 3, 0, 228, 128, 88, 0,
- 0, 4, 4, 0, 7, 128,
- 4, 0, 228, 129, 2, 0,
- 85, 160, 2, 0, 0, 160,
- 4, 0, 0, 4, 5, 0,
- 15, 128, 2, 0, 36, 128,
- 0, 0, 85, 129, 2, 0,
- 42, 160, 4, 0, 0, 4,
- 0, 0, 6, 128, 2, 0,
- 228, 128, 0, 0, 85, 129,
- 2, 0, 0, 160, 6, 0,
- 0, 2, 3, 0, 8, 128,
- 1, 0, 255, 128, 5, 0,
- 0, 3, 2, 0, 7, 128,
- 1, 0, 228, 128, 3, 0,
- 255, 128, 6, 0, 0, 2,
- 4, 0, 8, 128, 2, 0,
- 0, 128, 4, 0, 0, 4,
- 4, 0, 8, 128, 5, 0,
- 255, 128, 4, 0, 255, 129,
- 2, 0, 0, 160, 11, 0,
+ 8, 128, 4, 0, 255, 128,
+ 5, 0, 85, 128, 10, 0,
0, 3, 5, 0, 8, 128,
4, 0, 255, 128, 2, 0,
- 85, 160, 88, 0, 0, 4,
- 6, 0, 1, 128, 2, 0,
- 0, 129, 4, 0, 0, 128,
- 5, 0, 255, 128, 6, 0,
- 0, 2, 5, 0, 8, 128,
- 2, 0, 85, 128, 4, 0,
- 0, 4, 5, 0, 8, 128,
- 0, 0, 85, 128, 5, 0,
- 255, 129, 2, 0, 0, 160,
- 11, 0, 0, 3, 6, 0,
- 8, 128, 5, 0, 255, 128,
- 2, 0, 85, 160, 88, 0,
- 0, 4, 6, 0, 2, 128,
- 2, 0, 85, 129, 4, 0,
- 85, 128, 6, 0, 255, 128,
- 6, 0, 0, 2, 5, 0,
- 8, 128, 2, 0, 170, 128,
- 4, 0, 0, 4, 5, 0,
- 8, 128, 0, 0, 170, 128,
- 5, 0, 255, 129, 2, 0,
- 0, 160, 11, 0, 0, 3,
- 6, 0, 8, 128, 5, 0,
- 255, 128, 2, 0, 85, 160,
- 88, 0, 0, 4, 6, 0,
- 4, 128, 2, 0, 170, 129,
- 4, 0, 170, 128, 6, 0,
- 255, 128, 11, 0, 0, 3,
- 4, 0, 7, 128, 2, 0,
- 228, 128, 3, 0, 228, 128,
+ 0, 160, 88, 0, 0, 4,
+ 4, 0, 8, 128, 3, 0,
+ 85, 129, 2, 0, 0, 160,
+ 5, 0, 255, 128, 88, 0,
+ 0, 4, 7, 0, 2, 128,
+ 6, 0, 85, 129, 2, 0,
+ 85, 160, 4, 0, 255, 128,
+ 6, 0, 0, 2, 4, 0,
+ 8, 128, 4, 0, 170, 128,
+ 5, 0, 0, 3, 4, 0,
+ 8, 128, 4, 0, 255, 128,
+ 5, 0, 170, 128, 10, 0,
+ 0, 3, 5, 0, 8, 128,
+ 4, 0, 255, 128, 2, 0,
+ 0, 160, 88, 0, 0, 4,
+ 4, 0, 8, 128, 3, 0,
+ 170, 129, 2, 0, 0, 160,
+ 5, 0, 255, 128, 88, 0,
+ 0, 4, 7, 0, 4, 128,
+ 6, 0, 170, 129, 2, 0,
+ 85, 160, 4, 0, 255, 128,
+ 5, 0, 0, 3, 3, 0,
+ 7, 128, 0, 0, 85, 128,
+ 2, 0, 228, 128, 4, 0,
+ 0, 4, 6, 0, 7, 128,
+ 2, 0, 228, 128, 0, 0,
+ 85, 128, 5, 0, 228, 128,
+ 4, 0, 0, 4, 6, 0,
+ 7, 128, 3, 0, 228, 128,
+ 5, 0, 228, 129, 6, 0,
+ 228, 128, 11, 0, 0, 3,
+ 8, 0, 7, 128, 3, 0,
+ 228, 128, 5, 0, 228, 128,
88, 0, 0, 4, 0, 0,
7, 128, 0, 0, 0, 129,
- 4, 0, 228, 128, 6, 0,
+ 8, 0, 228, 128, 7, 0,
228, 128, 2, 0, 0, 3,
- 4, 0, 15, 128, 0, 0,
+ 7, 0, 15, 128, 0, 0,
255, 128, 1, 0, 228, 160,
- 5, 0, 0, 3, 4, 0,
- 15, 128, 4, 0, 228, 128,
- 4, 0, 228, 128, 10, 0,
- 0, 3, 6, 0, 7, 128,
- 3, 0, 228, 128, 2, 0,
+ 5, 0, 0, 3, 7, 0,
+ 15, 128, 7, 0, 228, 128,
+ 7, 0, 228, 128, 10, 0,
+ 0, 3, 8, 0, 7, 128,
+ 5, 0, 228, 128, 3, 0,
228, 128, 88, 0, 0, 4,
- 0, 0, 7, 128, 4, 0,
- 255, 129, 6, 0, 228, 128,
+ 0, 0, 7, 128, 7, 0,
+ 255, 129, 8, 0, 228, 128,
0, 0, 228, 128, 4, 0,
- 0, 4, 6, 0, 7, 128,
- 3, 0, 228, 128, 2, 0,
+ 0, 4, 8, 0, 7, 128,
+ 5, 0, 228, 128, 2, 0,
255, 161, 2, 0, 0, 161,
- 2, 0, 0, 3, 6, 0,
- 7, 128, 6, 0, 228, 129,
+ 2, 0, 0, 3, 8, 0,
+ 7, 128, 8, 0, 228, 129,
2, 0, 0, 160, 4, 0,
- 0, 4, 7, 0, 7, 128,
- 1, 0, 228, 128, 3, 0,
- 255, 129, 2, 0, 0, 160,
- 4, 0, 0, 4, 8, 0,
- 7, 128, 1, 0, 228, 128,
- 3, 0, 255, 128, 3, 0,
- 228, 128, 4, 0, 0, 4,
- 8, 0, 7, 128, 2, 0,
- 228, 128, 3, 0, 228, 129,
- 8, 0, 228, 128, 4, 0,
- 0, 4, 6, 0, 7, 128,
- 7, 0, 228, 128, 6, 0,
+ 0, 4, 4, 0, 7, 128,
+ 4, 0, 228, 128, 8, 0,
228, 129, 2, 0, 0, 160,
- 2, 0, 0, 3, 7, 0,
- 7, 128, 3, 0, 228, 128,
- 3, 0, 228, 128, 5, 0,
- 0, 3, 3, 0, 7, 128,
- 3, 0, 228, 128, 2, 0,
- 228, 128, 5, 0, 0, 3,
- 7, 0, 7, 128, 2, 0,
- 228, 128, 7, 0, 228, 128,
- 88, 0, 0, 4, 5, 0,
+ 2, 0, 0, 3, 8, 0,
7, 128, 5, 0, 228, 128,
- 7, 0, 228, 128, 6, 0,
+ 5, 0, 228, 128, 5, 0,
+ 0, 3, 5, 0, 7, 128,
+ 5, 0, 228, 128, 3, 0,
+ 228, 128, 5, 0, 0, 3,
+ 8, 0, 7, 128, 3, 0,
+ 228, 128, 8, 0, 228, 128,
+ 88, 0, 0, 4, 1, 0,
+ 7, 128, 1, 0, 228, 128,
+ 8, 0, 228, 128, 4, 0,
228, 128, 88, 0, 0, 4,
- 0, 0, 7, 128, 4, 0,
- 170, 129, 5, 0, 228, 128,
+ 0, 0, 7, 128, 7, 0,
+ 170, 129, 1, 0, 228, 128,
0, 0, 228, 128, 88, 0,
0, 4, 0, 0, 7, 128,
- 4, 0, 85, 129, 8, 0,
+ 7, 0, 85, 129, 6, 0,
228, 128, 0, 0, 228, 128,
88, 0, 0, 4, 0, 0,
- 7, 128, 4, 0, 0, 129,
- 3, 0, 228, 128, 0, 0,
+ 7, 128, 7, 0, 0, 129,
+ 5, 0, 228, 128, 0, 0,
228, 128, 18, 0, 0, 4,
- 3, 0, 7, 128, 2, 0,
+ 4, 0, 7, 128, 1, 0,
255, 128, 0, 0, 228, 128,
- 2, 0, 228, 128, 5, 0,
- 0, 3, 3, 0, 8, 128,
- 2, 0, 255, 128, 2, 0,
+ 3, 0, 228, 128, 5, 0,
+ 0, 3, 4, 0, 8, 128,
+ 1, 0, 255, 128, 1, 0,
255, 128, 88, 0, 0, 4,
- 3, 0, 8, 128, 3, 0,
+ 4, 0, 8, 128, 4, 0,
255, 129, 2, 0, 0, 160,
2, 0, 85, 160, 5, 0,
0, 3, 0, 0, 7, 128,
- 1, 0, 255, 128, 3, 0,
+ 2, 0, 255, 128, 4, 0,
228, 128, 5, 0, 0, 3,
- 0, 0, 8, 128, 1, 0,
- 255, 128, 1, 0, 255, 128,
+ 0, 0, 8, 128, 2, 0,
+ 255, 128, 2, 0, 255, 128,
88, 0, 0, 4, 0, 0,
8, 128, 0, 0, 255, 129,
2, 0, 0, 160, 2, 0,
85, 160, 2, 0, 0, 3,
- 0, 0, 8, 128, 3, 0,
+ 0, 0, 8, 128, 4, 0,
255, 128, 0, 0, 255, 128,
- 88, 0, 0, 4, 1, 0,
+ 88, 0, 0, 4, 2, 0,
7, 128, 0, 0, 255, 129,
- 0, 0, 228, 128, 1, 0,
+ 0, 0, 228, 128, 2, 0,
228, 128, 1, 0, 0, 2,
- 0, 8, 15, 128, 1, 0,
+ 0, 8, 15, 128, 2, 0,
228, 128, 255, 255, 0, 0,
- 83, 72, 68, 82, 48, 6,
+ 83, 72, 68, 82, 8, 6,
0, 0, 64, 0, 0, 0,
- 140, 1, 0, 0, 89, 0,
+ 130, 1, 0, 0, 89, 0,
0, 4, 70, 142, 32, 0,
0, 0, 0, 0, 1, 0,
0, 0, 90, 0, 0, 3,
@@ -5326,1288 +5340,1296 @@ const BYTE d2deffect[] =
10, 128, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 64, 0, 0, 5, 0,
+ 0, 0, 31, 0, 4, 3,
+ 58, 0, 16, 0, 2, 0,
0, 0, 52, 0, 0, 7,
- 114, 0, 16, 0, 3, 0,
+ 114, 0, 16, 0, 2, 0,
0, 0, 70, 2, 16, 0,
0, 0, 0, 0, 70, 2,
16, 0, 1, 0, 0, 0,
- 49, 0, 0, 10, 114, 0,
- 16, 0, 4, 0, 0, 0,
+ 18, 0, 0, 1, 24, 0,
+ 0, 10, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
2, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 24, 0, 0, 10,
+ 114, 0, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 11, 114, 0, 16, 0,
- 5, 0, 0, 0, 70, 2,
- 16, 128, 65, 0, 0, 0,
- 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 2, 64,
0, 0, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 0, 0,
- 14, 0, 0, 7, 114, 0,
- 16, 0, 5, 0, 0, 0,
- 70, 2, 16, 0, 5, 0,
- 0, 0, 70, 2, 16, 0,
- 0, 0, 0, 0, 51, 0,
- 0, 10, 114, 0, 16, 0,
- 5, 0, 0, 0, 70, 2,
+ 0, 0, 0, 11, 114, 0,
16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
2, 64, 0, 0, 0, 0,
128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
- 0, 0, 0, 0, 0, 11,
- 114, 0, 16, 0, 5, 0,
- 0, 0, 70, 2, 16, 128,
- 65, 0, 0, 0, 5, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 5, 0, 0, 0,
+ 51, 0, 0, 10, 114, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
0, 0, 2, 64, 0, 0,
0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63,
- 0, 0, 0, 0, 57, 0,
- 0, 10, 114, 0, 16, 0,
- 1, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 1, 0, 0, 10,
- 114, 0, 16, 0, 1, 0,
- 0, 0, 70, 2, 16, 0,
- 1, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 128, 63,
+ 0, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
- 128, 63, 0, 0, 0, 0,
- 55, 0, 0, 9, 114, 0,
- 16, 0, 1, 0, 0, 0,
- 70, 2, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
- 5, 0, 0, 0, 70, 2,
- 16, 0, 1, 0, 0, 0,
- 55, 0, 0, 9, 114, 0,
- 16, 0, 2, 0, 0, 0,
- 246, 15, 16, 0, 2, 0,
+ 1, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 70, 2, 16, 0,
- 3, 0, 0, 0, 70, 2,
- 16, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 21, 0,
+ 0, 1, 21, 0, 0, 1,
21, 0, 0, 1, 21, 0,
0, 1, 21, 0, 0, 1,
- 21, 0, 0, 1, 0, 0,
- 0, 8, 18, 0, 16, 0,
- 1, 0, 0, 0, 58, 0,
- 16, 128, 65, 0, 0, 0,
- 1, 0, 0, 0, 1, 64,
- 0, 0, 0, 0, 128, 63,
- 56, 0, 0, 7, 226, 0,
+ 0, 0, 0, 8, 18, 0,
16, 0, 1, 0, 0, 0,
- 246, 15, 16, 0, 1, 0,
- 0, 0, 6, 9, 16, 0,
- 2, 0, 0, 0, 50, 0,
- 0, 9, 114, 0, 16, 0,
- 0, 0, 0, 0, 6, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 56, 0, 0, 7,
+ 226, 0, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 6, 9,
+ 16, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 150, 7,
16, 0, 1, 0, 0, 0,
- 70, 2, 16, 0, 0, 0,
- 0, 0, 150, 7, 16, 0,
- 1, 0, 0, 0, 56, 0,
- 0, 7, 114, 32, 16, 0,
- 0, 0, 0, 0, 246, 15,
+ 56, 0, 0, 7, 114, 32,
16, 0, 0, 0, 0, 0,
- 70, 2, 16, 0, 0, 0,
- 0, 0, 54, 0, 0, 5,
- 130, 32, 16, 0, 0, 0,
- 0, 0, 58, 0, 16, 0,
- 0, 0, 0, 0, 62, 0,
- 0, 1, 83, 84, 65, 84,
- 116, 0, 0, 0, 56, 0,
- 0, 0, 7, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
- 0, 0, 21, 0, 0, 0,
- 5, 0, 0, 0, 2, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 57, 0, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 25, 0,
+ 0, 0, 5, 0, 0, 0,
+ 1, 0, 0, 0, 7, 0,
0, 0, 6, 0, 0, 0,
- 5, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 20, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 82, 68, 69, 70, 100, 1,
- 0, 0, 1, 0, 0, 0,
- 232, 0, 0, 0, 5, 0,
- 0, 0, 28, 0, 0, 0,
- 0, 4, 255, 255, 0, 1,
- 0, 0, 48, 1, 0, 0,
- 188, 0, 0, 0, 3, 0,
+ 0, 0, 82, 68, 69, 70,
+ 100, 1, 0, 0, 1, 0,
+ 0, 0, 232, 0, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 48, 1,
+ 0, 0, 188, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 197, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 197, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 209, 0,
- 0, 0, 2, 0, 0, 0,
- 5, 0, 0, 0, 4, 0,
- 0, 0, 255, 255, 255, 255,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 12, 0, 0, 0,
- 213, 0, 0, 0, 2, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 209, 0, 0, 0, 2, 0,
0, 0, 5, 0, 0, 0,
4, 0, 0, 0, 255, 255,
- 255, 255, 1, 0, 0, 0,
+ 255, 255, 0, 0, 0, 0,
1, 0, 0, 0, 12, 0,
- 0, 0, 220, 0, 0, 0,
+ 0, 0, 213, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 220, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 115, 83,
- 97, 109, 112, 108, 101, 114,
- 0, 115, 66, 99, 107, 83,
- 97, 109, 112, 108, 101, 114,
- 0, 116, 101, 120, 0, 98,
- 99, 107, 116, 101, 120, 0,
- 36, 71, 108, 111, 98, 97,
- 108, 115, 0, 171, 171, 171,
- 220, 0, 0, 0, 1, 0,
- 0, 0, 0, 1, 0, 0,
- 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 24, 1, 0, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 2, 0, 0, 0, 32, 1,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 115, 66, 99,
+ 107, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 101, 120,
+ 0, 98, 99, 107, 116, 101,
+ 120, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 171, 171, 220, 0, 0, 0,
+ 1, 0, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 98, 108, 101, 110, 100, 111,
- 112, 0, 0, 0, 19, 0,
- 1, 0, 1, 0, 0, 0,
+ 0, 0, 24, 1, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 32, 1, 0, 0, 0, 0,
+ 0, 0, 98, 108, 101, 110,
+ 100, 111, 112, 0, 0, 0,
+ 19, 0, 1, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 77, 105, 99, 114, 111, 115,
- 111, 102, 116, 32, 40, 82,
- 41, 32, 72, 76, 83, 76,
- 32, 83, 104, 97, 100, 101,
- 114, 32, 67, 111, 109, 112,
- 105, 108, 101, 114, 32, 57,
- 46, 51, 48, 46, 57, 50,
- 48, 48, 46, 49, 54, 51,
- 56, 52, 0, 171, 73, 83,
- 71, 78, 104, 0, 0, 0,
- 3, 0, 0, 0, 8, 0,
- 0, 0, 80, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 73, 83, 71, 78, 104, 0,
0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 15, 0,
- 0, 0, 92, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 3, 0, 0, 0,
- 1, 0, 0, 0, 3, 3,
- 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171, 164, 16, 0, 0,
+ 0, 0, 0, 0, 83, 97,
+ 109, 112, 108, 101, 84, 101,
+ 120, 116, 117, 114, 101, 70,
+ 111, 114, 83, 101, 112, 97,
+ 114, 97, 98, 108, 101, 66,
+ 108, 101, 110, 100, 105, 110,
+ 103, 95, 50, 0, 68, 4,
+ 0, 0, 68, 88, 66, 67,
+ 77, 85, 167, 240, 56, 56,
+ 155, 78, 125, 96, 49, 253,
+ 103, 100, 22, 62, 1, 0,
+ 0, 0, 68, 4, 0, 0,
+ 6, 0, 0, 0, 56, 0,
+ 0, 0, 248, 0, 0, 0,
+ 244, 1, 0, 0, 112, 2,
+ 0, 0, 160, 3, 0, 0,
+ 212, 3, 0, 0, 65, 111,
+ 110, 57, 184, 0, 0, 0,
+ 184, 0, 0, 0, 0, 2,
+ 254, 255, 132, 0, 0, 0,
+ 52, 0, 0, 0, 1, 0,
+ 36, 0, 0, 0, 48, 0,
+ 0, 0, 48, 0, 0, 0,
+ 36, 0, 1, 0, 48, 0,
+ 0, 0, 0, 0, 3, 0,
1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 254, 255, 81, 0, 0, 5,
+ 4, 0, 15, 160, 0, 0,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 5, 0, 0, 128, 0, 0,
+ 15, 144, 4, 0, 0, 4,
+ 0, 0, 3, 224, 0, 0,
+ 228, 144, 2, 0, 238, 160,
+ 2, 0, 228, 160, 4, 0,
+ 0, 4, 0, 0, 12, 224,
+ 0, 0, 20, 144, 3, 0,
+ 180, 160, 3, 0, 20, 160,
+ 4, 0, 0, 4, 0, 0,
+ 3, 128, 0, 0, 228, 144,
+ 1, 0, 238, 160, 1, 0,
+ 228, 160, 2, 0, 0, 3,
+ 0, 0, 3, 192, 0, 0,
+ 228, 128, 0, 0, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 12, 192, 4, 0, 68, 160,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 244, 0, 0, 0,
+ 64, 0, 1, 0, 61, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
0, 0, 3, 0, 0, 0,
- 1, 0, 0, 0, 12, 0,
- 0, 0, 83, 86, 95, 80,
- 111, 115, 105, 116, 105, 111,
- 110, 0, 84, 69, 88, 67,
- 79, 79, 82, 68, 0, 171,
- 171, 171, 79, 83, 71, 78,
- 44, 0, 0, 0, 1, 0,
- 0, 0, 8, 0, 0, 0,
- 32, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 50, 16,
+ 16, 0, 0, 0, 0, 0,
+ 103, 0, 0, 4, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 50, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 194, 32, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 83, 86, 95, 84, 97, 114,
- 103, 101, 116, 0, 171, 171,
- 172, 16, 0, 0, 0, 0,
- 0, 0, 83, 97, 109, 112,
- 108, 101, 84, 101, 120, 116,
- 117, 114, 101, 70, 111, 114,
- 83, 101, 112, 97, 114, 97,
- 98, 108, 101, 66, 108, 101,
- 110, 100, 105, 110, 103, 95,
- 50, 0, 72, 4, 0, 0,
- 68, 88, 66, 67, 28, 248,
- 40, 83, 2, 166, 203, 194,
- 228, 163, 91, 123, 149, 165,
- 41, 212, 1, 0, 0, 0,
- 72, 4, 0, 0, 6, 0,
- 0, 0, 56, 0, 0, 0,
- 248, 0, 0, 0, 244, 1,
- 0, 0, 112, 2, 0, 0,
- 164, 3, 0, 0, 216, 3,
- 0, 0, 65, 111, 110, 57,
- 184, 0, 0, 0, 184, 0,
- 0, 0, 0, 2, 254, 255,
- 132, 0, 0, 0, 52, 0,
- 0, 0, 1, 0, 36, 0,
- 0, 0, 48, 0, 0, 0,
- 48, 0, 0, 0, 36, 0,
- 1, 0, 48, 0, 0, 0,
- 0, 0, 3, 0, 1, 0,
+ 70, 128, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 2, 254, 255,
- 81, 0, 0, 5, 4, 0,
- 15, 160, 0, 0, 0, 0,
- 0, 0, 128, 63, 0, 0,
+ 54, 0, 0, 8, 194, 32,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 31, 0, 0, 2, 5, 0,
- 0, 128, 0, 0, 15, 144,
- 4, 0, 0, 4, 0, 0,
- 3, 224, 0, 0, 228, 144,
- 2, 0, 238, 160, 2, 0,
- 228, 160, 4, 0, 0, 4,
- 0, 0, 12, 224, 0, 0,
- 20, 144, 3, 0, 180, 160,
- 3, 0, 20, 160, 4, 0,
- 0, 4, 0, 0, 3, 128,
- 0, 0, 228, 144, 1, 0,
- 238, 160, 1, 0, 228, 160,
- 2, 0, 0, 3, 0, 0,
- 3, 192, 0, 0, 228, 128,
- 0, 0, 228, 160, 1, 0,
- 0, 2, 0, 0, 12, 192,
- 4, 0, 68, 160, 255, 255,
- 0, 0, 83, 72, 68, 82,
- 244, 0, 0, 0, 64, 0,
- 1, 0, 61, 0, 0, 0,
- 89, 0, 0, 4, 70, 142,
- 32, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 95, 0,
- 0, 3, 50, 16, 16, 0,
- 0, 0, 0, 0, 103, 0,
- 0, 4, 242, 32, 16, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 50, 0, 0, 11,
50, 32, 16, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
- 194, 32, 16, 0, 1, 0,
- 0, 0, 50, 0, 0, 11,
- 50, 32, 16, 0, 0, 0,
0, 0, 70, 16, 16, 0,
0, 0, 0, 0, 230, 138,
32, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 70, 128,
+ 1, 0, 0, 0, 70, 128,
32, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 54, 0,
- 0, 8, 194, 32, 16, 0,
- 0, 0, 0, 0, 2, 64,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 194, 32, 16, 0,
+ 1, 0, 0, 0, 6, 20,
+ 16, 0, 0, 0, 0, 0,
+ 166, 142, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 6, 132, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 5, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 128, 63,
- 50, 0, 0, 11, 50, 32,
- 16, 0, 1, 0, 0, 0,
- 70, 16, 16, 0, 0, 0,
- 0, 0, 230, 138, 32, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 70, 128, 32, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 50, 0, 0, 11,
- 194, 32, 16, 0, 1, 0,
- 0, 0, 6, 20, 16, 0,
- 0, 0, 0, 0, 166, 142,
- 32, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 6, 132,
- 32, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 62, 0,
- 0, 1, 83, 84, 65, 84,
- 116, 0, 0, 0, 5, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 82, 68, 69, 70,
+ 40, 1, 0, 0, 1, 0,
+ 0, 0, 64, 0, 0, 0,
+ 1, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 254, 255,
+ 0, 1, 0, 0, 246, 0,
+ 0, 0, 60, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 82, 68, 69, 70, 44, 1,
0, 0, 1, 0, 0, 0,
- 64, 0, 0, 0, 1, 0,
- 0, 0, 28, 0, 0, 0,
- 0, 4, 254, 255, 0, 1,
- 0, 0, 246, 0, 0, 0,
- 60, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 99, 98, 48, 0,
- 60, 0, 0, 0, 4, 0,
- 0, 0, 88, 0, 0, 0,
- 64, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 184, 0, 0, 0, 0, 0,
- 0, 0, 16, 0, 0, 0,
- 2, 0, 0, 0, 196, 0,
- 0, 0, 0, 0, 0, 0,
- 212, 0, 0, 0, 16, 0,
- 0, 0, 16, 0, 0, 0,
- 2, 0, 0, 0, 196, 0,
+ 0, 0, 0, 0, 99, 98,
+ 48, 0, 60, 0, 0, 0,
+ 4, 0, 0, 0, 88, 0,
+ 0, 0, 64, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 222, 0, 0, 0, 32, 0,
- 0, 0, 16, 0, 0, 0,
- 2, 0, 0, 0, 196, 0,
+ 0, 0, 184, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 212, 0, 0, 0,
+ 16, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 222, 0, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 236, 0, 0, 0,
+ 48, 0, 0, 0, 16, 0,
0, 0, 0, 0, 0, 0,
- 236, 0, 0, 0, 48, 0,
- 0, 0, 16, 0, 0, 0,
- 0, 0, 0, 0, 196, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 81, 117, 97, 100,
+ 68, 101, 115, 99, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 81, 117, 97, 100, 68, 101,
- 115, 99, 0, 171, 171, 171,
- 1, 0, 3, 0, 1, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 84, 101,
- 120, 67, 111, 111, 114, 100,
- 115, 0, 77, 97, 115, 107,
84, 101, 120, 67, 111, 111,
- 114, 100, 115, 0, 84, 101,
- 120, 116, 67, 111, 108, 111,
- 114, 0, 77, 105, 99, 114,
- 111, 115, 111, 102, 116, 32,
- 40, 82, 41, 32, 72, 76,
- 83, 76, 32, 83, 104, 97,
- 100, 101, 114, 32, 67, 111,
- 109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
- 171, 171, 73, 83, 71, 78,
- 44, 0, 0, 0, 1, 0,
- 0, 0, 8, 0, 0, 0,
- 32, 0, 0, 0, 0, 0,
+ 114, 100, 115, 0, 77, 97,
+ 115, 107, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 84, 101, 120, 116, 67, 111,
+ 108, 111, 114, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 73, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 7, 3, 0, 0,
- 80, 79, 83, 73, 84, 73,
- 79, 78, 0, 171, 171, 171,
- 79, 83, 71, 78, 104, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 3, 0, 0, 0,
- 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 3,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 28, 30, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 88, 17,
+ 0, 0, 68, 88, 66, 67,
+ 62, 116, 36, 238, 73, 63,
+ 158, 95, 222, 192, 91, 113,
+ 112, 55, 55, 145, 1, 0,
+ 0, 0, 88, 17, 0, 0,
+ 6, 0, 0, 0, 56, 0,
+ 0, 0, 88, 6, 0, 0,
+ 204, 14, 0, 0, 72, 15,
+ 0, 0, 180, 16, 0, 0,
+ 36, 17, 0, 0, 65, 111,
+ 110, 57, 24, 6, 0, 0,
+ 24, 6, 0, 0, 0, 2,
+ 255, 255, 224, 5, 0, 0,
+ 56, 0, 0, 0, 1, 0,
+ 44, 0, 0, 0, 56, 0,
+ 0, 0, 56, 0, 2, 0,
+ 36, 0, 0, 0, 56, 0,
+ 0, 0, 0, 0, 1, 1,
+ 1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 3, 0,
+ 0, 0, 1, 2, 255, 255,
+ 81, 0, 0, 5, 1, 0,
+ 15, 160, 0, 0, 224, 192,
+ 0, 0, 0, 193, 0, 0,
+ 16, 193, 0, 0, 32, 193,
+ 81, 0, 0, 5, 2, 0,
+ 15, 160, 0, 0, 128, 63,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 92, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 12, 0, 0, 92, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 0, 0,
- 12, 3, 0, 0, 83, 86,
- 95, 80, 111, 115, 105, 116,
- 105, 111, 110, 0, 84, 69,
- 88, 67, 79, 79, 82, 68,
- 0, 171, 171, 171, 32, 30,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 2, 0,
- 0, 0, 0, 0, 0, 0,
- 248, 16, 0, 0, 68, 88,
- 66, 67, 145, 189, 134, 89,
- 67, 31, 14, 169, 255, 63,
- 144, 149, 250, 13, 86, 17,
- 1, 0, 0, 0, 248, 16,
- 0, 0, 6, 0, 0, 0,
- 56, 0, 0, 0, 12, 6,
- 0, 0, 108, 14, 0, 0,
- 232, 14, 0, 0, 84, 16,
- 0, 0, 196, 16, 0, 0,
- 65, 111, 110, 57, 204, 5,
- 0, 0, 204, 5, 0, 0,
- 0, 2, 255, 255, 148, 5,
- 0, 0, 56, 0, 0, 0,
- 1, 0, 44, 0, 0, 0,
- 56, 0, 0, 0, 56, 0,
- 2, 0, 36, 0, 0, 0,
- 56, 0, 0, 0, 0, 0,
- 1, 1, 1, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 0, 0, 0, 1, 2,
- 255, 255, 81, 0, 0, 5,
- 1, 0, 15, 160, 0, 0,
- 224, 192, 0, 0, 0, 193,
- 0, 0, 16, 193, 0, 0,
- 32, 193, 81, 0, 0, 5,
- 2, 0, 15, 160, 0, 0,
- 128, 63, 0, 0, 0, 0,
- 0, 0, 128, 62, 0, 0,
- 0, 63, 81, 0, 0, 5,
- 3, 0, 15, 160, 0, 0,
- 0, 64, 0, 0, 128, 191,
- 0, 0, 128, 65, 0, 0,
- 64, 193, 81, 0, 0, 5,
- 4, 0, 15, 160, 0, 0,
- 128, 64, 0, 0, 0, 64,
- 0, 0, 128, 63, 0, 0,
- 0, 0, 31, 0, 0, 2,
- 0, 0, 0, 128, 0, 0,
- 15, 176, 31, 0, 0, 2,
- 0, 0, 0, 144, 0, 8,
- 15, 160, 31, 0, 0, 2,
- 0, 0, 0, 144, 1, 8,
- 15, 160, 1, 0, 0, 2,
- 0, 0, 8, 128, 0, 0,
- 0, 160, 2, 0, 0, 3,
- 0, 0, 15, 128, 0, 0,
- 255, 128, 1, 0, 228, 160,
- 5, 0, 0, 3, 0, 0,
- 15, 128, 0, 0, 228, 128,
- 0, 0, 228, 128, 66, 0,
- 0, 3, 1, 0, 15, 128,
- 0, 0, 228, 176, 0, 8,
- 228, 160, 66, 0, 0, 3,
- 2, 0, 15, 128, 0, 0,
- 228, 176, 1, 8, 228, 160,
- 6, 0, 0, 2, 3, 0,
- 8, 128, 2, 0, 255, 128,
- 4, 0, 0, 4, 3, 0,
- 3, 128, 2, 0, 233, 128,
- 3, 0, 255, 129, 2, 0,
- 170, 160, 5, 0, 0, 3,
- 4, 0, 7, 128, 2, 0,
- 228, 128, 3, 0, 255, 128,
+ 128, 191, 0, 0, 128, 62,
+ 81, 0, 0, 5, 3, 0,
+ 15, 160, 0, 0, 0, 63,
+ 0, 0, 0, 64, 0, 0,
+ 128, 191, 0, 0, 128, 64,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 0, 0, 128, 65,
+ 0, 0, 64, 193, 0, 0,
+ 0, 64, 0, 0, 128, 63,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 0, 0, 0, 160,
+ 2, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 255, 128,
+ 1, 0, 228, 160, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 0, 0,
+ 228, 128, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 6, 0,
+ 0, 2, 3, 0, 8, 128,
+ 2, 0, 255, 128, 4, 0,
+ 0, 4, 3, 0, 3, 128,
+ 2, 0, 233, 128, 3, 0,
+ 255, 129, 2, 0, 255, 160,
+ 5, 0, 0, 3, 4, 0,
+ 7, 128, 2, 0, 228, 128,
+ 3, 0, 255, 128, 4, 0,
+ 0, 4, 5, 0, 7, 128,
+ 4, 0, 228, 128, 4, 0,
+ 0, 160, 4, 0, 85, 160,
4, 0, 0, 4, 5, 0,
+ 7, 128, 5, 0, 228, 128,
+ 4, 0, 228, 128, 3, 0,
+ 255, 160, 5, 0, 0, 3,
+ 5, 0, 7, 128, 4, 0,
+ 228, 128, 5, 0, 228, 128,
+ 7, 0, 0, 2, 4, 0,
+ 8, 128, 4, 0, 85, 128,
+ 6, 0, 0, 2, 4, 0,
+ 8, 128, 4, 0, 255, 128,
+ 88, 0, 0, 4, 4, 0,
+ 8, 128, 3, 0, 0, 128,
+ 5, 0, 85, 128, 4, 0,
+ 255, 128, 4, 0, 0, 4,
+ 4, 0, 8, 128, 2, 0,
+ 85, 128, 3, 0, 255, 129,
+ 4, 0, 255, 128, 6, 0,
+ 0, 2, 3, 0, 1, 128,
+ 1, 0, 255, 128, 5, 0,
+ 0, 3, 6, 0, 7, 128,
+ 1, 0, 228, 128, 3, 0,
+ 0, 128, 4, 0, 0, 4,
+ 7, 0, 7, 128, 6, 0,
+ 228, 128, 3, 0, 85, 160,
+ 3, 0, 170, 160, 4, 0,
+ 0, 4, 4, 0, 8, 128,
+ 7, 0, 85, 128, 4, 0,
+ 255, 128, 4, 0, 85, 128,
+ 4, 0, 0, 4, 8, 0,
+ 7, 128, 1, 0, 228, 128,
+ 3, 0, 0, 129, 3, 0,
+ 0, 160, 4, 0, 0, 4,
+ 9, 0, 15, 128, 2, 0,
+ 36, 128, 3, 0, 255, 129,
+ 2, 0, 192, 160, 4, 0,
+ 0, 4, 10, 0, 7, 128,
+ 6, 0, 228, 128, 4, 0,
+ 170, 161, 4, 0, 255, 160,
+ 5, 0, 0, 3, 10, 0,
7, 128, 4, 0, 228, 128,
- 3, 0, 170, 160, 3, 0,
- 255, 160, 4, 0, 0, 4,
- 5, 0, 7, 128, 5, 0,
- 228, 128, 4, 0, 228, 128,
- 4, 0, 0, 160, 5, 0,
- 0, 3, 5, 0, 7, 128,
- 4, 0, 228, 128, 5, 0,
- 228, 128, 7, 0, 0, 2,
+ 10, 0, 228, 128, 4, 0,
+ 0, 4, 10, 0, 7, 128,
+ 10, 0, 228, 128, 9, 0,
+ 228, 129, 4, 0, 228, 128,
+ 88, 0, 0, 4, 11, 0,
+ 2, 128, 8, 0, 85, 128,
+ 10, 0, 85, 128, 4, 0,
+ 255, 128, 7, 0, 0, 2,
4, 0, 8, 128, 4, 0,
- 85, 128, 6, 0, 0, 2,
+ 170, 128, 6, 0, 0, 2,
4, 0, 8, 128, 4, 0,
255, 128, 88, 0, 0, 4,
4, 0, 8, 128, 3, 0,
- 0, 128, 5, 0, 85, 128,
+ 85, 128, 5, 0, 170, 128,
4, 0, 255, 128, 4, 0,
0, 4, 4, 0, 8, 128,
- 2, 0, 85, 128, 3, 0,
+ 2, 0, 170, 128, 3, 0,
255, 129, 4, 0, 255, 128,
- 6, 0, 0, 2, 3, 0,
- 1, 128, 1, 0, 255, 128,
- 5, 0, 0, 3, 6, 0,
- 7, 128, 1, 0, 228, 128,
- 3, 0, 0, 128, 4, 0,
- 0, 4, 7, 0, 7, 128,
- 6, 0, 228, 128, 3, 0,
- 0, 160, 3, 0, 85, 160,
4, 0, 0, 4, 4, 0,
- 8, 128, 7, 0, 85, 128,
+ 8, 128, 7, 0, 170, 128,
4, 0, 255, 128, 4, 0,
- 85, 128, 4, 0, 0, 4,
- 8, 0, 7, 128, 1, 0,
- 228, 128, 3, 0, 0, 129,
- 2, 0, 255, 160, 4, 0,
- 0, 4, 9, 0, 15, 128,
- 2, 0, 36, 128, 3, 0,
- 255, 129, 2, 0, 128, 160,
- 4, 0, 0, 4, 10, 0,
- 7, 128, 6, 0, 228, 128,
- 4, 0, 85, 161, 4, 0,
- 170, 160, 5, 0, 0, 3,
- 10, 0, 7, 128, 4, 0,
- 228, 128, 10, 0, 228, 128,
- 4, 0, 0, 4, 10, 0,
- 7, 128, 10, 0, 228, 128,
- 9, 0, 228, 129, 4, 0,
- 228, 128, 88, 0, 0, 4,
- 11, 0, 2, 128, 8, 0,
- 85, 128, 10, 0, 85, 128,
+ 170, 128, 88, 0, 0, 4,
+ 11, 0, 4, 128, 8, 0,
+ 170, 128, 10, 0, 170, 128,
4, 0, 255, 128, 7, 0,
0, 2, 4, 0, 8, 128,
- 4, 0, 170, 128, 6, 0,
+ 4, 0, 0, 128, 6, 0,
0, 2, 4, 0, 8, 128,
4, 0, 255, 128, 88, 0,
0, 4, 4, 0, 8, 128,
- 3, 0, 85, 128, 5, 0,
- 170, 128, 4, 0, 255, 128,
+ 9, 0, 255, 128, 5, 0,
+ 0, 128, 4, 0, 255, 128,
4, 0, 0, 4, 4, 0,
- 8, 128, 2, 0, 170, 128,
+ 8, 128, 2, 0, 0, 128,
3, 0, 255, 129, 4, 0,
255, 128, 4, 0, 0, 4,
+ 2, 0, 7, 128, 2, 0,
+ 228, 128, 3, 0, 255, 128,
+ 2, 0, 170, 160, 5, 0,
+ 0, 3, 2, 0, 7, 128,
+ 2, 0, 228, 128, 2, 0,
+ 228, 128, 4, 0, 0, 4,
4, 0, 8, 128, 7, 0,
- 170, 128, 4, 0, 255, 128,
- 4, 0, 170, 128, 88, 0,
- 0, 4, 11, 0, 4, 128,
- 8, 0, 170, 128, 10, 0,
- 170, 128, 4, 0, 255, 128,
- 7, 0, 0, 2, 4, 0,
- 8, 128, 4, 0, 0, 128,
- 6, 0, 0, 2, 4, 0,
- 8, 128, 4, 0, 255, 128,
- 88, 0, 0, 4, 4, 0,
- 8, 128, 9, 0, 255, 128,
- 5, 0, 0, 128, 4, 0,
- 255, 128, 4, 0, 0, 4,
- 4, 0, 8, 128, 2, 0,
- 0, 128, 3, 0, 255, 129,
- 4, 0, 255, 128, 4, 0,
- 0, 4, 4, 0, 8, 128,
- 7, 0, 0, 128, 4, 0,
- 255, 128, 4, 0, 0, 128,
- 2, 0, 0, 3, 2, 0,
- 7, 128, 7, 0, 228, 129,
- 2, 0, 0, 160, 4, 0,
- 0, 4, 2, 0, 7, 128,
- 9, 0, 228, 128, 2, 0,
- 228, 129, 2, 0, 0, 160,
- 88, 0, 0, 4, 11, 0,
- 1, 128, 8, 0, 0, 128,
- 10, 0, 0, 128, 4, 0,
- 255, 128, 4, 0, 0, 4,
- 3, 0, 14, 128, 1, 0,
- 144, 128, 3, 0, 0, 128,
- 4, 0, 144, 129, 4, 0,
- 0, 4, 5, 0, 7, 128,
- 1, 0, 228, 128, 3, 0,
- 0, 128, 4, 0, 228, 128,
- 35, 0, 0, 2, 3, 0,
- 7, 128, 3, 0, 249, 128,
- 5, 0, 0, 3, 7, 0,
- 7, 128, 4, 0, 228, 128,
- 6, 0, 228, 128, 4, 0,
+ 0, 128, 4, 0, 255, 128,
+ 4, 0, 0, 128, 2, 0,
+ 0, 3, 3, 0, 14, 128,
+ 7, 0, 144, 129, 2, 0,
+ 0, 160, 4, 0, 0, 4,
+ 3, 0, 14, 128, 9, 0,
+ 144, 128, 3, 0, 228, 129,
+ 2, 0, 0, 160, 88, 0,
+ 0, 4, 11, 0, 1, 128,
+ 8, 0, 0, 128, 10, 0,
+ 0, 128, 4, 0, 255, 128,
+ 4, 0, 0, 4, 5, 0,
+ 7, 128, 1, 0, 228, 128,
+ 3, 0, 0, 128, 4, 0,
+ 228, 129, 4, 0, 0, 4,
+ 7, 0, 7, 128, 1, 0,
+ 228, 128, 3, 0, 0, 128,
+ 4, 0, 228, 128, 35, 0,
+ 0, 2, 5, 0, 7, 128,
+ 5, 0, 228, 128, 5, 0,
+ 0, 3, 10, 0, 7, 128,
+ 4, 0, 228, 128, 6, 0,
+ 228, 128, 4, 0, 0, 4,
+ 7, 0, 7, 128, 10, 0,
+ 228, 128, 3, 0, 85, 161,
+ 7, 0, 228, 128, 88, 0,
0, 4, 5, 0, 7, 128,
- 7, 0, 228, 128, 3, 0,
- 0, 161, 5, 0, 228, 128,
- 88, 0, 0, 4, 3, 0,
- 7, 128, 0, 0, 255, 129,
- 3, 0, 228, 128, 5, 0,
- 228, 128, 88, 0, 0, 4,
- 3, 0, 7, 128, 0, 0,
- 170, 129, 11, 0, 228, 128,
- 3, 0, 228, 128, 2, 0,
- 0, 3, 5, 0, 7, 128,
- 6, 0, 228, 128, 6, 0,
- 228, 128, 5, 0, 0, 3,
- 5, 0, 7, 128, 4, 0,
- 228, 128, 5, 0, 228, 128,
+ 0, 0, 255, 129, 5, 0,
+ 228, 128, 7, 0, 228, 128,
+ 88, 0, 0, 4, 5, 0,
+ 7, 128, 0, 0, 170, 129,
+ 11, 0, 228, 128, 5, 0,
+ 228, 128, 2, 0, 0, 3,
+ 7, 0, 7, 128, 6, 0,
+ 228, 128, 6, 0, 228, 128,
5, 0, 0, 3, 4, 0,
7, 128, 4, 0, 228, 128,
- 4, 0, 228, 128, 88, 0,
- 0, 4, 4, 0, 7, 128,
- 4, 0, 228, 129, 2, 0,
- 85, 160, 2, 0, 0, 160,
- 88, 0, 0, 4, 2, 0,
- 7, 128, 8, 0, 228, 128,
- 5, 0, 228, 128, 2, 0,
- 228, 128, 88, 0, 0, 4,
- 0, 0, 14, 128, 0, 0,
- 85, 129, 2, 0, 144, 128,
- 3, 0, 144, 128, 6, 0,
+ 7, 0, 228, 128, 88, 0,
+ 0, 4, 3, 0, 7, 128,
+ 8, 0, 228, 128, 4, 0,
+ 228, 128, 3, 0, 249, 128,
+ 88, 0, 0, 4, 0, 0,
+ 14, 128, 0, 0, 85, 129,
+ 3, 0, 144, 128, 5, 0,
+ 144, 128, 6, 0, 0, 2,
+ 6, 0, 8, 128, 6, 0,
+ 0, 128, 4, 0, 0, 4,
+ 6, 0, 8, 128, 9, 0,
+ 0, 128, 6, 0, 255, 129,
+ 2, 0, 0, 160, 11, 0,
+ 0, 3, 3, 0, 1, 128,
+ 6, 0, 255, 128, 2, 0,
+ 85, 160, 5, 0, 0, 3,
+ 3, 0, 14, 128, 6, 0,
+ 144, 128, 6, 0, 144, 128,
+ 88, 0, 0, 4, 6, 0,
+ 8, 128, 3, 0, 85, 129,
+ 2, 0, 85, 160, 3, 0,
+ 0, 128, 88, 0, 0, 4,
+ 4, 0, 1, 128, 2, 0,
+ 0, 129, 2, 0, 0, 160,
+ 6, 0, 255, 128, 6, 0,
0, 2, 4, 0, 8, 128,
- 6, 0, 0, 128, 4, 0,
+ 6, 0, 85, 128, 4, 0,
0, 4, 4, 0, 8, 128,
- 9, 0, 0, 128, 4, 0,
+ 9, 0, 85, 128, 4, 0,
255, 129, 2, 0, 0, 160,
11, 0, 0, 3, 6, 0,
8, 128, 4, 0, 255, 128,
2, 0, 85, 160, 88, 0,
- 0, 4, 2, 0, 1, 128,
- 6, 0, 0, 129, 4, 0,
- 0, 128, 6, 0, 255, 128,
- 6, 0, 0, 2, 6, 0,
- 8, 128, 6, 0, 85, 128,
- 4, 0, 0, 4, 6, 0,
- 8, 128, 9, 0, 85, 128,
- 6, 0, 255, 129, 2, 0,
- 0, 160, 11, 0, 0, 3,
- 3, 0, 1, 128, 6, 0,
- 255, 128, 2, 0, 85, 160,
- 88, 0, 0, 4, 2, 0,
- 2, 128, 6, 0, 85, 129,
- 4, 0, 85, 128, 3, 0,
- 0, 128, 6, 0, 0, 2,
- 6, 0, 8, 128, 6, 0,
+ 0, 4, 4, 0, 8, 128,
+ 3, 0, 170, 129, 2, 0,
+ 85, 160, 6, 0, 255, 128,
+ 88, 0, 0, 4, 4, 0,
+ 2, 128, 2, 0, 85, 129,
+ 2, 0, 0, 160, 4, 0,
+ 255, 128, 6, 0, 0, 2,
+ 4, 0, 8, 128, 6, 0,
170, 128, 4, 0, 0, 4,
- 6, 0, 8, 128, 9, 0,
- 170, 128, 6, 0, 255, 129,
+ 4, 0, 8, 128, 9, 0,
+ 170, 128, 4, 0, 255, 129,
2, 0, 0, 160, 11, 0,
- 0, 3, 3, 0, 1, 128,
- 6, 0, 255, 128, 2, 0,
+ 0, 3, 6, 0, 8, 128,
+ 4, 0, 255, 128, 2, 0,
85, 160, 88, 0, 0, 4,
- 2, 0, 4, 128, 6, 0,
- 170, 129, 4, 0, 170, 128,
- 3, 0, 0, 128, 88, 0,
- 0, 4, 0, 0, 7, 128,
- 0, 0, 0, 129, 2, 0,
- 228, 128, 0, 0, 249, 128,
- 18, 0, 0, 4, 3, 0,
- 7, 128, 2, 0, 255, 128,
- 0, 0, 228, 128, 6, 0,
+ 4, 0, 8, 128, 3, 0,
+ 255, 129, 2, 0, 85, 160,
+ 6, 0, 255, 128, 88, 0,
+ 0, 4, 4, 0, 4, 128,
+ 2, 0, 170, 129, 2, 0,
+ 0, 160, 4, 0, 255, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 0, 0, 0, 129,
+ 4, 0, 228, 128, 0, 0,
+ 249, 128, 18, 0, 0, 4,
+ 3, 0, 7, 128, 2, 0,
+ 255, 128, 0, 0, 228, 128,
+ 6, 0, 228, 128, 5, 0,
+ 0, 3, 3, 0, 8, 128,
+ 2, 0, 255, 128, 2, 0,
+ 255, 128, 88, 0, 0, 4,
+ 3, 0, 8, 128, 3, 0,
+ 255, 129, 2, 0, 0, 160,
+ 2, 0, 85, 160, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 1, 0, 255, 128, 3, 0,
228, 128, 5, 0, 0, 3,
- 3, 0, 8, 128, 2, 0,
- 255, 128, 2, 0, 255, 128,
- 88, 0, 0, 4, 3, 0,
- 8, 128, 3, 0, 255, 129,
+ 0, 0, 8, 128, 1, 0,
+ 255, 128, 1, 0, 255, 128,
+ 88, 0, 0, 4, 0, 0,
+ 8, 128, 0, 0, 255, 129,
2, 0, 0, 160, 2, 0,
- 85, 160, 5, 0, 0, 3,
- 0, 0, 7, 128, 1, 0,
- 255, 128, 3, 0, 228, 128,
- 5, 0, 0, 3, 0, 0,
- 8, 128, 1, 0, 255, 128,
- 1, 0, 255, 128, 88, 0,
- 0, 4, 0, 0, 8, 128,
- 0, 0, 255, 129, 2, 0,
- 0, 160, 2, 0, 85, 160,
- 2, 0, 0, 3, 0, 0,
- 8, 128, 3, 0, 255, 128,
- 0, 0, 255, 128, 88, 0,
- 0, 4, 1, 0, 7, 128,
- 0, 0, 255, 129, 0, 0,
- 228, 128, 1, 0, 228, 128,
- 1, 0, 0, 2, 0, 8,
- 15, 128, 1, 0, 228, 128,
- 255, 255, 0, 0, 83, 72,
- 68, 82, 88, 8, 0, 0,
- 64, 0, 0, 0, 22, 2,
- 0, 0, 89, 0, 0, 4,
- 70, 142, 32, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 0, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 1, 0, 0, 0,
+ 85, 160, 2, 0, 0, 3,
+ 0, 0, 8, 128, 3, 0,
+ 255, 128, 0, 0, 255, 128,
+ 88, 0, 0, 4, 1, 0,
+ 7, 128, 0, 0, 255, 129,
+ 0, 0, 228, 128, 1, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 1, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 108, 8,
+ 0, 0, 64, 0, 0, 0,
+ 27, 2, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 7, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 0, 0, 0, 0,
- 85, 85, 0, 0, 88, 24,
- 0, 4, 0, 112, 16, 0,
- 1, 0, 0, 0, 85, 85,
- 0, 0, 98, 16, 0, 3,
- 50, 16, 16, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 104, 0, 0, 2,
- 7, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 0, 0, 0, 0, 70, 16,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 1, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 1, 0, 0, 0, 70, 16,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
16, 0, 1, 0, 0, 0,
- 70, 126, 16, 0, 1, 0,
- 0, 0, 0, 96, 16, 0,
- 1, 0, 0, 0, 24, 0,
- 0, 7, 18, 0, 16, 0,
+ 24, 0, 0, 7, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 24, 0,
+ 0, 7, 34, 0, 16, 0,
2, 0, 0, 0, 58, 0,
- 16, 0, 0, 0, 0, 0,
+ 16, 0, 1, 0, 0, 0,
1, 64, 0, 0, 0, 0,
- 0, 0, 24, 0, 0, 7,
- 34, 0, 16, 0, 2, 0,
- 0, 0, 58, 0, 16, 0,
- 1, 0, 0, 0, 1, 64,
- 0, 0, 0, 0, 0, 0,
- 60, 0, 0, 7, 18, 0,
+ 0, 0, 60, 0, 0, 7,
+ 18, 0, 16, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 2, 0, 0, 0, 10, 0,
16, 0, 2, 0, 0, 0,
- 26, 0, 16, 0, 2, 0,
- 0, 0, 10, 0, 16, 0,
- 2, 0, 0, 0, 31, 0,
- 4, 3, 10, 0, 16, 0,
- 2, 0, 0, 0, 54, 0,
- 0, 5, 242, 32, 16, 0,
- 0, 0, 0, 0, 70, 14,
+ 31, 0, 4, 3, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 5, 242, 32,
16, 0, 0, 0, 0, 0,
- 62, 0, 0, 1, 21, 0,
- 0, 1, 14, 0, 0, 7,
- 114, 0, 16, 0, 0, 0,
- 0, 0, 70, 2, 16, 0,
- 0, 0, 0, 0, 246, 15,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 21, 0, 0, 1, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
16, 0, 0, 0, 0, 0,
- 14, 0, 0, 7, 114, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 246, 15,
16, 0, 1, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 246, 15, 16, 0,
- 1, 0, 0, 0, 32, 0,
- 0, 8, 18, 0, 16, 0,
- 2, 0, 0, 0, 10, 128,
- 32, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 64,
- 0, 0, 7, 0, 0, 0,
- 31, 0, 4, 3, 10, 0,
- 16, 0, 2, 0, 0, 0,
- 49, 0, 0, 10, 114, 0,
+ 32, 0, 0, 8, 18, 0,
16, 0, 2, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
+ 10, 128, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 7, 0,
+ 0, 0, 31, 0, 4, 3,
+ 10, 0, 16, 0, 2, 0,
+ 0, 0, 24, 0, 0, 10,
+ 114, 0, 16, 0, 2, 0,
0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 24, 0, 0, 10, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 11, 114, 0, 16, 0,
- 3, 0, 0, 0, 70, 2,
+ 4, 0, 0, 0, 70, 2,
16, 128, 65, 0, 0, 0,
1, 0, 0, 0, 2, 64,
0, 0, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 0, 0,
14, 0, 0, 7, 114, 0,
- 16, 0, 3, 0, 0, 0,
- 70, 2, 16, 0, 3, 0,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
0, 0, 0, 0, 51, 0,
0, 10, 114, 0, 16, 0,
- 3, 0, 0, 0, 70, 2,
- 16, 0, 3, 0, 0, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 4, 0, 0, 0,
2, 64, 0, 0, 0, 0,
128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
0, 0, 0, 0, 0, 11,
- 114, 0, 16, 0, 3, 0,
+ 114, 0, 16, 0, 4, 0,
0, 0, 70, 2, 16, 128,
- 65, 0, 0, 0, 3, 0,
+ 65, 0, 0, 0, 4, 0,
0, 0, 2, 64, 0, 0,
0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63,
- 0, 0, 0, 0, 57, 0,
- 0, 10, 114, 0, 16, 0,
- 4, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 4, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 18, 0,
+ 0, 1, 32, 0, 0, 8,
+ 130, 0, 16, 0, 2, 0,
+ 0, 0, 10, 128, 32, 0,
0, 0, 0, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 1, 0, 0, 10,
+ 0, 0, 1, 64, 0, 0,
+ 8, 0, 0, 0, 31, 0,
+ 4, 3, 58, 0, 16, 0,
+ 2, 0, 0, 0, 29, 0,
+ 0, 10, 114, 0, 16, 0,
+ 3, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 63, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
114, 0, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
- 4, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 191, 0, 0,
+ 128, 191, 0, 0, 128, 191,
+ 0, 0, 0, 0, 0, 0,
+ 0, 11, 114, 0, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 11, 114, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 128, 65, 0,
+ 0, 0, 5, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 50, 0, 0, 13,
+ 114, 0, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 6, 0,
+ 0, 0, 70, 2, 16, 0,
+ 5, 0, 0, 0, 2, 64,
0, 0, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 0, 0,
55, 0, 0, 9, 114, 0,
16, 0, 2, 0, 0, 0,
- 70, 2, 16, 0, 2, 0,
+ 70, 2, 16, 0, 3, 0,
0, 0, 70, 2, 16, 0,
- 3, 0, 0, 0, 70, 2,
- 16, 0, 4, 0, 0, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 5, 0, 0, 0,
18, 0, 0, 1, 32, 0,
0, 8, 130, 0, 16, 0,
2, 0, 0, 0, 10, 128,
32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 64,
- 0, 0, 8, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
31, 0, 4, 3, 58, 0,
16, 0, 2, 0, 0, 0,
29, 0, 0, 10, 114, 0,
16, 0, 3, 0, 0, 0,
2, 64, 0, 0, 0, 0,
- 0, 63, 0, 0, 0, 63,
- 0, 0, 0, 63, 0, 0,
+ 128, 62, 0, 0, 128, 62,
+ 0, 0, 128, 62, 0, 0,
0, 0, 70, 2, 16, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 7, 114, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
4, 0, 0, 0, 70, 2,
- 16, 0, 0, 0, 0, 0,
- 70, 2, 16, 0, 0, 0,
- 0, 0, 56, 0, 0, 7,
- 114, 0, 16, 0, 4, 0,
- 0, 0, 70, 2, 16, 0,
- 1, 0, 0, 0, 70, 2,
- 16, 0, 4, 0, 0, 0,
- 50, 0, 0, 15, 114, 0,
- 16, 0, 5, 0, 0, 0,
- 70, 2, 16, 0, 0, 0,
- 0, 0, 2, 64, 0, 0,
- 0, 0, 0, 64, 0, 0,
- 0, 64, 0, 0, 0, 64,
- 0, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 128, 191,
- 0, 0, 128, 191, 0, 0,
- 128, 191, 0, 0, 0, 0,
- 0, 0, 0, 11, 114, 0,
- 16, 0, 6, 0, 0, 0,
- 70, 2, 16, 128, 65, 0,
- 0, 0, 1, 0, 0, 0,
+ 16, 0, 1, 0, 0, 0,
2, 64, 0, 0, 0, 0,
- 128, 63, 0, 0, 128, 63,
- 0, 0, 128, 63, 0, 0,
- 0, 0, 0, 0, 0, 11,
- 114, 0, 16, 0, 5, 0,
- 0, 0, 70, 2, 16, 128,
- 65, 0, 0, 0, 5, 0,
+ 128, 65, 0, 0, 128, 65,
+ 0, 0, 128, 65, 0, 0,
0, 0, 2, 64, 0, 0,
- 0, 0, 128, 63, 0, 0,
- 128, 63, 0, 0, 128, 63,
+ 0, 0, 64, 193, 0, 0,
+ 64, 193, 0, 0, 64, 193,
0, 0, 0, 0, 50, 0,
- 0, 13, 114, 0, 16, 0,
- 5, 0, 0, 0, 70, 2,
- 16, 128, 65, 0, 0, 0,
- 6, 0, 0, 0, 70, 2,
- 16, 0, 5, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 128, 63, 0, 0, 128, 63,
- 0, 0, 128, 63, 0, 0,
- 0, 0, 55, 0, 0, 9,
- 114, 0, 16, 0, 2, 0,
- 0, 0, 70, 2, 16, 0,
- 3, 0, 0, 0, 70, 2,
- 16, 0, 4, 0, 0, 0,
- 70, 2, 16, 0, 5, 0,
- 0, 0, 18, 0, 0, 1,
- 32, 0, 0, 8, 130, 0,
- 16, 0, 2, 0, 0, 0,
- 10, 128, 32, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 64, 0, 0, 9, 0,
- 0, 0, 31, 0, 4, 3,
- 58, 0, 16, 0, 2, 0,
- 0, 0, 29, 0, 0, 10,
- 114, 0, 16, 0, 3, 0,
- 0, 0, 2, 64, 0, 0,
- 0, 0, 128, 62, 0, 0,
- 128, 62, 0, 0, 128, 62,
- 0, 0, 0, 0, 70, 2,
- 16, 0, 1, 0, 0, 0,
- 50, 0, 0, 15, 114, 0,
+ 0, 12, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
16, 0, 4, 0, 0, 0,
70, 2, 16, 0, 1, 0,
0, 0, 2, 64, 0, 0,
- 0, 0, 128, 65, 0, 0,
- 128, 65, 0, 0, 128, 65,
- 0, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 64, 193,
- 0, 0, 64, 193, 0, 0,
- 64, 193, 0, 0, 0, 0,
- 50, 0, 0, 12, 114, 0,
- 16, 0, 4, 0, 0, 0,
- 70, 2, 16, 0, 4, 0,
- 0, 0, 70, 2, 16, 0,
- 1, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 128, 64,
0, 0, 128, 64, 0, 0,
- 128, 64, 0, 0, 0, 0,
- 56, 0, 0, 7, 114, 0,
- 16, 0, 4, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 70, 2, 16, 0,
- 4, 0, 0, 0, 75, 0,
- 0, 5, 114, 0, 16, 0,
- 5, 0, 0, 0, 70, 2,
+ 128, 64, 0, 0, 128, 64,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
16, 0, 1, 0, 0, 0,
- 55, 0, 0, 9, 114, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 75, 0, 0, 5,
+ 114, 0, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 55, 0,
+ 0, 9, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
16, 0, 3, 0, 0, 0,
- 70, 2, 16, 0, 3, 0,
+ 70, 2, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
- 4, 0, 0, 0, 70, 2,
- 16, 0, 5, 0, 0, 0,
- 29, 0, 0, 10, 114, 0,
- 16, 0, 4, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 0, 63, 0, 0, 0, 63,
+ 5, 0, 0, 0, 29, 0,
+ 0, 10, 114, 0, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 63,
0, 0, 0, 63, 0, 0,
- 0, 0, 70, 2, 16, 0,
- 0, 0, 0, 0, 50, 0,
- 0, 16, 114, 0, 16, 0,
- 5, 0, 0, 0, 70, 2,
- 16, 128, 65, 0, 0, 0,
- 0, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 0, 64,
+ 0, 63, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 16,
+ 114, 0, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
0, 0, 0, 64, 0, 0,
- 0, 64, 0, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 128, 63, 0, 0, 128, 63,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
- 0, 0, 56, 0, 0, 7,
- 114, 0, 16, 0, 5, 0,
+ 128, 63, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
0, 0, 70, 2, 16, 0,
- 1, 0, 0, 0, 70, 2,
+ 5, 0, 0, 0, 0, 0,
+ 0, 11, 114, 0, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 50, 0, 0, 10, 114, 0,
16, 0, 5, 0, 0, 0,
- 0, 0, 0, 11, 114, 0,
- 16, 0, 6, 0, 0, 0,
70, 2, 16, 128, 65, 0,
- 0, 0, 1, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 128, 63, 0, 0, 128, 63,
- 0, 0, 128, 63, 0, 0,
- 0, 0, 50, 0, 0, 10,
- 114, 0, 16, 0, 5, 0,
- 0, 0, 70, 2, 16, 128,
- 65, 0, 0, 0, 5, 0,
+ 0, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 6, 0,
0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
6, 0, 0, 0, 70, 2,
- 16, 0, 1, 0, 0, 0,
- 50, 0, 0, 15, 114, 0,
- 16, 0, 6, 0, 0, 0,
- 70, 2, 16, 0, 0, 0,
- 0, 0, 2, 64, 0, 0,
- 0, 0, 0, 64, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
0, 64, 0, 0, 0, 64,
- 0, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 128, 191,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 2, 64, 0, 0,
0, 0, 128, 191, 0, 0,
- 128, 191, 0, 0, 0, 0,
- 0, 0, 0, 8, 114, 0,
+ 128, 191, 0, 0, 128, 191,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 70, 2,
16, 0, 3, 0, 0, 0,
- 70, 2, 16, 128, 65, 0,
- 0, 0, 1, 0, 0, 0,
- 70, 2, 16, 0, 3, 0,
- 0, 0, 50, 0, 0, 9,
- 114, 0, 16, 0, 3, 0,
- 0, 0, 70, 2, 16, 0,
- 6, 0, 0, 0, 70, 2,
+ 50, 0, 0, 9, 114, 0,
16, 0, 3, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 55, 0, 0, 9,
- 114, 0, 16, 0, 2, 0,
+ 70, 2, 16, 0, 6, 0,
0, 0, 70, 2, 16, 0,
- 4, 0, 0, 0, 70, 2,
- 16, 0, 5, 0, 0, 0,
- 70, 2, 16, 0, 3, 0,
- 0, 0, 18, 0, 0, 1,
- 32, 0, 0, 8, 130, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
16, 0, 2, 0, 0, 0,
- 10, 128, 32, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 64, 0, 0, 10, 0,
- 0, 0, 0, 0, 0, 8,
- 114, 0, 16, 0, 3, 0,
+ 70, 2, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
- 0, 0, 0, 0, 70, 2,
- 16, 128, 65, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 7, 114, 0, 16, 0,
- 4, 0, 0, 0, 70, 2,
- 16, 0, 0, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 56, 0, 0, 7,
- 114, 0, 16, 0, 1, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 18, 0, 0, 1, 32, 0,
+ 0, 8, 130, 0, 16, 0,
+ 2, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 10, 0, 0, 0,
+ 0, 0, 0, 8, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
0, 0, 0, 0, 70, 2,
16, 0, 1, 0, 0, 0,
- 50, 0, 0, 13, 114, 0,
+ 56, 0, 0, 7, 114, 0,
16, 0, 1, 0, 0, 0,
- 70, 2, 16, 128, 65, 0,
- 0, 0, 1, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 0, 64, 0, 0, 0, 64,
- 0, 0, 0, 64, 0, 0,
+ 70, 2, 16, 0, 0, 0,
0, 0, 70, 2, 16, 0,
- 4, 0, 0, 0, 55, 0,
- 0, 10, 114, 0, 16, 0,
- 2, 0, 0, 0, 246, 15,
- 16, 0, 2, 0, 0, 0,
- 70, 2, 16, 128, 129, 0,
- 0, 0, 3, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 21, 0, 0, 1,
+ 1, 0, 0, 0, 50, 0,
+ 0, 13, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 64,
+ 0, 0, 0, 64, 0, 0,
+ 0, 64, 0, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 55, 0, 0, 10,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 246, 15, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 128, 129, 0, 0, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
21, 0, 0, 1, 21, 0,
- 0, 1, 0, 0, 0, 8,
- 18, 0, 16, 0, 1, 0,
- 0, 0, 58, 0, 16, 128,
- 65, 0, 0, 0, 1, 0,
- 0, 0, 1, 64, 0, 0,
- 0, 0, 128, 63, 56, 0,
- 0, 7, 226, 0, 16, 0,
- 1, 0, 0, 0, 246, 15,
+ 0, 1, 21, 0, 0, 1,
+ 0, 0, 0, 8, 18, 0,
16, 0, 1, 0, 0, 0,
- 6, 9, 16, 0, 2, 0,
- 0, 0, 50, 0, 0, 9,
- 114, 0, 16, 0, 0, 0,
- 0, 0, 6, 0, 16, 0,
- 1, 0, 0, 0, 70, 2,
- 16, 0, 0, 0, 0, 0,
- 150, 7, 16, 0, 1, 0,
- 0, 0, 56, 0, 0, 7,
- 114, 32, 16, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 56, 0, 0, 7,
+ 226, 0, 16, 0, 1, 0,
0, 0, 246, 15, 16, 0,
- 0, 0, 0, 0, 70, 2,
+ 1, 0, 0, 0, 6, 9,
+ 16, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 114, 0,
16, 0, 0, 0, 0, 0,
- 54, 0, 0, 5, 130, 32,
+ 6, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 150, 7,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
16, 0, 0, 0, 0, 0,
- 58, 0, 16, 0, 0, 0,
- 0, 0, 62, 0, 0, 1,
- 83, 84, 65, 84, 116, 0,
- 0, 0, 66, 0, 0, 0,
- 7, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 27, 0, 0, 0, 4, 0,
- 0, 0, 2, 0, 0, 0,
- 5, 0, 0, 0, 4, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 66, 0, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 38, 0,
+ 0, 0, 4, 0, 0, 0,
+ 1, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 16, 0, 0, 0, 5, 0,
- 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 82, 68,
- 69, 70, 100, 1, 0, 0,
- 1, 0, 0, 0, 232, 0,
- 0, 0, 5, 0, 0, 0,
- 28, 0, 0, 0, 0, 4,
- 255, 255, 0, 1, 0, 0,
- 48, 1, 0, 0, 188, 0,
- 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 82, 68, 69, 70,
+ 100, 1, 0, 0, 1, 0,
+ 0, 0, 232, 0, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 48, 1,
+ 0, 0, 188, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 197, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 197, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 209, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 209, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 12, 0,
+ 0, 0, 213, 0, 0, 0,
2, 0, 0, 0, 5, 0,
0, 0, 4, 0, 0, 0,
- 255, 255, 255, 255, 0, 0,
+ 255, 255, 255, 255, 1, 0,
0, 0, 1, 0, 0, 0,
- 12, 0, 0, 0, 213, 0,
- 0, 0, 2, 0, 0, 0,
- 5, 0, 0, 0, 4, 0,
- 0, 0, 255, 255, 255, 255,
- 1, 0, 0, 0, 1, 0,
- 0, 0, 12, 0, 0, 0,
- 220, 0, 0, 0, 0, 0,
+ 12, 0, 0, 0, 220, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 115, 83, 97, 109,
- 112, 108, 101, 114, 0, 115,
- 66, 99, 107, 83, 97, 109,
- 112, 108, 101, 114, 0, 116,
- 101, 120, 0, 98, 99, 107,
- 116, 101, 120, 0, 36, 71,
- 108, 111, 98, 97, 108, 115,
- 0, 171, 171, 171, 220, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 1, 0, 0, 16, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 24, 1,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 2, 0,
- 0, 0, 32, 1, 0, 0,
- 0, 0, 0, 0, 98, 108,
- 101, 110, 100, 111, 112, 0,
- 0, 0, 19, 0, 1, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 73, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
- 0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 115, 66, 99,
+ 107, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 101, 120,
+ 0, 98, 99, 107, 116, 101,
+ 120, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 171, 171, 220, 0, 0, 0,
+ 1, 0, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 3, 0, 0,
- 92, 0, 0, 0, 1, 0,
+ 0, 0, 24, 1, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 32, 1, 0, 0, 0, 0,
+ 0, 0, 98, 108, 101, 110,
+ 100, 111, 112, 0, 0, 0,
+ 19, 0, 1, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 0, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 79, 83, 71, 78, 44, 0,
- 0, 0, 1, 0, 0, 0,
- 8, 0, 0, 0, 32, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 1, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 83, 86,
- 95, 84, 97, 114, 103, 101,
- 116, 0, 171, 171, 128, 34,
+ 15, 0, 0, 0, 92, 0,
0, 0, 0, 0, 0, 0,
- 83, 97, 109, 112, 108, 101,
- 84, 101, 120, 116, 117, 114,
- 101, 70, 111, 114, 78, 111,
- 110, 83, 101, 112, 97, 114,
- 97, 98, 108, 101, 66, 108,
- 101, 110, 100, 105, 110, 103,
- 0, 72, 4, 0, 0, 68,
- 88, 66, 67, 28, 248, 40,
- 83, 2, 166, 203, 194, 228,
- 163, 91, 123, 149, 165, 41,
- 212, 1, 0, 0, 0, 72,
- 4, 0, 0, 6, 0, 0,
- 0, 56, 0, 0, 0, 248,
- 0, 0, 0, 244, 1, 0,
- 0, 112, 2, 0, 0, 164,
- 3, 0, 0, 216, 3, 0,
- 0, 65, 111, 110, 57, 184,
- 0, 0, 0, 184, 0, 0,
- 0, 0, 2, 254, 255, 132,
- 0, 0, 0, 52, 0, 0,
- 0, 1, 0, 36, 0, 0,
- 0, 48, 0, 0, 0, 48,
- 0, 0, 0, 36, 0, 1,
- 0, 48, 0, 0, 0, 0,
- 0, 3, 0, 1, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 1, 2, 254, 255, 81,
- 0, 0, 5, 4, 0, 15,
- 160, 0, 0, 0, 0, 0,
- 0, 128, 63, 0, 0, 0,
- 0, 0, 0, 0, 0, 31,
- 0, 0, 2, 5, 0, 0,
- 128, 0, 0, 15, 144, 4,
- 0, 0, 4, 0, 0, 3,
- 224, 0, 0, 228, 144, 2,
- 0, 238, 160, 2, 0, 228,
- 160, 4, 0, 0, 4, 0,
- 0, 12, 224, 0, 0, 20,
- 144, 3, 0, 180, 160, 3,
- 0, 20, 160, 4, 0, 0,
- 4, 0, 0, 3, 128, 0,
- 0, 228, 144, 1, 0, 238,
- 160, 1, 0, 228, 160, 2,
- 0, 0, 3, 0, 0, 3,
- 192, 0, 0, 228, 128, 0,
- 0, 228, 160, 1, 0, 0,
- 2, 0, 0, 12, 192, 4,
- 0, 68, 160, 255, 255, 0,
- 0, 83, 72, 68, 82, 244,
- 0, 0, 0, 64, 0, 1,
- 0, 61, 0, 0, 0, 89,
- 0, 0, 4, 70, 142, 32,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171, 120, 34, 0, 0,
+ 0, 0, 0, 0, 83, 97,
+ 109, 112, 108, 101, 84, 101,
+ 120, 116, 117, 114, 101, 70,
+ 111, 114, 78, 111, 110, 83,
+ 101, 112, 97, 114, 97, 98,
+ 108, 101, 66, 108, 101, 110,
+ 100, 105, 110, 103, 0, 68,
+ 4, 0, 0, 68, 88, 66,
+ 67, 77, 85, 167, 240, 56,
+ 56, 155, 78, 125, 96, 49,
+ 253, 103, 100, 22, 62, 1,
+ 0, 0, 0, 68, 4, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 248, 0, 0,
+ 0, 244, 1, 0, 0, 112,
+ 2, 0, 0, 160, 3, 0,
+ 0, 212, 3, 0, 0, 65,
+ 111, 110, 57, 184, 0, 0,
+ 0, 184, 0, 0, 0, 0,
+ 2, 254, 255, 132, 0, 0,
+ 0, 52, 0, 0, 0, 1,
+ 0, 36, 0, 0, 0, 48,
+ 0, 0, 0, 48, 0, 0,
+ 0, 36, 0, 1, 0, 48,
0, 0, 0, 0, 0, 3,
- 0, 0, 0, 95, 0, 0,
- 3, 50, 16, 16, 0, 0,
- 0, 0, 0, 103, 0, 0,
- 4, 242, 32, 16, 0, 0,
- 0, 0, 0, 1, 0, 0,
- 0, 101, 0, 0, 3, 50,
- 32, 16, 0, 1, 0, 0,
- 0, 101, 0, 0, 3, 194,
- 32, 16, 0, 1, 0, 0,
- 0, 50, 0, 0, 11, 50,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 254, 255, 81, 0, 0,
+ 5, 4, 0, 15, 160, 0,
+ 0, 0, 0, 0, 0, 128,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 0, 0,
+ 2, 5, 0, 0, 128, 0,
+ 0, 15, 144, 4, 0, 0,
+ 4, 0, 0, 3, 224, 0,
+ 0, 228, 144, 2, 0, 238,
+ 160, 2, 0, 228, 160, 4,
+ 0, 0, 4, 0, 0, 12,
+ 224, 0, 0, 20, 144, 3,
+ 0, 180, 160, 3, 0, 20,
+ 160, 4, 0, 0, 4, 0,
+ 0, 3, 128, 0, 0, 228,
+ 144, 1, 0, 238, 160, 1,
+ 0, 228, 160, 2, 0, 0,
+ 3, 0, 0, 3, 192, 0,
+ 0, 228, 128, 0, 0, 228,
+ 160, 1, 0, 0, 2, 0,
+ 0, 12, 192, 4, 0, 68,
+ 160, 255, 255, 0, 0, 83,
+ 72, 68, 82, 244, 0, 0,
+ 0, 64, 0, 1, 0, 61,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 95, 0, 0, 3, 50,
+ 16, 16, 0, 0, 0, 0,
+ 0, 103, 0, 0, 4, 242,
32, 16, 0, 0, 0, 0,
- 0, 70, 16, 16, 0, 0,
- 0, 0, 0, 230, 138, 32,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 70, 128, 32,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 54, 0, 0,
- 8, 194, 32, 16, 0, 0,
- 0, 0, 0, 2, 64, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 128, 63, 50,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 50, 32, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 194, 32, 16,
+ 0, 1, 0, 0, 0, 50,
0, 0, 11, 50, 32, 16,
- 0, 1, 0, 0, 0, 70,
+ 0, 0, 0, 0, 0, 70,
16, 16, 0, 0, 0, 0,
0, 230, 138, 32, 0, 0,
- 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
0, 70, 128, 32, 0, 0,
- 0, 0, 0, 1, 0, 0,
- 0, 50, 0, 0, 11, 194,
- 32, 16, 0, 1, 0, 0,
- 0, 6, 20, 16, 0, 0,
- 0, 0, 0, 166, 142, 32,
- 0, 0, 0, 0, 0, 2,
- 0, 0, 0, 6, 132, 32,
- 0, 0, 0, 0, 0, 2,
- 0, 0, 0, 62, 0, 0,
- 1, 83, 84, 65, 84, 116,
- 0, 0, 0, 5, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 4, 0, 0,
+ 0, 54, 0, 0, 8, 194,
+ 32, 16, 0, 0, 0, 0,
+ 0, 2, 64, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 1, 0, 0, 0, 0,
+ 0, 128, 63, 50, 0, 0,
+ 11, 50, 32, 16, 0, 1,
+ 0, 0, 0, 70, 16, 16,
+ 0, 0, 0, 0, 0, 230,
+ 138, 32, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 70,
+ 128, 32, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 50,
+ 0, 0, 11, 194, 32, 16,
+ 0, 1, 0, 0, 0, 6,
+ 20, 16, 0, 0, 0, 0,
+ 0, 166, 142, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 6, 132, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 5, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 82,
- 68, 69, 70, 44, 1, 0,
- 0, 1, 0, 0, 0, 64,
- 0, 0, 0, 1, 0, 0,
- 0, 28, 0, 0, 0, 0,
- 4, 254, 255, 0, 1, 0,
- 0, 246, 0, 0, 0, 60,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 82, 68, 69,
+ 70, 40, 1, 0, 0, 1,
+ 0, 0, 0, 64, 0, 0,
+ 0, 1, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 254,
+ 255, 0, 1, 0, 0, 246,
+ 0, 0, 0, 60, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 99, 98, 48, 0, 60,
- 0, 0, 0, 4, 0, 0,
- 0, 88, 0, 0, 0, 64,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 184,
0, 0, 0, 0, 0, 0,
- 0, 16, 0, 0, 0, 2,
- 0, 0, 0, 196, 0, 0,
- 0, 0, 0, 0, 0, 212,
- 0, 0, 0, 16, 0, 0,
- 0, 16, 0, 0, 0, 2,
- 0, 0, 0, 196, 0, 0,
- 0, 0, 0, 0, 0, 222,
- 0, 0, 0, 32, 0, 0,
- 0, 16, 0, 0, 0, 2,
- 0, 0, 0, 196, 0, 0,
- 0, 0, 0, 0, 0, 236,
- 0, 0, 0, 48, 0, 0,
- 0, 16, 0, 0, 0, 0,
- 0, 0, 0, 196, 0, 0,
- 0, 0, 0, 0, 0, 81,
- 117, 97, 100, 68, 101, 115,
- 99, 0, 171, 171, 171, 1,
- 0, 3, 0, 1, 0, 4,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 99,
+ 98, 48, 0, 60, 0, 0,
+ 0, 4, 0, 0, 0, 88,
+ 0, 0, 0, 64, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 84, 101, 120,
+ 0, 0, 0, 184, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 212, 0, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 222, 0, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 236, 0, 0,
+ 0, 48, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 77,
+ 97, 115, 107, 84, 101, 120,
67, 111, 111, 114, 100, 115,
- 0, 77, 97, 115, 107, 84,
- 101, 120, 67, 111, 111, 114,
- 100, 115, 0, 84, 101, 120,
- 116, 67, 111, 108, 111, 114,
- 0, 77, 105, 99, 114, 111,
- 115, 111, 102, 116, 32, 40,
- 82, 41, 32, 72, 76, 83,
- 76, 32, 83, 104, 97, 100,
- 101, 114, 32, 67, 111, 109,
- 112, 105, 108, 101, 114, 32,
- 57, 46, 51, 48, 46, 57,
- 50, 48, 48, 46, 49, 54,
- 51, 56, 52, 0, 171, 171,
- 171, 73, 83, 71, 78, 44,
+ 0, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 73, 83, 71, 78, 44,
0, 0, 0, 1, 0, 0,
0, 8, 0, 0, 0, 32,
0, 0, 0, 0, 0, 0,
@@ -6634,14 +6656,14 @@ const BYTE d2deffect[] =
80, 111, 115, 105, 116, 105,
111, 110, 0, 84, 69, 88,
67, 79, 79, 82, 68, 0,
- 171, 171, 171, 169, 51, 0,
+ 171, 171, 171, 1, 52, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 216,
37, 0, 0, 68, 88, 66,
- 67, 14, 214, 69, 11, 113,
- 28, 134, 133, 126, 27, 169,
- 118, 168, 238, 179, 215, 1,
+ 67, 205, 124, 125, 227, 208,
+ 119, 203, 250, 120, 38, 135,
+ 194, 158, 189, 85, 176, 1,
0, 0, 0, 216, 37, 0,
0, 6, 0, 0, 0, 56,
0, 0, 0, 72, 13, 0,
@@ -8148,7 +8170,7 @@ const BYTE d2deffect[] =
84, 116, 0, 0, 0, 195,
0, 0, 0, 9, 0, 0,
0, 0, 0, 0, 0, 2,
- 0, 0, 0, 127, 0, 0,
+ 0, 0, 0, 128, 0, 0,
0, 3, 0, 0, 0, 1,
0, 0, 0, 7, 0, 0,
0, 6, 0, 0, 0, 0,
@@ -8158,7 +8180,7 @@ const BYTE d2deffect[] =
0, 2, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 38, 0, 0,
+ 0, 0, 0, 14, 0, 0,
0, 28, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -8223,9 +8245,9 @@ const BYTE d2deffect[] =
76, 32, 83, 104, 97, 100,
101, 114, 32, 67, 111, 109,
112, 105, 108, 101, 114, 32,
- 57, 46, 51, 48, 46, 57,
- 50, 48, 48, 46, 49, 54,
- 51, 56, 52, 0, 171, 73,
+ 54, 46, 51, 46, 57, 54,
+ 48, 48, 46, 49, 54, 51,
+ 56, 52, 0, 171, 171, 73,
83, 71, 78, 104, 0, 0,
0, 3, 0, 0, 0, 8,
0, 0, 0, 80, 0, 0,
@@ -8253,16 +8275,16 @@ const BYTE d2deffect[] =
0, 0, 0, 15, 0, 0,
0, 83, 86, 95, 84, 97,
114, 103, 101, 116, 0, 171,
- 171, 9, 56, 0, 0, 0,
+ 171, 93, 56, 0, 0, 0,
0, 0, 0, 83, 97, 109,
112, 108, 101, 82, 97, 100,
105, 97, 108, 71, 114, 97,
100, 105, 101, 110, 116, 0,
65, 80, 111, 115, 0, 44,
7, 0, 0, 68, 88, 66,
- 67, 2, 114, 191, 189, 220,
- 178, 221, 187, 160, 105, 131,
- 115, 60, 218, 164, 36, 1,
+ 67, 172, 27, 205, 113, 176,
+ 254, 27, 44, 22, 107, 179,
+ 112, 127, 38, 148, 161, 1,
0, 0, 0, 44, 7, 0,
0, 6, 0, 0, 0, 56,
0, 0, 0, 148, 1, 0,
@@ -8408,7 +8430,7 @@ const BYTE d2deffect[] =
84, 65, 84, 116, 0, 0,
0, 12, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
- 0, 4, 0, 0, 0, 6,
+ 0, 4, 0, 0, 0, 8,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
@@ -8418,7 +8440,7 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -8535,9 +8557,9 @@ const BYTE d2deffect[] =
83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
171, 73, 83, 71, 78, 44,
0, 0, 0, 1, 0, 0,
0, 8, 0, 0, 0, 32,
@@ -8565,14 +8587,14 @@ const BYTE d2deffect[] =
80, 111, 115, 105, 116, 105,
111, 110, 0, 84, 69, 88,
67, 79, 79, 82, 68, 0,
- 171, 171, 171, 7, 94, 0,
+ 171, 171, 171, 91, 94, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 224,
9, 0, 0, 68, 88, 66,
- 67, 127, 75, 112, 105, 91,
- 217, 180, 33, 240, 130, 210,
- 53, 48, 198, 20, 52, 1,
+ 67, 76, 106, 34, 250, 169,
+ 50, 124, 43, 130, 255, 198,
+ 178, 126, 127, 40, 188, 1,
0, 0, 0, 224, 9, 0,
0, 6, 0, 0, 0, 56,
0, 0, 0, 128, 2, 0,
@@ -8844,7 +8866,7 @@ const BYTE d2deffect[] =
0, 0, 0, 33, 0, 0,
0, 3, 0, 0, 0, 0,
0, 0, 0, 3, 0, 0,
- 0, 17, 0, 0, 0, 0,
+ 0, 19, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0,
0, 3, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
@@ -8854,7 +8876,7 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 8, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -8960,10 +8982,10 @@ const BYTE d2deffect[] =
72, 76, 83, 76, 32, 83,
104, 97, 100, 101, 114, 32,
67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 171, 73, 83, 71,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 171, 171, 171, 73, 83, 71,
78, 104, 0, 0, 0, 3,
0, 0, 0, 8, 0, 0,
0, 80, 0, 0, 0, 0,
@@ -8990,13 +9012,13 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 15, 0, 0, 0, 83,
86, 95, 84, 97, 114, 103,
- 101, 116, 0, 171, 171, 75,
+ 101, 116, 0, 171, 171, 159,
101, 0, 0, 0, 0, 0,
0, 65, 48, 0, 44, 7,
0, 0, 68, 88, 66, 67,
- 2, 114, 191, 189, 220, 178,
- 221, 187, 160, 105, 131, 115,
- 60, 218, 164, 36, 1, 0,
+ 172, 27, 205, 113, 176, 254,
+ 27, 44, 22, 107, 179, 112,
+ 127, 38, 148, 161, 1, 0,
0, 0, 44, 7, 0, 0,
6, 0, 0, 0, 56, 0,
0, 0, 148, 1, 0, 0,
@@ -9142,7 +9164,7 @@ const BYTE d2deffect[] =
65, 84, 116, 0, 0, 0,
12, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 6, 0,
+ 4, 0, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
@@ -9152,7 +9174,7 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -9269,9 +9291,9 @@ const BYTE d2deffect[] =
76, 32, 83, 104, 97, 100,
101, 114, 32, 67, 111, 109,
112, 105, 108, 101, 114, 32,
- 57, 46, 51, 48, 46, 57,
- 50, 48, 48, 46, 49, 54,
- 51, 56, 52, 0, 171, 171,
+ 54, 46, 51, 46, 57, 54,
+ 48, 48, 46, 49, 54, 51,
+ 56, 52, 0, 171, 171, 171,
73, 83, 71, 78, 44, 0,
0, 0, 1, 0, 0, 0,
8, 0, 0, 0, 32, 0,
@@ -9299,14 +9321,14 @@ const BYTE d2deffect[] =
111, 115, 105, 116, 105, 111,
110, 0, 84, 69, 88, 67,
79, 79, 82, 68, 0, 171,
- 171, 171, 58, 111, 0, 0,
+ 171, 171, 142, 111, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 192, 7,
0, 0, 68, 88, 66, 67,
- 6, 207, 28, 59, 230, 215,
- 167, 102, 185, 167, 244, 219,
- 74, 22, 220, 53, 1, 0,
+ 73, 174, 125, 52, 147, 212,
+ 172, 159, 223, 39, 1, 144,
+ 137, 10, 201, 206, 1, 0,
0, 0, 192, 7, 0, 0,
6, 0, 0, 0, 56, 0,
0, 0, 196, 1, 0, 0,
@@ -9487,7 +9509,7 @@ const BYTE d2deffect[] =
116, 0, 0, 0, 19, 0,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 3, 0,
- 0, 0, 9, 0, 0, 0,
+ 0, 0, 10, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
1, 0, 0, 0, 0, 0,
@@ -9497,7 +9519,7 @@ const BYTE d2deffect[] =
2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 6, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -9603,10 +9625,10 @@ const BYTE d2deffect[] =
32, 72, 76, 83, 76, 32,
83, 104, 97, 100, 101, 114,
32, 67, 111, 109, 112, 105,
- 108, 101, 114, 32, 57, 46,
- 51, 48, 46, 57, 50, 48,
- 48, 46, 49, 54, 51, 56,
- 52, 0, 171, 171, 73, 83,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 171, 171, 73, 83,
71, 78, 104, 0, 0, 0,
3, 0, 0, 0, 8, 0,
0, 0, 80, 0, 0, 0,
@@ -9634,13 +9656,13 @@ const BYTE d2deffect[] =
0, 0, 15, 0, 0, 0,
83, 86, 95, 84, 97, 114,
103, 101, 116, 0, 171, 171,
- 126, 118, 0, 0, 0, 0,
+ 210, 118, 0, 0, 0, 0,
0, 0, 65, 80, 111, 115,
87, 114, 97, 112, 0, 44,
7, 0, 0, 68, 88, 66,
- 67, 2, 114, 191, 189, 220,
- 178, 221, 187, 160, 105, 131,
- 115, 60, 218, 164, 36, 1,
+ 67, 172, 27, 205, 113, 176,
+ 254, 27, 44, 22, 107, 179,
+ 112, 127, 38, 148, 161, 1,
0, 0, 0, 44, 7, 0,
0, 6, 0, 0, 0, 56,
0, 0, 0, 148, 1, 0,
@@ -9786,7 +9808,7 @@ const BYTE d2deffect[] =
84, 65, 84, 116, 0, 0,
0, 12, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
- 0, 4, 0, 0, 0, 6,
+ 0, 4, 0, 0, 0, 8,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
@@ -9796,7 +9818,7 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -9913,9 +9935,9 @@ const BYTE d2deffect[] =
83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
171, 73, 83, 71, 78, 44,
0, 0, 0, 1, 0, 0,
0, 8, 0, 0, 0, 32,
@@ -9943,14 +9965,14 @@ const BYTE d2deffect[] =
80, 111, 115, 105, 116, 105,
111, 110, 0, 84, 69, 88,
67, 79, 79, 82, 68, 0,
- 171, 171, 171, 83, 126, 0,
+ 171, 171, 171, 167, 126, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 228,
9, 0, 0, 68, 88, 66,
- 67, 200, 229, 12, 172, 236,
- 253, 99, 236, 189, 154, 200,
- 222, 235, 13, 47, 209, 1,
+ 67, 193, 68, 83, 4, 120,
+ 206, 206, 65, 213, 56, 189,
+ 186, 120, 85, 235, 59, 1,
0, 0, 0, 228, 9, 0,
0, 6, 0, 0, 0, 56,
0, 0, 0, 128, 2, 0,
@@ -10222,7 +10244,7 @@ const BYTE d2deffect[] =
0, 0, 0, 33, 0, 0,
0, 3, 0, 0, 0, 0,
0, 0, 0, 3, 0, 0,
- 0, 17, 0, 0, 0, 0,
+ 0, 19, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0,
0, 3, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
@@ -10232,7 +10254,7 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 8, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -10339,9 +10361,9 @@ const BYTE d2deffect[] =
83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
171, 73, 83, 71, 78, 104,
0, 0, 0, 3, 0, 0,
0, 8, 0, 0, 0, 80,
@@ -10369,13 +10391,13 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 15,
0, 0, 0, 83, 86, 95,
84, 97, 114, 103, 101, 116,
- 0, 171, 171, 151, 133, 0,
+ 0, 171, 171, 235, 133, 0,
0, 0, 0, 0, 0, 65,
48, 87, 114, 97, 112, 0,
44, 7, 0, 0, 68, 88,
- 66, 67, 2, 114, 191, 189,
- 220, 178, 221, 187, 160, 105,
- 131, 115, 60, 218, 164, 36,
+ 66, 67, 172, 27, 205, 113,
+ 176, 254, 27, 44, 22, 107,
+ 179, 112, 127, 38, 148, 161,
1, 0, 0, 0, 44, 7,
0, 0, 6, 0, 0, 0,
56, 0, 0, 0, 148, 1,
@@ -10522,7 +10544,7 @@ const BYTE d2deffect[] =
0, 0, 12, 0, 0, 0,
2, 0, 0, 0, 0, 0,
0, 0, 4, 0, 0, 0,
- 6, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -10532,7 +10554,7 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -10648,9 +10670,9 @@ const BYTE d2deffect[] =
76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
- 114, 32, 57, 46, 51, 48,
- 46, 57, 50, 48, 48, 46,
- 49, 54, 51, 56, 52, 0,
+ 114, 32, 54, 46, 51, 46,
+ 57, 54, 48, 48, 46, 49,
+ 54, 51, 56, 52, 0, 171,
171, 171, 73, 83, 71, 78,
44, 0, 0, 0, 1, 0,
0, 0, 8, 0, 0, 0,
@@ -10678,14 +10700,14 @@ const BYTE d2deffect[] =
95, 80, 111, 115, 105, 116,
105, 111, 110, 0, 84, 69,
88, 67, 79, 79, 82, 68,
- 0, 171, 171, 171, 142, 143,
+ 0, 171, 171, 171, 226, 143,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
196, 7, 0, 0, 68, 88,
- 66, 67, 179, 20, 248, 91,
- 13, 228, 55, 25, 123, 146,
- 28, 72, 132, 38, 155, 180,
+ 66, 67, 223, 174, 80, 104,
+ 241, 52, 44, 173, 100, 134,
+ 52, 219, 15, 210, 214, 245,
1, 0, 0, 0, 196, 7,
0, 0, 6, 0, 0, 0,
56, 0, 0, 0, 196, 1,
@@ -10866,7 +10888,7 @@ const BYTE d2deffect[] =
65, 84, 116, 0, 0, 0,
19, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 9, 0,
+ 3, 0, 0, 0, 10, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 1, 0, 0, 0,
@@ -10876,7 +10898,7 @@ const BYTE d2deffect[] =
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 6, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -10983,10 +11005,10 @@ const BYTE d2deffect[] =
32, 72, 76, 83, 76, 32,
83, 104, 97, 100, 101, 114,
32, 67, 111, 109, 112, 105,
- 108, 101, 114, 32, 57, 46,
- 51, 48, 46, 57, 50, 48,
- 48, 46, 49, 54, 51, 56,
- 52, 0, 171, 171, 73, 83,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 171, 171, 73, 83,
71, 78, 104, 0, 0, 0,
3, 0, 0, 0, 8, 0,
0, 0, 80, 0, 0, 0,
@@ -11014,14 +11036,14 @@ const BYTE d2deffect[] =
0, 0, 15, 0, 0, 0,
83, 86, 95, 84, 97, 114,
103, 101, 116, 0, 171, 171,
- 210, 150, 0, 0, 0, 0,
+ 38, 151, 0, 0, 0, 0,
0, 0, 65, 80, 111, 115,
77, 105, 114, 114, 111, 114,
0, 44, 7, 0, 0, 68,
- 88, 66, 67, 2, 114, 191,
- 189, 220, 178, 221, 187, 160,
- 105, 131, 115, 60, 218, 164,
- 36, 1, 0, 0, 0, 44,
+ 88, 66, 67, 172, 27, 205,
+ 113, 176, 254, 27, 44, 22,
+ 107, 179, 112, 127, 38, 148,
+ 161, 1, 0, 0, 0, 44,
7, 0, 0, 6, 0, 0,
0, 56, 0, 0, 0, 148,
1, 0, 0, 104, 3, 0,
@@ -11167,7 +11189,7 @@ const BYTE d2deffect[] =
0, 0, 0, 12, 0, 0,
0, 2, 0, 0, 0, 0,
0, 0, 0, 4, 0, 0,
- 0, 6, 0, 0, 0, 0,
+ 0, 8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -11177,7 +11199,7 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 4, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -11293,10 +11315,10 @@ const BYTE d2deffect[] =
72, 76, 83, 76, 32, 83,
104, 97, 100, 101, 114, 32,
67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 171, 73, 83, 71,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 171, 171, 171, 73, 83, 71,
78, 44, 0, 0, 0, 1,
0, 0, 0, 8, 0, 0,
0, 32, 0, 0, 0, 0,
@@ -11323,15 +11345,15 @@ const BYTE d2deffect[] =
86, 95, 80, 111, 115, 105,
116, 105, 111, 110, 0, 84,
69, 88, 67, 79, 79, 82,
- 68, 0, 171, 171, 171, 173,
- 158, 0, 0, 0, 0, 0,
+ 68, 0, 171, 171, 171, 1,
+ 159, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
0, 232, 9, 0, 0, 68,
- 88, 66, 67, 212, 75, 202,
- 91, 7, 109, 80, 28, 42,
- 15, 64, 3, 93, 90, 7,
- 16, 1, 0, 0, 0, 232,
+ 88, 66, 67, 48, 133, 157,
+ 76, 135, 209, 82, 153, 49,
+ 138, 172, 57, 31, 63, 161,
+ 231, 1, 0, 0, 0, 232,
9, 0, 0, 6, 0, 0,
0, 56, 0, 0, 0, 128,
2, 0, 0, 88, 6, 0,
@@ -11602,7 +11624,7 @@ const BYTE d2deffect[] =
84, 116, 0, 0, 0, 33,
0, 0, 0, 3, 0, 0,
0, 0, 0, 0, 0, 3,
- 0, 0, 0, 17, 0, 0,
+ 0, 0, 0, 19, 0, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 3, 0, 0,
0, 2, 0, 0, 0, 0,
@@ -11612,7 +11634,7 @@ const BYTE d2deffect[] =
0, 2, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 8, 0, 0,
+ 0, 0, 0, 4, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -11720,9 +11742,9 @@ const BYTE d2deffect[] =
83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
171, 73, 83, 71, 78, 104,
0, 0, 0, 3, 0, 0,
0, 8, 0, 0, 0, 80,
@@ -11750,14 +11772,14 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 15,
0, 0, 0, 83, 86, 95,
84, 97, 114, 103, 101, 116,
- 0, 171, 171, 241, 165, 0,
+ 0, 171, 171, 69, 166, 0,
0, 0, 0, 0, 0, 65,
48, 77, 105, 114, 114, 111,
114, 0, 44, 7, 0, 0,
- 68, 88, 66, 67, 2, 114,
- 191, 189, 220, 178, 221, 187,
- 160, 105, 131, 115, 60, 218,
- 164, 36, 1, 0, 0, 0,
+ 68, 88, 66, 67, 172, 27,
+ 205, 113, 176, 254, 27, 44,
+ 22, 107, 179, 112, 127, 38,
+ 148, 161, 1, 0, 0, 0,
44, 7, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
148, 1, 0, 0, 104, 3,
@@ -11903,7 +11925,7 @@ const BYTE d2deffect[] =
116, 0, 0, 0, 12, 0,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 4, 0,
- 0, 0, 6, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -11913,7 +11935,7 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -12029,10 +12051,10 @@ const BYTE d2deffect[] =
32, 72, 76, 83, 76, 32,
83, 104, 97, 100, 101, 114,
32, 67, 111, 109, 112, 105,
- 108, 101, 114, 32, 57, 46,
- 51, 48, 46, 57, 50, 48,
- 48, 46, 49, 54, 51, 56,
- 52, 0, 171, 171, 73, 83,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 171, 171, 73, 83,
71, 78, 44, 0, 0, 0,
1, 0, 0, 0, 8, 0,
0, 0, 32, 0, 0, 0,
@@ -12060,14 +12082,14 @@ const BYTE d2deffect[] =
105, 116, 105, 111, 110, 0,
84, 69, 88, 67, 79, 79,
82, 68, 0, 171, 171, 171,
- 238, 175, 0, 0, 0, 0,
+ 66, 176, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
2, 0, 0, 0, 0, 0,
0, 0, 200, 7, 0, 0,
- 68, 88, 66, 67, 6, 226,
- 244, 224, 234, 191, 89, 63,
- 147, 234, 94, 217, 28, 153,
- 32, 29, 1, 0, 0, 0,
+ 68, 88, 66, 67, 238, 212,
+ 160, 43, 129, 11, 44, 225,
+ 62, 162, 102, 35, 9, 220,
+ 80, 177, 1, 0, 0, 0,
200, 7, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
196, 1, 0, 0, 56, 4,
@@ -12248,7 +12270,7 @@ const BYTE d2deffect[] =
0, 0, 19, 0, 0, 0,
2, 0, 0, 0, 0, 0,
0, 0, 3, 0, 0, 0,
- 9, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
@@ -12258,7 +12280,7 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 6, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -12365,10 +12387,10 @@ const BYTE d2deffect[] =
32, 72, 76, 83, 76, 32,
83, 104, 97, 100, 101, 114,
32, 67, 111, 109, 112, 105,
- 108, 101, 114, 32, 57, 46,
- 51, 48, 46, 57, 50, 48,
- 48, 46, 49, 54, 51, 56,
- 52, 0, 171, 171, 73, 83,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 171, 171, 73, 83,
71, 78, 104, 0, 0, 0,
3, 0, 0, 0, 8, 0,
0, 0, 80, 0, 0, 0,
@@ -12396,21 +12418,21 @@ const BYTE d2deffect[] =
0, 0, 15, 0, 0, 0,
83, 86, 95, 84, 97, 114,
103, 101, 116, 0, 171, 171,
- 50, 183, 0, 0, 0, 0,
+ 134, 183, 0, 0, 0, 0,
0, 0, 83, 97, 109, 112,
108, 101, 77, 97, 115, 107,
101, 100, 84, 101, 120, 116,
- 117, 114, 101, 0, 72, 4,
+ 117, 114, 101, 0, 68, 4,
0, 0, 68, 88, 66, 67,
- 28, 248, 40, 83, 2, 166,
- 203, 194, 228, 163, 91, 123,
- 149, 165, 41, 212, 1, 0,
- 0, 0, 72, 4, 0, 0,
+ 77, 85, 167, 240, 56, 56,
+ 155, 78, 125, 96, 49, 253,
+ 103, 100, 22, 62, 1, 0,
+ 0, 0, 68, 4, 0, 0,
6, 0, 0, 0, 56, 0,
0, 0, 248, 0, 0, 0,
244, 1, 0, 0, 112, 2,
- 0, 0, 164, 3, 0, 0,
- 216, 3, 0, 0, 65, 111,
+ 0, 0, 160, 3, 0, 0,
+ 212, 3, 0, 0, 65, 111,
110, 57, 184, 0, 0, 0,
184, 0, 0, 0, 0, 2,
254, 255, 132, 0, 0, 0,
@@ -12488,7 +12510,7 @@ const BYTE d2deffect[] =
65, 84, 116, 0, 0, 0,
5, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
@@ -12498,7 +12520,7 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -12506,7 +12528,7 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 82, 68, 69, 70,
- 44, 1, 0, 0, 1, 0,
+ 40, 1, 0, 0, 1, 0,
0, 0, 64, 0, 0, 0,
1, 0, 0, 0, 28, 0,
0, 0, 0, 4, 254, 255,
@@ -12553,459 +12575,479 @@ const BYTE d2deffect[] =
72, 76, 83, 76, 32, 83,
104, 97, 100, 101, 114, 32,
67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 171, 171, 73, 83,
- 71, 78, 44, 0, 0, 0,
- 1, 0, 0, 0, 8, 0,
- 0, 0, 32, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 7, 3,
- 0, 0, 80, 79, 83, 73,
- 84, 73, 79, 78, 0, 171,
- 171, 171, 79, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
- 0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 73, 83, 71, 78, 44, 0,
0, 0, 1, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 12, 0, 0,
- 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 3, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 26, 191, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
+ 7, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 3,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 110, 191, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 212, 3,
+ 0, 0, 68, 88, 66, 67,
+ 98, 136, 224, 212, 103, 235,
+ 205, 77, 125, 241, 101, 150,
+ 199, 56, 208, 85, 1, 0,
0, 0, 212, 3, 0, 0,
- 68, 88, 66, 67, 206, 93,
- 214, 184, 226, 209, 206, 42,
- 139, 90, 123, 148, 119, 142,
- 142, 148, 1, 0, 0, 0,
- 212, 3, 0, 0, 6, 0,
- 0, 0, 56, 0, 0, 0,
- 224, 0, 0, 0, 188, 1,
- 0, 0, 56, 2, 0, 0,
- 48, 3, 0, 0, 160, 3,
- 0, 0, 65, 111, 110, 57,
- 160, 0, 0, 0, 160, 0,
- 0, 0, 0, 2, 255, 255,
- 116, 0, 0, 0, 44, 0,
- 0, 0, 0, 0, 44, 0,
- 0, 0, 44, 0, 0, 0,
- 44, 0, 2, 0, 36, 0,
- 0, 0, 44, 0, 0, 0,
- 0, 0, 1, 1, 1, 0,
- 1, 2, 255, 255, 31, 0,
- 0, 2, 0, 0, 0, 128,
- 0, 0, 15, 176, 31, 0,
- 0, 2, 0, 0, 0, 144,
- 0, 8, 15, 160, 31, 0,
- 0, 2, 0, 0, 0, 144,
- 1, 8, 15, 160, 1, 0,
- 0, 2, 0, 0, 3, 128,
- 0, 0, 235, 176, 66, 0,
- 0, 3, 1, 0, 15, 128,
- 0, 0, 228, 176, 0, 8,
- 228, 160, 66, 0, 0, 3,
+ 6, 0, 0, 0, 56, 0,
+ 0, 0, 224, 0, 0, 0,
+ 188, 1, 0, 0, 56, 2,
+ 0, 0, 48, 3, 0, 0,
+ 160, 3, 0, 0, 65, 111,
+ 110, 57, 160, 0, 0, 0,
+ 160, 0, 0, 0, 0, 2,
+ 255, 255, 116, 0, 0, 0,
+ 44, 0, 0, 0, 0, 0,
+ 44, 0, 0, 0, 44, 0,
+ 0, 0, 44, 0, 2, 0,
+ 36, 0, 0, 0, 44, 0,
+ 0, 0, 0, 0, 1, 1,
+ 1, 0, 1, 2, 255, 255,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 1, 0, 0, 2, 0, 0,
+ 3, 128, 0, 0, 235, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 1, 8,
+ 228, 160, 5, 0, 0, 3,
0, 0, 15, 128, 0, 0,
- 228, 128, 1, 8, 228, 160,
- 5, 0, 0, 3, 0, 0,
- 15, 128, 0, 0, 255, 128,
- 1, 0, 228, 128, 1, 0,
- 0, 2, 0, 8, 15, 128,
- 0, 0, 228, 128, 255, 255,
- 0, 0, 83, 72, 68, 82,
- 212, 0, 0, 0, 64, 0,
- 0, 0, 53, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 0, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 1, 0, 0, 0,
+ 255, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 212, 0, 0, 0,
+ 64, 0, 0, 0, 53, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 98, 16,
+ 0, 3, 194, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 2, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 0, 0, 0, 0,
- 85, 85, 0, 0, 88, 24,
- 0, 4, 0, 112, 16, 0,
- 1, 0, 0, 0, 85, 85,
- 0, 0, 98, 16, 0, 3,
- 50, 16, 16, 0, 1, 0,
- 0, 0, 98, 16, 0, 3,
- 194, 16, 16, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 104, 0, 0, 2,
- 2, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 0, 0, 0, 0, 70, 16,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 1, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 1, 0, 0, 0, 230, 26,
+ 230, 26, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
16, 0, 1, 0, 0, 0,
- 70, 126, 16, 0, 1, 0,
- 0, 0, 0, 96, 16, 0,
- 1, 0, 0, 0, 56, 0,
- 0, 7, 242, 32, 16, 0,
- 0, 0, 0, 0, 70, 14,
+ 56, 0, 0, 7, 242, 32,
16, 0, 0, 0, 0, 0,
- 246, 15, 16, 0, 1, 0,
- 0, 0, 62, 0, 0, 1,
- 83, 84, 65, 84, 116, 0,
- 0, 0, 4, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 82, 68,
- 69, 70, 240, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 28, 0, 0, 0, 0, 4,
- 255, 255, 0, 1, 0, 0,
- 187, 0, 0, 0, 156, 0,
- 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 82, 68, 69, 70, 240, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 187, 0, 0, 0,
+ 156, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 165, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0,
- 0, 0, 178, 0, 0, 0,
- 2, 0, 0, 0, 5, 0,
- 0, 0, 4, 0, 0, 0,
- 255, 255, 255, 255, 0, 0,
+ 0, 0, 165, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0,
- 12, 0, 0, 0, 182, 0,
+ 0, 0, 0, 0, 178, 0,
0, 0, 2, 0, 0, 0,
5, 0, 0, 0, 4, 0,
0, 0, 255, 255, 255, 255,
- 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 12, 0, 0, 0,
- 115, 83, 97, 109, 112, 108,
- 101, 114, 0, 115, 77, 97,
- 115, 107, 83, 97, 109, 112,
- 108, 101, 114, 0, 116, 101,
- 120, 0, 109, 97, 115, 107,
- 0, 77, 105, 99, 114, 111,
- 115, 111, 102, 116, 32, 40,
- 82, 41, 32, 72, 76, 83,
- 76, 32, 83, 104, 97, 100,
- 101, 114, 32, 67, 111, 109,
- 112, 105, 108, 101, 114, 32,
- 57, 46, 51, 48, 46, 57,
- 50, 48, 48, 46, 49, 54,
- 51, 56, 52, 0, 171, 171,
- 73, 83, 71, 78, 104, 0,
- 0, 0, 3, 0, 0, 0,
- 8, 0, 0, 0, 80, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 3, 0,
+ 182, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 1, 0, 0, 0,
+ 1, 0, 0, 0, 12, 0,
+ 0, 0, 115, 83, 97, 109,
+ 112, 108, 101, 114, 0, 115,
+ 77, 97, 115, 107, 83, 97,
+ 109, 112, 108, 101, 114, 0,
+ 116, 101, 120, 0, 109, 97,
+ 115, 107, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 54, 46, 51, 46,
+ 57, 54, 48, 48, 46, 49,
+ 54, 51, 56, 52, 0, 171,
+ 171, 171, 73, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 92, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 92, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 3, 0, 0, 92, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 12, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 0, 0,
- 12, 12, 0, 0, 83, 86,
- 95, 80, 111, 115, 105, 116,
- 105, 111, 110, 0, 84, 69,
- 88, 67, 79, 79, 82, 68,
- 0, 171, 171, 171, 79, 83,
- 71, 78, 44, 0, 0, 0,
- 1, 0, 0, 0, 8, 0,
- 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 15, 0,
- 0, 0, 83, 86, 95, 84,
- 97, 114, 103, 101, 116, 0,
- 171, 171, 122, 195, 0, 0,
- 0, 0, 0, 0, 83, 97,
- 109, 112, 108, 101, 84, 101,
- 120, 116, 117, 114, 101, 87,
- 105, 116, 104, 83, 104, 97,
- 100, 111, 119, 0, 4, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 128, 63, 1, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171, 202, 195,
+ 0, 0, 0, 0, 0, 0,
+ 83, 97, 109, 112, 108, 101,
+ 84, 101, 120, 116, 117, 114,
+ 101, 87, 105, 116, 104, 83,
+ 104, 97, 100, 111, 119, 0,
+ 4, 0, 0, 0, 1, 0,
0, 0, 0, 0, 128, 63,
1, 0, 0, 0, 0, 0,
128, 63, 1, 0, 0, 0,
0, 0, 128, 63, 1, 0,
- 0, 0, 3, 0, 0, 0,
- 255, 255, 255, 255, 72, 4,
- 0, 0, 68, 88, 66, 67,
- 28, 248, 40, 83, 2, 166,
- 203, 194, 228, 163, 91, 123,
- 149, 165, 41, 212, 1, 0,
- 0, 0, 72, 4, 0, 0,
- 6, 0, 0, 0, 56, 0,
- 0, 0, 248, 0, 0, 0,
- 244, 1, 0, 0, 112, 2,
- 0, 0, 164, 3, 0, 0,
- 216, 3, 0, 0, 65, 111,
- 110, 57, 184, 0, 0, 0,
- 184, 0, 0, 0, 0, 2,
- 254, 255, 132, 0, 0, 0,
- 52, 0, 0, 0, 1, 0,
- 36, 0, 0, 0, 48, 0,
- 0, 0, 48, 0, 0, 0,
- 36, 0, 1, 0, 48, 0,
- 0, 0, 0, 0, 3, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 2,
- 254, 255, 81, 0, 0, 5,
- 4, 0, 15, 160, 0, 0,
0, 0, 0, 0, 128, 63,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 255, 255, 255, 255,
+ 68, 4, 0, 0, 68, 88,
+ 66, 67, 77, 85, 167, 240,
+ 56, 56, 155, 78, 125, 96,
+ 49, 253, 103, 100, 22, 62,
+ 1, 0, 0, 0, 68, 4,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 248, 0,
+ 0, 0, 244, 1, 0, 0,
+ 112, 2, 0, 0, 160, 3,
+ 0, 0, 212, 3, 0, 0,
+ 65, 111, 110, 57, 184, 0,
+ 0, 0, 184, 0, 0, 0,
+ 0, 2, 254, 255, 132, 0,
+ 0, 0, 52, 0, 0, 0,
+ 1, 0, 36, 0, 0, 0,
+ 48, 0, 0, 0, 48, 0,
+ 0, 0, 36, 0, 1, 0,
+ 48, 0, 0, 0, 0, 0,
+ 3, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 31, 0, 0, 2,
- 5, 0, 0, 128, 0, 0,
- 15, 144, 4, 0, 0, 4,
- 0, 0, 3, 224, 0, 0,
- 228, 144, 2, 0, 238, 160,
- 2, 0, 228, 160, 4, 0,
- 0, 4, 0, 0, 12, 224,
- 0, 0, 20, 144, 3, 0,
- 180, 160, 3, 0, 20, 160,
+ 1, 2, 254, 255, 81, 0,
+ 0, 5, 4, 0, 15, 160,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 5, 0, 0, 128,
+ 0, 0, 15, 144, 4, 0,
+ 0, 4, 0, 0, 3, 224,
+ 0, 0, 228, 144, 2, 0,
+ 238, 160, 2, 0, 228, 160,
4, 0, 0, 4, 0, 0,
- 3, 128, 0, 0, 228, 144,
- 1, 0, 238, 160, 1, 0,
- 228, 160, 2, 0, 0, 3,
- 0, 0, 3, 192, 0, 0,
- 228, 128, 0, 0, 228, 160,
- 1, 0, 0, 2, 0, 0,
- 12, 192, 4, 0, 68, 160,
- 255, 255, 0, 0, 83, 72,
- 68, 82, 244, 0, 0, 0,
- 64, 0, 1, 0, 61, 0,
- 0, 0, 89, 0, 0, 4,
- 70, 142, 32, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 95, 0, 0, 3, 50, 16,
- 16, 0, 0, 0, 0, 0,
- 103, 0, 0, 4, 242, 32,
- 16, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 101, 0,
- 0, 3, 50, 32, 16, 0,
- 1, 0, 0, 0, 101, 0,
- 0, 3, 194, 32, 16, 0,
- 1, 0, 0, 0, 50, 0,
- 0, 11, 50, 32, 16, 0,
- 0, 0, 0, 0, 70, 16,
+ 12, 224, 0, 0, 20, 144,
+ 3, 0, 180, 160, 3, 0,
+ 20, 160, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 1, 0, 238, 160,
+ 1, 0, 228, 160, 2, 0,
+ 0, 3, 0, 0, 3, 192,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 12, 192, 4, 0,
+ 68, 160, 255, 255, 0, 0,
+ 83, 72, 68, 82, 244, 0,
+ 0, 0, 64, 0, 1, 0,
+ 61, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 95, 0, 0, 3,
+ 50, 16, 16, 0, 0, 0,
+ 0, 0, 103, 0, 0, 4,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 50, 32,
16, 0, 0, 0, 0, 0,
- 230, 138, 32, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
0, 0, 0, 0, 0, 0,
- 70, 128, 32, 0, 0, 0,
+ 0, 0, 70, 128, 32, 0,
0, 0, 0, 0, 0, 0,
- 54, 0, 0, 8, 194, 32,
- 16, 0, 0, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 194, 32, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 128, 63, 50, 0, 0, 11,
- 50, 32, 16, 0, 1, 0,
- 0, 0, 70, 16, 16, 0,
- 0, 0, 0, 0, 230, 138,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 70, 128,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 50, 0,
- 0, 11, 194, 32, 16, 0,
- 1, 0, 0, 0, 6, 20,
+ 0, 0, 128, 63, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
16, 0, 0, 0, 0, 0,
- 166, 142, 32, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 6, 132, 32, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 62, 0, 0, 1, 83, 84,
- 65, 84, 116, 0, 0, 0,
- 5, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 6, 20, 16, 0, 0, 0,
+ 0, 0, 166, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 6, 132, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 82, 68, 69, 70,
- 44, 1, 0, 0, 1, 0,
- 0, 0, 64, 0, 0, 0,
- 1, 0, 0, 0, 28, 0,
- 0, 0, 0, 4, 254, 255,
- 0, 1, 0, 0, 246, 0,
- 0, 0, 60, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 40, 1, 0, 0,
+ 1, 0, 0, 0, 64, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 246, 0, 0, 0, 60, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 99, 98,
- 48, 0, 60, 0, 0, 0,
- 4, 0, 0, 0, 88, 0,
- 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 184, 0, 0, 0,
- 0, 0, 0, 0, 16, 0,
- 0, 0, 2, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 212, 0, 0, 0,
- 16, 0, 0, 0, 16, 0,
- 0, 0, 2, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 222, 0, 0, 0,
- 32, 0, 0, 0, 16, 0,
- 0, 0, 2, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 236, 0, 0, 0,
- 48, 0, 0, 0, 16, 0,
+ 99, 98, 48, 0, 60, 0,
+ 0, 0, 4, 0, 0, 0,
+ 88, 0, 0, 0, 64, 0,
0, 0, 0, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 81, 117, 97, 100,
- 68, 101, 115, 99, 0, 171,
- 171, 171, 1, 0, 3, 0,
- 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
0, 0, 0, 0, 0, 0,
- 84, 101, 120, 67, 111, 111,
- 114, 100, 115, 0, 77, 97,
- 115, 107, 84, 101, 120, 67,
- 111, 111, 114, 100, 115, 0,
- 84, 101, 120, 116, 67, 111,
- 108, 111, 114, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 171, 171, 73, 83,
- 71, 78, 44, 0, 0, 0,
- 1, 0, 0, 0, 8, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 212, 0,
+ 0, 0, 16, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 222, 0,
0, 0, 32, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 236, 0,
+ 0, 0, 48, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 81, 117,
+ 97, 100, 68, 101, 115, 99,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 7, 3,
- 0, 0, 80, 79, 83, 73,
- 84, 73, 79, 78, 0, 171,
- 171, 171, 79, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
+ 0, 0, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 77, 97, 115, 107, 84, 101,
+ 120, 67, 111, 111, 114, 100,
+ 115, 0, 84, 101, 120, 116,
+ 67, 111, 108, 111, 114, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 54,
+ 46, 51, 46, 57, 54, 48,
+ 48, 46, 49, 54, 51, 56,
+ 52, 0, 73, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 0, 0, 7, 3, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 171, 171, 171,
+ 79, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 12, 0, 0,
- 92, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 3, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 162, 199, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 1, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
- 0, 0, 232, 9, 0, 0,
- 68, 88, 66, 67, 108, 162,
- 44, 78, 210, 223, 228, 191,
- 29, 172, 165, 191, 36, 14,
- 229, 150, 1, 0, 0, 0,
- 232, 9, 0, 0, 6, 0,
- 0, 0, 56, 0, 0, 0,
- 248, 2, 0, 0, 8, 7,
- 0, 0, 132, 7, 0, 0,
- 68, 9, 0, 0, 180, 9,
- 0, 0, 65, 111, 110, 57,
- 184, 2, 0, 0, 184, 2,
- 0, 0, 0, 2, 255, 255,
- 120, 2, 0, 0, 64, 0,
- 0, 0, 2, 0, 40, 0,
- 0, 0, 64, 0, 0, 0,
- 64, 0, 1, 0, 36, 0,
+ 3, 12, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 242, 199,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 232, 9, 0, 0, 68, 88,
+ 66, 67, 128, 131, 241, 85,
+ 199, 21, 192, 89, 55, 255,
+ 82, 94, 121, 175, 16, 184,
+ 1, 0, 0, 0, 232, 9,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 248, 2,
+ 0, 0, 8, 7, 0, 0,
+ 132, 7, 0, 0, 68, 9,
+ 0, 0, 180, 9, 0, 0,
+ 65, 111, 110, 57, 184, 2,
+ 0, 0, 184, 2, 0, 0,
+ 0, 2, 255, 255, 120, 2,
0, 0, 64, 0, 0, 0,
+ 2, 0, 40, 0, 0, 0,
+ 64, 0, 0, 0, 64, 0,
+ 1, 0, 36, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 4, 0,
3, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 6, 0,
- 4, 0, 3, 0, 0, 0,
- 0, 0, 1, 2, 255, 255,
- 31, 0, 0, 2, 0, 0,
- 0, 128, 0, 0, 15, 176,
- 31, 0, 0, 2, 0, 0,
- 0, 144, 0, 8, 15, 160,
- 2, 0, 0, 3, 0, 0,
- 1, 128, 0, 0, 0, 176,
- 0, 0, 85, 160, 1, 0,
- 0, 2, 0, 0, 2, 128,
- 0, 0, 85, 176, 2, 0,
- 0, 3, 1, 0, 1, 128,
+ 1, 2, 255, 255, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 2, 0,
+ 0, 3, 0, 0, 1, 128,
0, 0, 0, 176, 0, 0,
- 0, 160, 1, 0, 0, 2,
- 1, 0, 2, 128, 0, 0,
- 85, 176, 66, 0, 0, 3,
- 0, 0, 15, 128, 0, 0,
+ 85, 160, 1, 0, 0, 2,
+ 0, 0, 2, 128, 0, 0,
+ 85, 176, 2, 0, 0, 3,
+ 1, 0, 1, 128, 0, 0,
+ 0, 176, 0, 0, 0, 160,
+ 1, 0, 0, 2, 1, 0,
+ 2, 128, 0, 0, 85, 176,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 0, 8,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 255, 128, 3, 0, 85, 160,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 3, 0, 0, 160,
+ 1, 0, 255, 128, 0, 0,
+ 0, 128, 2, 0, 0, 3,
+ 1, 0, 1, 128, 0, 0,
+ 0, 176, 0, 0, 170, 160,
+ 1, 0, 0, 2, 1, 0,
+ 2, 128, 0, 0, 85, 176,
+ 2, 0, 0, 3, 2, 0,
+ 1, 128, 0, 0, 0, 176,
+ 0, 0, 255, 160, 1, 0,
+ 0, 2, 2, 0, 2, 128,
+ 0, 0, 85, 176, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 0, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 2, 0,
228, 128, 0, 8, 228, 160,
- 66, 0, 0, 3, 1, 0,
- 15, 128, 1, 0, 228, 128,
- 0, 8, 228, 160, 5, 0,
- 0, 3, 0, 0, 1, 128,
- 0, 0, 255, 128, 3, 0,
- 85, 160, 4, 0, 0, 4,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 3, 0, 170, 160,
+ 1, 0, 255, 128, 0, 0,
+ 0, 128, 4, 0, 0, 4,
0, 0, 1, 128, 3, 0,
- 0, 160, 1, 0, 255, 128,
+ 255, 160, 2, 0, 255, 128,
0, 0, 0, 128, 2, 0,
0, 3, 1, 0, 1, 128,
- 0, 0, 0, 176, 0, 0,
- 170, 160, 1, 0, 0, 2,
+ 0, 0, 0, 176, 1, 0,
+ 0, 160, 1, 0, 0, 2,
1, 0, 2, 128, 0, 0,
85, 176, 2, 0, 0, 3,
2, 0, 1, 128, 0, 0,
- 0, 176, 0, 0, 255, 160,
+ 0, 176, 1, 0, 85, 160,
1, 0, 0, 2, 2, 0,
2, 128, 0, 0, 85, 176,
66, 0, 0, 3, 1, 0,
@@ -13014,20 +13056,20 @@ const BYTE d2deffect[] =
0, 3, 2, 0, 15, 128,
2, 0, 228, 128, 0, 8,
228, 160, 4, 0, 0, 4,
- 0, 0, 1, 128, 3, 0,
- 170, 160, 1, 0, 255, 128,
+ 0, 0, 1, 128, 4, 0,
+ 0, 160, 1, 0, 255, 128,
0, 0, 0, 128, 4, 0,
0, 4, 0, 0, 1, 128,
- 3, 0, 255, 160, 2, 0,
+ 4, 0, 85, 160, 2, 0,
255, 128, 0, 0, 0, 128,
2, 0, 0, 3, 1, 0,
1, 128, 0, 0, 0, 176,
- 1, 0, 0, 160, 1, 0,
+ 1, 0, 170, 160, 1, 0,
0, 2, 1, 0, 2, 128,
0, 0, 85, 176, 2, 0,
0, 3, 2, 0, 1, 128,
0, 0, 0, 176, 1, 0,
- 85, 160, 1, 0, 0, 2,
+ 255, 160, 1, 0, 0, 2,
2, 0, 2, 128, 0, 0,
85, 176, 66, 0, 0, 3,
1, 0, 15, 128, 1, 0,
@@ -13036,511 +13078,489 @@ const BYTE d2deffect[] =
15, 128, 2, 0, 228, 128,
0, 8, 228, 160, 4, 0,
0, 4, 0, 0, 1, 128,
- 4, 0, 0, 160, 1, 0,
+ 4, 0, 170, 160, 1, 0,
255, 128, 0, 0, 0, 128,
4, 0, 0, 4, 0, 0,
- 1, 128, 4, 0, 85, 160,
+ 1, 128, 4, 0, 255, 160,
2, 0, 255, 128, 0, 0,
0, 128, 2, 0, 0, 3,
1, 0, 1, 128, 0, 0,
- 0, 176, 1, 0, 170, 160,
+ 0, 176, 2, 0, 0, 160,
1, 0, 0, 2, 1, 0,
2, 128, 0, 0, 85, 176,
- 2, 0, 0, 3, 2, 0,
- 1, 128, 0, 0, 0, 176,
- 1, 0, 255, 160, 1, 0,
- 0, 2, 2, 0, 2, 128,
- 0, 0, 85, 176, 66, 0,
- 0, 3, 1, 0, 15, 128,
- 1, 0, 228, 128, 0, 8,
- 228, 160, 66, 0, 0, 3,
- 2, 0, 15, 128, 2, 0,
- 228, 128, 0, 8, 228, 160,
- 4, 0, 0, 4, 0, 0,
- 1, 128, 4, 0, 170, 160,
- 1, 0, 255, 128, 0, 0,
- 0, 128, 4, 0, 0, 4,
- 0, 0, 1, 128, 4, 0,
- 255, 160, 2, 0, 255, 128,
- 0, 0, 0, 128, 2, 0,
- 0, 3, 1, 0, 1, 128,
- 0, 0, 0, 176, 2, 0,
- 0, 160, 1, 0, 0, 2,
- 1, 0, 2, 128, 0, 0,
- 85, 176, 66, 0, 0, 3,
- 1, 0, 15, 128, 1, 0,
- 228, 128, 0, 8, 228, 160,
- 4, 0, 0, 4, 0, 0,
- 1, 128, 5, 0, 0, 160,
- 1, 0, 255, 128, 0, 0,
- 0, 128, 5, 0, 0, 3,
- 0, 0, 15, 128, 0, 0,
- 0, 128, 6, 0, 228, 160,
- 1, 0, 0, 2, 0, 8,
- 15, 128, 0, 0, 228, 128,
- 255, 255, 0, 0, 83, 72,
- 68, 82, 8, 4, 0, 0,
- 64, 0, 0, 0, 2, 1,
- 0, 0, 89, 0, 0, 4,
- 70, 142, 32, 0, 0, 0,
- 0, 0, 10, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 0, 0, 0, 0,
- 88, 24, 0, 4, 0, 112,
- 16, 0, 0, 0, 0, 0,
- 85, 85, 0, 0, 98, 16,
- 0, 3, 50, 16, 16, 0,
- 1, 0, 0, 0, 101, 0,
- 0, 3, 242, 32, 16, 0,
- 0, 0, 0, 0, 104, 0,
- 0, 2, 4, 0, 0, 0,
- 0, 0, 0, 8, 242, 0,
- 16, 0, 0, 0, 0, 0,
- 6, 16, 16, 0, 1, 0,
- 0, 0, 38, 135, 32, 0,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 0, 8, 228, 160, 4, 0,
+ 0, 4, 0, 0, 1, 128,
+ 5, 0, 0, 160, 1, 0,
+ 255, 128, 0, 0, 0, 128,
+ 5, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 0, 128,
+ 6, 0, 228, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 8, 4, 0, 0, 64, 0,
+ 0, 0, 2, 1, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 90, 0,
+ 0, 3, 0, 96, 16, 0,
+ 0, 0, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 0, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 4, 0, 0, 0, 0, 0,
+ 0, 8, 242, 0, 16, 0,
+ 0, 0, 0, 0, 6, 16,
+ 16, 0, 1, 0, 0, 0,
+ 38, 135, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 82, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 7, 16, 0, 0, 0,
0, 0, 54, 0, 0, 5,
- 82, 0, 16, 0, 1, 0,
- 0, 0, 86, 7, 16, 0,
- 0, 0, 0, 0, 54, 0,
+ 162, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 1, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 2, 0, 0, 0, 230, 10,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 18, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 50, 0, 0, 10, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 54, 0,
0, 5, 162, 0, 16, 0,
- 1, 0, 0, 0, 86, 21,
+ 0, 0, 0, 0, 86, 21,
16, 0, 1, 0, 0, 0,
69, 0, 0, 9, 242, 0,
16, 0, 2, 0, 0, 0,
- 230, 10, 16, 0, 1, 0,
+ 70, 0, 16, 0, 0, 0,
0, 0, 70, 126, 16, 0,
0, 0, 0, 0, 0, 96,
16, 0, 0, 0, 0, 0,
69, 0, 0, 9, 242, 0,
- 16, 0, 1, 0, 0, 0,
- 70, 0, 16, 0, 1, 0,
+ 16, 0, 0, 0, 0, 0,
+ 230, 10, 16, 0, 0, 0,
0, 0, 70, 126, 16, 0,
0, 0, 0, 0, 0, 96,
16, 0, 0, 0, 0, 0,
- 56, 0, 0, 8, 18, 0,
- 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 10, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
58, 0, 16, 0, 2, 0,
- 0, 0, 26, 128, 32, 0,
- 0, 0, 0, 0, 6, 0,
- 0, 0, 50, 0, 0, 10,
- 18, 0, 16, 0, 1, 0,
- 0, 0, 10, 128, 32, 0,
- 0, 0, 0, 0, 6, 0,
- 0, 0, 58, 0, 16, 0,
- 1, 0, 0, 0, 10, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 18, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 1, 0, 0, 0, 38, 135,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 82, 0, 16, 0,
+ 2, 0, 0, 0, 86, 7,
16, 0, 1, 0, 0, 0,
54, 0, 0, 5, 162, 0,
- 16, 0, 0, 0, 0, 0,
+ 16, 0, 2, 0, 0, 0,
86, 21, 16, 0, 1, 0,
0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 2, 0,
+ 242, 0, 16, 0, 3, 0,
0, 0, 70, 0, 16, 0,
- 0, 0, 0, 0, 70, 126,
+ 2, 0, 0, 0, 70, 126,
16, 0, 0, 0, 0, 0,
0, 96, 16, 0, 0, 0,
0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 0, 0,
+ 242, 0, 16, 0, 2, 0,
0, 0, 230, 10, 16, 0,
- 0, 0, 0, 0, 70, 126,
+ 2, 0, 0, 0, 70, 126,
16, 0, 0, 0, 0, 0,
0, 96, 16, 0, 0, 0,
0, 0, 50, 0, 0, 10,
18, 0, 16, 0, 0, 0,
- 0, 0, 42, 128, 32, 0,
- 0, 0, 0, 0, 6, 0,
+ 0, 0, 10, 128, 32, 0,
+ 0, 0, 0, 0, 7, 0,
0, 0, 58, 0, 16, 0,
- 2, 0, 0, 0, 10, 0,
- 16, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
50, 0, 0, 10, 18, 0,
16, 0, 0, 0, 0, 0,
- 58, 128, 32, 0, 0, 0,
- 0, 0, 6, 0, 0, 0,
- 58, 0, 16, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 58, 0, 16, 0, 2, 0,
0, 0, 10, 0, 16, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 8, 242, 0, 16, 0,
- 1, 0, 0, 0, 6, 16,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 162, 0, 16, 0,
+ 1, 0, 0, 0, 86, 21,
16, 0, 1, 0, 0, 0,
- 38, 135, 32, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 54, 0, 0, 5, 82, 0,
- 16, 0, 2, 0, 0, 0,
- 86, 7, 16, 0, 1, 0,
- 0, 0, 54, 0, 0, 5,
- 162, 0, 16, 0, 2, 0,
- 0, 0, 86, 21, 16, 0,
- 1, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 3, 0, 0, 0, 70, 0,
- 16, 0, 2, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 2, 0, 0, 0, 230, 10,
+ 69, 0, 0, 9, 242, 0,
16, 0, 2, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 50, 0,
- 0, 10, 18, 0, 16, 0,
- 0, 0, 0, 0, 10, 128,
- 32, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 58, 0,
- 16, 0, 3, 0, 0, 0,
- 10, 0, 16, 0, 0, 0,
- 0, 0, 50, 0, 0, 10,
- 18, 0, 16, 0, 0, 0,
- 0, 0, 26, 128, 32, 0,
- 0, 0, 0, 0, 7, 0,
- 0, 0, 58, 0, 16, 0,
- 2, 0, 0, 0, 10, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
16, 0, 0, 0, 0, 0,
- 54, 0, 0, 5, 162, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 1, 0, 0, 0,
- 86, 21, 16, 0, 1, 0,
- 0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 2, 0,
- 0, 0, 70, 0, 16, 0,
- 1, 0, 0, 0, 70, 126,
- 16, 0, 0, 0, 0, 0,
- 0, 96, 16, 0, 0, 0,
- 0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 1, 0,
- 0, 0, 230, 10, 16, 0,
- 1, 0, 0, 0, 70, 126,
- 16, 0, 0, 0, 0, 0,
- 0, 96, 16, 0, 0, 0,
- 0, 0, 50, 0, 0, 10,
- 18, 0, 16, 0, 0, 0,
- 0, 0, 42, 128, 32, 0,
- 0, 0, 0, 0, 7, 0,
- 0, 0, 58, 0, 16, 0,
- 2, 0, 0, 0, 10, 0,
+ 230, 10, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
16, 0, 0, 0, 0, 0,
50, 0, 0, 10, 18, 0,
16, 0, 0, 0, 0, 0,
- 58, 128, 32, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 58, 0, 16, 0, 1, 0,
+ 58, 0, 16, 0, 2, 0,
0, 0, 10, 0, 16, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 8, 18, 0, 16, 0,
- 1, 0, 0, 0, 10, 16,
+ 0, 0, 0, 0, 50, 0,
+ 0, 10, 18, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 58, 0,
16, 0, 1, 0, 0, 0,
- 10, 128, 32, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 54, 0, 0, 5, 34, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 18, 0, 16, 0, 1, 0,
+ 0, 0, 10, 16, 16, 0,
+ 1, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 54, 0,
+ 0, 5, 34, 0, 16, 0,
+ 1, 0, 0, 0, 26, 16,
16, 0, 1, 0, 0, 0,
- 26, 16, 16, 0, 1, 0,
- 0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 1, 0,
- 0, 0, 70, 0, 16, 0,
- 1, 0, 0, 0, 70, 126,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
16, 0, 0, 0, 0, 0,
- 0, 96, 16, 0, 0, 0,
- 0, 0, 50, 0, 0, 10,
- 18, 0, 16, 0, 0, 0,
- 0, 0, 10, 128, 32, 0,
- 0, 0, 0, 0, 8, 0,
- 0, 0, 58, 0, 16, 0,
- 1, 0, 0, 0, 10, 0,
+ 50, 0, 0, 10, 18, 0,
16, 0, 0, 0, 0, 0,
- 56, 0, 0, 8, 242, 32,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 242, 32, 16, 0,
+ 0, 0, 0, 0, 6, 0,
16, 0, 0, 0, 0, 0,
- 6, 0, 16, 0, 0, 0,
- 0, 0, 70, 142, 32, 0,
- 0, 0, 0, 0, 9, 0,
- 0, 0, 62, 0, 0, 1,
- 83, 84, 65, 84, 116, 0,
- 0, 0, 30, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 5, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 30, 0, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 13, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 9, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 10, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 82, 68,
- 69, 70, 184, 1, 0, 0,
- 1, 0, 0, 0, 148, 0,
- 0, 0, 3, 0, 0, 0,
- 28, 0, 0, 0, 0, 4,
- 255, 255, 0, 1, 0, 0,
- 132, 1, 0, 0, 124, 0,
- 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 0, 0, 82, 68, 69, 70,
+ 184, 1, 0, 0, 1, 0,
+ 0, 0, 148, 0, 0, 0,
+ 3, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 132, 1,
+ 0, 0, 124, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 139, 0, 0, 0, 2, 0,
- 0, 0, 5, 0, 0, 0,
- 4, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 1, 0, 0, 0, 12, 0,
- 0, 0, 143, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 139, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 143, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 115, 83,
- 104, 97, 100, 111, 119, 83,
- 97, 109, 112, 108, 101, 114,
- 0, 116, 101, 120, 0, 99,
- 98, 49, 0, 171, 143, 0,
- 0, 0, 4, 0, 0, 0,
- 172, 0, 0, 0, 160, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 12, 1,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 83, 104, 97,
+ 100, 111, 119, 83, 97, 109,
+ 112, 108, 101, 114, 0, 116,
+ 101, 120, 0, 99, 98, 49,
+ 0, 171, 143, 0, 0, 0,
+ 4, 0, 0, 0, 172, 0,
+ 0, 0, 160, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 48, 0, 0, 0, 2, 0,
- 0, 0, 28, 1, 0, 0,
- 0, 0, 0, 0, 44, 1,
- 0, 0, 48, 0, 0, 0,
- 48, 0, 0, 0, 0, 0,
- 0, 0, 60, 1, 0, 0,
- 0, 0, 0, 0, 76, 1,
- 0, 0, 96, 0, 0, 0,
- 48, 0, 0, 0, 2, 0,
- 0, 0, 88, 1, 0, 0,
- 0, 0, 0, 0, 104, 1,
- 0, 0, 144, 0, 0, 0,
- 16, 0, 0, 0, 2, 0,
- 0, 0, 116, 1, 0, 0,
+ 0, 0, 12, 1, 0, 0,
+ 0, 0, 0, 0, 48, 0,
+ 0, 0, 2, 0, 0, 0,
+ 28, 1, 0, 0, 0, 0,
+ 0, 0, 44, 1, 0, 0,
+ 48, 0, 0, 0, 48, 0,
+ 0, 0, 0, 0, 0, 0,
+ 60, 1, 0, 0, 0, 0,
+ 0, 0, 76, 1, 0, 0,
+ 96, 0, 0, 0, 48, 0,
+ 0, 0, 2, 0, 0, 0,
+ 88, 1, 0, 0, 0, 0,
+ 0, 0, 104, 1, 0, 0,
+ 144, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 116, 1, 0, 0, 0, 0,
+ 0, 0, 66, 108, 117, 114,
+ 79, 102, 102, 115, 101, 116,
+ 115, 72, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 3, 0, 0, 0,
0, 0, 0, 0, 66, 108,
117, 114, 79, 102, 102, 115,
- 101, 116, 115, 72, 0, 171,
+ 101, 116, 115, 86, 0, 171,
171, 171, 1, 0, 3, 0,
1, 0, 4, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 66, 108, 117, 114, 79, 102,
- 102, 115, 101, 116, 115, 86,
- 0, 171, 171, 171, 1, 0,
+ 66, 108, 117, 114, 87, 101,
+ 105, 103, 104, 116, 115, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 83, 104,
+ 97, 100, 111, 119, 67, 111,
+ 108, 111, 114, 0, 1, 0,
3, 0, 1, 0, 4, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 66, 108, 117, 114,
- 87, 101, 105, 103, 104, 116,
- 115, 0, 1, 0, 3, 0,
- 1, 0, 4, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 83, 104, 97, 100, 111, 119,
- 67, 111, 108, 111, 114, 0,
- 1, 0, 3, 0, 1, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 73, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
- 0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 3, 0, 0,
- 92, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 0, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 79, 83, 71, 78, 44, 0,
- 0, 0, 1, 0, 0, 0,
- 8, 0, 0, 0, 32, 0,
+ 15, 0, 0, 0, 92, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 83, 86,
- 95, 84, 97, 114, 103, 101,
- 116, 0, 171, 171, 2, 204,
- 0, 0, 0, 0, 0, 0,
- 80, 49, 0, 4, 0, 0,
- 0, 1, 0, 0, 0, 0,
- 0, 128, 63, 1, 0, 0,
- 0, 0, 0, 128, 63, 1,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171, 78, 204, 0, 0,
+ 0, 0, 0, 0, 80, 49,
+ 0, 4, 0, 0, 0, 1,
0, 0, 0, 0, 0, 128,
63, 1, 0, 0, 0, 0,
0, 128, 63, 1, 0, 0,
- 0, 3, 0, 0, 0, 255,
- 255, 255, 255, 72, 4, 0,
- 0, 68, 88, 66, 67, 28,
- 248, 40, 83, 2, 166, 203,
- 194, 228, 163, 91, 123, 149,
- 165, 41, 212, 1, 0, 0,
- 0, 72, 4, 0, 0, 6,
- 0, 0, 0, 56, 0, 0,
- 0, 248, 0, 0, 0, 244,
- 1, 0, 0, 112, 2, 0,
- 0, 164, 3, 0, 0, 216,
- 3, 0, 0, 65, 111, 110,
- 57, 184, 0, 0, 0, 184,
- 0, 0, 0, 0, 2, 254,
- 255, 132, 0, 0, 0, 52,
- 0, 0, 0, 1, 0, 36,
- 0, 0, 0, 48, 0, 0,
- 0, 48, 0, 0, 0, 36,
- 0, 1, 0, 48, 0, 0,
- 0, 0, 0, 3, 0, 1,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 2, 254,
- 255, 81, 0, 0, 5, 4,
- 0, 15, 160, 0, 0, 0,
- 0, 0, 0, 128, 63, 0,
+ 0, 0, 0, 128, 63, 1,
+ 0, 0, 0, 0, 0, 128,
+ 63, 1, 0, 0, 0, 3,
+ 0, 0, 0, 255, 255, 255,
+ 255, 68, 4, 0, 0, 68,
+ 88, 66, 67, 77, 85, 167,
+ 240, 56, 56, 155, 78, 125,
+ 96, 49, 253, 103, 100, 22,
+ 62, 1, 0, 0, 0, 68,
+ 4, 0, 0, 6, 0, 0,
+ 0, 56, 0, 0, 0, 248,
+ 0, 0, 0, 244, 1, 0,
+ 0, 112, 2, 0, 0, 160,
+ 3, 0, 0, 212, 3, 0,
+ 0, 65, 111, 110, 57, 184,
+ 0, 0, 0, 184, 0, 0,
+ 0, 0, 2, 254, 255, 132,
+ 0, 0, 0, 52, 0, 0,
+ 0, 1, 0, 36, 0, 0,
+ 0, 48, 0, 0, 0, 48,
+ 0, 0, 0, 36, 0, 1,
+ 0, 48, 0, 0, 0, 0,
+ 0, 3, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 31, 0, 0, 2, 5,
- 0, 0, 128, 0, 0, 15,
- 144, 4, 0, 0, 4, 0,
- 0, 3, 224, 0, 0, 228,
- 144, 2, 0, 238, 160, 2,
- 0, 228, 160, 4, 0, 0,
- 4, 0, 0, 12, 224, 0,
- 0, 20, 144, 3, 0, 180,
- 160, 3, 0, 20, 160, 4,
+ 0, 1, 2, 254, 255, 81,
+ 0, 0, 5, 4, 0, 15,
+ 160, 0, 0, 0, 0, 0,
+ 0, 128, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 31,
+ 0, 0, 2, 5, 0, 0,
+ 128, 0, 0, 15, 144, 4,
0, 0, 4, 0, 0, 3,
- 128, 0, 0, 228, 144, 1,
- 0, 238, 160, 1, 0, 228,
- 160, 2, 0, 0, 3, 0,
- 0, 3, 192, 0, 0, 228,
- 128, 0, 0, 228, 160, 1,
- 0, 0, 2, 0, 0, 12,
- 192, 4, 0, 68, 160, 255,
- 255, 0, 0, 83, 72, 68,
- 82, 244, 0, 0, 0, 64,
- 0, 1, 0, 61, 0, 0,
- 0, 89, 0, 0, 4, 70,
- 142, 32, 0, 0, 0, 0,
- 0, 3, 0, 0, 0, 95,
- 0, 0, 3, 50, 16, 16,
- 0, 0, 0, 0, 0, 103,
- 0, 0, 4, 242, 32, 16,
- 0, 0, 0, 0, 0, 1,
- 0, 0, 0, 101, 0, 0,
- 3, 50, 32, 16, 0, 1,
- 0, 0, 0, 101, 0, 0,
- 3, 194, 32, 16, 0, 1,
- 0, 0, 0, 50, 0, 0,
- 11, 50, 32, 16, 0, 0,
- 0, 0, 0, 70, 16, 16,
- 0, 0, 0, 0, 0, 230,
- 138, 32, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 70,
- 128, 32, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 54,
- 0, 0, 8, 194, 32, 16,
- 0, 0, 0, 0, 0, 2,
- 64, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 128,
- 63, 50, 0, 0, 11, 50,
+ 224, 0, 0, 228, 144, 2,
+ 0, 238, 160, 2, 0, 228,
+ 160, 4, 0, 0, 4, 0,
+ 0, 12, 224, 0, 0, 20,
+ 144, 3, 0, 180, 160, 3,
+ 0, 20, 160, 4, 0, 0,
+ 4, 0, 0, 3, 128, 0,
+ 0, 228, 144, 1, 0, 238,
+ 160, 1, 0, 228, 160, 2,
+ 0, 0, 3, 0, 0, 3,
+ 192, 0, 0, 228, 128, 0,
+ 0, 228, 160, 1, 0, 0,
+ 2, 0, 0, 12, 192, 4,
+ 0, 68, 160, 255, 255, 0,
+ 0, 83, 72, 68, 82, 244,
+ 0, 0, 0, 64, 0, 1,
+ 0, 61, 0, 0, 0, 89,
+ 0, 0, 4, 70, 142, 32,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 95, 0, 0,
+ 3, 50, 16, 16, 0, 0,
+ 0, 0, 0, 103, 0, 0,
+ 4, 242, 32, 16, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 101, 0, 0, 3, 50,
32, 16, 0, 1, 0, 0,
+ 0, 101, 0, 0, 3, 194,
+ 32, 16, 0, 1, 0, 0,
+ 0, 50, 0, 0, 11, 50,
+ 32, 16, 0, 0, 0, 0,
0, 70, 16, 16, 0, 0,
0, 0, 0, 230, 138, 32,
- 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 70, 128, 32,
- 0, 0, 0, 0, 0, 1,
- 0, 0, 0, 50, 0, 0,
- 11, 194, 32, 16, 0, 1,
- 0, 0, 0, 6, 20, 16,
- 0, 0, 0, 0, 0, 166,
- 142, 32, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 6,
- 132, 32, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 62,
- 0, 0, 1, 83, 84, 65,
- 84, 116, 0, 0, 0, 5,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 54, 0, 0,
+ 8, 194, 32, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 128, 63, 50,
+ 0, 0, 11, 50, 32, 16,
+ 0, 1, 0, 0, 0, 70,
+ 16, 16, 0, 0, 0, 0,
+ 0, 230, 138, 32, 0, 0,
0, 0, 0, 1, 0, 0,
+ 0, 70, 128, 32, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 50, 0, 0, 11, 194,
+ 32, 16, 0, 1, 0, 0,
+ 0, 6, 20, 16, 0, 0,
+ 0, 0, 0, 166, 142, 32,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 6, 132, 32,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 62, 0, 0,
+ 1, 83, 84, 65, 84, 116,
+ 0, 0, 0, 5, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 4, 0, 0,
+ 0, 3, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 82, 68, 69, 70, 44,
- 1, 0, 0, 1, 0, 0,
- 0, 64, 0, 0, 0, 1,
- 0, 0, 0, 28, 0, 0,
- 0, 0, 4, 254, 255, 0,
- 1, 0, 0, 246, 0, 0,
- 0, 60, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 82,
+ 68, 69, 70, 40, 1, 0,
+ 0, 1, 0, 0, 0, 64,
+ 0, 0, 0, 1, 0, 0,
+ 0, 28, 0, 0, 0, 0,
+ 4, 254, 255, 0, 1, 0,
+ 0, 246, 0, 0, 0, 60,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 1, 0, 0, 0, 0,
- 0, 0, 0, 99, 98, 48,
- 0, 60, 0, 0, 0, 4,
- 0, 0, 0, 88, 0, 0,
- 0, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 184, 0, 0, 0, 0,
- 0, 0, 0, 16, 0, 0,
- 0, 2, 0, 0, 0, 196,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
- 0, 212, 0, 0, 0, 16,
- 0, 0, 0, 16, 0, 0,
- 0, 2, 0, 0, 0, 196,
+ 0, 99, 98, 48, 0, 60,
+ 0, 0, 0, 4, 0, 0,
+ 0, 88, 0, 0, 0, 64,
0, 0, 0, 0, 0, 0,
- 0, 222, 0, 0, 0, 32,
- 0, 0, 0, 16, 0, 0,
- 0, 2, 0, 0, 0, 196,
+ 0, 0, 0, 0, 0, 184,
0, 0, 0, 0, 0, 0,
- 0, 236, 0, 0, 0, 48,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 212,
0, 0, 0, 16, 0, 0,
- 0, 0, 0, 0, 0, 196,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 222,
+ 0, 0, 0, 32, 0, 0,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 236,
+ 0, 0, 0, 48, 0, 0,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 81,
+ 117, 97, 100, 68, 101, 115,
+ 99, 0, 171, 171, 171, 1,
+ 0, 3, 0, 1, 0, 4,
0, 0, 0, 0, 0, 0,
- 0, 81, 117, 97, 100, 68,
- 101, 115, 99, 0, 171, 171,
- 171, 1, 0, 3, 0, 1,
- 0, 4, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 84,
+ 0, 0, 0, 84, 101, 120,
+ 67, 111, 111, 114, 100, 115,
+ 0, 77, 97, 115, 107, 84,
101, 120, 67, 111, 111, 114,
- 100, 115, 0, 77, 97, 115,
- 107, 84, 101, 120, 67, 111,
- 111, 114, 100, 115, 0, 84,
- 101, 120, 116, 67, 111, 108,
- 111, 114, 0, 77, 105, 99,
- 114, 111, 115, 111, 102, 116,
- 32, 40, 82, 41, 32, 72,
- 76, 83, 76, 32, 83, 104,
- 97, 100, 101, 114, 32, 67,
- 111, 109, 112, 105, 108, 101,
- 114, 32, 57, 46, 51, 48,
- 46, 57, 50, 48, 48, 46,
- 49, 54, 51, 56, 52, 0,
- 171, 171, 171, 73, 83, 71,
+ 100, 115, 0, 84, 101, 120,
+ 116, 67, 111, 108, 111, 114,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 54, 46, 51, 46, 57, 54,
+ 48, 48, 46, 49, 54, 51,
+ 56, 52, 0, 73, 83, 71,
78, 44, 0, 0, 0, 1,
0, 0, 0, 8, 0, 0,
0, 32, 0, 0, 0, 0,
@@ -13567,15 +13587,15 @@ const BYTE d2deffect[] =
86, 95, 80, 111, 115, 105,
116, 105, 111, 110, 0, 84,
69, 88, 67, 79, 79, 82,
- 68, 0, 171, 171, 171, 41,
+ 68, 0, 171, 171, 171, 117,
214, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
0, 172, 9, 0, 0, 68,
- 88, 66, 67, 59, 162, 86,
- 163, 32, 38, 131, 23, 42,
- 142, 102, 208, 154, 242, 96,
- 4, 1, 0, 0, 0, 172,
+ 88, 66, 67, 67, 47, 1,
+ 244, 0, 102, 246, 41, 38,
+ 220, 84, 204, 156, 139, 96,
+ 25, 1, 0, 0, 0, 172,
9, 0, 0, 6, 0, 0,
0, 56, 0, 0, 0, 220,
2, 0, 0, 204, 6, 0,
@@ -13865,7 +13885,7 @@ const BYTE d2deffect[] =
84, 65, 84, 116, 0, 0,
0, 29, 0, 0, 0, 4,
0, 0, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 4,
+ 0, 2, 0, 0, 0, 12,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
@@ -13875,7 +13895,7 @@ const BYTE d2deffect[] =
0, 0, 0, 9, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 10,
+ 0, 0, 0, 0, 0, 7,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -13954,9 +13974,9 @@ const BYTE d2deffect[] =
76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
- 114, 32, 57, 46, 51, 48,
- 46, 57, 50, 48, 48, 46,
- 49, 54, 51, 56, 52, 0,
+ 114, 32, 54, 46, 51, 46,
+ 57, 54, 48, 48, 46, 49,
+ 54, 51, 56, 52, 0, 171,
171, 73, 83, 71, 78, 104,
0, 0, 0, 3, 0, 0,
0, 8, 0, 0, 0, 80,
@@ -13984,7 +14004,7 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 15,
0, 0, 0, 83, 86, 95,
84, 97, 114, 103, 101, 116,
- 0, 171, 171, 137, 218, 0,
+ 0, 171, 171, 209, 218, 0,
0, 0, 0, 0, 0, 80,
50, 0, 4, 0, 0, 0,
1, 0, 0, 0, 0, 0,
@@ -13994,16 +14014,16 @@ const BYTE d2deffect[] =
1, 0, 0, 0, 0, 0,
128, 63, 1, 0, 0, 0,
3, 0, 0, 0, 255, 255,
- 255, 255, 72, 4, 0, 0,
- 68, 88, 66, 67, 28, 248,
- 40, 83, 2, 166, 203, 194,
- 228, 163, 91, 123, 149, 165,
- 41, 212, 1, 0, 0, 0,
- 72, 4, 0, 0, 6, 0,
+ 255, 255, 68, 4, 0, 0,
+ 68, 88, 66, 67, 77, 85,
+ 167, 240, 56, 56, 155, 78,
+ 125, 96, 49, 253, 103, 100,
+ 22, 62, 1, 0, 0, 0,
+ 68, 4, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
248, 0, 0, 0, 244, 1,
0, 0, 112, 2, 0, 0,
- 164, 3, 0, 0, 216, 3,
+ 160, 3, 0, 0, 212, 3,
0, 0, 65, 111, 110, 57,
184, 0, 0, 0, 184, 0,
0, 0, 0, 2, 254, 255,
@@ -14082,7 +14102,7 @@ const BYTE d2deffect[] =
116, 0, 0, 0, 5, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 4, 0,
- 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -14092,14 +14112,14 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 82, 68, 69, 70, 44, 1,
+ 82, 68, 69, 70, 40, 1,
0, 0, 1, 0, 0, 0,
64, 0, 0, 0, 1, 0,
0, 0, 28, 0, 0, 0,
@@ -14147,117 +14167,95 @@ const BYTE d2deffect[] =
83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
- 171, 171, 73, 83, 71, 78,
- 44, 0, 0, 0, 1, 0,
- 0, 0, 8, 0, 0, 0,
- 32, 0, 0, 0, 0, 0,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 73, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 7, 3, 0, 0,
- 80, 79, 83, 73, 84, 73,
- 79, 78, 0, 171, 171, 171,
- 79, 83, 71, 78, 104, 0,
0, 0, 3, 0, 0, 0,
- 8, 0, 0, 0, 80, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 3, 0,
- 0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 92, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 12, 0, 0, 92, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 7, 3,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 12, 3, 0, 0, 83, 86,
- 95, 80, 111, 115, 105, 116,
- 105, 111, 110, 0, 84, 69,
- 88, 67, 79, 79, 82, 68,
- 0, 171, 171, 171, 116, 228,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 2, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 12, 0, 0,
+ 92, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 164, 10, 0, 0, 68, 88,
- 66, 67, 219, 231, 18, 29,
- 129, 252, 34, 48, 152, 246,
- 123, 61, 199, 166, 77, 90,
- 1, 0, 0, 0, 164, 10,
- 0, 0, 6, 0, 0, 0,
- 56, 0, 0, 0, 24, 3,
- 0, 0, 112, 7, 0, 0,
- 236, 7, 0, 0, 0, 10,
- 0, 0, 112, 10, 0, 0,
- 65, 111, 110, 57, 216, 2,
- 0, 0, 216, 2, 0, 0,
- 0, 2, 255, 255, 160, 2,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 3, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 188, 228, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 164, 10, 0, 0,
+ 68, 88, 66, 67, 70, 166,
+ 174, 156, 153, 145, 163, 116,
+ 127, 37, 205, 162, 136, 116,
+ 62, 222, 1, 0, 0, 0,
+ 164, 10, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
- 1, 0, 44, 0, 0, 0,
- 56, 0, 0, 0, 56, 0,
- 2, 0, 36, 0, 0, 0,
- 56, 0, 1, 0, 0, 0,
- 0, 1, 1, 0, 0, 0,
- 3, 0, 6, 0, 0, 0,
- 0, 0, 0, 0, 1, 2,
- 255, 255, 31, 0, 0, 2,
- 0, 0, 0, 128, 0, 0,
- 15, 176, 31, 0, 0, 2,
- 0, 0, 0, 144, 0, 8,
- 15, 160, 31, 0, 0, 2,
- 0, 0, 0, 144, 1, 8,
- 15, 160, 2, 0, 0, 3,
- 0, 0, 2, 128, 0, 0,
- 85, 176, 0, 0, 85, 160,
- 1, 0, 0, 2, 0, 0,
+ 24, 3, 0, 0, 112, 7,
+ 0, 0, 236, 7, 0, 0,
+ 0, 10, 0, 0, 112, 10,
+ 0, 0, 65, 111, 110, 57,
+ 216, 2, 0, 0, 216, 2,
+ 0, 0, 0, 2, 255, 255,
+ 160, 2, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 1, 0,
+ 0, 0, 0, 1, 1, 0,
+ 0, 0, 3, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 2, 0,
+ 0, 3, 0, 0, 2, 128,
+ 0, 0, 85, 176, 0, 0,
+ 85, 160, 1, 0, 0, 2,
+ 0, 0, 1, 128, 0, 0,
+ 0, 176, 2, 0, 0, 3,
+ 1, 0, 2, 128, 0, 0,
+ 85, 176, 0, 0, 0, 160,
+ 1, 0, 0, 2, 1, 0,
1, 128, 0, 0, 0, 176,
- 2, 0, 0, 3, 1, 0,
- 2, 128, 0, 0, 85, 176,
- 0, 0, 0, 160, 1, 0,
- 0, 2, 1, 0, 1, 128,
- 0, 0, 0, 176, 66, 0,
- 0, 3, 0, 0, 15, 128,
- 0, 0, 228, 128, 1, 8,
- 228, 160, 66, 0, 0, 3,
- 1, 0, 15, 128, 1, 0,
- 228, 128, 1, 8, 228, 160,
- 5, 0, 0, 3, 0, 0,
+ 66, 0, 0, 3, 0, 0,
15, 128, 0, 0, 228, 128,
- 3, 0, 85, 160, 4, 0,
- 0, 4, 0, 0, 15, 128,
- 3, 0, 0, 160, 1, 0,
- 228, 128, 0, 0, 228, 128,
- 2, 0, 0, 3, 1, 0,
- 2, 128, 0, 0, 85, 176,
- 0, 0, 170, 160, 1, 0,
- 0, 2, 1, 0, 1, 128,
- 0, 0, 0, 176, 2, 0,
- 0, 3, 2, 0, 2, 128,
- 0, 0, 85, 176, 0, 0,
- 255, 160, 1, 0, 0, 2,
- 2, 0, 1, 128, 0, 0,
- 0, 176, 66, 0, 0, 3,
- 1, 0, 15, 128, 1, 0,
- 228, 128, 1, 8, 228, 160,
- 66, 0, 0, 3, 2, 0,
- 15, 128, 2, 0, 228, 128,
- 1, 8, 228, 160, 4, 0,
- 0, 4, 0, 0, 15, 128,
- 3, 0, 170, 160, 1, 0,
- 228, 128, 0, 0, 228, 128,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 1, 8,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 3, 0, 85, 160,
4, 0, 0, 4, 0, 0,
- 15, 128, 3, 0, 255, 160,
- 2, 0, 228, 128, 0, 0,
+ 15, 128, 3, 0, 0, 160,
+ 1, 0, 228, 128, 0, 0,
228, 128, 2, 0, 0, 3,
1, 0, 2, 128, 0, 0,
- 85, 176, 1, 0, 0, 160,
+ 85, 176, 0, 0, 170, 160,
1, 0, 0, 2, 1, 0,
1, 128, 0, 0, 0, 176,
2, 0, 0, 3, 2, 0,
2, 128, 0, 0, 85, 176,
- 1, 0, 85, 160, 1, 0,
+ 0, 0, 255, 160, 1, 0,
0, 2, 2, 0, 1, 128,
0, 0, 0, 176, 66, 0,
0, 3, 1, 0, 15, 128,
@@ -14266,19 +14264,19 @@ const BYTE d2deffect[] =
2, 0, 15, 128, 2, 0,
228, 128, 1, 8, 228, 160,
4, 0, 0, 4, 0, 0,
- 15, 128, 4, 0, 0, 160,
+ 15, 128, 3, 0, 170, 160,
1, 0, 228, 128, 0, 0,
228, 128, 4, 0, 0, 4,
- 0, 0, 15, 128, 4, 0,
- 85, 160, 2, 0, 228, 128,
+ 0, 0, 15, 128, 3, 0,
+ 255, 160, 2, 0, 228, 128,
0, 0, 228, 128, 2, 0,
0, 3, 1, 0, 2, 128,
0, 0, 85, 176, 1, 0,
- 170, 160, 1, 0, 0, 2,
+ 0, 160, 1, 0, 0, 2,
1, 0, 1, 128, 0, 0,
0, 176, 2, 0, 0, 3,
2, 0, 2, 128, 0, 0,
- 85, 176, 1, 0, 255, 160,
+ 85, 176, 1, 0, 85, 160,
1, 0, 0, 2, 2, 0,
1, 128, 0, 0, 0, 176,
66, 0, 0, 3, 1, 0,
@@ -14288,132 +14286,116 @@ const BYTE d2deffect[] =
2, 0, 228, 128, 1, 8,
228, 160, 4, 0, 0, 4,
0, 0, 15, 128, 4, 0,
- 170, 160, 1, 0, 228, 128,
+ 0, 160, 1, 0, 228, 128,
0, 0, 228, 128, 4, 0,
0, 4, 0, 0, 15, 128,
- 4, 0, 255, 160, 2, 0,
+ 4, 0, 85, 160, 2, 0,
228, 128, 0, 0, 228, 128,
2, 0, 0, 3, 1, 0,
2, 128, 0, 0, 85, 176,
- 2, 0, 0, 160, 1, 0,
+ 1, 0, 170, 160, 1, 0,
0, 2, 1, 0, 1, 128,
- 0, 0, 0, 176, 1, 0,
- 0, 2, 2, 0, 3, 128,
- 0, 0, 235, 176, 66, 0,
- 0, 3, 1, 0, 15, 128,
- 1, 0, 228, 128, 1, 8,
- 228, 160, 66, 0, 0, 3,
- 2, 0, 15, 128, 2, 0,
- 228, 128, 0, 8, 228, 160,
+ 0, 0, 0, 176, 2, 0,
+ 0, 3, 2, 0, 2, 128,
+ 0, 0, 85, 176, 1, 0,
+ 255, 160, 1, 0, 0, 2,
+ 2, 0, 1, 128, 0, 0,
+ 0, 176, 66, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 1, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 2, 0, 228, 128,
+ 1, 8, 228, 160, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 4, 0, 170, 160, 1, 0,
+ 228, 128, 0, 0, 228, 128,
4, 0, 0, 4, 0, 0,
- 15, 128, 5, 0, 0, 160,
- 1, 0, 228, 128, 0, 0,
- 228, 128, 5, 0, 0, 3,
- 0, 0, 15, 128, 2, 0,
- 255, 128, 0, 0, 228, 128,
- 1, 0, 0, 2, 0, 8,
- 15, 128, 0, 0, 228, 128,
- 255, 255, 0, 0, 83, 72,
- 68, 82, 80, 4, 0, 0,
- 64, 0, 0, 0, 20, 1,
- 0, 0, 89, 0, 0, 4,
- 70, 142, 32, 0, 0, 0,
- 0, 0, 9, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 0, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 1, 0, 0, 0,
+ 15, 128, 4, 0, 255, 160,
+ 2, 0, 228, 128, 0, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 2, 128, 0, 0,
+ 85, 176, 2, 0, 0, 160,
+ 1, 0, 0, 2, 1, 0,
+ 1, 128, 0, 0, 0, 176,
+ 1, 0, 0, 2, 2, 0,
+ 3, 128, 0, 0, 235, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 2, 0, 228, 128, 0, 8,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 5, 0,
+ 0, 160, 1, 0, 228, 128,
+ 0, 0, 228, 128, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 2, 0, 255, 128, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 80, 4,
+ 0, 0, 64, 0, 0, 0,
+ 20, 1, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 9, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
88, 24, 0, 4, 0, 112,
- 16, 0, 0, 0, 0, 0,
- 85, 85, 0, 0, 88, 24,
- 0, 4, 0, 112, 16, 0,
- 1, 0, 0, 0, 85, 85,
- 0, 0, 98, 16, 0, 3,
- 50, 16, 16, 0, 1, 0,
- 0, 0, 98, 16, 0, 3,
- 194, 16, 16, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 104, 0, 0, 2,
- 4, 0, 0, 0, 54, 0,
- 0, 5, 82, 0, 16, 0,
- 0, 0, 0, 0, 6, 16,
- 16, 0, 1, 0, 0, 0,
- 0, 0, 0, 8, 242, 0,
16, 0, 1, 0, 0, 0,
- 86, 21, 16, 0, 1, 0,
- 0, 0, 134, 141, 32, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 54, 0, 0, 5,
- 162, 0, 16, 0, 0, 0,
- 0, 0, 6, 8, 16, 0,
- 1, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 2, 0, 0, 0, 230, 10,
- 16, 0, 0, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 1, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 0, 0, 0, 0, 70, 0,
- 16, 0, 0, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 1, 0, 0, 0, 56, 0,
- 0, 8, 242, 0, 16, 0,
- 2, 0, 0, 0, 70, 14,
- 16, 0, 2, 0, 0, 0,
- 86, 133, 32, 0, 0, 0,
- 0, 0, 6, 0, 0, 0,
- 50, 0, 0, 10, 242, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 98, 16,
+ 0, 3, 194, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 82, 0,
16, 0, 0, 0, 0, 0,
- 6, 128, 32, 0, 0, 0,
- 0, 0, 6, 0, 0, 0,
- 70, 14, 16, 0, 0, 0,
- 0, 0, 70, 14, 16, 0,
- 2, 0, 0, 0, 54, 0,
- 0, 5, 82, 0, 16, 0,
- 1, 0, 0, 0, 6, 16,
+ 6, 16, 16, 0, 1, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 1, 0, 0, 0, 134, 141,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 54, 0,
+ 0, 5, 162, 0, 16, 0,
+ 0, 0, 0, 0, 6, 8,
16, 0, 1, 0, 0, 0,
69, 0, 0, 9, 242, 0,
16, 0, 2, 0, 0, 0,
- 70, 0, 16, 0, 1, 0,
+ 230, 10, 16, 0, 0, 0,
0, 0, 70, 126, 16, 0,
0, 0, 0, 0, 0, 96,
16, 0, 1, 0, 0, 0,
69, 0, 0, 9, 242, 0,
- 16, 0, 1, 0, 0, 0,
- 230, 10, 16, 0, 1, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
0, 0, 70, 126, 16, 0,
0, 0, 0, 0, 0, 96,
16, 0, 1, 0, 0, 0,
- 50, 0, 0, 10, 242, 0,
- 16, 0, 0, 0, 0, 0,
- 166, 138, 32, 0, 0, 0,
- 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 8, 242, 0,
+ 16, 0, 2, 0, 0, 0,
70, 14, 16, 0, 2, 0,
+ 0, 0, 86, 133, 32, 0,
+ 0, 0, 0, 0, 6, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 128, 32, 0,
+ 0, 0, 0, 0, 6, 0,
0, 0, 70, 14, 16, 0,
- 0, 0, 0, 0, 50, 0,
- 0, 10, 242, 0, 16, 0,
- 0, 0, 0, 0, 246, 143,
- 32, 0, 0, 0, 0, 0,
- 6, 0, 0, 0, 70, 14,
- 16, 0, 1, 0, 0, 0,
- 70, 14, 16, 0, 0, 0,
- 0, 0, 54, 0, 0, 5,
- 82, 0, 16, 0, 1, 0,
- 0, 0, 6, 16, 16, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 8, 242, 0, 16, 0,
- 2, 0, 0, 0, 86, 21,
- 16, 0, 1, 0, 0, 0,
- 134, 141, 32, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 54, 0, 0, 5, 162, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 5, 82, 0,
16, 0, 1, 0, 0, 0,
- 6, 8, 16, 0, 2, 0,
+ 6, 16, 16, 0, 1, 0,
0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 3, 0,
+ 242, 0, 16, 0, 2, 0,
0, 0, 70, 0, 16, 0,
1, 0, 0, 0, 70, 126,
16, 0, 0, 0, 0, 0,
@@ -14426,320 +14408,357 @@ const BYTE d2deffect[] =
0, 96, 16, 0, 1, 0,
0, 0, 50, 0, 0, 10,
242, 0, 16, 0, 0, 0,
- 0, 0, 6, 128, 32, 0,
- 0, 0, 0, 0, 7, 0,
+ 0, 0, 166, 138, 32, 0,
+ 0, 0, 0, 0, 6, 0,
0, 0, 70, 14, 16, 0,
- 3, 0, 0, 0, 70, 14,
+ 2, 0, 0, 0, 70, 14,
16, 0, 0, 0, 0, 0,
50, 0, 0, 10, 242, 0,
16, 0, 0, 0, 0, 0,
- 86, 133, 32, 0, 0, 0,
- 0, 0, 7, 0, 0, 0,
+ 246, 143, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
70, 14, 16, 0, 1, 0,
0, 0, 70, 14, 16, 0,
0, 0, 0, 0, 54, 0,
0, 5, 82, 0, 16, 0,
- 2, 0, 0, 0, 6, 16,
+ 1, 0, 0, 0, 6, 16,
16, 0, 1, 0, 0, 0,
- 69, 0, 0, 9, 242, 0,
+ 0, 0, 0, 8, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 1, 0,
+ 0, 0, 134, 141, 32, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 54, 0, 0, 5,
+ 162, 0, 16, 0, 1, 0,
+ 0, 0, 6, 8, 16, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 3, 0, 0, 0, 70, 0,
16, 0, 1, 0, 0, 0,
- 70, 0, 16, 0, 2, 0,
- 0, 0, 70, 126, 16, 0,
- 0, 0, 0, 0, 0, 96,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 1, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 230, 10,
16, 0, 1, 0, 0, 0,
- 69, 0, 0, 9, 242, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 0, 0, 0, 0, 6, 128,
+ 32, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 86, 133, 32, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 82, 0,
16, 0, 2, 0, 0, 0,
- 230, 10, 16, 0, 2, 0,
- 0, 0, 70, 126, 16, 0,
- 0, 0, 0, 0, 0, 96,
- 16, 0, 1, 0, 0, 0,
+ 6, 16, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 0, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 230, 10, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 166, 138, 32, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
50, 0, 0, 10, 242, 0,
16, 0, 0, 0, 0, 0,
- 166, 138, 32, 0, 0, 0,
+ 246, 143, 32, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 70, 14, 16, 0, 1, 0,
+ 70, 14, 16, 0, 2, 0,
0, 0, 70, 14, 16, 0,
- 0, 0, 0, 0, 50, 0,
- 0, 10, 242, 0, 16, 0,
- 0, 0, 0, 0, 246, 143,
- 32, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 70, 14,
- 16, 0, 2, 0, 0, 0,
- 70, 14, 16, 0, 0, 0,
- 0, 0, 0, 0, 0, 8,
- 34, 0, 16, 0, 1, 0,
- 0, 0, 26, 16, 16, 0,
- 1, 0, 0, 0, 10, 128,
- 32, 0, 0, 0, 0, 0,
- 5, 0, 0, 0, 54, 0,
- 0, 5, 18, 0, 16, 0,
- 1, 0, 0, 0, 10, 16,
- 16, 0, 1, 0, 0, 0,
- 69, 0, 0, 9, 242, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 34, 0, 16, 0,
+ 1, 0, 0, 0, 26, 16,
16, 0, 1, 0, 0, 0,
- 70, 0, 16, 0, 1, 0,
- 0, 0, 70, 126, 16, 0,
- 0, 0, 0, 0, 0, 96,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 54, 0, 0, 5, 18, 0,
16, 0, 1, 0, 0, 0,
- 50, 0, 0, 10, 242, 0,
+ 10, 16, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 0, 16, 0,
+ 1, 0, 0, 0, 70, 126,
16, 0, 0, 0, 0, 0,
- 6, 128, 32, 0, 0, 0,
- 0, 0, 8, 0, 0, 0,
- 70, 14, 16, 0, 1, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 128, 32, 0,
+ 0, 0, 0, 0, 8, 0,
0, 0, 70, 14, 16, 0,
- 0, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 1, 0, 0, 0, 230, 26,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 1, 0, 0, 0,
- 70, 126, 16, 0, 1, 0,
- 0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 56, 0,
- 0, 7, 242, 32, 16, 0,
- 0, 0, 0, 0, 70, 14,
+ 230, 26, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
16, 0, 0, 0, 0, 0,
- 246, 15, 16, 0, 1, 0,
- 0, 0, 62, 0, 0, 1,
- 83, 84, 65, 84, 116, 0,
- 0, 0, 31, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 5, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 31, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 13, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 10, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 82, 68,
- 69, 70, 12, 2, 0, 0,
- 1, 0, 0, 0, 232, 0,
- 0, 0, 5, 0, 0, 0,
- 28, 0, 0, 0, 0, 4,
- 255, 255, 0, 1, 0, 0,
- 216, 1, 0, 0, 188, 0,
- 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 201, 0, 0, 0, 3, 0,
+ 82, 68, 69, 70, 12, 2,
+ 0, 0, 1, 0, 0, 0,
+ 232, 0, 0, 0, 5, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 216, 1, 0, 0,
+ 188, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0,
- 0, 0, 216, 0, 0, 0,
- 2, 0, 0, 0, 5, 0,
- 0, 0, 4, 0, 0, 0,
- 255, 255, 255, 255, 0, 0,
+ 0, 0, 201, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0,
- 12, 0, 0, 0, 220, 0,
+ 0, 0, 0, 0, 216, 0,
0, 0, 2, 0, 0, 0,
5, 0, 0, 0, 4, 0,
0, 0, 255, 255, 255, 255,
- 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 12, 0, 0, 0,
- 225, 0, 0, 0, 0, 0,
+ 220, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 1, 0, 0, 0,
+ 1, 0, 0, 0, 12, 0,
+ 0, 0, 225, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 115, 77, 97, 115,
- 107, 83, 97, 109, 112, 108,
- 101, 114, 0, 115, 83, 104,
- 97, 100, 111, 119, 83, 97,
- 109, 112, 108, 101, 114, 0,
- 116, 101, 120, 0, 109, 97,
- 115, 107, 0, 99, 98, 49,
- 0, 171, 171, 171, 225, 0,
- 0, 0, 4, 0, 0, 0,
- 0, 1, 0, 0, 160, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 115, 77,
+ 97, 115, 107, 83, 97, 109,
+ 112, 108, 101, 114, 0, 115,
+ 83, 104, 97, 100, 111, 119,
+ 83, 97, 109, 112, 108, 101,
+ 114, 0, 116, 101, 120, 0,
+ 109, 97, 115, 107, 0, 99,
+ 98, 49, 0, 171, 171, 171,
+ 225, 0, 0, 0, 4, 0,
+ 0, 0, 0, 1, 0, 0,
+ 160, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 96, 1,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 48, 0, 0, 0,
+ 0, 0, 0, 0, 112, 1,
0, 0, 0, 0, 0, 0,
- 48, 0, 0, 0, 0, 0,
- 0, 0, 112, 1, 0, 0,
- 0, 0, 0, 0, 128, 1,
+ 128, 1, 0, 0, 48, 0,
0, 0, 48, 0, 0, 0,
- 48, 0, 0, 0, 2, 0,
- 0, 0, 144, 1, 0, 0,
- 0, 0, 0, 0, 160, 1,
- 0, 0, 96, 0, 0, 0,
- 48, 0, 0, 0, 2, 0,
- 0, 0, 172, 1, 0, 0,
- 0, 0, 0, 0, 188, 1,
- 0, 0, 144, 0, 0, 0,
- 16, 0, 0, 0, 0, 0,
- 0, 0, 200, 1, 0, 0,
- 0, 0, 0, 0, 66, 108,
- 117, 114, 79, 102, 102, 115,
- 101, 116, 115, 72, 0, 171,
- 171, 171, 1, 0, 3, 0,
- 1, 0, 4, 0, 3, 0,
+ 2, 0, 0, 0, 144, 1,
+ 0, 0, 0, 0, 0, 0,
+ 160, 1, 0, 0, 96, 0,
+ 0, 0, 48, 0, 0, 0,
+ 2, 0, 0, 0, 172, 1,
+ 0, 0, 0, 0, 0, 0,
+ 188, 1, 0, 0, 144, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 200, 1,
0, 0, 0, 0, 0, 0,
66, 108, 117, 114, 79, 102,
- 102, 115, 101, 116, 115, 86,
+ 102, 115, 101, 116, 115, 72,
0, 171, 171, 171, 1, 0,
3, 0, 1, 0, 4, 0,
3, 0, 0, 0, 0, 0,
0, 0, 66, 108, 117, 114,
- 87, 101, 105, 103, 104, 116,
- 115, 0, 1, 0, 3, 0,
- 1, 0, 4, 0, 3, 0,
- 0, 0, 0, 0, 0, 0,
- 83, 104, 97, 100, 111, 119,
- 67, 111, 108, 111, 114, 0,
+ 79, 102, 102, 115, 101, 116,
+ 115, 86, 0, 171, 171, 171,
1, 0, 3, 0, 1, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 73, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
- 0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
+ 4, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 66, 108,
+ 117, 114, 87, 101, 105, 103,
+ 104, 116, 115, 0, 1, 0,
+ 3, 0, 1, 0, 4, 0,
3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 3, 0, 0,
- 92, 0, 0, 0, 1, 0,
- 0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 12, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 79, 83, 71, 78, 44, 0,
- 0, 0, 1, 0, 0, 0,
- 8, 0, 0, 0, 32, 0,
+ 0, 0, 83, 104, 97, 100,
+ 111, 119, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 54,
+ 46, 51, 46, 57, 54, 48,
+ 48, 46, 49, 54, 51, 56,
+ 52, 0, 171, 171, 73, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 83, 86,
- 95, 84, 97, 114, 103, 101,
- 116, 0, 171, 171, 212, 232,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 3,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 12,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
+ 0, 0, 8, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 83, 97, 109, 112, 108, 101,
- 84, 101, 120, 116, 84, 101,
- 120, 116, 117, 114, 101, 0,
- 85, 110, 109, 97, 115, 107,
- 101, 100, 0, 4, 0, 0,
- 0, 1, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 83, 86, 95, 84, 97, 114,
+ 103, 101, 116, 0, 171, 171,
+ 24, 233, 0, 0, 0, 0,
+ 0, 0, 83, 97, 109, 112,
+ 108, 101, 84, 101, 120, 116,
+ 84, 101, 120, 116, 117, 114,
+ 101, 0, 85, 110, 109, 97,
+ 115, 107, 101, 100, 0, 4,
0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0,
- 0, 3, 0, 0, 0, 255,
- 255, 255, 255, 72, 4, 0,
- 0, 68, 88, 66, 67, 28,
- 248, 40, 83, 2, 166, 203,
- 194, 228, 163, 91, 123, 149,
- 165, 41, 212, 1, 0, 0,
- 0, 72, 4, 0, 0, 6,
- 0, 0, 0, 56, 0, 0,
- 0, 248, 0, 0, 0, 244,
- 1, 0, 0, 112, 2, 0,
- 0, 164, 3, 0, 0, 216,
- 3, 0, 0, 65, 111, 110,
- 57, 184, 0, 0, 0, 184,
- 0, 0, 0, 0, 2, 254,
- 255, 132, 0, 0, 0, 52,
- 0, 0, 0, 1, 0, 36,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 255, 255, 255, 255, 68,
+ 4, 0, 0, 68, 88, 66,
+ 67, 77, 85, 167, 240, 56,
+ 56, 155, 78, 125, 96, 49,
+ 253, 103, 100, 22, 62, 1,
+ 0, 0, 0, 68, 4, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 248, 0, 0,
+ 0, 244, 1, 0, 0, 112,
+ 2, 0, 0, 160, 3, 0,
+ 0, 212, 3, 0, 0, 65,
+ 111, 110, 57, 184, 0, 0,
+ 0, 184, 0, 0, 0, 0,
+ 2, 254, 255, 132, 0, 0,
+ 0, 52, 0, 0, 0, 1,
+ 0, 36, 0, 0, 0, 48,
0, 0, 0, 48, 0, 0,
- 0, 48, 0, 0, 0, 36,
- 0, 1, 0, 48, 0, 0,
- 0, 0, 0, 3, 0, 1,
+ 0, 36, 0, 1, 0, 48,
+ 0, 0, 0, 0, 0, 3,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 254, 255, 81, 0, 0,
+ 5, 4, 0, 15, 160, 0,
+ 0, 0, 0, 0, 0, 128,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 0, 0,
+ 2, 5, 0, 0, 128, 0,
+ 0, 15, 144, 4, 0, 0,
+ 4, 0, 0, 3, 224, 0,
+ 0, 228, 144, 2, 0, 238,
+ 160, 2, 0, 228, 160, 4,
+ 0, 0, 4, 0, 0, 12,
+ 224, 0, 0, 20, 144, 3,
+ 0, 180, 160, 3, 0, 20,
+ 160, 4, 0, 0, 4, 0,
+ 0, 3, 128, 0, 0, 228,
+ 144, 1, 0, 238, 160, 1,
+ 0, 228, 160, 2, 0, 0,
+ 3, 0, 0, 3, 192, 0,
+ 0, 228, 128, 0, 0, 228,
+ 160, 1, 0, 0, 2, 0,
+ 0, 12, 192, 4, 0, 68,
+ 160, 255, 255, 0, 0, 83,
+ 72, 68, 82, 244, 0, 0,
+ 0, 64, 0, 1, 0, 61,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 95, 0, 0, 3, 50,
+ 16, 16, 0, 0, 0, 0,
+ 0, 103, 0, 0, 4, 242,
+ 32, 16, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 50, 32, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 194, 32, 16,
+ 0, 1, 0, 0, 0, 50,
+ 0, 0, 11, 50, 32, 16,
+ 0, 0, 0, 0, 0, 70,
+ 16, 16, 0, 0, 0, 0,
+ 0, 230, 138, 32, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 2, 254,
- 255, 81, 0, 0, 5, 4,
- 0, 15, 160, 0, 0, 0,
- 0, 0, 0, 128, 63, 0,
+ 0, 70, 128, 32, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 31, 0, 0, 2, 5,
- 0, 0, 128, 0, 0, 15,
- 144, 4, 0, 0, 4, 0,
- 0, 3, 224, 0, 0, 228,
- 144, 2, 0, 238, 160, 2,
- 0, 228, 160, 4, 0, 0,
- 4, 0, 0, 12, 224, 0,
- 0, 20, 144, 3, 0, 180,
- 160, 3, 0, 20, 160, 4,
- 0, 0, 4, 0, 0, 3,
- 128, 0, 0, 228, 144, 1,
- 0, 238, 160, 1, 0, 228,
- 160, 2, 0, 0, 3, 0,
- 0, 3, 192, 0, 0, 228,
- 128, 0, 0, 228, 160, 1,
- 0, 0, 2, 0, 0, 12,
- 192, 4, 0, 68, 160, 255,
- 255, 0, 0, 83, 72, 68,
- 82, 244, 0, 0, 0, 64,
- 0, 1, 0, 61, 0, 0,
- 0, 89, 0, 0, 4, 70,
- 142, 32, 0, 0, 0, 0,
- 0, 3, 0, 0, 0, 95,
- 0, 0, 3, 50, 16, 16,
- 0, 0, 0, 0, 0, 103,
- 0, 0, 4, 242, 32, 16,
- 0, 0, 0, 0, 0, 1,
- 0, 0, 0, 101, 0, 0,
- 3, 50, 32, 16, 0, 1,
- 0, 0, 0, 101, 0, 0,
- 3, 194, 32, 16, 0, 1,
- 0, 0, 0, 50, 0, 0,
- 11, 50, 32, 16, 0, 0,
+ 0, 54, 0, 0, 8, 194,
+ 32, 16, 0, 0, 0, 0,
+ 0, 2, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 128, 63, 50, 0, 0,
+ 11, 50, 32, 16, 0, 1,
0, 0, 0, 70, 16, 16,
0, 0, 0, 0, 0, 230,
138, 32, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 70,
+ 0, 1, 0, 0, 0, 70,
128, 32, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 54,
- 0, 0, 8, 194, 32, 16,
- 0, 0, 0, 0, 0, 2,
- 64, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 128,
- 63, 50, 0, 0, 11, 50,
- 32, 16, 0, 1, 0, 0,
- 0, 70, 16, 16, 0, 0,
- 0, 0, 0, 230, 138, 32,
- 0, 0, 0, 0, 0, 1,
- 0, 0, 0, 70, 128, 32,
- 0, 0, 0, 0, 0, 1,
- 0, 0, 0, 50, 0, 0,
- 11, 194, 32, 16, 0, 1,
- 0, 0, 0, 6, 20, 16,
- 0, 0, 0, 0, 0, 166,
- 142, 32, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 6,
- 132, 32, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 62,
- 0, 0, 1, 83, 84, 65,
- 84, 116, 0, 0, 0, 5,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 4,
+ 0, 1, 0, 0, 0, 50,
+ 0, 0, 11, 194, 32, 16,
+ 0, 1, 0, 0, 0, 6,
+ 20, 16, 0, 0, 0, 0,
+ 0, 166, 142, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 6, 132, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 5, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -14747,262 +14766,261 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 82, 68, 69, 70, 44,
- 1, 0, 0, 1, 0, 0,
- 0, 64, 0, 0, 0, 1,
- 0, 0, 0, 28, 0, 0,
- 0, 0, 4, 254, 255, 0,
- 1, 0, 0, 246, 0, 0,
- 0, 60, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 82, 68, 69,
+ 70, 40, 1, 0, 0, 1,
+ 0, 0, 0, 64, 0, 0,
+ 0, 1, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 254,
+ 255, 0, 1, 0, 0, 246,
+ 0, 0, 0, 60, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 1, 0, 0, 0, 0,
- 0, 0, 0, 99, 98, 48,
- 0, 60, 0, 0, 0, 4,
- 0, 0, 0, 88, 0, 0,
- 0, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 184, 0, 0, 0, 0,
- 0, 0, 0, 16, 0, 0,
- 0, 2, 0, 0, 0, 196,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 99,
+ 98, 48, 0, 60, 0, 0,
+ 0, 4, 0, 0, 0, 88,
+ 0, 0, 0, 64, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 212, 0, 0, 0, 16,
- 0, 0, 0, 16, 0, 0,
- 0, 2, 0, 0, 0, 196,
+ 0, 0, 0, 184, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 212, 0, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 222, 0, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 236, 0, 0,
+ 0, 48, 0, 0, 0, 16,
0, 0, 0, 0, 0, 0,
- 0, 222, 0, 0, 0, 32,
- 0, 0, 0, 16, 0, 0,
- 0, 2, 0, 0, 0, 196,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 236, 0, 0, 0, 48,
- 0, 0, 0, 16, 0, 0,
- 0, 0, 0, 0, 0, 196,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 77,
+ 97, 115, 107, 84, 101, 120,
+ 67, 111, 111, 114, 100, 115,
+ 0, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 73, 83, 71, 78, 44,
+ 0, 0, 0, 1, 0, 0,
+ 0, 8, 0, 0, 0, 32,
0, 0, 0, 0, 0, 0,
- 0, 81, 117, 97, 100, 68,
- 101, 115, 99, 0, 171, 171,
- 171, 1, 0, 3, 0, 1,
- 0, 4, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 84,
- 101, 120, 67, 111, 111, 114,
- 100, 115, 0, 77, 97, 115,
- 107, 84, 101, 120, 67, 111,
- 111, 114, 100, 115, 0, 84,
- 101, 120, 116, 67, 111, 108,
- 111, 114, 0, 77, 105, 99,
- 114, 111, 115, 111, 102, 116,
- 32, 40, 82, 41, 32, 72,
- 76, 83, 76, 32, 83, 104,
- 97, 100, 101, 114, 32, 67,
- 111, 109, 112, 105, 108, 101,
- 114, 32, 57, 46, 51, 48,
- 46, 57, 50, 48, 48, 46,
- 49, 54, 51, 56, 52, 0,
- 171, 171, 171, 73, 83, 71,
- 78, 44, 0, 0, 0, 1,
- 0, 0, 0, 8, 0, 0,
- 0, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
- 0, 3, 0, 0, 0, 0,
- 0, 0, 0, 7, 3, 0,
- 0, 80, 79, 83, 73, 84,
- 73, 79, 78, 0, 171, 171,
- 171, 79, 83, 71, 78, 104,
+ 0, 7, 3, 0, 0, 80,
+ 79, 83, 73, 84, 73, 79,
+ 78, 0, 171, 171, 171, 79,
+ 83, 71, 78, 104, 0, 0,
+ 0, 3, 0, 0, 0, 8,
+ 0, 0, 0, 80, 0, 0,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 3, 0, 0,
- 0, 8, 0, 0, 0, 80,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 92, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
0, 1, 0, 0, 0, 3,
+ 12, 0, 0, 92, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 12,
+ 3, 0, 0, 83, 86, 95,
+ 80, 111, 115, 105, 116, 105,
+ 111, 110, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 171, 19, 244, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 152,
+ 4, 0, 0, 68, 88, 66,
+ 67, 227, 84, 48, 176, 142,
+ 231, 109, 63, 97, 30, 1,
+ 57, 105, 137, 178, 120, 1,
+ 0, 0, 0, 152, 4, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 4, 1, 0,
+ 0, 224, 1, 0, 0, 92,
+ 2, 0, 0, 220, 3, 0,
+ 0, 76, 4, 0, 0, 65,
+ 111, 110, 57, 196, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 2, 255, 255, 144, 0, 0,
+ 0, 52, 0, 0, 0, 1,
+ 0, 40, 0, 0, 0, 52,
+ 0, 0, 0, 52, 0, 1,
+ 0, 36, 0, 0, 0, 52,
0, 0, 0, 0, 0, 0,
- 0, 15, 0, 0, 0, 92,
+ 0, 3, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 255, 255, 81, 0, 0,
+ 5, 1, 0, 15, 160, 0,
+ 0, 128, 63, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 3,
- 0, 0, 0, 1, 0, 0,
- 0, 3, 12, 0, 0, 92,
+ 0, 0, 0, 31, 0, 0,
+ 2, 0, 0, 0, 128, 0,
+ 0, 15, 176, 31, 0, 0,
+ 2, 0, 0, 0, 144, 0,
+ 8, 15, 160, 1, 0, 0,
+ 2, 0, 0, 7, 128, 0,
+ 0, 228, 160, 4, 0, 0,
+ 4, 0, 0, 15, 128, 0,
+ 0, 36, 128, 1, 0, 64,
+ 160, 1, 0, 21, 160, 1,
+ 0, 0, 2, 0, 8, 15,
+ 128, 0, 0, 228, 128, 66,
+ 0, 0, 3, 0, 0, 15,
+ 128, 0, 0, 228, 176, 0,
+ 8, 228, 160, 5, 0, 0,
+ 3, 0, 0, 15, 128, 0,
+ 0, 70, 128, 0, 0, 255,
+ 160, 1, 0, 0, 2, 1,
+ 8, 15, 128, 0, 0, 228,
+ 128, 255, 255, 0, 0, 83,
+ 72, 68, 82, 212, 0, 0,
+ 0, 64, 0, 0, 0, 53,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 4, 0, 0,
+ 0, 90, 0, 0, 3, 0,
+ 96, 16, 0, 0, 0, 0,
+ 0, 88, 24, 0, 4, 0,
+ 112, 16, 0, 0, 0, 0,
+ 0, 85, 85, 0, 0, 98,
+ 16, 0, 3, 50, 16, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 242, 32, 16,
+ 0, 0, 0, 0, 0, 101,
+ 0, 0, 3, 242, 32, 16,
+ 0, 1, 0, 0, 0, 104,
+ 0, 0, 2, 1, 0, 0,
+ 0, 54, 0, 0, 6, 114,
+ 32, 16, 0, 0, 0, 0,
+ 0, 70, 130, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 54, 0, 0, 5, 130,
+ 32, 16, 0, 0, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 69, 0, 0,
+ 9, 242, 0, 16, 0, 0,
+ 0, 0, 0, 70, 16, 16,
+ 0, 1, 0, 0, 0, 70,
+ 126, 16, 0, 0, 0, 0,
+ 0, 0, 96, 16, 0, 0,
+ 0, 0, 0, 56, 0, 0,
+ 8, 242, 32, 16, 0, 1,
+ 0, 0, 0, 102, 4, 16,
+ 0, 0, 0, 0, 0, 246,
+ 143, 32, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 62,
+ 0, 0, 1, 83, 84, 65,
+ 84, 116, 0, 0, 0, 5,
0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 3,
0, 0, 0, 1, 0, 0,
- 0, 12, 3, 0, 0, 83,
- 86, 95, 80, 111, 115, 105,
- 116, 105, 111, 110, 0, 84,
- 69, 88, 67, 79, 79, 82,
- 68, 0, 171, 171, 171, 207,
- 243, 0, 0, 0, 0, 0,
- 0, 1, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
- 0, 156, 4, 0, 0, 68,
- 88, 66, 67, 157, 57, 76,
- 176, 214, 127, 147, 239, 222,
- 22, 182, 42, 105, 157, 114,
- 166, 1, 0, 0, 0, 156,
- 4, 0, 0, 6, 0, 0,
- 0, 56, 0, 0, 0, 4,
- 1, 0, 0, 224, 1, 0,
- 0, 92, 2, 0, 0, 224,
- 3, 0, 0, 80, 4, 0,
- 0, 65, 111, 110, 57, 196,
- 0, 0, 0, 196, 0, 0,
- 0, 0, 2, 255, 255, 144,
- 0, 0, 0, 52, 0, 0,
- 0, 1, 0, 40, 0, 0,
- 0, 52, 0, 0, 0, 52,
- 0, 1, 0, 36, 0, 0,
- 0, 52, 0, 0, 0, 0,
- 0, 0, 0, 3, 0, 1,
+ 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 1, 2, 255, 255, 81,
- 0, 0, 5, 1, 0, 15,
- 160, 0, 0, 128, 63, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 31,
- 0, 0, 2, 0, 0, 0,
- 128, 0, 0, 15, 176, 31,
- 0, 0, 2, 0, 0, 0,
- 144, 0, 8, 15, 160, 1,
- 0, 0, 2, 0, 0, 7,
- 128, 0, 0, 228, 160, 4,
- 0, 0, 4, 0, 0, 15,
- 128, 0, 0, 36, 128, 1,
- 0, 64, 160, 1, 0, 21,
- 160, 1, 0, 0, 2, 0,
- 8, 15, 128, 0, 0, 228,
- 128, 66, 0, 0, 3, 0,
- 0, 15, 128, 0, 0, 228,
- 176, 0, 8, 228, 160, 5,
- 0, 0, 3, 0, 0, 15,
- 128, 0, 0, 70, 128, 0,
- 0, 255, 160, 1, 0, 0,
- 2, 1, 8, 15, 128, 0,
- 0, 228, 128, 255, 255, 0,
- 0, 83, 72, 68, 82, 212,
- 0, 0, 0, 64, 0, 0,
- 0, 53, 0, 0, 0, 89,
- 0, 0, 4, 70, 142, 32,
- 0, 0, 0, 0, 0, 4,
- 0, 0, 0, 90, 0, 0,
- 3, 0, 96, 16, 0, 0,
- 0, 0, 0, 88, 24, 0,
- 4, 0, 112, 16, 0, 0,
- 0, 0, 0, 85, 85, 0,
- 0, 98, 16, 0, 3, 50,
- 16, 16, 0, 1, 0, 0,
- 0, 101, 0, 0, 3, 242,
- 32, 16, 0, 0, 0, 0,
- 0, 101, 0, 0, 3, 242,
- 32, 16, 0, 1, 0, 0,
- 0, 104, 0, 0, 2, 1,
- 0, 0, 0, 54, 0, 0,
- 6, 114, 32, 16, 0, 0,
- 0, 0, 0, 70, 130, 32,
- 0, 0, 0, 0, 0, 3,
- 0, 0, 0, 54, 0, 0,
- 5, 130, 32, 16, 0, 0,
- 0, 0, 0, 1, 64, 0,
- 0, 0, 0, 128, 63, 69,
- 0, 0, 9, 242, 0, 16,
- 0, 0, 0, 0, 0, 70,
- 16, 16, 0, 1, 0, 0,
- 0, 70, 126, 16, 0, 0,
- 0, 0, 0, 0, 96, 16,
- 0, 0, 0, 0, 0, 56,
- 0, 0, 8, 242, 32, 16,
- 0, 1, 0, 0, 0, 102,
- 4, 16, 0, 0, 0, 0,
- 0, 246, 143, 32, 0, 0,
- 0, 0, 0, 3, 0, 0,
- 0, 62, 0, 0, 1, 83,
- 84, 65, 84, 116, 0, 0,
- 0, 5, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
- 0, 3, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 1,
+ 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 82, 68, 69, 70, 120,
+ 1, 0, 0, 1, 0, 0,
+ 0, 144, 0, 0, 0, 3,
+ 0, 0, 0, 28, 0, 0,
+ 0, 0, 4, 255, 255, 0,
+ 1, 0, 0, 70, 1, 0,
+ 0, 124, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 133, 0, 0,
+ 0, 2, 0, 0, 0, 5,
+ 0, 0, 0, 4, 0, 0,
+ 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 12, 0, 0, 0, 137,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 82, 68, 69,
- 70, 124, 1, 0, 0, 1,
- 0, 0, 0, 144, 0, 0,
- 0, 3, 0, 0, 0, 28,
- 0, 0, 0, 0, 4, 255,
- 255, 0, 1, 0, 0, 70,
- 1, 0, 0, 124, 0, 0,
- 0, 3, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 0, 0,
- 0, 0, 0, 0, 0, 133,
- 0, 0, 0, 2, 0, 0,
- 0, 5, 0, 0, 0, 4,
- 0, 0, 0, 255, 255, 255,
- 255, 0, 0, 0, 0, 1,
- 0, 0, 0, 12, 0, 0,
- 0, 137, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
+ 0, 115, 83, 97, 109, 112,
+ 108, 101, 114, 0, 116, 101,
+ 120, 0, 99, 98, 48, 0,
+ 171, 171, 171, 137, 0, 0,
+ 0, 4, 0, 0, 0, 168,
+ 0, 0, 0, 64, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 8, 1, 0,
+ 0, 0, 0, 0, 0, 16,
0, 0, 0, 0, 0, 0,
- 0, 1, 0, 0, 0, 0,
- 0, 0, 0, 115, 83, 97,
- 109, 112, 108, 101, 114, 0,
- 116, 101, 120, 0, 99, 98,
- 48, 0, 171, 171, 171, 137,
- 0, 0, 0, 4, 0, 0,
- 0, 168, 0, 0, 0, 64,
+ 0, 20, 1, 0, 0, 0,
+ 0, 0, 0, 36, 1, 0,
+ 0, 16, 0, 0, 0, 16,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 8,
- 1, 0, 0, 0, 0, 0,
- 0, 16, 0, 0, 0, 0,
- 0, 0, 0, 20, 1, 0,
- 0, 0, 0, 0, 0, 36,
- 1, 0, 0, 16, 0, 0,
- 0, 16, 0, 0, 0, 0,
- 0, 0, 0, 20, 1, 0,
- 0, 0, 0, 0, 0, 46,
- 1, 0, 0, 32, 0, 0,
- 0, 16, 0, 0, 0, 0,
- 0, 0, 0, 20, 1, 0,
- 0, 0, 0, 0, 0, 60,
- 1, 0, 0, 48, 0, 0,
- 0, 16, 0, 0, 0, 2,
- 0, 0, 0, 20, 1, 0,
- 0, 0, 0, 0, 0, 81,
- 117, 97, 100, 68, 101, 115,
- 99, 0, 171, 171, 171, 1,
- 0, 3, 0, 1, 0, 4,
+ 0, 20, 1, 0, 0, 0,
+ 0, 0, 0, 46, 1, 0,
+ 0, 32, 0, 0, 0, 16,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 84, 101, 120,
+ 0, 20, 1, 0, 0, 0,
+ 0, 0, 0, 60, 1, 0,
+ 0, 48, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 20, 1, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 77,
+ 97, 115, 107, 84, 101, 120,
67, 111, 111, 114, 100, 115,
- 0, 77, 97, 115, 107, 84,
- 101, 120, 67, 111, 111, 114,
- 100, 115, 0, 84, 101, 120,
- 116, 67, 111, 108, 111, 114,
- 0, 77, 105, 99, 114, 111,
- 115, 111, 102, 116, 32, 40,
- 82, 41, 32, 72, 76, 83,
- 76, 32, 83, 104, 97, 100,
- 101, 114, 32, 67, 111, 109,
- 112, 105, 108, 101, 114, 32,
- 57, 46, 51, 48, 46, 57,
- 50, 48, 48, 46, 49, 54,
- 51, 56, 52, 0, 171, 171,
- 171, 73, 83, 71, 78, 104,
+ 0, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 73, 83, 71, 78, 104,
0, 0, 0, 3, 0, 0,
0, 8, 0, 0, 0, 80,
0, 0, 0, 0, 0, 0,
@@ -15033,7 +15051,7 @@ const BYTE d2deffect[] =
0, 1, 0, 0, 0, 15,
0, 0, 0, 83, 86, 95,
84, 97, 114, 103, 101, 116,
- 0, 171, 171, 47, 248, 0,
+ 0, 171, 171, 111, 248, 0,
0, 0, 0, 0, 0, 77,
97, 115, 107, 101, 100, 0,
4, 0, 0, 0, 1, 0,
@@ -15044,16 +15062,16 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 3, 0,
0, 0, 255, 255, 255, 255,
- 72, 4, 0, 0, 68, 88,
- 66, 67, 28, 248, 40, 83,
- 2, 166, 203, 194, 228, 163,
- 91, 123, 149, 165, 41, 212,
- 1, 0, 0, 0, 72, 4,
+ 68, 4, 0, 0, 68, 88,
+ 66, 67, 77, 85, 167, 240,
+ 56, 56, 155, 78, 125, 96,
+ 49, 253, 103, 100, 22, 62,
+ 1, 0, 0, 0, 68, 4,
0, 0, 6, 0, 0, 0,
56, 0, 0, 0, 248, 0,
0, 0, 244, 1, 0, 0,
- 112, 2, 0, 0, 164, 3,
- 0, 0, 216, 3, 0, 0,
+ 112, 2, 0, 0, 160, 3,
+ 0, 0, 212, 3, 0, 0,
65, 111, 110, 57, 184, 0,
0, 0, 184, 0, 0, 0,
0, 2, 254, 255, 132, 0,
@@ -15132,7 +15150,7 @@ const BYTE d2deffect[] =
0, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 4, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -15142,14 +15160,14 @@ const BYTE d2deffect[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 82, 68,
- 69, 70, 44, 1, 0, 0,
+ 69, 70, 40, 1, 0, 0,
1, 0, 0, 0, 64, 0,
0, 0, 1, 0, 0, 0,
28, 0, 0, 0, 0, 4,
@@ -15196,559 +15214,543 @@ const BYTE d2deffect[] =
41, 32, 72, 76, 83, 76,
32, 83, 104, 97, 100, 101,
114, 32, 67, 111, 109, 112,
- 105, 108, 101, 114, 32, 57,
- 46, 51, 48, 46, 57, 50,
- 48, 48, 46, 49, 54, 51,
- 56, 52, 0, 171, 171, 171,
- 73, 83, 71, 78, 44, 0,
- 0, 0, 1, 0, 0, 0,
- 8, 0, 0, 0, 32, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 105, 108, 101, 114, 32, 54,
+ 46, 51, 46, 57, 54, 48,
+ 48, 46, 49, 54, 51, 56,
+ 52, 0, 73, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
+ 0, 0, 8, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 7, 3, 0, 0, 80, 79,
- 83, 73, 84, 73, 79, 78,
- 0, 171, 171, 171, 79, 83,
- 71, 78, 104, 0, 0, 0,
- 3, 0, 0, 0, 8, 0,
- 0, 0, 80, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 3, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 171, 171, 171,
+ 79, 83, 71, 78, 104, 0,
0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 15, 0,
- 0, 0, 92, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 1, 0, 0, 0, 3, 12,
- 0, 0, 92, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 1, 0, 0, 0, 12, 3,
- 0, 0, 83, 86, 95, 80,
- 111, 115, 105, 116, 105, 111,
- 110, 0, 84, 69, 88, 67,
- 79, 79, 82, 68, 0, 171,
- 171, 171, 14, 253, 0, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 2, 0, 0, 0,
- 0, 0, 0, 0, 144, 5,
- 0, 0, 68, 88, 66, 67,
- 77, 72, 172, 53, 12, 183,
- 105, 209, 231, 49, 89, 164,
- 104, 240, 17, 81, 1, 0,
- 0, 0, 144, 5, 0, 0,
- 6, 0, 0, 0, 56, 0,
- 0, 0, 64, 1, 0, 0,
- 132, 2, 0, 0, 0, 3,
- 0, 0, 212, 4, 0, 0,
- 68, 5, 0, 0, 65, 111,
- 110, 57, 0, 1, 0, 0,
- 0, 1, 0, 0, 0, 2,
- 255, 255, 200, 0, 0, 0,
- 56, 0, 0, 0, 1, 0,
- 44, 0, 0, 0, 56, 0,
- 0, 0, 56, 0, 2, 0,
- 36, 0, 0, 0, 56, 0,
- 0, 0, 0, 0, 1, 1,
1, 0, 0, 0, 3, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 1, 2, 255, 255,
- 81, 0, 0, 5, 1, 0,
- 15, 160, 0, 0, 128, 63,
0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
0, 0, 0, 0, 0, 0,
- 31, 0, 0, 2, 0, 0,
- 0, 128, 0, 0, 15, 176,
- 31, 0, 0, 2, 0, 0,
- 0, 144, 0, 8, 15, 160,
- 31, 0, 0, 2, 0, 0,
- 0, 144, 1, 8, 15, 160,
- 1, 0, 0, 2, 0, 0,
- 7, 128, 0, 0, 228, 160,
- 4, 0, 0, 4, 0, 0,
- 15, 128, 0, 0, 36, 128,
- 1, 0, 64, 160, 1, 0,
- 21, 160, 1, 0, 0, 2,
- 0, 8, 15, 128, 0, 0,
- 228, 128, 1, 0, 0, 2,
- 0, 0, 3, 128, 0, 0,
- 235, 176, 66, 0, 0, 3,
- 1, 0, 15, 128, 0, 0,
- 228, 176, 0, 8, 228, 160,
- 66, 0, 0, 3, 0, 0,
- 15, 128, 0, 0, 228, 128,
- 1, 8, 228, 160, 5, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 12, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 74, 253,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 140, 5, 0, 0, 68, 88,
+ 66, 67, 233, 167, 4, 110,
+ 60, 182, 197, 16, 114, 252,
+ 67, 184, 217, 172, 169, 241,
+ 1, 0, 0, 0, 140, 5,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 64, 1,
+ 0, 0, 132, 2, 0, 0,
+ 0, 3, 0, 0, 208, 4,
+ 0, 0, 64, 5, 0, 0,
+ 65, 111, 110, 57, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 255, 255, 200, 0,
+ 0, 0, 56, 0, 0, 0,
+ 1, 0, 44, 0, 0, 0,
+ 56, 0, 0, 0, 56, 0,
+ 2, 0, 36, 0, 0, 0,
+ 56, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0,
+ 3, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 1, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 1, 0, 0, 2,
+ 0, 0, 7, 128, 0, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 0, 0,
+ 36, 128, 1, 0, 64, 160,
+ 1, 0, 21, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 1, 0,
+ 0, 2, 0, 0, 3, 128,
+ 0, 0, 235, 176, 66, 0,
0, 3, 1, 0, 15, 128,
- 1, 0, 70, 128, 0, 0,
- 255, 160, 5, 0, 0, 3,
+ 0, 0, 228, 176, 0, 8,
+ 228, 160, 66, 0, 0, 3,
0, 0, 15, 128, 0, 0,
- 255, 128, 1, 0, 228, 128,
- 1, 0, 0, 2, 1, 8,
- 15, 128, 0, 0, 228, 128,
- 255, 255, 0, 0, 83, 72,
- 68, 82, 60, 1, 0, 0,
- 64, 0, 0, 0, 79, 0,
- 0, 0, 89, 0, 0, 4,
- 70, 142, 32, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 0, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 1, 0, 0, 0,
+ 228, 128, 1, 8, 228, 160,
+ 5, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 70, 128,
+ 0, 0, 255, 160, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 255, 128, 1, 0,
+ 228, 128, 1, 0, 0, 2,
+ 1, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 60, 1,
+ 0, 0, 64, 0, 0, 0,
+ 79, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 98, 16,
+ 0, 3, 194, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 1, 0, 0, 0, 104, 0,
+ 0, 2, 2, 0, 0, 0,
+ 54, 0, 0, 6, 114, 32,
16, 0, 0, 0, 0, 0,
- 85, 85, 0, 0, 88, 24,
- 0, 4, 0, 112, 16, 0,
- 1, 0, 0, 0, 85, 85,
- 0, 0, 98, 16, 0, 3,
- 50, 16, 16, 0, 1, 0,
- 0, 0, 98, 16, 0, 3,
- 194, 16, 16, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 101, 0, 0, 3,
- 242, 32, 16, 0, 1, 0,
- 0, 0, 104, 0, 0, 2,
- 2, 0, 0, 0, 54, 0,
- 0, 6, 114, 32, 16, 0,
- 0, 0, 0, 0, 70, 130,
- 32, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 54, 0,
- 0, 5, 130, 32, 16, 0,
- 0, 0, 0, 0, 1, 64,
- 0, 0, 0, 0, 128, 63,
- 69, 0, 0, 9, 242, 0,
- 16, 0, 0, 0, 0, 0,
- 70, 16, 16, 0, 1, 0,
- 0, 0, 70, 126, 16, 0,
- 0, 0, 0, 0, 0, 96,
- 16, 0, 0, 0, 0, 0,
- 56, 0, 0, 8, 242, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
16, 0, 0, 0, 0, 0,
- 102, 4, 16, 0, 0, 0,
- 0, 0, 246, 143, 32, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 1, 0,
- 0, 0, 230, 26, 16, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 102, 4, 16, 0,
+ 0, 0, 0, 0, 246, 143,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 230, 26,
16, 0, 1, 0, 0, 0,
- 0, 96, 16, 0, 1, 0,
- 0, 0, 56, 0, 0, 7,
- 242, 32, 16, 0, 1, 0,
- 0, 0, 70, 14, 16, 0,
- 0, 0, 0, 0, 246, 15,
- 16, 0, 1, 0, 0, 0,
- 62, 0, 0, 1, 83, 84,
- 65, 84, 116, 0, 0, 0,
- 7, 0, 0, 0, 2, 0,
- 0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 2, 0,
+ 70, 126, 16, 0, 1, 0,
+ 0, 0, 0, 96, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 32, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 1, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 7, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 200, 1, 0, 0,
+ 1, 0, 0, 0, 224, 0,
+ 0, 0, 5, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 150, 1, 0, 0, 188, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 82, 68, 69, 70,
- 204, 1, 0, 0, 1, 0,
- 0, 0, 224, 0, 0, 0,
- 5, 0, 0, 0, 28, 0,
- 0, 0, 0, 4, 255, 255,
- 0, 1, 0, 0, 150, 1,
- 0, 0, 188, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 197, 0,
- 0, 0, 3, 0, 0, 0,
+ 197, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 1, 0,
- 0, 0, 0, 0, 0, 0,
- 210, 0, 0, 0, 2, 0,
- 0, 0, 5, 0, 0, 0,
- 4, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 1, 0, 0, 0, 12, 0,
- 0, 0, 214, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 210, 0, 0, 0,
2, 0, 0, 0, 5, 0,
0, 0, 4, 0, 0, 0,
- 255, 255, 255, 255, 1, 0,
+ 255, 255, 255, 255, 0, 0,
0, 0, 1, 0, 0, 0,
- 12, 0, 0, 0, 219, 0,
- 0, 0, 0, 0, 0, 0,
+ 12, 0, 0, 0, 214, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 219, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 115, 83, 97, 109, 112, 108,
- 101, 114, 0, 115, 77, 97,
- 115, 107, 83, 97, 109, 112,
- 108, 101, 114, 0, 116, 101,
- 120, 0, 109, 97, 115, 107,
- 0, 99, 98, 48, 0, 171,
- 219, 0, 0, 0, 4, 0,
- 0, 0, 248, 0, 0, 0,
- 64, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 83, 97, 109,
+ 112, 108, 101, 114, 0, 115,
+ 77, 97, 115, 107, 83, 97,
+ 109, 112, 108, 101, 114, 0,
+ 116, 101, 120, 0, 109, 97,
+ 115, 107, 0, 99, 98, 48,
+ 0, 171, 219, 0, 0, 0,
+ 4, 0, 0, 0, 248, 0,
+ 0, 0, 64, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 88, 1, 0, 0, 0, 0,
- 0, 0, 16, 0, 0, 0,
- 0, 0, 0, 0, 100, 1,
+ 0, 0, 88, 1, 0, 0,
+ 0, 0, 0, 0, 16, 0,
0, 0, 0, 0, 0, 0,
- 116, 1, 0, 0, 16, 0,
- 0, 0, 16, 0, 0, 0,
- 0, 0, 0, 0, 100, 1,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 116, 1, 0, 0,
+ 16, 0, 0, 0, 16, 0,
0, 0, 0, 0, 0, 0,
- 126, 1, 0, 0, 32, 0,
- 0, 0, 16, 0, 0, 0,
- 0, 0, 0, 0, 100, 1,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 126, 1, 0, 0,
+ 32, 0, 0, 0, 16, 0,
0, 0, 0, 0, 0, 0,
- 140, 1, 0, 0, 48, 0,
- 0, 0, 16, 0, 0, 0,
- 2, 0, 0, 0, 100, 1,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 140, 1, 0, 0,
+ 48, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 81, 117, 97, 100,
+ 68, 101, 115, 99, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 81, 117, 97, 100, 68, 101,
- 115, 99, 0, 171, 171, 171,
- 1, 0, 3, 0, 1, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 84, 101,
- 120, 67, 111, 111, 114, 100,
- 115, 0, 77, 97, 115, 107,
84, 101, 120, 67, 111, 111,
- 114, 100, 115, 0, 84, 101,
- 120, 116, 67, 111, 108, 111,
- 114, 0, 77, 105, 99, 114,
- 111, 115, 111, 102, 116, 32,
- 40, 82, 41, 32, 72, 76,
- 83, 76, 32, 83, 104, 97,
- 100, 101, 114, 32, 67, 111,
- 109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
- 171, 171, 73, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
- 0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 114, 100, 115, 0, 77, 97,
+ 115, 107, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 84, 101, 120, 116, 67, 111,
+ 108, 111, 114, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 3, 0, 0,
- 92, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 12, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 79, 83, 71, 78, 68, 0,
- 0, 0, 2, 0, 0, 0,
- 8, 0, 0, 0, 56, 0,
+ 15, 0, 0, 0, 92, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
- 0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 56, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 3, 0,
0, 0, 1, 0, 0, 0,
- 15, 0, 0, 0, 83, 86,
- 95, 84, 97, 114, 103, 101,
- 116, 0, 171, 171, 110, 1,
- 1, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 16, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 46, 0, 0, 0, 18, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
+ 12, 12, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 68, 0, 0, 0,
+ 2, 0, 0, 0, 8, 0,
+ 0, 0, 56, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 54, 0,
- 0, 0, 64, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 56, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171, 166, 1, 1, 0,
0, 0, 0, 0, 4, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 255, 255, 255, 255,
- 0, 0, 0, 0, 93, 0,
- 0, 0, 65, 0, 0, 0,
+ 0, 0, 0, 0, 46, 0,
+ 0, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 102, 0, 0, 0,
+ 0, 0, 54, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 93, 0, 0, 0,
65, 0, 0, 0, 0, 0,
- 0, 0, 16, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 112, 0, 0, 0, 65, 0,
0, 0, 0, 0, 0, 0,
- 32, 0, 0, 0, 0, 0,
+ 102, 0, 0, 0, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 126, 0,
+ 0, 0, 0, 0, 112, 0,
0, 0, 65, 0, 0, 0,
- 0, 0, 0, 0, 48, 0,
+ 0, 0, 0, 0, 32, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 136, 0, 0, 0,
- 160, 0, 0, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 255, 255, 255, 255, 0, 0,
- 0, 0, 168, 0, 0, 0,
- 140, 0, 0, 0, 0, 0,
+ 0, 0, 126, 0, 0, 0,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 48, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 136, 0, 0, 0, 160, 0,
0, 0, 0, 0, 0, 0,
- 181, 0, 0, 0, 140, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 168, 0, 0, 0, 140, 0,
0, 0, 0, 0, 0, 0,
- 48, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 194, 0,
- 0, 0, 140, 0, 0, 0,
- 0, 0, 0, 0, 96, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 181, 0,
+ 0, 0, 140, 0, 0, 0,
+ 0, 0, 0, 0, 48, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 206, 0, 0, 0,
- 65, 0, 0, 0, 0, 0,
- 0, 0, 144, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 194, 0, 0, 0,
+ 140, 0, 0, 0, 0, 0,
+ 0, 0, 96, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 218, 0, 0, 0, 112, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 3, 1, 0, 0, 231, 0,
+ 206, 0, 0, 0, 65, 0,
0, 0, 0, 0, 0, 0,
+ 144, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 218, 0,
+ 0, 0, 112, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 3, 1,
+ 0, 0, 231, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 61, 1,
- 0, 0, 33, 1, 0, 0,
- 0, 0, 0, 0, 48, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 107, 1, 0, 0,
- 79, 1, 0, 0, 0, 0,
- 0, 0, 64, 0, 0, 0,
+ 0, 0, 61, 1, 0, 0,
+ 33, 1, 0, 0, 0, 0,
+ 0, 0, 48, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 112, 1, 0, 0, 33, 1,
+ 107, 1, 0, 0, 79, 1,
0, 0, 0, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 154, 1,
- 0, 0, 126, 1, 0, 0,
- 0, 0, 0, 0, 88, 0,
+ 0, 0, 0, 0, 112, 1,
+ 0, 0, 33, 1, 0, 0,
+ 0, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 156, 1, 0, 0,
+ 0, 0, 154, 1, 0, 0,
126, 1, 0, 0, 0, 0,
- 0, 0, 92, 0, 0, 0,
+ 0, 0, 88, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 164, 1, 0, 0, 126, 1,
+ 156, 1, 0, 0, 126, 1,
0, 0, 0, 0, 0, 0,
- 96, 0, 0, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 213, 1,
- 0, 0, 185, 1, 0, 0,
- 0, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 217, 1, 0, 0, 185, 1,
+ 0, 0, 0, 0, 164, 1,
+ 0, 0, 126, 1, 0, 0,
+ 0, 0, 0, 0, 96, 0,
0, 0, 0, 0, 0, 0,
- 255, 255, 255, 255, 0, 0,
- 0, 0, 224, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 213, 1, 0, 0,
185, 1, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255,
- 0, 0, 0, 0, 14, 2,
- 0, 0, 242, 1, 0, 0,
+ 0, 0, 0, 0, 217, 1,
+ 0, 0, 185, 1, 0, 0,
0, 0, 0, 0, 255, 255,
- 255, 255, 4, 0, 0, 0,
- 45, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 23, 2, 0, 0, 55, 0,
+ 255, 255, 0, 0, 0, 0,
+ 224, 1, 0, 0, 185, 1,
0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 213, 1,
- 0, 0, 46, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 35, 2, 0, 0,
- 47, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 47, 2, 0, 0, 0, 0,
- 0, 0, 59, 2, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 14, 2, 0, 0,
242, 1, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255,
4, 0, 0, 0, 45, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 71, 2,
+ 1, 0, 0, 0, 23, 2,
0, 0, 55, 0, 0, 0,
0, 0, 0, 0, 2, 0,
- 0, 0, 217, 1, 0, 0,
+ 0, 0, 213, 1, 0, 0,
46, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 83, 2, 0, 0, 47, 0,
+ 35, 2, 0, 0, 47, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 95, 2,
+ 1, 0, 0, 0, 47, 2,
0, 0, 0, 0, 0, 0,
- 107, 2, 0, 0, 242, 1,
+ 59, 2, 0, 0, 242, 1,
0, 0, 0, 0, 0, 0,
255, 255, 255, 255, 4, 0,
0, 0, 45, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 120, 2, 0, 0,
+ 0, 0, 71, 2, 0, 0,
55, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
- 213, 1, 0, 0, 46, 0,
+ 217, 1, 0, 0, 46, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 132, 2,
+ 1, 0, 0, 0, 83, 2,
0, 0, 47, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 144, 2, 0, 0,
- 0, 0, 0, 0, 156, 2,
+ 0, 0, 95, 2, 0, 0,
+ 0, 0, 0, 0, 107, 2,
0, 0, 242, 1, 0, 0,
0, 0, 0, 0, 255, 255,
255, 255, 4, 0, 0, 0,
45, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 171, 2, 0, 0, 55, 0,
+ 120, 2, 0, 0, 55, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 213, 1,
0, 0, 46, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 183, 2, 0, 0,
+ 0, 0, 132, 2, 0, 0,
47, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 195, 2, 0, 0, 0, 0,
- 0, 0, 207, 2, 0, 0,
+ 144, 2, 0, 0, 0, 0,
+ 0, 0, 156, 2, 0, 0,
242, 1, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255,
4, 0, 0, 0, 45, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 220, 2,
+ 1, 0, 0, 0, 171, 2,
0, 0, 55, 0, 0, 0,
0, 0, 0, 0, 2, 0,
- 0, 0, 224, 1, 0, 0,
+ 0, 0, 213, 1, 0, 0,
46, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 232, 2, 0, 0, 47, 0,
+ 183, 2, 0, 0, 47, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 244, 2,
+ 1, 0, 0, 0, 195, 2,
0, 0, 0, 0, 0, 0,
- 0, 3, 0, 0, 242, 1,
+ 207, 2, 0, 0, 242, 1,
0, 0, 0, 0, 0, 0,
- 255, 255, 255, 255, 5, 0,
+ 255, 255, 255, 255, 4, 0,
0, 0, 45, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 15, 3, 0, 0,
+ 0, 0, 220, 2, 0, 0,
55, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
- 213, 1, 0, 0, 46, 0,
+ 224, 1, 0, 0, 46, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 27, 3,
+ 1, 0, 0, 0, 232, 2,
0, 0, 47, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 39, 3, 0, 0,
- 52, 0, 0, 0, 0, 0,
+ 0, 0, 244, 2, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 242, 1, 0, 0,
+ 0, 0, 0, 0, 255, 255,
+ 255, 255, 5, 0, 0, 0,
+ 45, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 15, 3, 0, 0, 55, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 213, 1,
+ 0, 0, 46, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 27, 3, 0, 0,
+ 47, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 51, 3, 0, 0, 0, 0,
- 0, 0, 131, 3, 0, 0,
- 103, 3, 0, 0, 0, 0,
+ 39, 3, 0, 0, 52, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 51, 3,
+ 0, 0, 0, 0, 0, 0,
+ 131, 3, 0, 0, 103, 3,
+ 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 2, 0,
+ 0, 0, 19, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 143, 3, 0, 0,
+ 13, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 155, 3, 0, 0, 0, 0,
+ 0, 0, 206, 3, 0, 0,
+ 178, 3, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255,
- 2, 0, 0, 0, 19, 0,
+ 2, 0, 0, 0, 37, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 143, 3,
- 0, 0, 13, 0, 0, 0,
+ 1, 0, 0, 0, 219, 3,
+ 0, 0, 44, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 155, 3, 0, 0,
- 0, 0, 0, 0, 206, 3,
+ 0, 0, 231, 3, 0, 0,
+ 0, 0, 0, 0, 243, 3,
0, 0, 178, 3, 0, 0,
0, 0, 0, 0, 255, 255,
- 255, 255, 2, 0, 0, 0,
+ 255, 255, 8, 0, 0, 0,
37, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 219, 3, 0, 0, 44, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 231, 3,
+ 0, 4, 0, 0, 38, 0,
0, 0, 0, 0, 0, 0,
- 243, 3, 0, 0, 178, 3,
- 0, 0, 0, 0, 0, 0,
- 255, 255, 255, 255, 8, 0,
- 0, 0, 37, 0, 0, 0,
+ 1, 0, 0, 0, 12, 4,
+ 0, 0, 39, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 0, 4, 0, 0,
- 38, 0, 0, 0, 0, 0,
+ 0, 0, 24, 4, 0, 0,
+ 40, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 12, 4, 0, 0, 39, 0,
+ 36, 4, 0, 0, 41, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 24, 4,
- 0, 0, 40, 0, 0, 0,
+ 1, 0, 0, 0, 48, 4,
+ 0, 0, 42, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 36, 4, 0, 0,
- 41, 0, 0, 0, 0, 0,
+ 0, 0, 60, 4, 0, 0,
+ 43, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 48, 4, 0, 0, 42, 0,
+ 72, 4, 0, 0, 44, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 60, 4,
- 0, 0, 43, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 72, 4, 0, 0,
- 44, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 84, 4, 0, 0, 0, 0,
- 0, 0, 96, 4, 0, 0,
- 178, 3, 0, 0, 0, 0,
- 0, 0, 255, 255, 255, 255,
- 9, 0, 0, 0, 36, 0,
+ 1, 0, 0, 0, 84, 4,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 107, 4,
- 0, 0, 37, 0, 0, 0,
+ 96, 4, 0, 0, 178, 3,
+ 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 9, 0,
+ 0, 0, 36, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 119, 4, 0, 0,
- 38, 0, 0, 0, 0, 0,
+ 0, 0, 107, 4, 0, 0,
+ 37, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 131, 4, 0, 0, 39, 0,
+ 119, 4, 0, 0, 38, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 143, 4,
- 0, 0, 40, 0, 0, 0,
+ 1, 0, 0, 0, 131, 4,
+ 0, 0, 39, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 155, 4, 0, 0,
- 41, 0, 0, 0, 0, 0,
+ 0, 0, 143, 4, 0, 0,
+ 40, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 167, 4, 0, 0, 42, 0,
+ 155, 4, 0, 0, 41, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 179, 4,
- 0, 0, 43, 0, 0, 0,
+ 1, 0, 0, 0, 167, 4,
+ 0, 0, 42, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 191, 4, 0, 0,
- 44, 0, 0, 0, 0, 0,
+ 0, 0, 179, 4, 0, 0,
+ 43, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 203, 4, 0, 0, 0, 0,
- 0, 0, 215, 4, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 229, 4, 0, 0,
- 4, 0, 0, 0, 0, 0,
+ 191, 4, 0, 0, 44, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
- 0, 0, 131, 3, 0, 0,
- 6, 0, 0, 0, 0, 0,
- 0, 0, 7, 0, 0, 0,
- 52, 9, 0, 0, 8, 0,
+ 1, 0, 0, 0, 203, 4,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 60, 9,
- 0, 0, 7, 0, 0, 0,
- 0, 0, 0, 0, 7, 0,
- 0, 0, 32, 12, 0, 0,
- 40, 12, 0, 0, 1, 0,
+ 215, 4, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
229, 4, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0,
@@ -15756,13 +15758,13 @@ const BYTE d2deffect[] =
0, 0, 2, 0, 0, 0,
131, 3, 0, 0, 6, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 152, 16,
+ 7, 0, 0, 0, 48, 9,
0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 160, 16, 0, 0,
+ 0, 0, 56, 9, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 244, 29, 0, 0, 252, 29,
+ 28, 12, 0, 0, 36, 12,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 229, 4,
0, 0, 4, 0, 0, 0,
@@ -15771,13 +15773,13 @@ const BYTE d2deffect[] =
2, 0, 0, 0, 131, 3,
0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 108, 34, 0, 0,
+ 0, 0, 144, 16, 0, 0,
8, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 116, 34, 0, 0, 7, 0,
+ 152, 16, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 124, 51,
- 0, 0, 132, 51, 0, 0,
+ 7, 0, 0, 0, 240, 29,
+ 0, 0, 248, 29, 0, 0,
1, 0, 0, 0, 0, 0,
0, 0, 229, 4, 0, 0,
4, 0, 0, 0, 0, 0,
@@ -15786,210 +15788,225 @@ const BYTE d2deffect[] =
0, 0, 131, 3, 0, 0,
6, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 245, 55, 0, 0, 8, 0,
+ 100, 34, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 253, 55,
+ 1, 0, 0, 0, 108, 34,
0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 229, 93, 0, 0,
- 237, 93, 0, 0, 6, 0,
+ 0, 0, 212, 51, 0, 0,
+ 220, 51, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 2, 94, 0, 0, 4, 0,
+ 229, 4, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
131, 3, 0, 0, 6, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 55, 101,
+ 7, 0, 0, 0, 73, 56,
0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 63, 101, 0, 0,
+ 0, 0, 81, 56, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 47, 111, 0, 0, 55, 111,
+ 57, 94, 0, 0, 65, 94,
+ 0, 0, 6, 0, 0, 0,
+ 0, 0, 0, 0, 86, 94,
0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 131, 3,
0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 106, 118, 0, 0,
+ 0, 0, 139, 101, 0, 0,
8, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 114, 118, 0, 0, 7, 0,
+ 147, 101, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 66, 126,
- 0, 0, 74, 126, 0, 0,
+ 7, 0, 0, 0, 131, 111,
+ 0, 0, 139, 111, 0, 0,
4, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 131, 3, 0, 0,
6, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 131, 133, 0, 0, 8, 0,
+ 190, 118, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 139, 133,
+ 1, 0, 0, 0, 198, 118,
0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 127, 143, 0, 0,
- 135, 143, 0, 0, 4, 0,
+ 0, 0, 150, 126, 0, 0,
+ 158, 126, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
131, 3, 0, 0, 6, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 190, 150,
+ 7, 0, 0, 0, 215, 133,
0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 198, 150, 0, 0,
+ 0, 0, 223, 133, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 154, 158, 0, 0, 162, 158,
+ 211, 143, 0, 0, 219, 143,
0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 131, 3,
0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 221, 165, 0, 0,
+ 0, 0, 18, 151, 0, 0,
8, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 229, 165, 0, 0, 7, 0,
+ 26, 151, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 221, 175,
- 0, 0, 229, 175, 0, 0,
+ 7, 0, 0, 0, 238, 158,
+ 0, 0, 246, 158, 0, 0,
4, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 131, 3, 0, 0,
6, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 30, 183, 0, 0, 8, 0,
+ 49, 166, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 38, 183,
+ 1, 0, 0, 0, 57, 166,
0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 254, 190, 0, 0,
- 6, 191, 0, 0, 1, 0,
- 0, 0, 0, 0, 0, 0,
- 229, 4, 0, 0, 4, 0,
+ 0, 0, 49, 176, 0, 0,
+ 57, 176, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
131, 3, 0, 0, 6, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 102, 195,
+ 7, 0, 0, 0, 114, 183,
0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 110, 195, 0, 0,
+ 0, 0, 122, 183, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 82, 199, 0, 0, 90, 199,
- 0, 0, 3, 0, 0, 0,
+ 82, 191, 0, 0, 90, 191,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 229, 4,
- 0, 0, 7, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 131, 3,
- 0, 0, 10, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 114, 199, 0, 0,
- 11, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 150, 199, 0, 0, 2, 0,
- 0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 206, 3,
0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 238, 203, 0, 0,
+ 0, 0, 182, 195, 0, 0,
8, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 246, 203, 0, 0, 7, 0,
+ 190, 195, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 238, 213,
- 0, 0, 246, 213, 0, 0,
+ 7, 0, 0, 0, 162, 199,
+ 0, 0, 170, 199, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 229, 4, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 131, 3, 0, 0,
10, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 249, 213, 0, 0, 11, 0,
+ 194, 199, 0, 0, 11, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 29, 214,
+ 1, 0, 0, 0, 230, 199,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 2, 0,
- 0, 0, 243, 3, 0, 0,
+ 0, 0, 206, 3, 0, 0,
6, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 117, 218, 0, 0, 8, 0,
+ 58, 204, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 125, 218,
+ 1, 0, 0, 0, 66, 204,
0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 57, 228, 0, 0,
- 65, 228, 0, 0, 7, 0,
+ 0, 0, 58, 214, 0, 0,
+ 66, 214, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
131, 3, 0, 0, 10, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 68, 228,
+ 1, 0, 0, 0, 69, 214,
0, 0, 11, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 104, 228, 0, 0,
+ 0, 0, 105, 214, 0, 0,
2, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
243, 3, 0, 0, 6, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 192, 232,
+ 7, 0, 0, 0, 189, 218,
0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 200, 232, 0, 0,
+ 0, 0, 197, 218, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 124, 243, 0, 0, 132, 243,
- 0, 0, 2, 0, 0, 0,
- 0, 0, 0, 0, 150, 243,
+ 129, 228, 0, 0, 137, 228,
0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 131, 3,
0, 0, 10, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 159, 243, 0, 0,
+ 0, 0, 140, 228, 0, 0,
11, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 195, 243, 0, 0, 2, 0,
+ 176, 228, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 96, 4,
+ 2, 0, 0, 0, 243, 3,
0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 27, 248, 0, 0,
+ 0, 0, 4, 233, 0, 0,
8, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 35, 248, 0, 0, 7, 0,
+ 12, 233, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 207, 252,
- 0, 0, 215, 252, 0, 0,
+ 7, 0, 0, 0, 192, 243,
+ 0, 0, 200, 243, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 218, 243, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 131, 3, 0, 0,
10, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 222, 252, 0, 0, 11, 0,
+ 227, 243, 0, 0, 11, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 2, 253,
+ 1, 0, 0, 0, 7, 244,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 96, 4, 0, 0,
6, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 90, 1, 1, 0, 8, 0,
+ 91, 248, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 98, 1,
- 1, 0, 7, 0, 0, 0,
+ 1, 0, 0, 0, 99, 248,
+ 0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 2, 7, 1, 0
+ 0, 0, 11, 253, 0, 0,
+ 19, 253, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 131, 3, 0, 0, 10, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 253,
+ 0, 0, 11, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 62, 253, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 96, 4, 0, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 146, 1,
+ 1, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 154, 1, 1, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 54, 7, 1, 0
};
diff --git a/gfx/2d/ShadersD2D1.h b/gfx/2d/ShadersD2D1.h
index 0a447cbe0..2d004a000 100644
--- a/gfx/2d/ShadersD2D1.h
+++ b/gfx/2d/ShadersD2D1.h
@@ -1,9 +1,8 @@
#if 0
//
-// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
-///
// Buffer Definitions:
//
// cbuffer constants
@@ -14,6 +13,8 @@
// float A; // Offset: 24 Size: 4
// float radius1; // Offset: 28 Size: 4
// float sq_radius1; // Offset: 32 Size: 4
+// float repeat_correct; // Offset: 36 Size: 4
+// float allow_odd; // Offset: 40 Size: 4
// float3x2 transform; // Offset: 48 Size: 28
//
// }
@@ -89,11 +90,24 @@
mul r0.xzw, r0, r1.x
mov r1.w, c1.w
mad r1.xyz, r0.xzww, c0.z, r1.w
- cmp r2.x, r1.x, r0.x, r0.w
+ cmp r1.w, r1.x, r0.x, r0.w
cmp r0.xzw, r1.xyyz, c6.xyxy, c6.zyzw
- mov r2.y, c5.x
+ frc r1.x, r1.w
+ add r1.x, -r1.x, r1.w
+ mul r1.y, r1.x, c5.x
+ abs r1.y, r1.y
+ frc r1.y, r1.y
+ cmp r1.y, r1.x, r1.y, -r1.y
+ add r1.x, -r1.x, r1.w
+ add r1.y, r1.y, r1.y
+ abs r1.y, r1.y
+ mul r1.y, r1.y, c2.z
+ frc r1.z, -r1.w
+ lrp r2.w, r1.y, r1.z, r1.x
+ lrp r3.x, c2.y, r2.w, r1.w
+ mov r3.y, c5.x
texld r1, t1, s0
- texld r2, r2, s1
+ texld r2, r3, s1
mul r2.xyz, r2.w, r2
mul r1, r1, r2
add r0.w, r0.w, r0.x
@@ -102,7 +116,7 @@
mul r0, r0.y, r1
mov oC0, r0
-// approximately 33 instruction slots used (2 texture, 31 arithmetic)
+// approximately 46 instruction slots used (2 texture, 44 arithmetic)
ps_4_0
dcl_constantbuffer cb0[5], immediateIndexed
dcl_sampler s0, mode_default
@@ -134,36 +148,52 @@ add r0.w, -r0.z, r0.x
mul r1.xy, r0.xzxx, cb0[0].zzzz
ge r1.xy, r1.xyxx, -cb0[1].wwww
and r1.xy, r1.xyxx, l(0x3f800000, 0x3f800000, 0, 0)
-mad r2.x, r1.x, r0.w, r0.z
-max r0.x, r1.y, r1.x
-ge r0.x, l(0.000000), r0.x
-movc r0.x, r0.x, l(-0.000000), l(1.000000)
-mov r2.y, l(0.500000)
-sample r1.xyzw, r2.xyxx, t1.xyzw, s1
+mad r0.x, r1.x, r0.w, r0.z
+max r0.z, r1.y, r1.x
+ge r0.z, l(0.000000), r0.z
+movc r0.z, r0.z, l(-0.000000), l(1.000000)
+round_pi r0.w, r0.x
+add r0.w, -r0.x, r0.w
+round_ni r1.x, r0.x
+mul r1.y, r1.x, l(0.500000)
+add r1.x, r0.x, -r1.x
+ge r1.z, r1.y, -r1.y
+frc r1.y, |r1.y|
+movc r1.y, r1.z, r1.y, -r1.y
+add r1.y, r1.y, r1.y
+mul r1.z, |r1.y|, cb0[2].z
+mad r1.y, -|r1.y|, cb0[2].z, l(1.000000)
+mul r0.w, r0.w, r1.z
+mad r0.w, r1.x, r1.y, r0.w
+mul r0.w, r0.w, cb0[2].y
+add r1.x, l(1.000000), -cb0[2].y
+mad r1.x, r0.x, r1.x, r0.w
+mov r1.y, l(0.500000)
+sample r1.xyzw, r1.xyxx, t1.xyzw, s1
mul r1.xyz, r1.wwww, r1.xyzx
sample r2.xyzw, v2.xyxx, t0.xyzw, s0
mul r1.xyzw, r1.xyzw, r2.xyzw
-mul r1.xyzw, r0.xxxx, r1.xyzw
+mul r1.xyzw, r0.zzzz, r1.xyzw
mul o0.xyzw, r0.yyyy, r1.xyzw
ret
-// Approximately 33 instruction slots used
+// Approximately 49 instruction slots used
#endif
const BYTE SampleRadialGradientPS[] =
{
- 68, 88, 66, 67, 215, 144,
- 212, 127, 245, 34, 39, 73,
- 96, 47, 76, 239, 115, 172,
- 47, 64, 1, 0, 0, 0,
- 172, 10, 0, 0, 6, 0,
+ 68, 88, 66, 67, 20, 173,
+ 189, 124, 239, 6, 22, 67,
+ 226, 55, 243, 56, 30, 182,
+ 172, 36, 1, 0, 0, 0,
+ 180, 13, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
- 192, 2, 0, 0, 40, 7,
- 0, 0, 164, 7, 0, 0,
- 252, 9, 0, 0, 120, 10,
+ 136, 3, 0, 0, 232, 9,
+ 0, 0, 100, 10, 0, 0,
+ 4, 13, 0, 0, 128, 13,
0, 0, 65, 111, 110, 57,
- 128, 2, 0, 0, 128, 2,
+ 72, 3, 0, 0, 72, 3,
0, 0, 0, 2, 255, 255,
- 72, 2, 0, 0, 56, 0,
+ 16, 3, 0, 0, 56, 0,
0, 0, 1, 0, 44, 0,
0, 0, 56, 0, 0, 0,
56, 0, 2, 0, 36, 0,
@@ -235,385 +265,513 @@ const BYTE SampleRadialGradientPS[] =
0, 4, 1, 0, 7, 128,
0, 0, 248, 128, 0, 0,
170, 160, 1, 0, 255, 128,
- 88, 0, 0, 4, 2, 0,
- 1, 128, 1, 0, 0, 128,
+ 88, 0, 0, 4, 1, 0,
+ 8, 128, 1, 0, 0, 128,
0, 0, 0, 128, 0, 0,
255, 128, 88, 0, 0, 4,
0, 0, 13, 128, 1, 0,
148, 128, 6, 0, 68, 160,
- 6, 0, 230, 160, 1, 0,
- 0, 2, 2, 0, 2, 128,
- 5, 0, 0, 160, 66, 0,
- 0, 3, 1, 0, 15, 128,
- 1, 0, 228, 176, 0, 8,
- 228, 160, 66, 0, 0, 3,
- 2, 0, 15, 128, 2, 0,
- 228, 128, 1, 8, 228, 160,
- 5, 0, 0, 3, 2, 0,
- 7, 128, 2, 0, 255, 128,
- 2, 0, 228, 128, 5, 0,
- 0, 3, 1, 0, 15, 128,
- 1, 0, 228, 128, 2, 0,
- 228, 128, 2, 0, 0, 3,
- 0, 0, 8, 128, 0, 0,
+ 6, 0, 230, 160, 19, 0,
+ 0, 2, 1, 0, 1, 128,
+ 1, 0, 255, 128, 2, 0,
+ 0, 3, 1, 0, 1, 128,
+ 1, 0, 0, 129, 1, 0,
+ 255, 128, 5, 0, 0, 3,
+ 1, 0, 2, 128, 1, 0,
+ 0, 128, 5, 0, 0, 160,
+ 35, 0, 0, 2, 1, 0,
+ 2, 128, 1, 0, 85, 128,
+ 19, 0, 0, 2, 1, 0,
+ 2, 128, 1, 0, 85, 128,
+ 88, 0, 0, 4, 1, 0,
+ 2, 128, 1, 0, 0, 128,
+ 1, 0, 85, 128, 1, 0,
+ 85, 129, 2, 0, 0, 3,
+ 1, 0, 1, 128, 1, 0,
+ 0, 129, 1, 0, 255, 128,
+ 2, 0, 0, 3, 1, 0,
+ 2, 128, 1, 0, 85, 128,
+ 1, 0, 85, 128, 35, 0,
+ 0, 2, 1, 0, 2, 128,
+ 1, 0, 85, 128, 5, 0,
+ 0, 3, 1, 0, 2, 128,
+ 1, 0, 85, 128, 2, 0,
+ 170, 160, 19, 0, 0, 2,
+ 1, 0, 4, 128, 1, 0,
+ 255, 129, 18, 0, 0, 4,
+ 2, 0, 8, 128, 1, 0,
+ 85, 128, 1, 0, 170, 128,
+ 1, 0, 0, 128, 18, 0,
+ 0, 4, 3, 0, 1, 128,
+ 2, 0, 85, 160, 2, 0,
+ 255, 128, 1, 0, 255, 128,
+ 1, 0, 0, 2, 3, 0,
+ 2, 128, 5, 0, 0, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 3, 0, 228, 128, 1, 8,
+ 228, 160, 5, 0, 0, 3,
+ 2, 0, 7, 128, 2, 0,
+ 255, 128, 2, 0, 228, 128,
+ 5, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 2, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 8, 128,
+ 0, 0, 255, 128, 0, 0,
+ 0, 128, 88, 0, 0, 4,
+ 0, 0, 1, 128, 0, 0,
255, 128, 0, 0, 0, 128,
- 88, 0, 0, 4, 0, 0,
- 1, 128, 0, 0, 255, 128,
- 0, 0, 0, 128, 0, 0,
- 170, 128, 5, 0, 0, 3,
- 1, 0, 15, 128, 0, 0,
- 0, 128, 1, 0, 228, 128,
- 5, 0, 0, 3, 0, 0,
- 15, 128, 0, 0, 85, 128,
- 1, 0, 228, 128, 1, 0,
- 0, 2, 0, 8, 15, 128,
- 0, 0, 228, 128, 255, 255,
- 0, 0, 83, 72, 68, 82,
- 96, 4, 0, 0, 64, 0,
- 0, 0, 24, 1, 0, 0,
- 89, 0, 0, 4, 70, 142,
- 32, 0, 0, 0, 0, 0,
- 5, 0, 0, 0, 90, 0,
- 0, 3, 0, 96, 16, 0,
- 0, 0, 0, 0, 90, 0,
- 0, 3, 0, 96, 16, 0,
- 1, 0, 0, 0, 88, 24,
+ 0, 0, 170, 128, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 0, 128, 1, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 85, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 88, 6, 0, 0,
+ 64, 0, 0, 0, 150, 1,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 1, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
0, 4, 0, 112, 16, 0,
- 0, 0, 0, 0, 85, 85,
- 0, 0, 88, 24, 0, 4,
- 0, 112, 16, 0, 1, 0,
- 0, 0, 85, 85, 0, 0,
- 98, 16, 0, 3, 50, 16,
+ 1, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 3, 0, 0, 0, 15, 0,
+ 0, 8, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
16, 0, 1, 0, 0, 0,
- 98, 16, 0, 3, 50, 16,
- 16, 0, 2, 0, 0, 0,
- 101, 0, 0, 3, 242, 32,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 8, 18, 0,
16, 0, 0, 0, 0, 0,
- 104, 0, 0, 2, 3, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 128, 32, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 15, 0, 0, 8,
- 18, 0, 16, 0, 0, 0,
+ 66, 0, 16, 0, 0, 0,
0, 0, 70, 16, 16, 0,
1, 0, 0, 0, 70, 128,
32, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 8, 18, 0, 16, 0,
- 0, 0, 0, 0, 10, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 8, 34, 0, 16, 0,
+ 0, 0, 0, 0, 42, 0,
16, 0, 0, 0, 0, 0,
42, 128, 32, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 15, 0, 0, 8, 66, 0,
- 16, 0, 0, 0, 0, 0,
- 70, 16, 16, 0, 1, 0,
- 0, 0, 70, 128, 32, 0,
- 0, 0, 0, 0, 4, 0,
- 0, 0, 0, 0, 0, 8,
- 34, 0, 16, 0, 0, 0,
- 0, 0, 42, 0, 16, 0,
- 0, 0, 0, 0, 42, 128,
- 32, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 9, 50, 0, 16, 0,
- 0, 0, 0, 0, 70, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 9, 50, 0,
16, 0, 0, 0, 0, 0,
- 70, 128, 32, 128, 65, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 15, 0,
- 0, 7, 130, 0, 16, 0,
- 0, 0, 0, 0, 70, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 15, 0, 0, 7, 130, 0,
16, 0, 0, 0, 0, 0,
70, 0, 16, 0, 0, 0,
- 0, 0, 0, 0, 0, 9,
- 130, 0, 16, 0, 0, 0,
- 0, 0, 58, 0, 16, 0,
- 0, 0, 0, 0, 10, 128,
- 32, 128, 65, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
- 0, 0, 56, 0, 0, 8,
- 130, 0, 16, 0, 0, 0,
- 0, 0, 58, 0, 16, 0,
- 0, 0, 0, 0, 42, 128,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 54, 0,
- 0, 6, 66, 0, 16, 0,
- 0, 0, 0, 0, 58, 128,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 16, 0,
- 0, 8, 18, 0, 16, 0,
- 0, 0, 0, 0, 70, 2,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 9, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
16, 0, 0, 0, 0, 0,
- 70, 130, 32, 0, 0, 0,
+ 10, 128, 32, 128, 65, 0,
0, 0, 0, 0, 0, 0,
- 50, 0, 0, 10, 34, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 8, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
16, 0, 0, 0, 0, 0,
- 10, 0, 16, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 54, 0, 0, 6, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 16, 0, 0, 8, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 50, 0, 0, 10,
+ 34, 0, 16, 0, 0, 0,
0, 0, 10, 0, 16, 0,
- 0, 0, 0, 0, 58, 0,
- 16, 128, 65, 0, 0, 0,
- 0, 0, 0, 0, 75, 0,
- 0, 6, 18, 0, 16, 0,
- 1, 0, 0, 0, 26, 0,
- 16, 128, 129, 0, 0, 0,
- 0, 0, 0, 0, 29, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 75, 0, 0, 6, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 128, 129, 0,
+ 0, 0, 0, 0, 0, 0,
+ 29, 0, 0, 7, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 7, 34, 0, 16, 0,
0, 0, 0, 0, 26, 0,
16, 0, 0, 0, 0, 0,
1, 64, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 7,
- 34, 0, 16, 0, 0, 0,
- 0, 0, 26, 0, 16, 0,
- 0, 0, 0, 0, 1, 64,
- 0, 0, 0, 0, 128, 63,
- 54, 0, 0, 6, 34, 0,
+ 128, 63, 54, 0, 0, 6,
+ 34, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 7,
+ 82, 0, 16, 0, 0, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 6, 1,
16, 0, 1, 0, 0, 0,
- 10, 0, 16, 128, 65, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 7, 82, 0,
+ 14, 0, 0, 8, 82, 0,
16, 0, 0, 0, 0, 0,
- 6, 0, 16, 0, 0, 0,
- 0, 0, 6, 1, 16, 0,
- 1, 0, 0, 0, 14, 0,
- 0, 8, 82, 0, 16, 0,
- 0, 0, 0, 0, 6, 2,
+ 6, 2, 16, 0, 0, 0,
+ 0, 0, 166, 138, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 50, 0, 16, 0,
+ 1, 0, 0, 0, 134, 0,
16, 0, 0, 0, 0, 0,
166, 138, 32, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 8, 130, 0,
- 16, 0, 0, 0, 0, 0,
- 42, 0, 16, 128, 65, 0,
0, 0, 0, 0, 0, 0,
- 10, 0, 16, 0, 0, 0,
- 0, 0, 56, 0, 0, 8,
- 50, 0, 16, 0, 1, 0,
- 0, 0, 134, 0, 16, 0,
- 0, 0, 0, 0, 166, 138,
- 32, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 29, 0,
- 0, 9, 50, 0, 16, 0,
- 1, 0, 0, 0, 70, 0,
+ 29, 0, 0, 9, 50, 0,
16, 0, 1, 0, 0, 0,
- 246, 143, 32, 128, 65, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 1, 0,
- 0, 10, 50, 0, 16, 0,
- 1, 0, 0, 0, 70, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 246, 143, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 10, 50, 0,
16, 0, 1, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 128, 63, 0, 0, 128, 63,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 50, 0, 0, 9,
- 18, 0, 16, 0, 2, 0,
- 0, 0, 10, 0, 16, 0,
- 1, 0, 0, 0, 58, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 52, 0,
+ 0, 7, 66, 0, 16, 0,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 29, 0, 0, 7,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 66, 0,
16, 0, 0, 0, 0, 0,
42, 0, 16, 0, 0, 0,
- 0, 0, 52, 0, 0, 7,
- 18, 0, 16, 0, 0, 0,
- 0, 0, 26, 0, 16, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 128, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 66, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 65, 0,
+ 0, 5, 18, 0, 16, 0,
1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 34, 0,
16, 0, 1, 0, 0, 0,
- 29, 0, 0, 7, 18, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 8, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 0,
16, 0, 0, 0, 0, 0,
- 1, 64, 0, 0, 0, 0,
- 0, 0, 10, 0, 16, 0,
- 0, 0, 0, 0, 55, 0,
- 0, 9, 18, 0, 16, 0,
+ 10, 0, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 29, 0, 0, 8, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 26, 0, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 0, 6,
+ 34, 0, 16, 0, 1, 0,
+ 0, 0, 26, 0, 16, 128,
+ 129, 0, 0, 0, 1, 0,
+ 0, 0, 55, 0, 0, 10,
+ 34, 0, 16, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 9, 66, 0, 16, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 128, 129, 0, 0, 0,
+ 1, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 34, 0, 16, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 128, 193, 0, 0, 0,
+ 1, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 56, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 9, 130, 0, 16, 0,
0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
16, 0, 0, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 9, 18, 0,
+ 16, 0, 1, 0, 0, 0,
1, 64, 0, 0, 0, 0,
- 0, 128, 1, 64, 0, 0,
- 0, 0, 128, 63, 54, 0,
- 0, 5, 34, 0, 16, 0,
- 2, 0, 0, 0, 1, 64,
- 0, 0, 0, 0, 0, 63,
- 69, 0, 0, 9, 242, 0,
+ 128, 63, 26, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 18, 0,
16, 0, 1, 0, 0, 0,
- 70, 0, 16, 0, 2, 0,
- 0, 0, 70, 126, 16, 0,
- 1, 0, 0, 0, 0, 96,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 34, 0,
16, 0, 1, 0, 0, 0,
- 56, 0, 0, 7, 114, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 0, 16, 0,
+ 1, 0, 0, 0, 70, 126,
16, 0, 1, 0, 0, 0,
- 246, 15, 16, 0, 1, 0,
- 0, 0, 70, 2, 16, 0,
- 1, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 2, 0, 0, 0, 70, 16,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 2, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 56, 0,
- 0, 7, 242, 0, 16, 0,
- 1, 0, 0, 0, 70, 14,
+ 70, 16, 16, 0, 2, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 242, 0,
16, 0, 1, 0, 0, 0,
- 70, 14, 16, 0, 2, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 7, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 10,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
0, 0, 56, 0, 0, 7,
- 242, 0, 16, 0, 1, 0,
- 0, 0, 6, 0, 16, 0,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 86, 5, 16, 0,
0, 0, 0, 0, 70, 14,
16, 0, 1, 0, 0, 0,
- 56, 0, 0, 7, 242, 32,
- 16, 0, 0, 0, 0, 0,
- 86, 5, 16, 0, 0, 0,
- 0, 0, 70, 14, 16, 0,
- 1, 0, 0, 0, 62, 0,
- 0, 1, 83, 84, 65, 84,
- 116, 0, 0, 0, 33, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 23, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
- 0, 0, 1, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 49, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 40, 0,
0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 2, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 82, 68, 69, 70, 80, 2,
- 0, 0, 1, 0, 0, 0,
- 0, 1, 0, 0, 5, 0,
- 0, 0, 28, 0, 0, 0,
- 0, 4, 255, 255, 0, 1,
- 0, 0, 28, 2, 0, 0,
- 188, 0, 0, 0, 3, 0,
+ 0, 0, 82, 68, 69, 70,
+ 152, 2, 0, 0, 1, 0,
+ 0, 0, 0, 1, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 100, 2,
+ 0, 0, 188, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 201, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 1, 0,
- 0, 0, 201, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0,
- 1, 0, 0, 0, 217, 0,
- 0, 0, 2, 0, 0, 0,
- 5, 0, 0, 0, 4, 0,
- 0, 0, 255, 255, 255, 255,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 13, 0, 0, 0,
- 230, 0, 0, 0, 2, 0,
+ 217, 0, 0, 0, 2, 0,
0, 0, 5, 0, 0, 0,
4, 0, 0, 0, 255, 255,
- 255, 255, 1, 0, 0, 0,
+ 255, 255, 0, 0, 0, 0,
1, 0, 0, 0, 13, 0,
- 0, 0, 246, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 1, 0, 0, 0, 73, 110,
- 112, 117, 116, 83, 97, 109,
- 112, 108, 101, 114, 0, 71,
- 114, 97, 100, 105, 101, 110,
- 116, 83, 97, 109, 112, 108,
- 101, 114, 0, 73, 110, 112,
- 117, 116, 84, 101, 120, 116,
- 117, 114, 101, 0, 71, 114,
- 97, 100, 105, 101, 110, 116,
- 84, 101, 120, 116, 117, 114,
- 101, 0, 99, 111, 110, 115,
- 116, 97, 110, 116, 115, 0,
- 246, 0, 0, 0, 6, 0,
- 0, 0, 24, 1, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 168, 1, 0, 0, 0, 0,
- 0, 0, 12, 0, 0, 0,
- 2, 0, 0, 0, 176, 1,
- 0, 0, 0, 0, 0, 0,
- 192, 1, 0, 0, 16, 0,
- 0, 0, 8, 0, 0, 0,
- 2, 0, 0, 0, 200, 1,
- 0, 0, 0, 0, 0, 0,
- 216, 1, 0, 0, 24, 0,
- 0, 0, 4, 0, 0, 0,
- 2, 0, 0, 0, 220, 1,
- 0, 0, 0, 0, 0, 0,
- 236, 1, 0, 0, 28, 0,
- 0, 0, 4, 0, 0, 0,
- 2, 0, 0, 0, 220, 1,
- 0, 0, 0, 0, 0, 0,
- 244, 1, 0, 0, 32, 0,
+ 0, 0, 230, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
0, 0, 4, 0, 0, 0,
- 2, 0, 0, 0, 220, 1,
- 0, 0, 0, 0, 0, 0,
- 255, 1, 0, 0, 48, 0,
- 0, 0, 28, 0, 0, 0,
- 2, 0, 0, 0, 12, 2,
+ 255, 255, 255, 255, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 246, 0,
0, 0, 0, 0, 0, 0,
- 100, 105, 102, 102, 0, 171,
- 171, 171, 1, 0, 3, 0,
- 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 99, 101, 110, 116, 101, 114,
- 49, 0, 1, 0, 3, 0,
- 1, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 65, 0, 171, 171, 0, 0,
- 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 73, 110, 112, 117, 116, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 71, 114, 97, 100, 105,
+ 101, 110, 116, 83, 97, 109,
+ 112, 108, 101, 114, 0, 73,
+ 110, 112, 117, 116, 84, 101,
+ 120, 116, 117, 114, 101, 0,
+ 71, 114, 97, 100, 105, 101,
+ 110, 116, 84, 101, 120, 116,
+ 117, 114, 101, 0, 99, 111,
+ 110, 115, 116, 97, 110, 116,
+ 115, 0, 246, 0, 0, 0,
+ 8, 0, 0, 0, 24, 1,
+ 0, 0, 80, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 114, 97, 100, 105,
- 117, 115, 49, 0, 115, 113,
- 95, 114, 97, 100, 105, 117,
- 115, 49, 0, 116, 114, 97,
- 110, 115, 102, 111, 114, 109,
- 0, 171, 171, 171, 3, 0,
- 3, 0, 3, 0, 2, 0,
+ 0, 0, 216, 1, 0, 0,
+ 0, 0, 0, 0, 12, 0,
+ 0, 0, 2, 0, 0, 0,
+ 224, 1, 0, 0, 0, 0,
+ 0, 0, 240, 1, 0, 0,
+ 16, 0, 0, 0, 8, 0,
+ 0, 0, 2, 0, 0, 0,
+ 248, 1, 0, 0, 0, 0,
+ 0, 0, 8, 2, 0, 0,
+ 24, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 28, 2, 0, 0,
+ 28, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 36, 2, 0, 0,
+ 32, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 47, 2, 0, 0,
+ 36, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 62, 2, 0, 0,
+ 40, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 72, 2, 0, 0,
+ 48, 0, 0, 0, 28, 0,
+ 0, 0, 2, 0, 0, 0,
+ 84, 2, 0, 0, 0, 0,
+ 0, 0, 100, 105, 102, 102,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 77, 105, 99, 114,
- 111, 115, 111, 102, 116, 32,
- 40, 82, 41, 32, 72, 76,
- 83, 76, 32, 83, 104, 97,
- 100, 101, 114, 32, 67, 111,
- 109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
- 73, 83, 71, 78, 116, 0,
- 0, 0, 3, 0, 0, 0,
- 8, 0, 0, 0, 80, 0,
+ 0, 0, 99, 101, 110, 116,
+ 101, 114, 49, 0, 1, 0,
+ 3, 0, 1, 0, 2, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 3, 0,
+ 0, 0, 65, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 114, 97,
+ 100, 105, 117, 115, 49, 0,
+ 115, 113, 95, 114, 97, 100,
+ 105, 117, 115, 49, 0, 114,
+ 101, 112, 101, 97, 116, 95,
+ 99, 111, 114, 114, 101, 99,
+ 116, 0, 97, 108, 108, 111,
+ 119, 95, 111, 100, 100, 0,
+ 116, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 171, 171,
+ 3, 0, 3, 0, 3, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 171, 171, 73, 83, 71, 78,
+ 116, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 92, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 15, 3, 0, 0,
+ 107, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 3, 0, 0,
+ 83, 86, 95, 80, 79, 83,
+ 73, 84, 73, 79, 78, 0,
+ 83, 67, 69, 78, 69, 95,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 79, 83, 71, 78, 44, 0,
0, 0, 1, 0, 0, 0,
- 15, 3, 0, 0, 107, 0,
+ 8, 0, 0, 0, 32, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
- 0, 0, 2, 0, 0, 0,
- 15, 3, 0, 0, 83, 86,
- 95, 80, 79, 83, 73, 84,
- 73, 79, 78, 0, 83, 67,
- 69, 78, 69, 95, 80, 79,
- 83, 73, 84, 73, 79, 78,
- 0, 84, 69, 88, 67, 79,
- 79, 82, 68, 0, 79, 83,
- 71, 78, 44, 0, 0, 0,
- 1, 0, 0, 0, 8, 0,
- 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 15, 0,
- 0, 0, 83, 86, 95, 84,
- 97, 114, 103, 101, 116, 0,
- 171, 171
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
};
#if 0
//
-// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
-///
// Buffer Definitions:
//
// cbuffer constants
@@ -624,6 +782,8 @@ const BYTE SampleRadialGradientPS[] =
// float A; // Offset: 24 Size: 4 [unused]
// float radius1; // Offset: 28 Size: 4
// float sq_radius1; // Offset: 32 Size: 4 [unused]
+// float repeat_correct; // Offset: 36 Size: 4
+// float allow_odd; // Offset: 40 Size: 4
// float3x2 transform; // Offset: 48 Size: 28
//
// }
@@ -661,8 +821,7 @@ const BYTE SampleRadialGradientPS[] =
//
// Target Reg Buffer Start Reg # of Regs Data Conversion
// ---------- ------- --------- --------- ----------------------
-// c0 cb0 0 2 ( FLT, FLT, FLT, FLT)
-// c2 cb0 3 2 ( FLT, FLT, FLT, FLT)
+// c0 cb0 0 5 ( FLT, FLT, FLT, FLT)
//
//
// Sampler/Resource to DX9 shader sampler mappings:
@@ -676,33 +835,46 @@ const BYTE SampleRadialGradientPS[] =
// Level9 shader bytecode:
//
ps_2_x
- def c4, 0.5, -0, 1, 0
+ def c5, 0.5, -0, 1, 0
dcl t0
dcl t1
dcl_2d s0
dcl_2d s1
- dp2add r0.x, t0, c2, c2.z
- dp2add r0.y, t0, c3, c3.z
+ dp2add r0.x, t0, c3, c3.z
+ dp2add r0.y, t0, c4, c4.z
add r0.xy, r0, -c1
mul r0.w, c1.w, c1.w
dp2add r0.w, r0, r0, -r0.w
- mul r0.w, r0.w, c4.x
+ mul r0.w, r0.w, c5.x
mov r0.z, c1.w
dp3 r0.x, r0, c0
rcp r0.x, r0.x
- mul r0.x, r0.x, r0.w
- mov r0.y, c4.x
- texld r1, t1, s0
- texld r2, r0, s1
+ mul r0.y, r0.x, r0.w
+ frc r0.z, r0.y
+ add r0.z, -r0.z, r0.y
+ mul r1.w, r0.z, c5.x
+ abs r1.x, r1.w
+ frc r1.x, r1.x
+ cmp r1.x, r0.z, r1.x, -r1.x
+ mad r0.x, r0.w, r0.x, -r0.z
+ add r0.z, r1.x, r1.x
+ abs r0.z, r0.z
+ mul r0.z, r0.z, c2.z
+ frc r0.w, -r0.y
+ lrp r1.x, r0.z, r0.w, r0.x
+ lrp r2.x, c2.y, r1.x, r0.y
mov r0.w, c1.w
- mad r0.x, r0.x, -c0.z, -r0.w
- cmp r0.x, r0.x, c4.y, c4.z
+ mad r0.x, r0.y, -c0.z, -r0.w
+ cmp r0.x, r0.x, c5.y, c5.z
+ mov r2.y, c5.x
+ texld r1, t1, s0
+ texld r2, r2, s1
mul r2.xyz, r2.w, r2
mul r1, r1, r2
mul r0, r0.x, r1
mov oC0, r0
-// approximately 23 instruction slots used (2 texture, 21 arithmetic)
+// approximately 36 instruction slots used (2 texture, 34 arithmetic)
ps_4_0
dcl_constantbuffer cb0[5], immediateIndexed
dcl_sampler s0, mode_default
@@ -724,45 +896,58 @@ mul r0.w, r0.w, l(0.500000)
mov r0.z, cb0[1].w
dp3 r0.x, r0.xyzx, cb0[0].xyzx
div r0.x, r0.w, r0.x
-mov r0.y, l(0.500000)
-sample r1.xyzw, r0.xyxx, t1.xyzw, s1
+round_pi r0.y, r0.x
+round_ni r0.z, r0.x
+mul r0.w, r0.z, l(0.500000)
+add r0.yz, -r0.xxzx, r0.yyxy
+ge r1.x, r0.w, -r0.w
+frc r0.w, |r0.w|
+movc r0.w, r1.x, r0.w, -r0.w
+add r0.w, r0.w, r0.w
+mul r1.x, |r0.w|, cb0[2].z
+mad r0.w, -|r0.w|, cb0[2].z, l(1.000000)
+mul r0.y, r0.y, r1.x
+mad r0.y, r0.z, r0.w, r0.y
+mul r0.y, r0.y, cb0[2].y
+add r0.z, l(1.000000), -cb0[2].y
+mad r1.x, r0.x, r0.z, r0.y
mul r0.x, r0.x, cb0[0].z
ge r0.x, -cb0[1].w, r0.x
movc r0.x, r0.x, l(-0.000000), l(1.000000)
+mov r1.y, l(0.500000)
+sample r1.xyzw, r1.xyxx, t1.xyzw, s1
mul r1.xyz, r1.wwww, r1.xyzx
sample r2.xyzw, v2.xyxx, t0.xyzw, s0
mul r1.xyzw, r1.xyzw, r2.xyzw
mul o0.xyzw, r0.xxxx, r1.xyzw
ret
-// Approximately 21 instruction slots used
+// Approximately 36 instruction slots used
#endif
const BYTE SampleRadialGradientA0PS[] =
{
- 68, 88, 66, 67, 67, 228,
- 119, 188, 222, 208, 62, 54,
- 201, 121, 212, 21, 98, 33,
- 252, 111, 1, 0, 0, 0,
- 152, 8, 0, 0, 6, 0,
+ 68, 88, 66, 67, 47, 105,
+ 118, 126, 8, 122, 228, 233,
+ 56, 98, 50, 148, 135, 10,
+ 63, 196, 1, 0, 0, 0,
+ 120, 11, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
- 20, 2, 0, 0, 20, 5,
- 0, 0, 144, 5, 0, 0,
- 232, 7, 0, 0, 100, 8,
+ 212, 2, 0, 0, 172, 7,
+ 0, 0, 40, 8, 0, 0,
+ 200, 10, 0, 0, 68, 11,
0, 0, 65, 111, 110, 57,
- 212, 1, 0, 0, 212, 1,
+ 148, 2, 0, 0, 148, 2,
0, 0, 0, 2, 255, 255,
- 144, 1, 0, 0, 68, 0,
- 0, 0, 2, 0, 44, 0,
- 0, 0, 68, 0, 0, 0,
- 68, 0, 2, 0, 36, 0,
- 0, 0, 68, 0, 0, 0,
+ 92, 2, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 0, 0,
0, 0, 1, 1, 1, 0,
- 0, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 5, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 2, 0,
- 2, 0, 0, 0, 0, 0,
1, 2, 255, 255, 81, 0,
- 0, 5, 4, 0, 15, 160,
+ 0, 5, 5, 0, 15, 160,
0, 0, 0, 63, 0, 0,
0, 128, 0, 0, 128, 63,
0, 0, 0, 0, 31, 0,
@@ -775,11 +960,11 @@ const BYTE SampleRadialGradientA0PS[] =
0, 2, 0, 0, 0, 144,
1, 8, 15, 160, 90, 0,
0, 4, 0, 0, 1, 128,
- 0, 0, 228, 176, 2, 0,
- 228, 160, 2, 0, 170, 160,
+ 0, 0, 228, 176, 3, 0,
+ 228, 160, 3, 0, 170, 160,
90, 0, 0, 4, 0, 0,
2, 128, 0, 0, 228, 176,
- 3, 0, 228, 160, 3, 0,
+ 4, 0, 228, 160, 4, 0,
170, 160, 2, 0, 0, 3,
0, 0, 3, 128, 0, 0,
228, 128, 1, 0, 228, 161,
@@ -791,7 +976,7 @@ const BYTE SampleRadialGradientA0PS[] =
228, 128, 0, 0, 255, 129,
5, 0, 0, 3, 0, 0,
8, 128, 0, 0, 255, 128,
- 4, 0, 0, 160, 1, 0,
+ 5, 0, 0, 160, 1, 0,
0, 2, 0, 0, 4, 128,
1, 0, 255, 160, 8, 0,
0, 3, 0, 0, 1, 128,
@@ -799,24 +984,58 @@ const BYTE SampleRadialGradientA0PS[] =
228, 160, 6, 0, 0, 2,
0, 0, 1, 128, 0, 0,
0, 128, 5, 0, 0, 3,
- 0, 0, 1, 128, 0, 0,
+ 0, 0, 2, 128, 0, 0,
0, 128, 0, 0, 255, 128,
+ 19, 0, 0, 2, 0, 0,
+ 4, 128, 0, 0, 85, 128,
+ 2, 0, 0, 3, 0, 0,
+ 4, 128, 0, 0, 170, 129,
+ 0, 0, 85, 128, 5, 0,
+ 0, 3, 1, 0, 8, 128,
+ 0, 0, 170, 128, 5, 0,
+ 0, 160, 35, 0, 0, 2,
+ 1, 0, 1, 128, 1, 0,
+ 255, 128, 19, 0, 0, 2,
+ 1, 0, 1, 128, 1, 0,
+ 0, 128, 88, 0, 0, 4,
+ 1, 0, 1, 128, 0, 0,
+ 170, 128, 1, 0, 0, 128,
+ 1, 0, 0, 129, 4, 0,
+ 0, 4, 0, 0, 1, 128,
+ 0, 0, 255, 128, 0, 0,
+ 0, 128, 0, 0, 170, 129,
+ 2, 0, 0, 3, 0, 0,
+ 4, 128, 1, 0, 0, 128,
+ 1, 0, 0, 128, 35, 0,
+ 0, 2, 0, 0, 4, 128,
+ 0, 0, 170, 128, 5, 0,
+ 0, 3, 0, 0, 4, 128,
+ 0, 0, 170, 128, 2, 0,
+ 170, 160, 19, 0, 0, 2,
+ 0, 0, 8, 128, 0, 0,
+ 85, 129, 18, 0, 0, 4,
+ 1, 0, 1, 128, 0, 0,
+ 170, 128, 0, 0, 255, 128,
+ 0, 0, 0, 128, 18, 0,
+ 0, 4, 2, 0, 1, 128,
+ 2, 0, 85, 160, 1, 0,
+ 0, 128, 0, 0, 85, 128,
1, 0, 0, 2, 0, 0,
- 2, 128, 4, 0, 0, 160,
- 66, 0, 0, 3, 1, 0,
- 15, 128, 1, 0, 228, 176,
- 0, 8, 228, 160, 66, 0,
- 0, 3, 2, 0, 15, 128,
- 0, 0, 228, 128, 1, 8,
- 228, 160, 1, 0, 0, 2,
- 0, 0, 8, 128, 1, 0,
- 255, 160, 4, 0, 0, 4,
+ 8, 128, 1, 0, 255, 160,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 0, 0, 85, 128,
+ 0, 0, 170, 161, 0, 0,
+ 255, 129, 88, 0, 0, 4,
0, 0, 1, 128, 0, 0,
- 0, 128, 0, 0, 170, 161,
- 0, 0, 255, 129, 88, 0,
- 0, 4, 0, 0, 1, 128,
- 0, 0, 0, 128, 4, 0,
- 85, 160, 4, 0, 170, 160,
+ 0, 128, 5, 0, 85, 160,
+ 5, 0, 170, 160, 1, 0,
+ 0, 2, 2, 0, 2, 128,
+ 5, 0, 0, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 176, 0, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 2, 0,
+ 228, 128, 1, 8, 228, 160,
5, 0, 0, 3, 2, 0,
7, 128, 2, 0, 255, 128,
2, 0, 228, 128, 5, 0,
@@ -828,8 +1047,8 @@ const BYTE SampleRadialGradientA0PS[] =
1, 0, 0, 2, 0, 8,
15, 128, 0, 0, 228, 128,
255, 255, 0, 0, 83, 72,
- 68, 82, 248, 2, 0, 0,
- 64, 0, 0, 0, 190, 0,
+ 68, 82, 208, 4, 0, 0,
+ 64, 0, 0, 0, 52, 1,
0, 0, 89, 0, 0, 4,
70, 142, 32, 0, 0, 0,
0, 0, 5, 0, 0, 0,
@@ -908,202 +1127,293 @@ const BYTE SampleRadialGradientA0PS[] =
0, 0, 0, 0, 58, 0,
16, 0, 0, 0, 0, 0,
10, 0, 16, 0, 0, 0,
- 0, 0, 54, 0, 0, 5,
+ 0, 0, 66, 0, 0, 5,
34, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 65, 0,
+ 0, 5, 66, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
0, 0, 1, 64, 0, 0,
- 0, 0, 0, 63, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 1, 0, 0, 0, 70, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 8, 98, 0, 16, 0,
+ 0, 0, 0, 0, 6, 2,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 86, 4,
16, 0, 0, 0, 0, 0,
- 70, 126, 16, 0, 1, 0,
- 0, 0, 0, 96, 16, 0,
- 1, 0, 0, 0, 56, 0,
- 0, 8, 18, 0, 16, 0,
- 0, 0, 0, 0, 10, 0,
+ 29, 0, 0, 8, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 26, 0, 0, 6,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 128,
+ 129, 0, 0, 0, 0, 0,
+ 0, 0, 55, 0, 0, 10,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
16, 0, 0, 0, 0, 0,
- 42, 128, 32, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
0, 0, 0, 0, 0, 0,
- 29, 0, 0, 9, 18, 0,
+ 0, 0, 0, 7, 130, 0,
16, 0, 0, 0, 0, 0,
- 58, 128, 32, 128, 65, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 10, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 9, 18, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 128, 129, 0, 0, 0,
+ 0, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 128, 193, 0, 0, 0,
+ 0, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 56, 0, 0, 7, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 9, 34, 0, 16, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 34, 0, 16, 0,
+ 0, 0, 0, 0, 26, 0,
16, 0, 0, 0, 0, 0,
- 55, 0, 0, 9, 18, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 9, 66, 0,
16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 26, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 8, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 128, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 29, 0, 0, 9,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 58, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
10, 0, 16, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 128,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 54, 0, 0, 5,
+ 34, 0, 16, 0, 1, 0,
0, 0, 1, 64, 0, 0,
- 0, 0, 0, 128, 1, 64,
- 0, 0, 0, 0, 128, 63,
- 56, 0, 0, 7, 114, 0,
- 16, 0, 1, 0, 0, 0,
- 246, 15, 16, 0, 1, 0,
- 0, 0, 70, 2, 16, 0,
- 1, 0, 0, 0, 69, 0,
+ 0, 0, 0, 63, 69, 0,
0, 9, 242, 0, 16, 0,
- 2, 0, 0, 0, 70, 16,
- 16, 0, 2, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
+ 1, 0, 0, 0, 70, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 1, 0,
0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 56, 0,
- 0, 7, 242, 0, 16, 0,
- 1, 0, 0, 0, 70, 14,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 1, 0, 0, 0, 246, 15,
16, 0, 1, 0, 0, 0,
- 70, 14, 16, 0, 2, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 70, 16, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
0, 0, 56, 0, 0, 7,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 6, 0, 16, 0,
- 0, 0, 0, 0, 70, 14,
- 16, 0, 1, 0, 0, 0,
- 62, 0, 0, 1, 83, 84,
- 65, 84, 116, 0, 0, 0,
- 21, 0, 0, 0, 3, 0,
- 0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 14, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 0, 0, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 56, 0, 0, 7, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 36, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 29, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 4, 0,
- 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 2, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 82, 68, 69, 70,
- 80, 2, 0, 0, 1, 0,
- 0, 0, 0, 1, 0, 0,
- 5, 0, 0, 0, 28, 0,
- 0, 0, 0, 4, 255, 255,
- 0, 1, 0, 0, 28, 2,
- 0, 0, 188, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 82, 68, 69, 70, 152, 2,
0, 0, 1, 0, 0, 0,
- 1, 0, 0, 0, 201, 0,
- 0, 0, 3, 0, 0, 0,
+ 0, 1, 0, 0, 5, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 100, 2, 0, 0,
+ 188, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 1, 0,
+ 0, 0, 201, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0,
- 217, 0, 0, 0, 2, 0,
+ 1, 0, 0, 0, 217, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 230, 0, 0, 0, 2, 0,
0, 0, 5, 0, 0, 0,
4, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
+ 255, 255, 1, 0, 0, 0,
1, 0, 0, 0, 13, 0,
- 0, 0, 230, 0, 0, 0,
- 2, 0, 0, 0, 5, 0,
- 0, 0, 4, 0, 0, 0,
- 255, 255, 255, 255, 1, 0,
- 0, 0, 1, 0, 0, 0,
- 13, 0, 0, 0, 246, 0,
+ 0, 0, 246, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0,
- 73, 110, 112, 117, 116, 83,
- 97, 109, 112, 108, 101, 114,
- 0, 71, 114, 97, 100, 105,
- 101, 110, 116, 83, 97, 109,
- 112, 108, 101, 114, 0, 73,
- 110, 112, 117, 116, 84, 101,
- 120, 116, 117, 114, 101, 0,
- 71, 114, 97, 100, 105, 101,
- 110, 116, 84, 101, 120, 116,
- 117, 114, 101, 0, 99, 111,
- 110, 115, 116, 97, 110, 116,
- 115, 0, 246, 0, 0, 0,
- 6, 0, 0, 0, 24, 1,
- 0, 0, 80, 0, 0, 0,
+ 1, 0, 0, 0, 73, 110,
+ 112, 117, 116, 83, 97, 109,
+ 112, 108, 101, 114, 0, 71,
+ 114, 97, 100, 105, 101, 110,
+ 116, 83, 97, 109, 112, 108,
+ 101, 114, 0, 73, 110, 112,
+ 117, 116, 84, 101, 120, 116,
+ 117, 114, 101, 0, 71, 114,
+ 97, 100, 105, 101, 110, 116,
+ 84, 101, 120, 116, 117, 114,
+ 101, 0, 99, 111, 110, 115,
+ 116, 97, 110, 116, 115, 0,
+ 246, 0, 0, 0, 8, 0,
+ 0, 0, 24, 1, 0, 0,
+ 80, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 168, 1, 0, 0,
- 0, 0, 0, 0, 12, 0,
- 0, 0, 2, 0, 0, 0,
- 176, 1, 0, 0, 0, 0,
- 0, 0, 192, 1, 0, 0,
- 16, 0, 0, 0, 8, 0,
- 0, 0, 2, 0, 0, 0,
- 200, 1, 0, 0, 0, 0,
- 0, 0, 216, 1, 0, 0,
- 24, 0, 0, 0, 4, 0,
+ 216, 1, 0, 0, 0, 0,
+ 0, 0, 12, 0, 0, 0,
+ 2, 0, 0, 0, 224, 1,
0, 0, 0, 0, 0, 0,
- 220, 1, 0, 0, 0, 0,
- 0, 0, 236, 1, 0, 0,
- 28, 0, 0, 0, 4, 0,
- 0, 0, 2, 0, 0, 0,
- 220, 1, 0, 0, 0, 0,
- 0, 0, 244, 1, 0, 0,
- 32, 0, 0, 0, 4, 0,
+ 240, 1, 0, 0, 16, 0,
+ 0, 0, 8, 0, 0, 0,
+ 2, 0, 0, 0, 248, 1,
0, 0, 0, 0, 0, 0,
- 220, 1, 0, 0, 0, 0,
- 0, 0, 255, 1, 0, 0,
- 48, 0, 0, 0, 28, 0,
- 0, 0, 2, 0, 0, 0,
- 12, 2, 0, 0, 0, 0,
- 0, 0, 100, 105, 102, 102,
- 0, 171, 171, 171, 1, 0,
- 3, 0, 1, 0, 3, 0,
+ 8, 2, 0, 0, 24, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 12, 2,
0, 0, 0, 0, 0, 0,
- 0, 0, 99, 101, 110, 116,
- 101, 114, 49, 0, 1, 0,
- 3, 0, 1, 0, 2, 0,
+ 28, 2, 0, 0, 28, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 12, 2,
0, 0, 0, 0, 0, 0,
- 0, 0, 65, 0, 171, 171,
- 0, 0, 3, 0, 1, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 114, 97,
- 100, 105, 117, 115, 49, 0,
- 115, 113, 95, 114, 97, 100,
- 105, 117, 115, 49, 0, 116,
- 114, 97, 110, 115, 102, 111,
- 114, 109, 0, 171, 171, 171,
- 3, 0, 3, 0, 3, 0,
- 2, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 73, 83, 71, 78,
- 116, 0, 0, 0, 3, 0,
- 0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 36, 2, 0, 0, 32, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 12, 2,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 15, 3, 0, 0,
- 107, 0, 0, 0, 0, 0,
+ 47, 2, 0, 0, 36, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 12, 2,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 2, 0,
- 0, 0, 15, 3, 0, 0,
- 83, 86, 95, 80, 79, 83,
- 73, 84, 73, 79, 78, 0,
- 83, 67, 69, 78, 69, 95,
- 80, 79, 83, 73, 84, 73,
- 79, 78, 0, 84, 69, 88,
- 67, 79, 79, 82, 68, 0,
- 79, 83, 71, 78, 44, 0,
+ 62, 2, 0, 0, 40, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 12, 2,
+ 0, 0, 0, 0, 0, 0,
+ 72, 2, 0, 0, 48, 0,
+ 0, 0, 28, 0, 0, 0,
+ 2, 0, 0, 0, 84, 2,
+ 0, 0, 0, 0, 0, 0,
+ 100, 105, 102, 102, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 101, 110, 116, 101, 114,
+ 49, 0, 1, 0, 3, 0,
+ 1, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 65, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 114, 97, 100, 105,
+ 117, 115, 49, 0, 115, 113,
+ 95, 114, 97, 100, 105, 117,
+ 115, 49, 0, 114, 101, 112,
+ 101, 97, 116, 95, 99, 111,
+ 114, 114, 101, 99, 116, 0,
+ 97, 108, 108, 111, 119, 95,
+ 111, 100, 100, 0, 116, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 171, 171, 3, 0,
+ 3, 0, 3, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 73, 83, 71, 78, 116, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 1, 0, 0, 0,
- 8, 0, 0, 0, 32, 0,
+ 15, 3, 0, 0, 107, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 3, 0, 0, 83, 86,
+ 95, 80, 79, 83, 73, 84,
+ 73, 79, 78, 0, 83, 67,
+ 69, 78, 69, 95, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 84, 69, 88, 67, 79,
+ 79, 82, 68, 0, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 83, 86,
- 95, 84, 97, 114, 103, 101,
- 116, 0, 171, 171
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
};
diff --git a/gfx/2d/ShadersD2D1.hlsl b/gfx/2d/ShadersD2D1.hlsl
index b763786c2..42337afc2 100644
--- a/gfx/2d/ShadersD2D1.hlsl
+++ b/gfx/2d/ShadersD2D1.hlsl
@@ -16,6 +16,13 @@ cbuffer constants : register(b0)
float A : packoffset(c1.z);
float radius1 : packoffset(c1.w);
float sq_radius1 : packoffset(c2.x);
+
+ // The next two values are used for a hack to compensate for an apparent
+ // bug in D2D where the GradientSampler SamplerState doesn't get the
+ // correct addressing modes.
+ float repeat_correct : packoffset(c2.y);
+ float allow_odd : packoffset(c2.z);
+
float3x2 transform : packoffset(c3.x);
}
@@ -52,7 +59,13 @@ float4 SampleRadialGradientPS(
float upper_t = lerp(t.y, t.x, isValid.x);
- float4 output = GradientTexture.Sample(GradientSampler, float2(upper_t, 0.5));
+ // Addressing mode bug work-around.. first let's see if we should consider odd repetitions separately.
+ float oddeven = abs(fmod(floor(upper_t), 2)) * allow_odd;
+
+ // Now let's calculate even or odd addressing in a branchless manner.
+ float upper_t_repeated = ((upper_t - floor(upper_t)) * (1.0f - oddeven)) + ((ceil(upper_t) - upper_t) * oddeven);
+
+ float4 output = GradientTexture.Sample(GradientSampler, float2(upper_t * (1.0f - repeat_correct) + upper_t_repeated * repeat_correct, 0.5));
// Premultiply
output.rgb *= output.a;
// Multiply the output color by the input mask for the operation.
@@ -84,7 +97,13 @@ float4 SampleRadialGradientA0PS(
float t = 0.5 * C / B;
- float4 output = GradientTexture.Sample(GradientSampler, float2(t, 0.5));
+ // Addressing mode bug work-around.. first let's see if we should consider odd repetitions separately.
+ float oddeven = abs(fmod(floor(t), 2)) * allow_odd;
+
+ // Now let's calculate even or odd addressing in a branchless manner.
+ float t_repeated = ((t - floor(t)) * (1.0f - oddeven)) + ((ceil(t) - t) * oddeven);
+
+ float4 output = GradientTexture.Sample(GradientSampler, float2(t * (1.0f - repeat_correct) + t_repeated * repeat_correct, 0.5));
// Premultiply
output.rgb *= output.a;
// Multiply the output color by the input mask for the operation.
@@ -95,3 +114,4 @@ float4 SampleRadialGradientA0PS(
// -radius1 >= t * diff.z
return output * abs(step(t * diff.z, -radius1) - 1.0f);
};
+
diff --git a/gfx/2d/SourceSurfaceCG.cpp b/gfx/2d/SourceSurfaceCG.cpp
index 7be284c30..12b038c20 100644
--- a/gfx/2d/SourceSurfaceCG.cpp
+++ b/gfx/2d/SourceSurfaceCG.cpp
@@ -5,8 +5,12 @@
#include "SourceSurfaceCG.h"
#include "DrawTargetCG.h"
+#include "DataSourceSurfaceWrapper.h"
+#include "DataSurfaceHelpers.h"
+#include "mozilla/Types.h" // for decltype
-#include "QuartzSupport.h"
+#include "MacIOSurface.h"
+#include "Tools.h"
namespace mozilla {
namespace gfx {
@@ -37,22 +41,40 @@ SourceSurfaceCG::GetDataSurface()
{
//XXX: we should be more disciplined about who takes a reference and where
CGImageRetain(mImage);
- RefPtr<DataSourceSurfaceCG> dataSurf =
- new DataSourceSurfaceCG(mImage);
- return dataSurf;
+ RefPtr<DataSourceSurface> dataSurf = new DataSourceSurfaceCG(mImage);
+
+ // We also need to make sure that the returned surface has
+ // surface->GetType() == SurfaceType::DATA.
+ return new DataSourceSurfaceWrapper(dataSurf);
}
static void releaseCallback(void *info, const void *data, size_t size) {
free(info);
}
-static CGImageRef
+CGImageRef
CreateCGImage(void *aInfo,
const void *aData,
const IntSize &aSize,
int32_t aStride,
SurfaceFormat aFormat)
{
+ return CreateCGImage(releaseCallback,
+ aInfo,
+ aData,
+ aSize,
+ aStride,
+ aFormat);
+}
+
+CGImageRef
+CreateCGImage(CGDataProviderReleaseDataCallback aCallback,
+ void *aInfo,
+ const void *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat)
+{
//XXX: we should avoid creating this colorspace everytime
CGColorSpaceRef colorSpace = nullptr;
CGBitmapInfo bitinfo = 0;
@@ -60,21 +82,21 @@ CreateCGImage(void *aInfo,
int bitsPerPixel = 0;
switch (aFormat) {
- case FORMAT_B8G8R8A8:
+ case SurfaceFormat::B8G8R8A8:
colorSpace = CGColorSpaceCreateDeviceRGB();
bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
bitsPerComponent = 8;
bitsPerPixel = 32;
break;
- case FORMAT_B8G8R8X8:
+ case SurfaceFormat::B8G8R8X8:
colorSpace = CGColorSpaceCreateDeviceRGB();
bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
bitsPerComponent = 8;
bitsPerPixel = 32;
break;
- case FORMAT_A8:
+ case SurfaceFormat::A8:
// XXX: why don't we set a colorspace here?
bitsPerComponent = 8;
bitsPerPixel = 8;
@@ -84,13 +106,17 @@ CreateCGImage(void *aInfo,
MOZ_CRASH();
}
+ size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height);
+ if (bufLen == 0) {
+ return nullptr;
+ }
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(aInfo,
aData,
- aSize.height * aStride,
- releaseCallback);
+ bufLen,
+ aCallback);
CGImageRef image;
- if (aFormat == FORMAT_A8) {
+ if (aFormat == SurfaceFormat::A8) {
CGFloat decode[] = {1.0, 0.0};
image = CGImageMaskCreate (aSize.width, aSize.height,
bitsPerComponent,
@@ -126,8 +152,16 @@ SourceSurfaceCG::InitFromData(unsigned char *aData,
{
assert(aSize.width >= 0 && aSize.height >= 0);
- void *data = malloc(aStride * aSize.height);
- memcpy(data, aData, aStride * aSize.height);
+ size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height);
+ if (bufLen == 0) {
+ mImage = nullptr;
+ return false;
+ }
+
+ void *data = malloc(bufLen);
+ // Copy all the data except the stride padding on the very last
+ // row since we can't guarantee that is readable.
+ memcpy(data, aData, bufLen - aStride + (aSize.width * BytesPerPixel(aFormat)));
mFormat = aFormat;
mImage = CreateCGImage(data, data, aSize, aStride, aFormat);
@@ -157,12 +191,28 @@ DataSourceSurfaceCG::InitFromData(unsigned char *aData,
int32_t aStride,
SurfaceFormat aFormat)
{
- void *data = malloc(aStride * aSize.height);
- memcpy(data, aData, aStride * aSize.height);
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ return false;
+ }
+
+ size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height);
+ if (bufLen == 0) {
+ mImage = nullptr;
+ return false;
+ }
+
+ void *data = malloc(bufLen);
+ memcpy(data, aData, bufLen - aStride + (aSize.width * BytesPerPixel(aFormat)));
+ mFormat = aFormat;
mImage = CreateCGImage(data, data, aSize, aStride, aFormat);
- return mImage;
+ if (!mImage) {
+ free(data);
+ return false;
+ }
+
+ return true;
}
CGContextRef CreateBitmapContextForImage(CGImageRef image)
@@ -201,6 +251,7 @@ CGContextRef CreateBitmapContextForImage(CGImageRef image)
DataSourceSurfaceCG::DataSourceSurfaceCG(CGImageRef aImage)
{
+ mFormat = SurfaceFormat::B8G8R8A8;
mImage = aImage;
mCg = CreateBitmapContextForImage(aImage);
if (mCg == nullptr) {
@@ -241,7 +292,8 @@ DataSourceSurfaceCG::GetData()
SourceSurfaceCGBitmapContext::SourceSurfaceCGBitmapContext(DrawTargetCG *aDrawTarget)
{
mDrawTarget = aDrawTarget;
- mCg = (CGContextRef)aDrawTarget->GetNativeSurface(NATIVE_SURFACE_CGCONTEXT);
+ mFormat = aDrawTarget->GetFormat();
+ mCg = (CGContextRef)aDrawTarget->GetNativeSurface(NativeSurfaceType::CGCONTEXT);
if (!mCg)
abort();
@@ -263,21 +315,8 @@ void SourceSurfaceCGBitmapContext::EnsureImage() const
// memcpy when the bitmap context is modified gives us more predictable
// performance characteristics.
if (!mImage) {
- void *info;
- if (mCg) {
- // if we have an mCg than it owns the data
- // and we don't want to tranfer ownership
- // to the CGDataProviderCreateWithData
- info = nullptr;
- } else {
- // otherwise we transfer ownership to
- // the dataProvider
- info = mData;
- }
-
if (!mData) abort();
-
- mImage = CreateCGImage(info, mData, mSize, mStride, FORMAT_B8G8R8A8);
+ mImage = CreateCGImage(nullptr, mData, mSize, mStride, mFormat);
}
}
@@ -295,13 +334,21 @@ SourceSurfaceCGBitmapContext::DrawTargetWillChange()
size_t stride = CGBitmapContextGetBytesPerRow(mCg);
size_t height = CGBitmapContextGetHeight(mCg);
- //XXX: infalliable malloc?
- mData = malloc(stride * height);
-
- // copy out the data from the CGBitmapContext
- // we'll maintain ownership of mData until
- // we transfer it to mImage
- memcpy(mData, CGBitmapContextGetData(mCg), stride*height);
+ size_t bufLen = BufferSizeFromStrideAndHeight(stride, height);
+ if (bufLen == 0) {
+ mDataHolder.Dealloc();
+ mData = nullptr;
+ } else {
+ static_assert(sizeof(decltype(mDataHolder[0])) == 1,
+ "mDataHolder.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen");
+ mDataHolder.Realloc(/* actually an object count */ bufLen);
+ mData = mDataHolder;
+
+ // copy out the data from the CGBitmapContext
+ // we'll maintain ownership of mData until
+ // we transfer it to mImage
+ memcpy(mData, CGBitmapContextGetData(mCg), bufLen);
+ }
// drop the current image for the data associated with the CGBitmapContext
if (mImage)
@@ -313,22 +360,37 @@ SourceSurfaceCGBitmapContext::DrawTargetWillChange()
}
}
-SourceSurfaceCGBitmapContext::~SourceSurfaceCGBitmapContext()
+void
+SourceSurfaceCGBitmapContext::DrawTargetWillGoAway()
{
- if (!mImage && !mCg) {
- // neither mImage or mCg owns the data
- free(mData);
+ if (mDrawTarget) {
+ if (mDrawTarget->mData != CGBitmapContextGetData(mCg)) {
+ DrawTargetWillChange();
+ return;
+ }
+
+ // Instead of copying the data over, we can just swap it.
+ mDataHolder.Swap(mDrawTarget->mData);
+ mData = mDataHolder;
+ mCg = nullptr;
+ mDrawTarget = nullptr;
+ // mImage is still valid because it still points to the same data.
}
+}
+
+SourceSurfaceCGBitmapContext::~SourceSurfaceCGBitmapContext()
+{
if (mImage)
CGImageRelease(mImage);
}
SourceSurfaceCGIOSurfaceContext::SourceSurfaceCGIOSurfaceContext(DrawTargetCG *aDrawTarget)
{
- CGContextRef cg = (CGContextRef)aDrawTarget->GetNativeSurface(NATIVE_SURFACE_CGCONTEXT_ACCELERATED);
+ CGContextRef cg = (CGContextRef)aDrawTarget->GetNativeSurface(NativeSurfaceType::CGCONTEXT_ACCELERATED);
RefPtr<MacIOSurface> surf = MacIOSurface::IOSurfaceContextGetSurface(cg);
+ mFormat = aDrawTarget->GetFormat();
mSize.width = surf->GetWidth();
mSize.height = surf->GetHeight();
@@ -361,7 +423,7 @@ void SourceSurfaceCGIOSurfaceContext::EnsureImage() const
// memcpy when the bitmap context is modified gives us more predictable
// performance characteristics.
if (!mImage) {
- mImage = CreateCGImage(mData, mData, mSize, mStride, FORMAT_B8G8R8A8);
+ mImage = CreateCGImage(mData, mData, mSize, mStride, SurfaceFormat::B8G8R8A8);
}
}
diff --git a/gfx/2d/SourceSurfaceCG.h b/gfx/2d/SourceSurfaceCG.h
index 63bd675eb..9e1798490 100644
--- a/gfx/2d/SourceSurfaceCG.h
+++ b/gfx/2d/SourceSurfaceCG.h
@@ -3,7 +3,8 @@
* 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/. */
-#pragma once
+#ifndef _MOZILLA_GFX_SOURCESURFACECG_H
+#define _MOZILLA_GFX_SOURCESURFACECG_H
#include <ApplicationServices/ApplicationServices.h>
@@ -14,16 +15,32 @@ class MacIOSurface;
namespace mozilla {
namespace gfx {
+CGImageRef
+CreateCGImage(CGDataProviderReleaseDataCallback aCallback,
+ void *aInfo,
+ const void *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat);
+
+CGImageRef
+CreateCGImage(void *aInfo,
+ const void *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat);
+
class DrawTargetCG;
class SourceSurfaceCG : public SourceSurface
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCG)
SourceSurfaceCG() {}
- SourceSurfaceCG(CGImageRef aImage) : mImage(aImage) {}
+ explicit SourceSurfaceCG(CGImageRef aImage) : mImage(aImage) {}
~SourceSurfaceCG();
- virtual SurfaceType GetType() const { return SURFACE_COREGRAPHICS_IMAGE; }
+ virtual SurfaceType GetType() const { return SurfaceType::COREGRAPHICS_IMAGE; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
virtual TemporaryRef<DataSourceSurface> GetDataSurface();
@@ -47,13 +64,14 @@ private:
class DataSourceSurfaceCG : public DataSourceSurface
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCG)
DataSourceSurfaceCG() {}
- DataSourceSurfaceCG(CGImageRef aImage);
+ explicit DataSourceSurfaceCG(CGImageRef aImage);
~DataSourceSurfaceCG();
- virtual SurfaceType GetType() const { return SURFACE_DATA; }
+ virtual SurfaceType GetType() const { return SurfaceType::DATA; }
virtual IntSize GetSize() const;
- virtual SurfaceFormat GetFormat() const { return FORMAT_B8G8R8A8; }
+ virtual SurfaceFormat GetFormat() const { return mFormat; }
CGImageRef GetImage() { return mImage; }
@@ -70,6 +88,7 @@ public:
private:
CGContextRef mCg;
CGImageRef mImage;
+ SurfaceFormat mFormat;
//XXX: we don't need to store mData we can just get it from the CGContext
void *mData;
/* It might be better to just use the bitmap info from the CGImageRef to
@@ -80,19 +99,36 @@ private:
class SourceSurfaceCGContext : public DataSourceSurface
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCGContext)
virtual void DrawTargetWillChange() = 0;
+ virtual void DrawTargetWillGoAway() = 0;
virtual CGImageRef GetImage() = 0;
};
class SourceSurfaceCGBitmapContext : public SourceSurfaceCGContext
{
public:
- SourceSurfaceCGBitmapContext(DrawTargetCG *);
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCGBitmapContext)
+ explicit SourceSurfaceCGBitmapContext(DrawTargetCG *);
~SourceSurfaceCGBitmapContext();
- virtual SurfaceType GetType() const { return SURFACE_COREGRAPHICS_CGCONTEXT; }
+ virtual SurfaceType GetType() const { return SurfaceType::COREGRAPHICS_CGCONTEXT; }
virtual IntSize GetSize() const;
- virtual SurfaceFormat GetFormat() const { return FORMAT_B8G8R8A8; }
+ virtual SurfaceFormat GetFormat() const { return mFormat; }
+ virtual TemporaryRef<DataSourceSurface> GetDataSurface()
+ {
+ // This call to DrawTargetWillChange() is needed to make a local copy of
+ // the data from mDrawTarget. If we don't do that, the data can end up
+ // getting deleted before the CGImageRef it belongs to.
+ //
+ // Another reason we need a local copy of the data is that the data in
+ // mDrawTarget could change when someone touches the original DrawTargetCG
+ // object. But a SourceSurface object should be immutable.
+ //
+ // For more information see bug 925448.
+ DrawTargetWillChange();
+ return this;
+ }
CGImageRef GetImage() { EnsureImage(); return mImage; }
@@ -104,12 +140,14 @@ private:
//XXX: do the other backends friend their DrawTarget?
friend class DrawTargetCG;
virtual void DrawTargetWillChange();
+ virtual void DrawTargetWillGoAway();
void EnsureImage() const;
// We hold a weak reference to these two objects.
// The cycle is broken by DrawTargetWillChange
DrawTargetCG *mDrawTarget;
CGContextRef mCg;
+ SurfaceFormat mFormat;
mutable CGImageRef mImage;
@@ -117,6 +155,9 @@ private:
// mImage, mCg or SourceSurfaceCGBitmapContext
void *mData;
+ // The image buffer, if the buffer is owned by this class.
+ AlignedArray<uint8_t> mDataHolder;
+
int32_t mStride;
IntSize mSize;
};
@@ -124,12 +165,13 @@ private:
class SourceSurfaceCGIOSurfaceContext : public SourceSurfaceCGContext
{
public:
- SourceSurfaceCGIOSurfaceContext(DrawTargetCG *);
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCGIOSurfaceContext)
+ explicit SourceSurfaceCGIOSurfaceContext(DrawTargetCG *);
~SourceSurfaceCGIOSurfaceContext();
- virtual SurfaceType GetType() const { return SURFACE_COREGRAPHICS_CGCONTEXT; }
+ virtual SurfaceType GetType() const { return SurfaceType::COREGRAPHICS_CGCONTEXT; }
virtual IntSize GetSize() const;
- virtual SurfaceFormat GetFormat() const { return FORMAT_B8G8R8A8; }
+ virtual SurfaceFormat GetFormat() const { return mFormat; }
CGImageRef GetImage() { EnsureImage(); return mImage; }
@@ -141,8 +183,10 @@ private:
//XXX: do the other backends friend their DrawTarget?
friend class DrawTargetCG;
virtual void DrawTargetWillChange();
+ virtual void DrawTargetWillGoAway() { DrawTargetWillChange(); }
void EnsureImage() const;
+ SurfaceFormat mFormat;
mutable CGImageRef mImage;
MacIOSurface* mIOSurface;
@@ -155,3 +199,5 @@ private:
}
}
+
+#endif // _MOZILLA_GFX_SOURCESURFACECG_H
diff --git a/gfx/2d/SourceSurfaceCairo.cpp b/gfx/2d/SourceSurfaceCairo.cpp
index 81438b6a4..a1ecfe51d 100644
--- a/gfx/2d/SourceSurfaceCairo.cpp
+++ b/gfx/2d/SourceSurfaceCairo.cpp
@@ -6,6 +6,7 @@
#include "SourceSurfaceCairo.h"
#include "DrawTargetCairo.h"
#include "HelpersCairo.h"
+#include "DataSourceSurfaceWrapper.h"
#include "cairo.h"
@@ -18,13 +19,13 @@ CairoFormatToSurfaceFormat(cairo_format_t format)
switch (format)
{
case CAIRO_FORMAT_ARGB32:
- return FORMAT_B8G8R8A8;
+ return SurfaceFormat::B8G8R8A8;
case CAIRO_FORMAT_RGB24:
- return FORMAT_B8G8R8X8;
+ return SurfaceFormat::B8G8R8X8;
case CAIRO_FORMAT_A8:
- return FORMAT_A8;
+ return SurfaceFormat::A8;
default:
- return FORMAT_B8G8R8A8;
+ return SurfaceFormat::B8G8R8A8;
}
}
@@ -60,7 +61,7 @@ SourceSurfaceCairo::GetFormat() const
TemporaryRef<DataSourceSurface>
SourceSurfaceCairo::GetDataSurface()
{
- RefPtr<DataSourceSurfaceCairo> dataSurf;
+ RefPtr<DataSourceSurface> dataSurf;
if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
dataSurf = new DataSourceSurfaceCairo(mSurface);
@@ -78,7 +79,9 @@ SourceSurfaceCairo::GetDataSurface()
cairo_surface_destroy(imageSurf);
}
- return dataSurf;
+ // We also need to make sure that the returned surface has
+ // surface->GetType() == SurfaceType::DATA.
+ return new DataSourceSurfaceWrapper(dataSurf);
}
cairo_surface_t*
diff --git a/gfx/2d/SourceSurfaceCairo.h b/gfx/2d/SourceSurfaceCairo.h
index 4f26de1fa..a9390e759 100644
--- a/gfx/2d/SourceSurfaceCairo.h
+++ b/gfx/2d/SourceSurfaceCairo.h
@@ -3,7 +3,7 @@
* 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 _MOZILLA_GFX_OP_SOURCESURFACE_CAIRO_H_
+#ifndef _MOZILLA_GFX_OP_SOURCESURFACE_CAIRO_H
#define _MOZILLA_GFX_OP_SOURCESURFACE_CAIRO_H
#include "2D.h"
@@ -16,6 +16,7 @@ class DrawTargetCairo;
class SourceSurfaceCairo : public SourceSurface
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCairo)
// Create a SourceSurfaceCairo. The surface will not be copied, but simply
// referenced.
// If aDrawTarget is non-nullptr, it is assumed that this is a snapshot source
@@ -26,7 +27,7 @@ public:
DrawTargetCairo* aDrawTarget = nullptr);
virtual ~SourceSurfaceCairo();
- virtual SurfaceType GetType() const { return SURFACE_CAIRO; }
+ virtual SurfaceType GetType() const { return SurfaceType::CAIRO; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
virtual TemporaryRef<DataSourceSurface> GetDataSurface();
@@ -47,12 +48,13 @@ private: // data
class DataSourceSurfaceCairo : public DataSourceSurface
{
public:
- DataSourceSurfaceCairo(cairo_surface_t* imageSurf);
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCairo)
+ explicit DataSourceSurfaceCairo(cairo_surface_t* imageSurf);
virtual ~DataSourceSurfaceCairo();
virtual unsigned char *GetData();
virtual int32_t Stride();
- virtual SurfaceType GetType() const { return SURFACE_CAIRO_IMAGE; }
+ virtual SurfaceType GetType() const { return SurfaceType::CAIRO_IMAGE; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
diff --git a/gfx/2d/SourceSurfaceD2D.cpp b/gfx/2d/SourceSurfaceD2D.cpp
index 005a6901e..538f0d2bd 100644
--- a/gfx/2d/SourceSurfaceD2D.cpp
+++ b/gfx/2d/SourceSurfaceD2D.cpp
@@ -45,7 +45,7 @@ SourceSurfaceD2D::GetDataSurface()
{
RefPtr<DataSourceSurfaceD2D> result = new DataSourceSurfaceD2D(this);
if (result->IsValid()) {
- return result;
+ return result.forget();
}
return nullptr;
}
@@ -68,12 +68,18 @@ SourceSurfaceD2D::InitFromData(unsigned char *aData,
return false;
}
- D2D1_BITMAP_PROPERTIES props =
- D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(aFormat), AlphaMode(aFormat)));
- hr = aRT->CreateBitmap(D2DIntSize(aSize), aData, aStride, props, byRef(mBitmap));
+ D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(aFormat));
+ hr = aRT->CreateBitmap(D2DIntSize(aSize), props, byRef(mBitmap));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create D2D Bitmap for data. Code: " << hr;
+ gfxWarning() << "Failed to create D2D Bitmap for data. Code: " << hexa(hr);
+ return false;
+ }
+
+ hr = mBitmap->CopyFromMemory(nullptr, aData, aStride);
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to copy data to D2D bitmap. Code: " << hexa(hr);
return false;
}
@@ -95,7 +101,7 @@ SourceSurfaceD2D::InitFromTexture(ID3D10Texture2D *aTexture,
hr = aTexture->QueryInterface((IDXGISurface**)&surf);
if (FAILED(hr)) {
- gfxWarning() << "Failed to QI texture to surface. Code: " << hr;
+ gfxWarning() << "Failed to QI texture to surface. Code: " << hexa(hr);
return false;
}
@@ -105,12 +111,11 @@ SourceSurfaceD2D::InitFromTexture(ID3D10Texture2D *aTexture,
mSize = IntSize(desc.Width, desc.Height);
mFormat = aFormat;
- D2D1_BITMAP_PROPERTIES props =
- D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(aFormat), AlphaMode(aFormat)));
+ D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(aFormat));
hr = aRT->CreateSharedBitmap(IID_IDXGISurface, surf, &props, byRef(mBitmap));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create SharedBitmap. Code: " << hr;
+ gfxWarning() << "Failed to create SharedBitmap. Code: " << hexa(hr);
return false;
}
@@ -145,14 +150,14 @@ DataSourceSurfaceD2D::DataSourceSurfaceD2D(SourceSurfaceD2D* aSourceSurface)
HRESULT hr = aSourceSurface->mDevice->CreateTexture2D(&desc, nullptr,
byRef(sourceTexture));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create texture. Code: " << hr;
+ gfxWarning() << "Failed to create texture. Code: " << hexa(hr);
return;
}
RefPtr<IDXGISurface> dxgiSurface;
hr = sourceTexture->QueryInterface((IDXGISurface**)byRef(dxgiSurface));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create DXGI surface. Code: " << hr;
+ gfxWarning() << "Failed to create DXGI surface. Code: " << hexa(hr);
return;
}
@@ -165,23 +170,35 @@ DataSourceSurfaceD2D::DataSourceSurfaceD2D(SourceSurfaceD2D* aSourceSurface)
&rtProps,
byRef(renderTarget));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create render target. Code: " << hr;
+ gfxWarning() << "Failed to create render target. Code: " << hexa(hr);
return;
}
renderTarget->BeginDraw();
- renderTarget->DrawBitmap(aSourceSurface->mBitmap,
- D2D1::RectF(0, 0,
- Float(mSize.width),
- Float(mSize.height)));
- renderTarget->EndDraw();
+ renderTarget->Clear(D2D1::ColorF(0, 0.0f));
+ if (aSourceSurface->GetFormat() != SurfaceFormat::A8) {
+ renderTarget->DrawBitmap(aSourceSurface->mBitmap,
+ D2D1::RectF(0, 0,
+ Float(mSize.width),
+ Float(mSize.height)));
+ } else {
+ RefPtr<ID2D1SolidColorBrush> brush;
+ renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), byRef(brush));
+ renderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+ renderTarget->FillOpacityMask(aSourceSurface->mBitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS);
+ }
+ hr = renderTarget->EndDraw();
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to draw bitmap. Code: " << hexa(hr);
+ return;
+ }
- desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ;
+ desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ | D3D10_CPU_ACCESS_WRITE;
desc.Usage = D3D10_USAGE_STAGING;
desc.BindFlags = 0;
hr = aSourceSurface->mDevice->CreateTexture2D(&desc, nullptr, byRef(mTexture));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create staging texture. Code: " << hr;
+ gfxWarning() << "Failed to create staging texture. Code: " << hexa(hr);
mTexture = nullptr;
return;
}
@@ -230,9 +247,58 @@ DataSourceSurfaceD2D::GetFormat() const
return mFormat;
}
+bool
+DataSourceSurfaceD2D::Map(MapType aMapType, MappedSurface *aMappedSurface)
+{
+ // DataSourceSurfaces used with the new Map API should not be used with GetData!!
+ MOZ_ASSERT(!mMapped);
+ MOZ_ASSERT(!mIsMapped);
+
+ if (!mTexture) {
+ return false;
+ }
+
+ D3D10_MAP mapType;
+
+ if (aMapType == MapType::READ) {
+ mapType = D3D10_MAP_READ;
+ } else if (aMapType == MapType::WRITE) {
+ mapType = D3D10_MAP_WRITE;
+ } else {
+ mapType = D3D10_MAP_READ_WRITE;
+ }
+
+ D3D10_MAPPED_TEXTURE2D map;
+
+ HRESULT hr = mTexture->Map(0, mapType, 0, &map);
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Texture map failed with code: " << hexa(hr);
+ return false;
+ }
+
+ aMappedSurface->mData = (uint8_t*)map.pData;
+ aMappedSurface->mStride = map.RowPitch;
+ mIsMapped = !!aMappedSurface->mData;
+
+ return mIsMapped;
+}
+
+void
+DataSourceSurfaceD2D::Unmap()
+{
+ MOZ_ASSERT(mIsMapped);
+
+ mIsMapped = false;
+ mTexture->Unmap(0);
+}
+
void
DataSourceSurfaceD2D::EnsureMappedTexture()
{
+ // Do not use GetData() after having used Map!
+ MOZ_ASSERT(!mIsMapped);
+
if (mMapped ||
!mTexture) {
return;
@@ -240,7 +306,7 @@ DataSourceSurfaceD2D::EnsureMappedTexture()
HRESULT hr = mTexture->Map(0, D3D10_MAP_READ, 0, &mData);
if (FAILED(hr)) {
- gfxWarning() << "Failed to map texture. Code: " << hr;
+ gfxWarning() << "Failed to map texture. Code: " << hexa(hr);
mTexture = nullptr;
} else {
mMapped = true;
diff --git a/gfx/2d/SourceSurfaceD2D.h b/gfx/2d/SourceSurfaceD2D.h
index bafbd2019..c2622d76c 100644
--- a/gfx/2d/SourceSurfaceD2D.h
+++ b/gfx/2d/SourceSurfaceD2D.h
@@ -18,10 +18,11 @@ class DataSourceSurfaceD2D;
class SourceSurfaceD2D : public SourceSurface
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceD2D)
SourceSurfaceD2D();
~SourceSurfaceD2D();
- virtual SurfaceType GetType() const { return SURFACE_D2D1_BITMAP; }
+ virtual SurfaceType GetType() const { return SurfaceType::D2D1_BITMAP; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
virtual bool IsValid() const;
@@ -55,6 +56,7 @@ private:
class DataSourceSurfaceD2D : public DataSourceSurface
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceD2D)
DataSourceSurfaceD2D(SourceSurfaceD2D* aSourceSurface);
virtual ~DataSourceSurfaceD2D();
@@ -62,6 +64,8 @@ public:
virtual int32_t Stride();
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
+ virtual bool Map(MapType, MappedSurface *aMappedSurface);
+ virtual void Unmap();
bool IsValid()
{
diff --git a/gfx/2d/SourceSurfaceD2D1.cpp b/gfx/2d/SourceSurfaceD2D1.cpp
new file mode 100644
index 000000000..ee742777f
--- /dev/null
+++ b/gfx/2d/SourceSurfaceD2D1.cpp
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "SourceSurfaceD2D1.h"
+#include "DrawTargetD2D1.h"
+#include "Tools.h"
+
+namespace mozilla {
+namespace gfx {
+
+SourceSurfaceD2D1::SourceSurfaceD2D1(ID2D1Image *aImage, ID2D1DeviceContext *aDC,
+ SurfaceFormat aFormat, const IntSize &aSize,
+ DrawTargetD2D1 *aDT)
+ : mImage(aImage)
+ , mDC(aDC)
+ , mDrawTarget(aDT)
+ , mDevice(Factory::GetD2D1Device())
+{
+ aImage->QueryInterface((ID2D1Bitmap1**)byRef(mRealizedBitmap));
+
+ mFormat = aFormat;
+ mSize = aSize;
+}
+
+SourceSurfaceD2D1::~SourceSurfaceD2D1()
+{
+}
+
+bool
+SourceSurfaceD2D1::IsValid() const
+{
+ return mDevice == Factory::GetD2D1Device();
+}
+
+TemporaryRef<DataSourceSurface>
+SourceSurfaceD2D1::GetDataSurface()
+{
+ HRESULT hr;
+
+ EnsureRealizedBitmap();
+
+ RefPtr<ID2D1Bitmap1> softwareBitmap;
+ D2D1_BITMAP_PROPERTIES1 props;
+ props.dpiX = 96;
+ props.dpiY = 96;
+ props.pixelFormat = D2DPixelFormat(mFormat);
+ props.colorContext = nullptr;
+ props.bitmapOptions = D2D1_BITMAP_OPTIONS_CANNOT_DRAW |
+ D2D1_BITMAP_OPTIONS_CPU_READ;
+ hr = mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(softwareBitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "Failed to create software bitmap: " << mSize << " Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ D2D1_POINT_2U point = D2D1::Point2U(0, 0);
+ D2D1_RECT_U rect = D2D1::RectU(0, 0, mSize.width, mSize.height);
+
+ hr = softwareBitmap->CopyFromBitmap(&point, mRealizedBitmap, &rect);
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to readback into software bitmap. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ return new DataSourceSurfaceD2D1(softwareBitmap, mFormat);
+}
+
+void
+SourceSurfaceD2D1::EnsureRealizedBitmap()
+{
+ if (mRealizedBitmap) {
+ return;
+ }
+
+ RefPtr<ID2D1DeviceContext> dc;
+ Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, byRef(dc));
+
+ D2D1_BITMAP_PROPERTIES1 props;
+ props.dpiX = 96;
+ props.dpiY = 96;
+ props.pixelFormat = D2DPixelFormat(mFormat);
+ props.colorContext = nullptr;
+ props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
+ dc->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mRealizedBitmap));
+
+ dc->SetTarget(mRealizedBitmap);
+
+ dc->BeginDraw();
+ dc->DrawImage(mImage);
+ dc->EndDraw();
+}
+
+void
+SourceSurfaceD2D1::DrawTargetWillChange()
+{
+ // At this point in time this should always be true here.
+ MOZ_ASSERT(mRealizedBitmap);
+
+ RefPtr<ID2D1Bitmap1> oldBitmap = mRealizedBitmap;
+
+ D2D1_BITMAP_PROPERTIES1 props;
+ props.dpiX = 96;
+ props.dpiY = 96;
+ props.pixelFormat = D2DPixelFormat(mFormat);
+ props.colorContext = nullptr;
+ props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
+ HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mRealizedBitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "Failed to create bitmap to make DrawTarget copy. Size: " << mSize << " Code: " << hexa(hr);
+ MarkIndependent();
+ return;
+ }
+
+ D2D1_POINT_2U point = D2D1::Point2U(0, 0);
+ D2D1_RECT_U rect = D2D1::RectU(0, 0, mSize.width, mSize.height);
+ mRealizedBitmap->CopyFromBitmap(&point, oldBitmap, &rect);
+ mImage = mRealizedBitmap;
+
+ DrawTargetD2D1::mVRAMUsageSS += mSize.width * mSize.height * BytesPerPixel(mFormat);
+
+ // We now no longer depend on the source surface content remaining the same.
+ MarkIndependent();
+}
+
+void
+SourceSurfaceD2D1::MarkIndependent()
+{
+ if (mDrawTarget) {
+ MOZ_ASSERT(mDrawTarget->mSnapshot == this);
+ mDrawTarget->mSnapshot = nullptr;
+ mDrawTarget = nullptr;
+ }
+}
+
+DataSourceSurfaceD2D1::DataSourceSurfaceD2D1(ID2D1Bitmap1 *aMappableBitmap, SurfaceFormat aFormat)
+ : mBitmap(aMappableBitmap)
+ , mFormat(aFormat)
+ , mMapped(false)
+{
+}
+
+DataSourceSurfaceD2D1::~DataSourceSurfaceD2D1()
+{
+ if (mMapped) {
+ mBitmap->Unmap();
+ }
+}
+
+IntSize
+DataSourceSurfaceD2D1::GetSize() const
+{
+ D2D1_SIZE_F size = mBitmap->GetSize();
+
+ return IntSize(int32_t(size.width), int32_t(size.height));
+}
+
+uint8_t*
+DataSourceSurfaceD2D1::GetData()
+{
+ EnsureMapped();
+
+ return mMap.bits;
+}
+
+bool
+DataSourceSurfaceD2D1::Map(MapType aMapType, MappedSurface *aMappedSurface)
+{
+ // DataSourceSurfaces used with the new Map API should not be used with GetData!!
+ MOZ_ASSERT(!mMapped);
+ MOZ_ASSERT(!mIsMapped);
+
+ D2D1_MAP_OPTIONS options;
+ if (aMapType == MapType::READ) {
+ options = D2D1_MAP_OPTIONS_READ;
+ } else {
+ MOZ_CRASH("No support for Write maps on D2D1 DataSourceSurfaces yet!");
+ }
+
+ D2D1_MAPPED_RECT map;
+ if (FAILED(mBitmap->Map(D2D1_MAP_OPTIONS_READ, &map))) {
+ gfxCriticalError() << "Failed to map bitmap.";
+ return false;
+ }
+ aMappedSurface->mData = map.bits;
+ aMappedSurface->mStride = map.pitch;
+
+ mIsMapped = !!aMappedSurface->mData;
+ return mIsMapped;
+}
+
+void
+DataSourceSurfaceD2D1::Unmap()
+{
+ MOZ_ASSERT(mIsMapped);
+
+ mIsMapped = false;
+ mBitmap->Unmap();
+}
+
+int32_t
+DataSourceSurfaceD2D1::Stride()
+{
+ EnsureMapped();
+
+ return mMap.pitch;
+}
+
+void
+DataSourceSurfaceD2D1::EnsureMapped()
+{
+ // Do not use GetData() after having used Map!
+ MOZ_ASSERT(!mIsMapped);
+ if (mMapped) {
+ return;
+ }
+ if (FAILED(mBitmap->Map(D2D1_MAP_OPTIONS_READ, &mMap))) {
+ gfxCriticalError() << "Failed to map bitmap.";
+ return;
+ }
+ mMapped = true;
+}
+
+}
+}
diff --git a/gfx/2d/SourceSurfaceD2D1.h b/gfx/2d/SourceSurfaceD2D1.h
new file mode 100644
index 000000000..1ecf31449
--- /dev/null
+++ b/gfx/2d/SourceSurfaceD2D1.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_SOURCESURFACED2D1_H_
+#define MOZILLA_GFX_SOURCESURFACED2D1_H_
+
+#include "2D.h"
+#include "HelpersD2D.h"
+#include <vector>
+#include <d3d11.h>
+#include <d2d1_1.h>
+
+namespace mozilla {
+namespace gfx {
+
+class DrawTargetD2D1;
+
+class SourceSurfaceD2D1 : public SourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceD2D1)
+ SourceSurfaceD2D1(ID2D1Image* aImage, ID2D1DeviceContext *aDC,
+ SurfaceFormat aFormat, const IntSize &aSize,
+ DrawTargetD2D1 *aDT = nullptr);
+ ~SourceSurfaceD2D1();
+
+ virtual SurfaceType GetType() const { return SurfaceType::D2D1_1_IMAGE; }
+ virtual IntSize GetSize() const { return mSize; }
+ virtual SurfaceFormat GetFormat() const { return mFormat; }
+ virtual bool IsValid() const;
+ virtual TemporaryRef<DataSourceSurface> GetDataSurface();
+
+ ID2D1Image *GetImage() { return mImage; }
+
+ void EnsureIndependent() { if (!mDrawTarget) return; DrawTargetWillChange(); }
+
+private:
+ friend class DrawTargetD2D1;
+
+ void EnsureRealizedBitmap();
+
+ // This function is called by the draw target this texture belongs to when
+ // it is about to be changed. The texture will be required to make a copy
+ // of itself when this happens.
+ void DrawTargetWillChange();
+
+ // This will mark the surface as no longer depending on its drawtarget,
+ // this may happen on destruction or copying.
+ void MarkIndependent();
+
+ RefPtr<ID2D1Image> mImage;
+ // This may be null if we were created for a non-bitmap image and have not
+ // had a reason yet to realize ourselves.
+ RefPtr<ID2D1Bitmap1> mRealizedBitmap;
+ RefPtr<ID2D1DeviceContext> mDC;
+ // Keep this around to verify whether out image is still valid in the future.
+ RefPtr<ID2D1Device> mDevice;
+
+ SurfaceFormat mFormat;
+ IntSize mSize;
+ DrawTargetD2D1* mDrawTarget;
+};
+
+class DataSourceSurfaceD2D1 : public DataSourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceD2D1)
+ DataSourceSurfaceD2D1(ID2D1Bitmap1 *aMappableBitmap, SurfaceFormat aFormat);
+ ~DataSourceSurfaceD2D1();
+
+ virtual SurfaceType GetType() const { return SurfaceType::DATA; }
+ virtual IntSize GetSize() const;
+ virtual SurfaceFormat GetFormat() const { return mFormat; }
+ virtual bool IsValid() const { return !!mBitmap; }
+ virtual uint8_t *GetData();
+ virtual int32_t Stride();
+ virtual bool Map(MapType, MappedSurface *aMappedSurface);
+ virtual void Unmap();
+
+private:
+ friend class SourceSurfaceD2DTarget;
+ void EnsureMapped();
+
+ mutable RefPtr<ID2D1Bitmap1> mBitmap;
+ SurfaceFormat mFormat;
+ D2D1_MAPPED_RECT mMap;
+ bool mMapped;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_SOURCESURFACED2D2TARGET_H_ */
diff --git a/gfx/2d/SourceSurfaceD2DTarget.cpp b/gfx/2d/SourceSurfaceD2DTarget.cpp
index 4f7e484cb..3cc3bbecb 100644
--- a/gfx/2d/SourceSurfaceD2DTarget.cpp
+++ b/gfx/2d/SourceSurfaceD2DTarget.cpp
@@ -54,7 +54,7 @@ TemporaryRef<DataSourceSurface>
SourceSurfaceD2DTarget::GetDataSurface()
{
RefPtr<DataSourceSurfaceD2DTarget> dataSurf =
- new DataSourceSurfaceD2DTarget();
+ new DataSourceSurfaceD2DTarget(mFormat);
D3D10_TEXTURE2D_DESC desc;
mTexture->GetDesc(&desc);
@@ -67,12 +67,21 @@ SourceSurfaceD2DTarget::GetDataSurface()
HRESULT hr = Factory::GetDirect3D10Device()->CreateTexture2D(&desc, nullptr, byRef(dataSurf->mTexture));
if (FAILED(hr)) {
- gfxDebug() << "Failed to create staging texture for SourceSurface. Code: " << hr;
+ gfxDebug() << "Failed to create staging texture for SourceSurface. Code: " << hexa(hr);
return nullptr;
}
Factory::GetDirect3D10Device()->CopyResource(dataSurf->mTexture, mTexture);
- return dataSurf;
+ return dataSurf.forget();
+}
+
+void*
+SourceSurfaceD2DTarget::GetNativeSurface(NativeSurfaceType aType)
+{
+ if (aType == NativeSurfaceType::D3D10_TEXTURE) {
+ return static_cast<void*>(mTexture.get());
+ }
+ return nullptr;
}
ID3D10ShaderResourceView*
@@ -85,7 +94,7 @@ SourceSurfaceD2DTarget::GetSRView()
HRESULT hr = Factory::GetDirect3D10Device()->CreateShaderResourceView(mTexture, nullptr, byRef(mSRView));
if (FAILED(hr)) {
- gfxWarning() << "Failed to create ShaderResourceView. Code: " << hr;
+ gfxWarning() << "Failed to create ShaderResourceView. Code: " << hexa(hr);
}
return mSRView;
@@ -134,20 +143,23 @@ SourceSurfaceD2DTarget::GetBitmap(ID2D1RenderTarget *aRT)
hr = mTexture->QueryInterface((IDXGISurface**)byRef(surf));
if (FAILED(hr)) {
- gfxWarning() << "Failed to query interface texture to DXGISurface. Code: " << hr;
+ gfxWarning() << "Failed to query interface texture to DXGISurface. Code: " << hexa(hr);
return nullptr;
}
- D2D1_BITMAP_PROPERTIES props =
- D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(mFormat), AlphaMode(mFormat)));
+ D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(mFormat));
hr = aRT->CreateSharedBitmap(IID_IDXGISurface, surf, &props, byRef(mBitmap));
if (FAILED(hr)) {
- // This seems to happen for FORMAT_A8 sometimes...
- aRT->CreateBitmap(D2D1::SizeU(desc.Width, desc.Height),
- D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(mFormat),
- AlphaMode(mFormat))),
- byRef(mBitmap));
+ // This seems to happen for SurfaceFormat::A8 sometimes...
+ hr = aRT->CreateBitmap(D2D1::SizeU(desc.Width, desc.Height),
+ D2D1::BitmapProperties(D2DPixelFormat(mFormat)),
+ byRef(mBitmap));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed in CreateBitmap. Code: " << hexa(hr);
+ return nullptr;
+ }
RefPtr<ID2D1RenderTarget> rt;
@@ -169,7 +181,7 @@ SourceSurfaceD2DTarget::GetBitmap(ID2D1RenderTarget *aRT)
}
D2D1_RENDER_TARGET_PROPERTIES props =
- D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGIFormat(mFormat), AlphaMode(mFormat)));
+ D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2DPixelFormat(mFormat));
hr = DrawTargetD2D::factory()->CreateDxgiSurfaceRenderTarget(surface, props, byRef(rt));
if (FAILED(hr)) {
@@ -195,8 +207,8 @@ SourceSurfaceD2DTarget::MarkIndependent()
}
}
-DataSourceSurfaceD2DTarget::DataSourceSurfaceD2DTarget()
- : mFormat(FORMAT_B8G8R8A8)
+DataSourceSurfaceD2DTarget::DataSourceSurfaceD2DTarget(SurfaceFormat aFormat)
+ : mFormat(aFormat)
, mMapped(false)
{
}
@@ -238,13 +250,61 @@ DataSourceSurfaceD2DTarget::Stride()
return mMap.RowPitch;
}
+bool
+DataSourceSurfaceD2DTarget::Map(MapType aMapType, MappedSurface *aMappedSurface)
+{
+ // DataSourceSurfaces used with the new Map API should not be used with GetData!!
+ MOZ_ASSERT(!mMapped);
+ MOZ_ASSERT(!mIsMapped);
+
+ if (!mTexture) {
+ return false;
+ }
+
+ D3D10_MAP mapType;
+
+ if (aMapType == MapType::READ) {
+ mapType = D3D10_MAP_READ;
+ } else if (aMapType == MapType::WRITE) {
+ mapType = D3D10_MAP_WRITE;
+ } else {
+ mapType = D3D10_MAP_READ_WRITE;
+ }
+
+ D3D10_MAPPED_TEXTURE2D map;
+
+ HRESULT hr = mTexture->Map(0, mapType, 0, &map);
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Texture map failed with code: " << hexa(hr);
+ return false;
+ }
+
+ aMappedSurface->mData = (uint8_t*)map.pData;
+ aMappedSurface->mStride = map.RowPitch;
+ mIsMapped = !!aMappedSurface->mData;
+
+ return mIsMapped;
+}
+
+void
+DataSourceSurfaceD2DTarget::Unmap()
+{
+ MOZ_ASSERT(mIsMapped);
+
+ mIsMapped = false;
+ mTexture->Unmap(0);
+}
+
void
DataSourceSurfaceD2DTarget::EnsureMapped()
{
+ // Do not use GetData() after having used Map!
+ MOZ_ASSERT(!mIsMapped);
if (!mMapped) {
HRESULT hr = mTexture->Map(0, D3D10_MAP_READ, 0, &mMap);
if (FAILED(hr)) {
- gfxWarning() << "Failed to map texture to memory. Code: " << hr;
+ gfxWarning() << "Failed to map texture to memory. Code: " << hexa(hr);
return;
}
mMapped = true;
diff --git a/gfx/2d/SourceSurfaceD2DTarget.h b/gfx/2d/SourceSurfaceD2DTarget.h
index 2be02df97..a66505047 100644
--- a/gfx/2d/SourceSurfaceD2DTarget.h
+++ b/gfx/2d/SourceSurfaceD2DTarget.h
@@ -19,17 +19,23 @@ class DrawTargetD2D;
class SourceSurfaceD2DTarget : public SourceSurface
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceD2DTarget)
SourceSurfaceD2DTarget(DrawTargetD2D* aDrawTarget, ID3D10Texture2D* aTexture,
SurfaceFormat aFormat);
~SourceSurfaceD2DTarget();
- virtual SurfaceType GetType() const { return SURFACE_D2D1_DRAWTARGET; }
+ virtual SurfaceType GetType() const { return SurfaceType::D2D1_DRAWTARGET; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
virtual TemporaryRef<DataSourceSurface> GetDataSurface();
+ virtual void *GetNativeSurface(NativeSurfaceType aType);
+
+ DrawTargetD2D* GetDT() { return mDrawTarget; }
+ ID2D1Bitmap *GetBitmap(ID2D1RenderTarget *aRT);
private:
friend class DrawTargetD2D;
+
ID3D10ShaderResourceView *GetSRView();
// This function is called by the draw target this texture belongs to when
@@ -41,8 +47,6 @@ private:
// this may happen on destruction or copying.
void MarkIndependent();
- ID2D1Bitmap *GetBitmap(ID2D1RenderTarget *aRT);
-
RefPtr<ID3D10ShaderResourceView> mSRView;
RefPtr<ID2D1Bitmap> mBitmap;
// Non-null if this is a "lazy copy" of the given draw target.
@@ -58,14 +62,17 @@ private:
class DataSourceSurfaceD2DTarget : public DataSourceSurface
{
public:
- DataSourceSurfaceD2DTarget();
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceD2DTarget)
+ DataSourceSurfaceD2DTarget(SurfaceFormat aFormat);
~DataSourceSurfaceD2DTarget();
- virtual SurfaceType GetType() const { return SURFACE_DATA; }
+ virtual SurfaceType GetType() const { return SurfaceType::DATA; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
virtual uint8_t *GetData();
virtual int32_t Stride();
+ virtual bool Map(MapType, MappedSurface *aMappedSurface);
+ virtual void Unmap();
private:
friend class SourceSurfaceD2DTarget;
diff --git a/gfx/2d/SourceSurfaceDual.h b/gfx/2d/SourceSurfaceDual.h
index 0b783cbb4..aaa85dcc8 100644
--- a/gfx/2d/SourceSurfaceDual.h
+++ b/gfx/2d/SourceSurfaceDual.h
@@ -17,12 +17,13 @@ class DualPattern;
class SourceSurfaceDual : public SourceSurface
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceDual)
SourceSurfaceDual(DrawTarget *aDTA, DrawTarget *aDTB)
: mA(aDTA->Snapshot())
, mB(aDTB->Snapshot())
{ }
- virtual SurfaceType GetType() const { return SURFACE_DUAL_DT; }
+ virtual SurfaceType GetType() const { return SurfaceType::DUAL_DT; }
virtual IntSize GetSize() const { return mA->GetSize(); }
virtual SurfaceFormat GetFormat() const { return mA->GetFormat(); }
diff --git a/gfx/2d/SourceSurfaceRawData.cpp b/gfx/2d/SourceSurfaceRawData.cpp
index 7f775e5c8..901a5f567 100644
--- a/gfx/2d/SourceSurfaceRawData.cpp
+++ b/gfx/2d/SourceSurfaceRawData.cpp
@@ -4,8 +4,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SourceSurfaceRawData.h"
+
+#include "DataSurfaceHelpers.h"
#include "Logging.h"
-#include "Tools.h"
+#include "mozilla/Types.h" // for decltype
namespace mozilla {
namespace gfx {
@@ -26,5 +28,64 @@ SourceSurfaceRawData::InitWrappingData(uint8_t *aData,
return true;
}
+void
+SourceSurfaceRawData::GuaranteePersistance()
+{
+ if (mOwnData) {
+ return;
+ }
+
+ uint8_t* oldData = mRawData;
+ mRawData = new uint8_t[mStride * mSize.height];
+
+ memcpy(mRawData, oldData, mStride * mSize.height);
+ mOwnData = true;
+}
+
+bool
+SourceSurfaceAlignedRawData::Init(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ bool aZero)
+{
+ mFormat = aFormat;
+ mStride = GetAlignedStride<16>(aSize.width * BytesPerPixel(aFormat));
+
+ size_t bufLen = BufferSizeFromStrideAndHeight(mStride, aSize.height);
+ if (bufLen > 0) {
+ static_assert(sizeof(decltype(mArray[0])) == 1,
+ "mArray.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen");
+ mArray.Realloc(/* actually an object count */ bufLen, aZero);
+ mSize = aSize;
+ } else {
+ mArray.Dealloc();
+ mSize.SizeTo(0, 0);
+ }
+
+ return mArray != nullptr;
+}
+
+bool
+SourceSurfaceAlignedRawData::InitWithStride(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ int32_t aStride,
+ bool aZero)
+{
+ mFormat = aFormat;
+ mStride = aStride;
+
+ size_t bufLen = BufferSizeFromStrideAndHeight(mStride, aSize.height);
+ if (bufLen > 0) {
+ static_assert(sizeof(decltype(mArray[0])) == 1,
+ "mArray.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen");
+ mArray.Realloc(/* actually an object count */ bufLen, aZero);
+ mSize = aSize;
+ } else {
+ mArray.Dealloc();
+ mSize.SizeTo(0, 0);
+ }
+
+ return mArray != nullptr;
+}
+
}
}
diff --git a/gfx/2d/SourceSurfaceRawData.h b/gfx/2d/SourceSurfaceRawData.h
index 4b7a33fb3..13a84e5d0 100644
--- a/gfx/2d/SourceSurfaceRawData.h
+++ b/gfx/2d/SourceSurfaceRawData.h
@@ -7,6 +7,7 @@
#define MOZILLA_GFX_SOURCESURFACERAWDATA_H_
#include "2D.h"
+#include "Tools.h"
namespace mozilla {
namespace gfx {
@@ -14,13 +15,14 @@ namespace gfx {
class SourceSurfaceRawData : public DataSourceSurface
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceRawData)
SourceSurfaceRawData() {}
~SourceSurfaceRawData() { if(mOwnData) delete [] mRawData; }
virtual uint8_t *GetData() { return mRawData; }
virtual int32_t Stride() { return mStride; }
- virtual SurfaceType GetType() const { return SURFACE_DATA; }
+ virtual SurfaceType GetType() const { return SurfaceType::DATA; }
virtual IntSize GetSize() const { return mSize; }
virtual SurfaceFormat GetFormat() const { return mFormat; }
@@ -30,6 +32,8 @@ public:
SurfaceFormat aFormat,
bool aOwnData);
+ virtual void GuaranteePersistance();
+
private:
uint8_t *mRawData;
int32_t mStride;
@@ -38,6 +42,34 @@ private:
bool mOwnData;
};
+class SourceSurfaceAlignedRawData : public DataSourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceAlignedRawData)
+ SourceSurfaceAlignedRawData() {}
+
+ virtual uint8_t *GetData() { return mArray; }
+ virtual int32_t Stride() { return mStride; }
+
+ virtual SurfaceType GetType() const { return SurfaceType::DATA; }
+ virtual IntSize GetSize() const { return mSize; }
+ virtual SurfaceFormat GetFormat() const { return mFormat; }
+
+ bool Init(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ bool aZero);
+ bool InitWithStride(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ int32_t aStride,
+ bool aZero);
+
+private:
+ AlignedArray<uint8_t> mArray;
+ int32_t mStride;
+ SurfaceFormat mFormat;
+ IntSize mSize;
+};
+
}
}
diff --git a/gfx/2d/SourceSurfaceSkia.cpp b/gfx/2d/SourceSurfaceSkia.cpp
index fcee181cc..d7e071448 100644
--- a/gfx/2d/SourceSurfaceSkia.cpp
+++ b/gfx/2d/SourceSurfaceSkia.cpp
@@ -10,6 +10,8 @@
#include "skia/SkDevice.h"
#include "HelpersSkia.h"
#include "DrawTargetSkia.h"
+#include "DataSurfaceHelpers.h"
+#include "skia/SkGrPixelRef.h"
namespace mozilla {
namespace gfx {
@@ -22,7 +24,10 @@ SourceSurfaceSkia::SourceSurfaceSkia()
SourceSurfaceSkia::~SourceSurfaceSkia()
{
MaybeUnlock();
- MarkIndependent();
+ if (mDrawTarget) {
+ mDrawTarget->SnapshotDestroyed();
+ mDrawTarget = nullptr;
+ }
}
IntSize
@@ -45,6 +50,7 @@ SourceSurfaceSkia::InitFromCanvas(SkCanvas* aCanvas,
SkISize size = aCanvas->getDeviceSize();
mBitmap = (SkBitmap)aCanvas->getDevice()->accessBitmap(false);
+
mFormat = aFormat;
mSize = IntSize(size.fWidth, size.fHeight);
@@ -61,25 +67,55 @@ SourceSurfaceSkia::InitFromData(unsigned char* aData,
SurfaceFormat aFormat)
{
SkBitmap temp;
- temp.setConfig(GfxFormatToSkiaConfig(aFormat), aSize.width, aSize.height, aStride);
+ SkAlphaType alphaType = (aFormat == SurfaceFormat::B8G8R8X8) ?
+ kOpaque_SkAlphaType : kPremul_SkAlphaType;
+
+ SkImageInfo info = SkImageInfo::Make(aSize.width,
+ aSize.height,
+ GfxFormatToSkiaColorType(aFormat),
+ alphaType);
+ temp.setInfo(info, aStride);
temp.setPixels(aData);
- if (!temp.copyTo(&mBitmap, GfxFormatToSkiaConfig(aFormat))) {
+ if (!temp.copyTo(&mBitmap, GfxFormatToSkiaColorType(aFormat))) {
return false;
}
- if (aFormat == FORMAT_B8G8R8X8) {
- mBitmap.lockPixels();
- // We have to manually set the A channel to be 255 as Skia doesn't understand BGRX
- ConvertBGRXToBGRA(reinterpret_cast<unsigned char*>(mBitmap.getPixels()), aSize, aStride);
- mBitmap.unlockPixels();
- mBitmap.notifyPixelsChanged();
- mBitmap.setIsOpaque(true);
+ if (aFormat == SurfaceFormat::B8G8R8X8) {
+ mBitmap.setAlphaType(kIgnore_SkAlphaType);
}
mSize = aSize;
mFormat = aFormat;
- mStride = aStride;
+ mStride = mBitmap.rowBytes();
+ return true;
+}
+
+bool
+SourceSurfaceSkia::InitFromTexture(DrawTargetSkia* aOwner,
+ unsigned int aTexture,
+ const IntSize &aSize,
+ SurfaceFormat aFormat)
+{
+ MOZ_ASSERT(aOwner, "null GrContext");
+ GrBackendTextureDesc skiaTexGlue;
+ mSize.width = skiaTexGlue.fWidth = aSize.width;
+ mSize.height = skiaTexGlue.fHeight = aSize.height;
+ skiaTexGlue.fFlags = kNone_GrBackendTextureFlag;
+ skiaTexGlue.fOrigin = kBottomLeft_GrSurfaceOrigin;
+ skiaTexGlue.fConfig = GfxFormatToGrConfig(aFormat);
+ skiaTexGlue.fSampleCnt = 0;
+ skiaTexGlue.fTextureHandle = aTexture;
+
+ GrTexture *skiaTexture = aOwner->mGrContext->wrapBackendTexture(skiaTexGlue);
+ SkImageInfo imgInfo = SkImageInfo::Make(aSize.width, aSize.height, GfxFormatToSkiaColorType(aFormat), kOpaque_SkAlphaType);
+ SkGrPixelRef *texRef = new SkGrPixelRef(imgInfo, skiaTexture, false);
+ mBitmap.setInfo(imgInfo);
+ mBitmap.setPixelRef(texRef);
+ mFormat = aFormat;
+ mStride = mBitmap.rowBytes();
+
+ mDrawTarget = aOwner;
return true;
}
@@ -104,22 +140,7 @@ SourceSurfaceSkia::DrawTargetWillChange()
mDrawTarget = nullptr;
SkBitmap temp = mBitmap;
mBitmap.reset();
- temp.copyTo(&mBitmap, temp.getConfig());
- }
-}
-
-void
-SourceSurfaceSkia::DrawTargetDestroyed()
-{
- mDrawTarget = nullptr;
-}
-
-void
-SourceSurfaceSkia::MarkIndependent()
-{
- if (mDrawTarget) {
- mDrawTarget->RemoveSnapshot(this);
- mDrawTarget = nullptr;
+ temp.copyTo(&mBitmap, temp.colorType());
}
}
diff --git a/gfx/2d/SourceSurfaceSkia.h b/gfx/2d/SourceSurfaceSkia.h
index 0e8f7fab6..de57602b4 100644
--- a/gfx/2d/SourceSurfaceSkia.h
+++ b/gfx/2d/SourceSurfaceSkia.h
@@ -11,7 +11,10 @@
#include "skia/SkCanvas.h"
#include "skia/SkBitmap.h"
+class GrContext;
+
namespace mozilla {
+
namespace gfx {
class DrawTargetSkia;
@@ -19,10 +22,11 @@ class DrawTargetSkia;
class SourceSurfaceSkia : public DataSourceSurface
{
public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceSkia)
SourceSurfaceSkia();
~SourceSurfaceSkia();
- virtual SurfaceType GetType() const { return SURFACE_SKIA; }
+ virtual SurfaceType GetType() const { return SurfaceType::SKIA; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
@@ -37,6 +41,15 @@ public:
SurfaceFormat aFormat,
DrawTargetSkia* aOwner);
+ /**
+ * NOTE: While wrapping a Texture for SkiaGL, the texture *must* be created
+ * with the same GLcontext of DrawTargetSkia
+ */
+ bool InitFromTexture(DrawTargetSkia* aOwner,
+ unsigned int aTexture,
+ const IntSize &aSize,
+ SurfaceFormat aFormat);
+
virtual unsigned char *GetData();
virtual int32_t Stride() { return mStride; }
@@ -45,15 +58,13 @@ private:
friend class DrawTargetSkia;
void DrawTargetWillChange();
- void DrawTargetDestroyed();
- void MarkIndependent();
void MaybeUnlock();
SkBitmap mBitmap;
SurfaceFormat mFormat;
IntSize mSize;
int32_t mStride;
- DrawTargetSkia* mDrawTarget;
+ RefPtr<DrawTargetSkia> mDrawTarget;
bool mLocked;
};
diff --git a/gfx/2d/StackArray.h b/gfx/2d/StackArray.h
new file mode 100644
index 000000000..e3c2684a0
--- /dev/null
+++ b/gfx/2d/StackArray.h
@@ -0,0 +1,30 @@
+/* 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/. */
+
+/* A handy class that will allocate data for size*T objects on the stack and
+ * otherwise allocate them on the heap. It is similar in purpose to nsAutoTArray */
+
+template <class T, size_t size>
+class StackArray
+{
+public:
+ StackArray(size_t count) {
+ if (count > size) {
+ mData = new T[count];
+ } else {
+ mData = mStackData;
+ }
+ }
+ ~StackArray() {
+ if (mData != mStackData) {
+ delete[] mData;
+ }
+ }
+ T& operator[](size_t n) { return mData[n]; }
+ const T& operator[](size_t n) const { return mData[n]; }
+ T* data() { return mData; };
+private:
+ T mStackData[size];
+ T* mData;
+};
diff --git a/gfx/2d/Tools.h b/gfx/2d/Tools.h
index 0aca4eddf..d7405c75b 100644
--- a/gfx/2d/Tools.h
+++ b/gfx/2d/Tools.h
@@ -6,6 +6,9 @@
#ifndef MOZILLA_GFX_TOOLS_H_
#define MOZILLA_GFX_TOOLS_H_
+#include "mozilla/CheckedInt.h"
+#include "mozilla/Move.h"
+#include "mozilla/TypeTraits.h"
#include "Types.h"
#include "Point.h"
#include <math.h>
@@ -19,11 +22,11 @@ namespace gfx {
static inline bool
IsOperatorBoundByMask(CompositionOp aOp) {
switch (aOp) {
- case OP_IN:
- case OP_OUT:
- case OP_DEST_IN:
- case OP_DEST_ATOP:
- case OP_SOURCE:
+ case CompositionOp::OP_IN:
+ case CompositionOp::OP_OUT:
+ case CompositionOp::OP_DEST_IN:
+ case CompositionOp::OP_DEST_ATOP:
+ case CompositionOp::OP_SOURCE:
return false;
default:
return true;
@@ -62,6 +65,15 @@ NudgeToInteger(float *aVal)
}
}
+static inline void
+NudgeToInteger(float *aVal, float aErr)
+{
+ float r = floorf(*aVal + 0.5f);
+ if (FuzzyEqual(r, *aVal, aErr)) {
+ *aVal = r;
+ }
+}
+
static inline Float
Distance(Point aA, Point aB)
{
@@ -72,9 +84,9 @@ static inline int
BytesPerPixel(SurfaceFormat aFormat)
{
switch (aFormat) {
- case FORMAT_A8:
+ case SurfaceFormat::A8:
return 1;
- case FORMAT_R5G6B5:
+ case SurfaceFormat::R5G6B5:
return 2;
default:
return 4;
@@ -84,40 +96,94 @@ BytesPerPixel(SurfaceFormat aFormat)
template<typename T, int alignment = 16>
struct AlignedArray
{
+ typedef T value_type;
+
AlignedArray()
- : mStorage(nullptr)
- , mPtr(nullptr)
+ : mPtr(nullptr)
+ , mStorage(nullptr)
{
}
- MOZ_ALWAYS_INLINE AlignedArray(size_t aSize)
+ explicit MOZ_ALWAYS_INLINE AlignedArray(size_t aCount, bool aZero = false)
: mStorage(nullptr)
+ , mCount(0)
{
- Realloc(aSize);
+ Realloc(aCount, aZero);
}
MOZ_ALWAYS_INLINE ~AlignedArray()
{
- delete [] mStorage;
+ Dealloc();
}
void Dealloc()
{
- delete [] mStorage;
- mStorage = mPtr = nullptr;
+ // If we fail this assert we'll need to uncomment the loop below to make
+ // sure dtors are properly invoked. If we do that, we should check that the
+ // comment about compiler dead code elimination is in fact true for all the
+ // compilers that we care about.
+ static_assert(mozilla::IsPod<T>::value,
+ "Destructors must be invoked for this type");
+#if 0
+ for (size_t i = 0; i < mCount; ++i) {
+ // Since we used the placement |operator new| function to construct the
+ // elements of this array we need to invoke their destructors manually.
+ // For types where the destructor does nothing the compiler's dead code
+ // elimination step should optimize this loop away.
+ mPtr[i].~T();
+ }
+#endif
+
+ moz_free(mStorage);
+ mStorage = nullptr;
+ mPtr = nullptr;
}
- MOZ_ALWAYS_INLINE void Realloc(size_t aSize)
+ MOZ_ALWAYS_INLINE void Realloc(size_t aCount, bool aZero = false)
{
- delete [] mStorage;
- mStorage = new (std::nothrow) T[aSize + (alignment - 1)];
+ moz_free(mStorage);
+ CheckedInt32 storageByteCount =
+ CheckedInt32(sizeof(T)) * aCount + (alignment - 1);
+ if (!storageByteCount.isValid()) {
+ mStorage = nullptr;
+ mPtr = nullptr;
+ mCount = 0;
+ return;
+ }
+ // We don't create an array of T here, since we don't want ctors to be
+ // invoked at the wrong places if we realign below.
+ if (aZero) {
+ // calloc can be more efficient than new[] for large chunks,
+ // so we use calloc/malloc/free for everything.
+ mStorage = static_cast<uint8_t *>(moz_calloc(1, storageByteCount.value()));
+ } else {
+ mStorage = static_cast<uint8_t *>(moz_malloc(storageByteCount.value()));
+ }
+ if (!mStorage) {
+ mStorage = nullptr;
+ mPtr = nullptr;
+ mCount = 0;
+ return;
+ }
if (uintptr_t(mStorage) % alignment) {
- // Our storage does not start at a <alignment>-byte boundary. Make sure mData does!
- mPtr = (uint32_t*)(uintptr_t(mStorage) +
- (alignment - (uintptr_t(mStorage) % alignment)));
+ // Our storage does not start at a <alignment>-byte boundary. Make sure mPtr does!
+ mPtr = (T*)(uintptr_t(mStorage) + alignment - (uintptr_t(mStorage) % alignment));
} else {
- mPtr = mStorage;
+ mPtr = (T*)(mStorage);
}
+ // Now that mPtr is pointing to the aligned position we can use placement
+ // |operator new| to invoke any ctors at the correct positions. For types
+ // that have a no-op default constructor the compiler's dead code
+ // elimination step should optimize this away.
+ mPtr = new (mPtr) T[aCount];
+ mCount = aCount;
+ }
+
+ void Swap(AlignedArray<T, alignment>& aOther)
+ {
+ mozilla::Swap(mPtr, aOther.mPtr);
+ mozilla::Swap(mStorage, aOther.mStorage);
+ mozilla::Swap(mCount, aOther.mCount);
}
MOZ_ALWAYS_INLINE operator T*()
@@ -125,18 +191,28 @@ struct AlignedArray
return mPtr;
}
- T *mStorage;
T *mPtr;
+
+private:
+ uint8_t *mStorage;
+ size_t mCount;
};
+/**
+ * Returns aStride increased, if necessary, so that it divides exactly into
+ * |alignment|.
+ *
+ * Note that currently |alignment| must be a power-of-2. If for some reason we
+ * want to support NPOT alignment we can revert back to this functions old
+ * implementation.
+ */
template<int alignment>
int32_t GetAlignedStride(int32_t aStride)
{
- if (aStride % alignment) {
- return aStride + (alignment - (aStride % alignment));
- }
-
- return aStride;
+ static_assert(alignment > 0 && (alignment & (alignment-1)) == 0,
+ "This implementation currently require power-of-two alignment");
+ const int32_t mask = alignment - 1;
+ return (aStride + mask) & ~mask;
}
}
diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h
index 8a3a19586..29cf37e0b 100644
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -6,99 +6,219 @@
#ifndef MOZILLA_GFX_TYPES_H_
#define MOZILLA_GFX_TYPES_H_
-#include "mozilla/StandardInteger.h"
-#include "mozilla/NullPtr.h"
-
#include <stddef.h>
+#include <stdint.h>
namespace mozilla {
namespace gfx {
typedef float Float;
-enum SurfaceType
-{
- SURFACE_DATA, /* Data surface - bitmap in memory */
- SURFACE_D2D1_BITMAP, /* Surface wrapping a ID2D1Bitmap */
- SURFACE_D2D1_DRAWTARGET, /* Surface made from a D2D draw target */
- SURFACE_CAIRO, /* Surface wrapping a cairo surface */
- SURFACE_CAIRO_IMAGE, /* Data surface wrapping a cairo image surface */
- SURFACE_COREGRAPHICS_IMAGE, /* Surface wrapping a CoreGraphics Image */
- SURFACE_COREGRAPHICS_CGCONTEXT, /* Surface wrapping a CG context */
- SURFACE_SKIA, /* Surface wrapping a Skia bitmap */
- SURFACE_DUAL_DT, /* Snapshot of a dual drawtarget */
- SURFACE_RECORDING /* Surface used for recording */
-};
-
-enum SurfaceFormat
-{
- FORMAT_B8G8R8A8,
- FORMAT_B8G8R8X8,
- FORMAT_R8G8B8A8,
- FORMAT_R8G8B8X8,
- FORMAT_R5G6B5,
- FORMAT_A8,
- FORMAT_YUV,
- FORMAT_UNKNOWN
+enum class SurfaceType : int8_t {
+ DATA, /* Data surface - bitmap in memory */
+ D2D1_BITMAP, /* Surface wrapping a ID2D1Bitmap */
+ D2D1_DRAWTARGET, /* Surface made from a D2D draw target */
+ CAIRO, /* Surface wrapping a cairo surface */
+ CAIRO_IMAGE, /* Data surface wrapping a cairo image surface */
+ COREGRAPHICS_IMAGE, /* Surface wrapping a CoreGraphics Image */
+ COREGRAPHICS_CGCONTEXT, /* Surface wrapping a CG context */
+ SKIA, /* Surface wrapping a Skia bitmap */
+ DUAL_DT, /* Snapshot of a dual drawtarget */
+ D2D1_1_IMAGE, /* A D2D 1.1 ID2D1Image SourceSurface */
+ RECORDING, /* Surface used for recording */
+ TILED /* Surface from a tiled DrawTarget */
};
-enum BackendType
-{
- BACKEND_NONE = 0,
- BACKEND_DIRECT2D,
- BACKEND_COREGRAPHICS,
- BACKEND_COREGRAPHICS_ACCELERATED,
- BACKEND_CAIRO,
- BACKEND_SKIA,
- BACKEND_RECORDING
+enum class SurfaceFormat : int8_t {
+ B8G8R8A8,
+ B8G8R8X8,
+ R8G8B8A8,
+ R8G8B8X8,
+ R5G6B5,
+ A8,
+ YUV,
+ UNKNOWN
};
-enum FontType
+inline bool IsOpaque(SurfaceFormat aFormat)
{
- FONT_DWRITE,
- FONT_GDI,
- FONT_MAC,
- FONT_SKIA,
- FONT_CAIRO,
- FONT_COREGRAPHICS
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8X8:
+ case SurfaceFormat::R8G8B8X8:
+ case SurfaceFormat::R5G6B5:
+ case SurfaceFormat::YUV:
+ return true;
+ default:
+ return false;
+ }
+}
+
+enum class FilterType : int8_t {
+ BLEND = 0,
+ TRANSFORM,
+ MORPHOLOGY,
+ COLOR_MATRIX,
+ FLOOD,
+ TILE,
+ TABLE_TRANSFER,
+ DISCRETE_TRANSFER,
+ LINEAR_TRANSFER,
+ GAMMA_TRANSFER,
+ CONVOLVE_MATRIX,
+ DISPLACEMENT_MAP,
+ TURBULENCE,
+ ARITHMETIC_COMBINE,
+ COMPOSITE,
+ DIRECTIONAL_BLUR,
+ GAUSSIAN_BLUR,
+ POINT_DIFFUSE,
+ POINT_SPECULAR,
+ SPOT_DIFFUSE,
+ SPOT_SPECULAR,
+ DISTANT_DIFFUSE,
+ DISTANT_SPECULAR,
+ CROP,
+ PREMULTIPLY,
+ UNPREMULTIPLY
};
-enum NativeSurfaceType
-{
- NATIVE_SURFACE_D3D10_TEXTURE,
- NATIVE_SURFACE_CAIRO_SURFACE,
- NATIVE_SURFACE_CGCONTEXT,
- NATIVE_SURFACE_CGCONTEXT_ACCELERATED
+enum class DrawTargetType : int8_t {
+ SOFTWARE_RASTER = 0,
+ HARDWARE_RASTER,
+ VECTOR
};
-enum NativeFontType
-{
- NATIVE_FONT_DWRITE_FONT_FACE,
- NATIVE_FONT_GDI_FONT_FACE,
- NATIVE_FONT_MAC_FONT_FACE,
- NATIVE_FONT_SKIA_FONT_FACE,
- NATIVE_FONT_CAIRO_FONT_FACE
+enum class BackendType : int8_t {
+ NONE = 0,
+ DIRECT2D,
+ COREGRAPHICS,
+ COREGRAPHICS_ACCELERATED,
+ CAIRO,
+ SKIA,
+ RECORDING,
+ DIRECT2D1_1
};
-enum FontStyle
-{
- FONT_STYLE_NORMAL,
- FONT_STYLE_ITALIC,
- FONT_STYLE_BOLD,
- FONT_STYLE_BOLD_ITALIC
-};
-
-enum CompositionOp { OP_OVER, OP_ADD, OP_ATOP, OP_OUT, OP_IN, OP_SOURCE, OP_DEST_IN, OP_DEST_OUT, OP_DEST_OVER, OP_DEST_ATOP, OP_XOR,
- OP_MULTIPLY, OP_SCREEN, OP_OVERLAY, OP_DARKEN, OP_LIGHTEN, OP_COLOR_DODGE, OP_COLOR_BURN, OP_HARD_LIGHT, OP_SOFT_LIGHT, OP_DIFFERENCE, OP_EXCLUSION, OP_HUE, OP_SATURATION, OP_COLOR, OP_LUMINOSITY, OP_COUNT };
-enum ExtendMode { EXTEND_CLAMP, EXTEND_REPEAT, EXTEND_REFLECT };
-enum FillRule { FILL_WINDING, FILL_EVEN_ODD };
-enum AntialiasMode { AA_NONE, AA_GRAY, AA_SUBPIXEL, AA_DEFAULT };
-enum Snapping { SNAP_NONE, SNAP_ALIGNED };
-enum Filter { FILTER_LINEAR, FILTER_POINT };
-enum PatternType { PATTERN_COLOR, PATTERN_SURFACE, PATTERN_LINEAR_GRADIENT, PATTERN_RADIAL_GRADIENT };
-enum JoinStyle { JOIN_BEVEL, JOIN_ROUND, JOIN_MITER, JOIN_MITER_OR_BEVEL };
-enum CapStyle { CAP_BUTT, CAP_ROUND, CAP_SQUARE };
-enum SamplingBounds { SAMPLING_UNBOUNDED, SAMPLING_BOUNDED };
+enum class FontType : int8_t {
+ DWRITE,
+ GDI,
+ MAC,
+ SKIA,
+ CAIRO,
+ COREGRAPHICS
+};
+
+enum class NativeSurfaceType : int8_t {
+ D3D10_TEXTURE,
+ CAIRO_SURFACE,
+ CAIRO_CONTEXT,
+ CGCONTEXT,
+ CGCONTEXT_ACCELERATED,
+ OPENGL_TEXTURE
+};
+
+enum class NativeFontType : int8_t {
+ DWRITE_FONT_FACE,
+ GDI_FONT_FACE,
+ MAC_FONT_FACE,
+ SKIA_FONT_FACE,
+ CAIRO_FONT_FACE
+};
+
+enum class FontStyle : int8_t {
+ NORMAL,
+ ITALIC,
+ BOLD,
+ BOLD_ITALIC
+};
+
+enum class FontHinting : int8_t {
+ NONE,
+ LIGHT,
+ NORMAL,
+ FULL
+};
+
+enum class CompositionOp : int8_t {
+ OP_OVER,
+ OP_ADD,
+ OP_ATOP,
+ OP_OUT,
+ OP_IN,
+ OP_SOURCE,
+ OP_DEST_IN,
+ OP_DEST_OUT,
+ OP_DEST_OVER,
+ OP_DEST_ATOP,
+ OP_XOR,
+ OP_MULTIPLY,
+ OP_SCREEN,
+ OP_OVERLAY,
+ OP_DARKEN,
+ OP_LIGHTEN,
+ OP_COLOR_DODGE,
+ OP_COLOR_BURN,
+ OP_HARD_LIGHT,
+ OP_SOFT_LIGHT,
+ OP_DIFFERENCE,
+ OP_EXCLUSION,
+ OP_HUE,
+ OP_SATURATION,
+ OP_COLOR,
+ OP_LUMINOSITY,
+ OP_COUNT
+};
+
+enum class ExtendMode : int8_t {
+ CLAMP,
+ REPEAT,
+ REFLECT
+};
+
+enum class FillRule : int8_t {
+ FILL_WINDING,
+ FILL_EVEN_ODD
+};
+
+enum class AntialiasMode : int8_t {
+ NONE,
+ GRAY,
+ SUBPIXEL,
+ DEFAULT
+};
+
+enum class Filter : int8_t {
+ GOOD,
+ LINEAR,
+ POINT
+};
+
+enum class PatternType : int8_t {
+ COLOR,
+ SURFACE,
+ LINEAR_GRADIENT,
+ RADIAL_GRADIENT
+};
+
+enum class JoinStyle : int8_t {
+ BEVEL,
+ ROUND,
+ MITER, //!< Mitered if within the miter limit, else, if the backed supports
+ //!< it (D2D), the miter is clamped. If the backend does not support
+ //!< miter clamping the behavior is as for MITER_OR_BEVEL.
+ MITER_OR_BEVEL //!< Mitered if within the miter limit, else beveled.
+};
+
+enum class CapStyle : int8_t {
+ BUTT,
+ ROUND,
+ SQUARE
+};
+
+enum class SamplingBounds : int8_t {
+ UNBOUNDED,
+ BOUNDED
+};
/* Color is stored in non-premultiplied form */
struct Color
@@ -124,12 +244,28 @@ public:
return newColor;
}
+ static Color FromARGB(uint32_t aColor)
+ {
+ Color newColor(((aColor >> 16) & 0xff) * (1.0f / 255.0f),
+ ((aColor >> 8) & 0xff) * (1.0f / 255.0f),
+ ((aColor >> 0) & 0xff) * (1.0f / 255.0f),
+ ((aColor >> 24) & 0xff) * (1.0f / 255.0f));
+
+ return newColor;
+ }
+
uint32_t ToABGR() const
{
return uint32_t(r * 255.0f) | uint32_t(g * 255.0f) << 8 |
uint32_t(b * 255.0f) << 16 | uint32_t(a * 255.0f) << 24;
}
+ uint32_t ToARGB() const
+ {
+ return uint32_t(b * 255.0f) | uint32_t(g * 255.0f) << 8 |
+ uint32_t(r * 255.0f) << 16 | uint32_t(a * 255.0f) << 24;
+ }
+
Float r, g, b, a;
};
@@ -156,17 +292,41 @@ struct GradientStop
#define GFX2D_API
#endif
-// Side constants for use in various places
namespace mozilla {
- namespace css {
- enum Side {eSideTop, eSideRight, eSideBottom, eSideLeft};
- }
-}
-// XXX - These don't really belong here. But for now this is where they go.
-#define NS_SIDE_TOP mozilla::css::eSideTop
-#define NS_SIDE_RIGHT mozilla::css::eSideRight
-#define NS_SIDE_BOTTOM mozilla::css::eSideBottom
-#define NS_SIDE_LEFT mozilla::css::eSideLeft
+// We can't use MOZ_BEGIN_ENUM_CLASS here because that prevents the enum
+// values from being used for indexing. Wrapping the enum in a struct does at
+// least gives us name scoping.
+struct RectCorner {
+ enum {
+ // This order is important since Rect::AtCorner, AppendRoundedRectToPath
+ // and other code depends on it!
+ TopLeft = 0,
+ TopRight = 1,
+ BottomRight = 2,
+ BottomLeft = 3,
+ Count = 4
+ };
+};
+
+// Side constants for use in various places.
+enum Side { eSideTop, eSideRight, eSideBottom, eSideLeft };
+
+enum SideBits {
+ eSideBitsNone = 0,
+ eSideBitsTop = 1 << eSideTop,
+ eSideBitsRight = 1 << eSideRight,
+ eSideBitsBottom = 1 << eSideBottom,
+ eSideBitsLeft = 1 << eSideLeft,
+ eSideBitsTopBottom = eSideBitsTop | eSideBitsBottom,
+ eSideBitsLeftRight = eSideBitsLeft | eSideBitsRight,
+ eSideBitsAll = eSideBitsTopBottom | eSideBitsLeftRight
+};
+} // namespace mozilla
+
+#define NS_SIDE_TOP mozilla::eSideTop
+#define NS_SIDE_RIGHT mozilla::eSideRight
+#define NS_SIDE_BOTTOM mozilla::eSideBottom
+#define NS_SIDE_LEFT mozilla::eSideLeft
#endif /* MOZILLA_GFX_TYPES_H_ */
diff --git a/gfx/2d/UserData.h b/gfx/2d/UserData.h
index c9a22534b..e803dc04e 100644
--- a/gfx/2d/UserData.h
+++ b/gfx/2d/UserData.h
@@ -73,7 +73,7 @@ public:
}
/* Retrives the userData for the associated key */
- void *Get(UserDataKey *key)
+ void *Get(UserDataKey *key) const
{
for (int i=0; i<count; i++) {
if (key == entries[i].key) {
diff --git a/gfx/2d/convolver.cpp b/gfx/2d/convolver.cpp
index 948ce73ea..f1480aaa4 100644
--- a/gfx/2d/convolver.cpp
+++ b/gfx/2d/convolver.cpp
@@ -26,19 +26,20 @@
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
+#include "2D.h"
#include "convolver.h"
#include <algorithm>
-#include "nsAlgorithm.h"
#include "skia/SkTypes.h"
-// note: SIMD_SSE2 is not enabled because of bugs, apparently
-#if defined(SIMD_SSE2)
-#include <emmintrin.h> // ARCH_CPU_X86_FAMILY was defined in build/config.h
+#if defined(USE_SSE2)
+#include "convolverSSE2.h"
#endif
+using mozilla::gfx::Factory;
+
#if defined(SK_CPU_LENDIAN)
#define R_OFFSET_IDX 0
#define G_OFFSET_IDX 1
@@ -152,15 +153,17 @@ class CircularRowBuffer {
std::vector<unsigned char*> row_addresses_;
};
+} // namespace
+
// Convolves horizontally along a single row. The row data is given in
-// |src_data| and continues for the num_values() of the filter.
+// |src_data| and continues for the [begin, end) of the filter.
template<bool has_alpha>
void ConvolveHorizontally(const unsigned char* src_data,
+ int begin, int end,
const ConvolutionFilter1D& filter,
unsigned char* out_row) {
// Loop over each pixel on this row in the output image.
- int num_values = filter.num_values();
- for (int out_x = 0; out_x < num_values; out_x++) {
+ for (int out_x = begin; out_x < end; out_x++) {
// Get the filter that determines the current output pixel.
int filter_offset, filter_length;
const ConvolutionFilter1D::Fixed* filter_values =
@@ -201,18 +204,17 @@ void ConvolveHorizontally(const unsigned char* src_data,
// Does vertical convolution to produce one output row. The filter values and
// length are given in the first two parameters. These are applied to each
// of the rows pointed to in the |source_data_rows| array, with each row
-// being |pixel_width| wide.
+// being |end - begin| wide.
//
-// The output must have room for |pixel_width * 4| bytes.
+// The output must have room for |(end - begin) * 4| bytes.
template<bool has_alpha>
void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
int filter_length,
unsigned char* const* source_data_rows,
- int pixel_width,
- unsigned char* out_row) {
+ int begin, int end, unsigned char* out_row) {
// We go through each column in the output and do a vertical convolution,
// generating one output pixel each time.
- for (int out_x = 0; out_x < pixel_width; out_x++) {
+ for (int out_x = begin; out_x < end; out_x++) {
// Compute the number of bytes over in each row that the current column
// we're convolving starts at. The pixel will cover the next 4 bytes.
int byte_offset = out_x * 4;
@@ -267,435 +269,61 @@ void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
}
}
-
-// Convolves horizontally along a single row. The row data is given in
-// |src_data| and continues for the num_values() of the filter.
-void ConvolveHorizontally_SSE2(const unsigned char* src_data,
- const ConvolutionFilter1D& filter,
- unsigned char* out_row) {
-#if defined(SIMD_SSE2)
- int num_values = filter.num_values();
-
- int filter_offset, filter_length;
- __m128i zero = _mm_setzero_si128();
- __m128i mask[4];
- // |mask| will be used to decimate all extra filter coefficients that are
- // loaded by SIMD when |filter_length| is not divisible by 4.
- // mask[0] is not used in following algorithm.
- mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1);
- mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1);
- mask[3] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1);
-
- // Output one pixel each iteration, calculating all channels (RGBA) together.
- for (int out_x = 0; out_x < num_values; out_x++) {
- const ConvolutionFilter1D::Fixed* filter_values =
- filter.FilterForValue(out_x, &filter_offset, &filter_length);
-
- __m128i accum = _mm_setzero_si128();
-
- // Compute the first pixel in this row that the filter affects. It will
- // touch |filter_length| pixels (4 bytes each) after this.
- const __m128i* row_to_filter =
- reinterpret_cast<const __m128i*>(&src_data[filter_offset << 2]);
-
- // We will load and accumulate with four coefficients per iteration.
- for (int filter_x = 0; filter_x < filter_length >> 2; filter_x++) {
-
- // Load 4 coefficients => duplicate 1st and 2nd of them for all channels.
- __m128i coeff, coeff16;
- // [16] xx xx xx xx c3 c2 c1 c0
- coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
- // [16] xx xx xx xx c1 c1 c0 c0
- coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
- // [16] c1 c1 c1 c1 c0 c0 c0 c0
- coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
-
- // Load four pixels => unpack the first two pixels to 16 bits =>
- // multiply with coefficients => accumulate the convolution result.
- // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
- __m128i src8 = _mm_loadu_si128(row_to_filter);
- // [16] a1 b1 g1 r1 a0 b0 g0 r0
- __m128i src16 = _mm_unpacklo_epi8(src8, zero);
- __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
- __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
- // [32] a0*c0 b0*c0 g0*c0 r0*c0
- __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
- accum = _mm_add_epi32(accum, t);
- // [32] a1*c1 b1*c1 g1*c1 r1*c1
- t = _mm_unpackhi_epi16(mul_lo, mul_hi);
- accum = _mm_add_epi32(accum, t);
-
- // Duplicate 3rd and 4th coefficients for all channels =>
- // unpack the 3rd and 4th pixels to 16 bits => multiply with coefficients
- // => accumulate the convolution results.
- // [16] xx xx xx xx c3 c3 c2 c2
- coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
- // [16] c3 c3 c3 c3 c2 c2 c2 c2
- coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
- // [16] a3 g3 b3 r3 a2 g2 b2 r2
- src16 = _mm_unpackhi_epi8(src8, zero);
- mul_hi = _mm_mulhi_epi16(src16, coeff16);
- mul_lo = _mm_mullo_epi16(src16, coeff16);
- // [32] a2*c2 b2*c2 g2*c2 r2*c2
- t = _mm_unpacklo_epi16(mul_lo, mul_hi);
- accum = _mm_add_epi32(accum, t);
- // [32] a3*c3 b3*c3 g3*c3 r3*c3
- t = _mm_unpackhi_epi16(mul_lo, mul_hi);
- accum = _mm_add_epi32(accum, t);
-
- // Advance the pixel and coefficients pointers.
- row_to_filter += 1;
- filter_values += 4;
- }
-
- // When |filter_length| is not divisible by 4, we need to decimate some of
- // the filter coefficient that was loaded incorrectly to zero; Other than
- // that the algorithm is same with above, exceot that the 4th pixel will be
- // always absent.
- int r = filter_length&3;
- if (r) {
- // Note: filter_values must be padded to align_up(filter_offset, 8).
- __m128i coeff, coeff16;
- coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
- // Mask out extra filter taps.
- coeff = _mm_and_si128(coeff, mask[r]);
- coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
- coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
-
- // Note: line buffer must be padded to align_up(filter_offset, 16).
- // We resolve this by use C-version for the last horizontal line.
- __m128i src8 = _mm_loadu_si128(row_to_filter);
- __m128i src16 = _mm_unpacklo_epi8(src8, zero);
- __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
- __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
- __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
- accum = _mm_add_epi32(accum, t);
- t = _mm_unpackhi_epi16(mul_lo, mul_hi);
- accum = _mm_add_epi32(accum, t);
-
- src16 = _mm_unpackhi_epi8(src8, zero);
- coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
- coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
- mul_hi = _mm_mulhi_epi16(src16, coeff16);
- mul_lo = _mm_mullo_epi16(src16, coeff16);
- t = _mm_unpacklo_epi16(mul_lo, mul_hi);
- accum = _mm_add_epi32(accum, t);
- }
-
- // Shift right for fixed point implementation.
- accum = _mm_srai_epi32(accum, ConvolutionFilter1D::kShiftBits);
-
- // Packing 32 bits |accum| to 16 bits per channel (signed saturation).
- accum = _mm_packs_epi32(accum, zero);
- // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation).
- accum = _mm_packus_epi16(accum, zero);
-
- // Store the pixel value of 32 bits.
- *(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum);
- out_row += 4;
- }
-#endif
-}
-
-// Convolves horizontally along four rows. The row data is given in
-// |src_data| and continues for the num_values() of the filter.
-// The algorithm is almost same as |ConvolveHorizontally_SSE2|. Please
-// refer to that function for detailed comments.
-void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4],
- const ConvolutionFilter1D& filter,
- unsigned char* out_row[4]) {
-#if defined(SIMD_SSE2)
- int num_values = filter.num_values();
-
- int filter_offset, filter_length;
- __m128i zero = _mm_setzero_si128();
- __m128i mask[4];
- // |mask| will be used to decimate all extra filter coefficients that are
- // loaded by SIMD when |filter_length| is not divisible by 4.
- // mask[0] is not used in following algorithm.
- mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1);
- mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1);
- mask[3] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1);
-
- // Output one pixel each iteration, calculating all channels (RGBA) together.
- for (int out_x = 0; out_x < num_values; out_x++) {
- const ConvolutionFilter1D::Fixed* filter_values =
- filter.FilterForValue(out_x, &filter_offset, &filter_length);
-
- // four pixels in a column per iteration.
- __m128i accum0 = _mm_setzero_si128();
- __m128i accum1 = _mm_setzero_si128();
- __m128i accum2 = _mm_setzero_si128();
- __m128i accum3 = _mm_setzero_si128();
- int start = (filter_offset<<2);
- // We will load and accumulate with four coefficients per iteration.
- for (int filter_x = 0; filter_x < (filter_length >> 2); filter_x++) {
- __m128i coeff, coeff16lo, coeff16hi;
- // [16] xx xx xx xx c3 c2 c1 c0
- coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
- // [16] xx xx xx xx c1 c1 c0 c0
- coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
- // [16] c1 c1 c1 c1 c0 c0 c0 c0
- coeff16lo = _mm_unpacklo_epi16(coeff16lo, coeff16lo);
- // [16] xx xx xx xx c3 c3 c2 c2
- coeff16hi = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
- // [16] c3 c3 c3 c3 c2 c2 c2 c2
- coeff16hi = _mm_unpacklo_epi16(coeff16hi, coeff16hi);
-
- __m128i src8, src16, mul_hi, mul_lo, t;
-
-#define ITERATION(src, accum) \
- src8 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src)); \
- src16 = _mm_unpacklo_epi8(src8, zero); \
- mul_hi = _mm_mulhi_epi16(src16, coeff16lo); \
- mul_lo = _mm_mullo_epi16(src16, coeff16lo); \
- t = _mm_unpacklo_epi16(mul_lo, mul_hi); \
- accum = _mm_add_epi32(accum, t); \
- t = _mm_unpackhi_epi16(mul_lo, mul_hi); \
- accum = _mm_add_epi32(accum, t); \
- src16 = _mm_unpackhi_epi8(src8, zero); \
- mul_hi = _mm_mulhi_epi16(src16, coeff16hi); \
- mul_lo = _mm_mullo_epi16(src16, coeff16hi); \
- t = _mm_unpacklo_epi16(mul_lo, mul_hi); \
- accum = _mm_add_epi32(accum, t); \
- t = _mm_unpackhi_epi16(mul_lo, mul_hi); \
- accum = _mm_add_epi32(accum, t)
-
- ITERATION(src_data[0] + start, accum0);
- ITERATION(src_data[1] + start, accum1);
- ITERATION(src_data[2] + start, accum2);
- ITERATION(src_data[3] + start, accum3);
-
- start += 16;
- filter_values += 4;
- }
-
- int r = filter_length & 3;
- if (r) {
- // Note: filter_values must be padded to align_up(filter_offset, 8);
- __m128i coeff;
- coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
- // Mask out extra filter taps.
- coeff = _mm_and_si128(coeff, mask[r]);
-
- __m128i coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
- /* c1 c1 c1 c1 c0 c0 c0 c0 */
- coeff16lo = _mm_unpacklo_epi16(coeff16lo, coeff16lo);
- __m128i coeff16hi = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
- coeff16hi = _mm_unpacklo_epi16(coeff16hi, coeff16hi);
-
- __m128i src8, src16, mul_hi, mul_lo, t;
-
- ITERATION(src_data[0] + start, accum0);
- ITERATION(src_data[1] + start, accum1);
- ITERATION(src_data[2] + start, accum2);
- ITERATION(src_data[3] + start, accum3);
- }
-
- accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits);
- accum0 = _mm_packs_epi32(accum0, zero);
- accum0 = _mm_packus_epi16(accum0, zero);
- accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits);
- accum1 = _mm_packs_epi32(accum1, zero);
- accum1 = _mm_packus_epi16(accum1, zero);
- accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits);
- accum2 = _mm_packs_epi32(accum2, zero);
- accum2 = _mm_packus_epi16(accum2, zero);
- accum3 = _mm_srai_epi32(accum3, ConvolutionFilter1D::kShiftBits);
- accum3 = _mm_packs_epi32(accum3, zero);
- accum3 = _mm_packus_epi16(accum3, zero);
-
- *(reinterpret_cast<int*>(out_row[0])) = _mm_cvtsi128_si32(accum0);
- *(reinterpret_cast<int*>(out_row[1])) = _mm_cvtsi128_si32(accum1);
- *(reinterpret_cast<int*>(out_row[2])) = _mm_cvtsi128_si32(accum2);
- *(reinterpret_cast<int*>(out_row[3])) = _mm_cvtsi128_si32(accum3);
-
- out_row[0] += 4;
- out_row[1] += 4;
- out_row[2] += 4;
- out_row[3] += 4;
+void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int width, unsigned char* out_row,
+ bool has_alpha, bool use_sse2) {
+ int processed = 0;
+
+#if defined(USE_SSE2)
+ // If the binary was not built with SSE2 support, we had to fallback to C version.
+ int simd_width = width & ~3;
+ if (use_sse2 && simd_width) {
+ ConvolveVertically_SSE2(filter_values, filter_length,
+ source_data_rows, 0, simd_width,
+ out_row, has_alpha);
+ processed = simd_width;
}
#endif
-}
-
-// Does vertical convolution to produce one output row. The filter values and
-// length are given in the first two parameters. These are applied to each
-// of the rows pointed to in the |source_data_rows| array, with each row
-// being |pixel_width| wide.
-//
-// The output must have room for |pixel_width * 4| bytes.
-template<bool has_alpha>
-void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values,
- int filter_length,
- unsigned char* const* source_data_rows,
- int pixel_width,
- unsigned char* out_row) {
-#if defined(SIMD_SSE2)
- int width = pixel_width & ~3;
-
- __m128i zero = _mm_setzero_si128();
- __m128i accum0, accum1, accum2, accum3, coeff16;
- const __m128i* src;
- // Output four pixels per iteration (16 bytes).
- for (int out_x = 0; out_x < width; out_x += 4) {
-
- // Accumulated result for each pixel. 32 bits per RGBA channel.
- accum0 = _mm_setzero_si128();
- accum1 = _mm_setzero_si128();
- accum2 = _mm_setzero_si128();
- accum3 = _mm_setzero_si128();
-
- // Convolve with one filter coefficient per iteration.
- for (int filter_y = 0; filter_y < filter_length; filter_y++) {
-
- // Duplicate the filter coefficient 8 times.
- // [16] cj cj cj cj cj cj cj cj
- coeff16 = _mm_set1_epi16(filter_values[filter_y]);
-
- // Load four pixels (16 bytes) together.
- // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
- src = reinterpret_cast<const __m128i*>(
- &source_data_rows[filter_y][out_x << 2]);
- __m128i src8 = _mm_loadu_si128(src);
-
- // Unpack 1st and 2nd pixels from 8 bits to 16 bits for each channels =>
- // multiply with current coefficient => accumulate the result.
- // [16] a1 b1 g1 r1 a0 b0 g0 r0
- __m128i src16 = _mm_unpacklo_epi8(src8, zero);
- __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
- __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
- // [32] a0 b0 g0 r0
- __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
- accum0 = _mm_add_epi32(accum0, t);
- // [32] a1 b1 g1 r1
- t = _mm_unpackhi_epi16(mul_lo, mul_hi);
- accum1 = _mm_add_epi32(accum1, t);
-
- // Unpack 3rd and 4th pixels from 8 bits to 16 bits for each channels =>
- // multiply with current coefficient => accumulate the result.
- // [16] a3 b3 g3 r3 a2 b2 g2 r2
- src16 = _mm_unpackhi_epi8(src8, zero);
- mul_hi = _mm_mulhi_epi16(src16, coeff16);
- mul_lo = _mm_mullo_epi16(src16, coeff16);
- // [32] a2 b2 g2 r2
- t = _mm_unpacklo_epi16(mul_lo, mul_hi);
- accum2 = _mm_add_epi32(accum2, t);
- // [32] a3 b3 g3 r3
- t = _mm_unpackhi_epi16(mul_lo, mul_hi);
- accum3 = _mm_add_epi32(accum3, t);
- }
-
- // Shift right for fixed point implementation.
- accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits);
- accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits);
- accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits);
- accum3 = _mm_srai_epi32(accum3, ConvolutionFilter1D::kShiftBits);
-
- // Packing 32 bits |accum| to 16 bits per channel (signed saturation).
- // [16] a1 b1 g1 r1 a0 b0 g0 r0
- accum0 = _mm_packs_epi32(accum0, accum1);
- // [16] a3 b3 g3 r3 a2 b2 g2 r2
- accum2 = _mm_packs_epi32(accum2, accum3);
-
- // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation).
- // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
- accum0 = _mm_packus_epi16(accum0, accum2);
-
+
+ if (width > processed) {
if (has_alpha) {
- // Compute the max(ri, gi, bi) for each pixel.
- // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0
- __m128i a = _mm_srli_epi32(accum0, 8);
- // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
- __m128i b = _mm_max_epu8(a, accum0); // Max of r and g.
- // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0
- a = _mm_srli_epi32(accum0, 16);
- // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
- b = _mm_max_epu8(a, b); // Max of r and g and b.
- // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00
- b = _mm_slli_epi32(b, 24);
-
- // Make sure the value of alpha channel is always larger than maximum
- // value of color channels.
- accum0 = _mm_max_epu8(b, accum0);
+ ConvolveVertically<true>(filter_values, filter_length, source_data_rows,
+ processed, width, out_row);
} else {
- // Set value of alpha channels to 0xFF.
- __m128i mask = _mm_set1_epi32(0xff000000);
- accum0 = _mm_or_si128(accum0, mask);
+ ConvolveVertically<false>(filter_values, filter_length, source_data_rows,
+ processed, width, out_row);
}
-
- // Store the convolution result (16 bytes) and advance the pixel pointers.
- _mm_storeu_si128(reinterpret_cast<__m128i*>(out_row), accum0);
- out_row += 16;
}
+}
- // When the width of the output is not divisible by 4, We need to save one
- // pixel (4 bytes) each time. And also the fourth pixel is always absent.
- if (pixel_width & 3) {
- accum0 = _mm_setzero_si128();
- accum1 = _mm_setzero_si128();
- accum2 = _mm_setzero_si128();
- for (int filter_y = 0; filter_y < filter_length; ++filter_y) {
- coeff16 = _mm_set1_epi16(filter_values[filter_y]);
- // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
- src = reinterpret_cast<const __m128i*>(
- &source_data_rows[filter_y][width<<2]);
- __m128i src8 = _mm_loadu_si128(src);
- // [16] a1 b1 g1 r1 a0 b0 g0 r0
- __m128i src16 = _mm_unpacklo_epi8(src8, zero);
- __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
- __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
- // [32] a0 b0 g0 r0
- __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
- accum0 = _mm_add_epi32(accum0, t);
- // [32] a1 b1 g1 r1
- t = _mm_unpackhi_epi16(mul_lo, mul_hi);
- accum1 = _mm_add_epi32(accum1, t);
- // [16] a3 b3 g3 r3 a2 b2 g2 r2
- src16 = _mm_unpackhi_epi8(src8, zero);
- mul_hi = _mm_mulhi_epi16(src16, coeff16);
- mul_lo = _mm_mullo_epi16(src16, coeff16);
- // [32] a2 b2 g2 r2
- t = _mm_unpacklo_epi16(mul_lo, mul_hi);
- accum2 = _mm_add_epi32(accum2, t);
- }
+void ConvolveHorizontally(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row,
+ bool has_alpha, bool use_sse2) {
+ int width = filter.num_values();
+ int processed = 0;
+#if defined(USE_SSE2)
+ int simd_width = width & ~3;
+ if (use_sse2 && simd_width) {
+ // SIMD implementation works with 4 pixels at a time.
+ // Therefore we process as much as we can using SSE and then use
+ // C implementation for leftovers
+ ConvolveHorizontally_SSE2(src_data, 0, simd_width, filter, out_row);
+ processed = simd_width;
+ }
+#endif
- accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits);
- accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits);
- accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits);
- // [16] a1 b1 g1 r1 a0 b0 g0 r0
- accum0 = _mm_packs_epi32(accum0, accum1);
- // [16] a3 b3 g3 r3 a2 b2 g2 r2
- accum2 = _mm_packs_epi32(accum2, zero);
- // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
- accum0 = _mm_packus_epi16(accum0, accum2);
+ if (width > processed) {
if (has_alpha) {
- // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0
- __m128i a = _mm_srli_epi32(accum0, 8);
- // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
- __m128i b = _mm_max_epu8(a, accum0); // Max of r and g.
- // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0
- a = _mm_srli_epi32(accum0, 16);
- // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
- b = _mm_max_epu8(a, b); // Max of r and g and b.
- // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00
- b = _mm_slli_epi32(b, 24);
- accum0 = _mm_max_epu8(b, accum0);
+ ConvolveHorizontally<true>(src_data, processed, width, filter, out_row);
} else {
- __m128i mask = _mm_set1_epi32(0xff000000);
- accum0 = _mm_or_si128(accum0, mask);
- }
-
- for (int out_x = width; out_x < pixel_width; out_x++) {
- *(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum0);
- accum0 = _mm_srli_si128(accum0, 4);
- out_row += 4;
+ ConvolveHorizontally<false>(src_data, processed, width, filter, out_row);
}
}
-#endif
}
-} // namespace
-
// ConvolutionFilter1D ---------------------------------------------------------
ConvolutionFilter1D::ConvolutionFilter1D()
@@ -765,14 +393,16 @@ void BGRAConvolve2D(const unsigned char* source_data,
const ConvolutionFilter1D& filter_x,
const ConvolutionFilter1D& filter_y,
int output_byte_row_stride,
- unsigned char* output,
- bool use_sse2) {
-#if !defined(SIMD_SSE2)
+ unsigned char* output) {
+ bool use_sse2 = Factory::HasSSE2();
+
+#if !defined(USE_SSE2)
// Even we have runtime support for SSE2 instructions, since the binary
// was not built with SSE2 support, we had to fallback to C version.
use_sse2 = false;
#endif
+
int max_y_filter_size = filter_y.max_filter();
// The next row in the input that we will generate a horizontally
@@ -805,6 +435,7 @@ void BGRAConvolve2D(const unsigned char* source_data,
// convolutions to run each subsequent vertical convolution.
SkASSERT(output_byte_row_stride >= filter_x.num_values() * 4);
int num_output_rows = filter_y.num_values();
+ int pixel_width = filter_x.num_values();
// We need to check which is the last line to convolve before we advance 4
// lines in one iteration.
@@ -818,47 +449,56 @@ void BGRAConvolve2D(const unsigned char* source_data,
// Generate output rows until we have enough to run the current filter.
if (use_sse2) {
+#if defined(USE_SSE2)
+ // We don't want to process too much rows in batches of 4 because
+ // we can go out-of-bounds at the end
while (next_x_row < filter_offset + filter_length) {
- if (next_x_row + 3 < last_filter_offset + last_filter_length - 1) {
+ if (next_x_row + 3 < last_filter_offset + last_filter_length - 3) {
const unsigned char* src[4];
unsigned char* out_row[4];
for (int i = 0; i < 4; ++i) {
src[i] = &source_data[(next_x_row + i) * source_byte_row_stride];
out_row[i] = row_buffer.AdvanceRow();
}
- ConvolveHorizontally4_SSE2(src, filter_x, out_row);
+ ConvolveHorizontally4_SSE2(src, 0, pixel_width, filter_x, out_row);
next_x_row += 4;
} else {
- // For the last row, SSE2 load possibly to access data beyond the
- // image area. therefore we use C version here.
- if (next_x_row == last_filter_offset + last_filter_length - 1) {
+ unsigned char* buffer = row_buffer.AdvanceRow();
+
+ // For last rows, SSE2 load possibly to access data beyond the
+ // image area. therefore we use cobined C+SSE version here
+ int simd_width = pixel_width & ~3;
+ if (simd_width) {
+ ConvolveHorizontally_SSE2(
+ &source_data[next_x_row * source_byte_row_stride],
+ 0, simd_width, filter_x, buffer);
+ }
+
+ if (pixel_width > simd_width) {
if (source_has_alpha) {
ConvolveHorizontally<true>(
&source_data[next_x_row * source_byte_row_stride],
- filter_x, row_buffer.AdvanceRow());
+ simd_width, pixel_width, filter_x, buffer);
} else {
ConvolveHorizontally<false>(
&source_data[next_x_row * source_byte_row_stride],
- filter_x, row_buffer.AdvanceRow());
+ simd_width, pixel_width, filter_x, buffer);
}
- } else {
- ConvolveHorizontally_SSE2(
- &source_data[next_x_row * source_byte_row_stride],
- filter_x, row_buffer.AdvanceRow());
}
next_x_row++;
}
}
+#endif
} else {
while (next_x_row < filter_offset + filter_length) {
if (source_has_alpha) {
ConvolveHorizontally<true>(
&source_data[next_x_row * source_byte_row_stride],
- filter_x, row_buffer.AdvanceRow());
+ 0, pixel_width, filter_x, row_buffer.AdvanceRow());
} else {
ConvolveHorizontally<false>(
&source_data[next_x_row * source_byte_row_stride],
- filter_x, row_buffer.AdvanceRow());
+ 0, pixel_width, filter_x, row_buffer.AdvanceRow());
}
next_x_row++;
}
@@ -877,27 +517,9 @@ void BGRAConvolve2D(const unsigned char* source_data,
unsigned char* const* first_row_for_filter =
&rows_to_convolve[filter_offset - first_row_in_circular_buffer];
- if (source_has_alpha) {
- if (use_sse2) {
- ConvolveVertically_SSE2<true>(filter_values, filter_length,
- first_row_for_filter,
- filter_x.num_values(), cur_output_row);
- } else {
- ConvolveVertically<true>(filter_values, filter_length,
- first_row_for_filter,
- filter_x.num_values(), cur_output_row);
- }
- } else {
- if (use_sse2) {
- ConvolveVertically_SSE2<false>(filter_values, filter_length,
- first_row_for_filter,
- filter_x.num_values(), cur_output_row);
- } else {
- ConvolveVertically<false>(filter_values, filter_length,
- first_row_for_filter,
- filter_x.num_values(), cur_output_row);
- }
- }
+ ConvolveVertically(filter_values, filter_length,
+ first_row_for_filter, pixel_width,
+ cur_output_row, source_has_alpha, use_sse2);
}
}
diff --git a/gfx/2d/convolver.h b/gfx/2d/convolver.h
index d7e83789e..6ef882f1c 100644
--- a/gfx/2d/convolver.h
+++ b/gfx/2d/convolver.h
@@ -74,8 +74,8 @@ class ConvolutionFilter1D {
// The cast relies on Fixed being a short, implying that on
// the platforms we care about all (16) bits will fit into
// the mantissa of a (32-bit) float.
- MOZ_STATIC_ASSERT(sizeof(Fixed) == 2,
- "fixed type should fit in float mantissa");
+ static_assert(sizeof(Fixed) == 2,
+ "fixed type should fit in float mantissa");
float raw = static_cast<float>(x);
return ldexpf(raw, -kShiftBits);
}
@@ -184,8 +184,19 @@ void BGRAConvolve2D(const unsigned char* source_data,
const ConvolutionFilter1D& xfilter,
const ConvolutionFilter1D& yfilter,
int output_byte_row_stride,
- unsigned char* output,
- bool use_sse2);
+ unsigned char* output);
+
+void ConvolveHorizontally(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row,
+ bool has_alpha, bool use_sse2);
+
+void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width, unsigned char* out_row,
+ bool has_alpha, bool use_sse2);
+
} // namespace skia
#endif // SKIA_EXT_CONVOLVER_H_
diff --git a/gfx/2d/convolverSSE2.cpp b/gfx/2d/convolverSSE2.cpp
new file mode 100644
index 000000000..4a177f1de
--- /dev/null
+++ b/gfx/2d/convolverSSE2.cpp
@@ -0,0 +1,476 @@
+// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in
+// the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google, Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this
+// software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+#include "convolver.h"
+#include <algorithm>
+#include "skia/SkTypes.h"
+
+#include <emmintrin.h> // ARCH_CPU_X86_FAMILY was defined in build/config.h
+
+namespace skia {
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+void ConvolveHorizontally_SSE2(const unsigned char* src_data,
+ int begin, int end,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row) {
+
+ int filter_offset, filter_length;
+ __m128i zero = _mm_setzero_si128();
+ __m128i mask[3];
+ // |mask| will be used to decimate all extra filter coefficients that are
+ // loaded by SIMD when |filter_length| is not divisible by 4.
+ mask[0] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1);
+ mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1);
+ mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1);
+
+ // This buffer is used for tails
+ __m128i buffer;
+
+ // Output one pixel each iteration, calculating all channels (RGBA) together.
+ for (int out_x = begin; out_x < end; out_x++) {
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+ __m128i accum = _mm_setzero_si128();
+
+ // Compute the first pixel in this row that the filter affects. It will
+ // touch |filter_length| pixels (4 bytes each) after this.
+ const __m128i* row_to_filter =
+ reinterpret_cast<const __m128i*>(&src_data[filter_offset << 2]);
+
+ // We will load and accumulate with four coefficients per iteration.
+ for (int filter_x = 0; filter_x < filter_length >> 2; filter_x++) {
+
+ // Load 4 coefficients => duplicate 1st and 2nd of them for all channels.
+ __m128i coeff, coeff16;
+ // [16] xx xx xx xx c3 c2 c1 c0
+ coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
+ // [16] xx xx xx xx c1 c1 c0 c0
+ coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
+ // [16] c1 c1 c1 c1 c0 c0 c0 c0
+ coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
+
+ // Load four pixels => unpack the first two pixels to 16 bits =>
+ // multiply with coefficients => accumulate the convolution result.
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ __m128i src8 = _mm_loadu_si128(row_to_filter);
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ __m128i src16 = _mm_unpacklo_epi8(src8, zero);
+ __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a0*c0 b0*c0 g0*c0 r0*c0
+ __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+ // [32] a1*c1 b1*c1 g1*c1 r1*c1
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+
+ // Duplicate 3rd and 4th coefficients for all channels =>
+ // unpack the 3rd and 4th pixels to 16 bits => multiply with coefficients
+ // => accumulate the convolution results.
+ // [16] xx xx xx xx c3 c3 c2 c2
+ coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
+ // [16] c3 c3 c3 c3 c2 c2 c2 c2
+ coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
+ // [16] a3 g3 b3 r3 a2 g2 b2 r2
+ src16 = _mm_unpackhi_epi8(src8, zero);
+ mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a2*c2 b2*c2 g2*c2 r2*c2
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+ // [32] a3*c3 b3*c3 g3*c3 r3*c3
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+
+ // Advance the pixel and coefficients pointers.
+ row_to_filter += 1;
+ filter_values += 4;
+ }
+
+ // When |filter_length| is not divisible by 4, we need to decimate some of
+ // the filter coefficient that was loaded incorrectly to zero; Other than
+ // that the algorithm is same with above, except that the 4th pixel will be
+ // always absent.
+ int r = filter_length & 3;
+ if (r) {
+ memcpy(&buffer, row_to_filter, r * 4);
+ // Note: filter_values must be padded to align_up(filter_offset, 8).
+ __m128i coeff, coeff16;
+ coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
+ // Mask out extra filter taps.
+ coeff = _mm_and_si128(coeff, mask[r-1]);
+ coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
+ coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
+
+ // Note: line buffer must be padded to align_up(filter_offset, 16).
+ // We resolve this by temporary buffer
+ __m128i src8 = _mm_loadu_si128(&buffer);
+ __m128i src16 = _mm_unpacklo_epi8(src8, zero);
+ __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
+ __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+
+ src16 = _mm_unpackhi_epi8(src8, zero);
+ coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
+ coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
+ mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ mul_lo = _mm_mullo_epi16(src16, coeff16);
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+ }
+
+ // Shift right for fixed point implementation.
+ accum = _mm_srai_epi32(accum, ConvolutionFilter1D::kShiftBits);
+
+ // Packing 32 bits |accum| to 16 bits per channel (signed saturation).
+ accum = _mm_packs_epi32(accum, zero);
+ // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation).
+ accum = _mm_packus_epi16(accum, zero);
+
+ // Store the pixel value of 32 bits.
+ *(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum);
+ out_row += 4;
+ }
+}
+
+// Convolves horizontally along four rows. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+// The algorithm is almost same as |ConvolveHorizontally_SSE2|. Please
+// refer to that function for detailed comments.
+void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4],
+ int begin, int end,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row[4]) {
+ int filter_offset, filter_length;
+ __m128i zero = _mm_setzero_si128();
+ __m128i mask[3];
+ // |mask| will be used to decimate all extra filter coefficients that are
+ // loaded by SIMD when |filter_length| is not divisible by 4.
+ mask[0] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1);
+ mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1);
+ mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1);
+
+ // Output one pixel each iteration, calculating all channels (RGBA) together.
+ for (int out_x = begin; out_x < end; out_x++) {
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+ // four pixels in a column per iteration.
+ __m128i accum0 = _mm_setzero_si128();
+ __m128i accum1 = _mm_setzero_si128();
+ __m128i accum2 = _mm_setzero_si128();
+ __m128i accum3 = _mm_setzero_si128();
+ int start = (filter_offset<<2);
+ // We will load and accumulate with four coefficients per iteration.
+ for (int filter_x = 0; filter_x < (filter_length >> 2); filter_x++) {
+ __m128i coeff, coeff16lo, coeff16hi;
+ // [16] xx xx xx xx c3 c2 c1 c0
+ coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
+ // [16] xx xx xx xx c1 c1 c0 c0
+ coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
+ // [16] c1 c1 c1 c1 c0 c0 c0 c0
+ coeff16lo = _mm_unpacklo_epi16(coeff16lo, coeff16lo);
+ // [16] xx xx xx xx c3 c3 c2 c2
+ coeff16hi = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
+ // [16] c3 c3 c3 c3 c2 c2 c2 c2
+ coeff16hi = _mm_unpacklo_epi16(coeff16hi, coeff16hi);
+
+ __m128i src8, src16, mul_hi, mul_lo, t;
+
+#define ITERATION(src, accum) \
+ src8 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src)); \
+ src16 = _mm_unpacklo_epi8(src8, zero); \
+ mul_hi = _mm_mulhi_epi16(src16, coeff16lo); \
+ mul_lo = _mm_mullo_epi16(src16, coeff16lo); \
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi); \
+ accum = _mm_add_epi32(accum, t); \
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi); \
+ accum = _mm_add_epi32(accum, t); \
+ src16 = _mm_unpackhi_epi8(src8, zero); \
+ mul_hi = _mm_mulhi_epi16(src16, coeff16hi); \
+ mul_lo = _mm_mullo_epi16(src16, coeff16hi); \
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi); \
+ accum = _mm_add_epi32(accum, t); \
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi); \
+ accum = _mm_add_epi32(accum, t)
+
+ ITERATION(src_data[0] + start, accum0);
+ ITERATION(src_data[1] + start, accum1);
+ ITERATION(src_data[2] + start, accum2);
+ ITERATION(src_data[3] + start, accum3);
+
+ start += 16;
+ filter_values += 4;
+ }
+
+ int r = filter_length & 3;
+ if (r) {
+ // Note: filter_values must be padded to align_up(filter_offset, 8);
+ __m128i coeff;
+ coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
+ // Mask out extra filter taps.
+ coeff = _mm_and_si128(coeff, mask[r-1]);
+
+ __m128i coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
+ /* c1 c1 c1 c1 c0 c0 c0 c0 */
+ coeff16lo = _mm_unpacklo_epi16(coeff16lo, coeff16lo);
+ __m128i coeff16hi = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
+ coeff16hi = _mm_unpacklo_epi16(coeff16hi, coeff16hi);
+
+ __m128i src8, src16, mul_hi, mul_lo, t;
+
+ ITERATION(src_data[0] + start, accum0);
+ ITERATION(src_data[1] + start, accum1);
+ ITERATION(src_data[2] + start, accum2);
+ ITERATION(src_data[3] + start, accum3);
+ }
+
+ accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits);
+ accum0 = _mm_packs_epi32(accum0, zero);
+ accum0 = _mm_packus_epi16(accum0, zero);
+ accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits);
+ accum1 = _mm_packs_epi32(accum1, zero);
+ accum1 = _mm_packus_epi16(accum1, zero);
+ accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits);
+ accum2 = _mm_packs_epi32(accum2, zero);
+ accum2 = _mm_packus_epi16(accum2, zero);
+ accum3 = _mm_srai_epi32(accum3, ConvolutionFilter1D::kShiftBits);
+ accum3 = _mm_packs_epi32(accum3, zero);
+ accum3 = _mm_packus_epi16(accum3, zero);
+
+ *(reinterpret_cast<int*>(out_row[0])) = _mm_cvtsi128_si32(accum0);
+ *(reinterpret_cast<int*>(out_row[1])) = _mm_cvtsi128_si32(accum1);
+ *(reinterpret_cast<int*>(out_row[2])) = _mm_cvtsi128_si32(accum2);
+ *(reinterpret_cast<int*>(out_row[3])) = _mm_cvtsi128_si32(accum3);
+
+ out_row[0] += 4;
+ out_row[1] += 4;
+ out_row[2] += 4;
+ out_row[3] += 4;
+ }
+}
+
+// Does vertical convolution to produce one output row. The filter values and
+// length are given in the first two parameters. These are applied to each
+// of the rows pointed to in the |source_data_rows| array, with each row
+// being |end - begin| wide.
+//
+// The output must have room for |(end - begin) * 4| bytes.
+template<bool has_alpha>
+void ConvolveVertically_SSE2_impl(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int begin, int end,
+ unsigned char* out_row) {
+ __m128i zero = _mm_setzero_si128();
+ __m128i accum0, accum1, accum2, accum3, coeff16;
+ const __m128i* src;
+ int out_x;
+ // Output four pixels per iteration (16 bytes).
+ for (out_x = begin; out_x + 3 < end; out_x += 4) {
+
+ // Accumulated result for each pixel. 32 bits per RGBA channel.
+ accum0 = _mm_setzero_si128();
+ accum1 = _mm_setzero_si128();
+ accum2 = _mm_setzero_si128();
+ accum3 = _mm_setzero_si128();
+
+ // Convolve with one filter coefficient per iteration.
+ for (int filter_y = 0; filter_y < filter_length; filter_y++) {
+
+ // Duplicate the filter coefficient 8 times.
+ // [16] cj cj cj cj cj cj cj cj
+ coeff16 = _mm_set1_epi16(filter_values[filter_y]);
+
+ // Load four pixels (16 bytes) together.
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ src = reinterpret_cast<const __m128i*>(
+ &source_data_rows[filter_y][out_x << 2]);
+ __m128i src8 = _mm_loadu_si128(src);
+
+ // Unpack 1st and 2nd pixels from 8 bits to 16 bits for each channels =>
+ // multiply with current coefficient => accumulate the result.
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ __m128i src16 = _mm_unpacklo_epi8(src8, zero);
+ __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a0 b0 g0 r0
+ __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum0 = _mm_add_epi32(accum0, t);
+ // [32] a1 b1 g1 r1
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum1 = _mm_add_epi32(accum1, t);
+
+ // Unpack 3rd and 4th pixels from 8 bits to 16 bits for each channels =>
+ // multiply with current coefficient => accumulate the result.
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ src16 = _mm_unpackhi_epi8(src8, zero);
+ mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a2 b2 g2 r2
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum2 = _mm_add_epi32(accum2, t);
+ // [32] a3 b3 g3 r3
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum3 = _mm_add_epi32(accum3, t);
+ }
+
+ // Shift right for fixed point implementation.
+ accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits);
+ accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits);
+ accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits);
+ accum3 = _mm_srai_epi32(accum3, ConvolutionFilter1D::kShiftBits);
+
+ // Packing 32 bits |accum| to 16 bits per channel (signed saturation).
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ accum0 = _mm_packs_epi32(accum0, accum1);
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ accum2 = _mm_packs_epi32(accum2, accum3);
+
+ // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation).
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ accum0 = _mm_packus_epi16(accum0, accum2);
+
+ if (has_alpha) {
+ // Compute the max(ri, gi, bi) for each pixel.
+ // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0
+ __m128i a = _mm_srli_epi32(accum0, 8);
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ __m128i b = _mm_max_epu8(a, accum0); // Max of r and g.
+ // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0
+ a = _mm_srli_epi32(accum0, 16);
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ b = _mm_max_epu8(a, b); // Max of r and g and b.
+ // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00
+ b = _mm_slli_epi32(b, 24);
+
+ // Make sure the value of alpha channel is always larger than maximum
+ // value of color channels.
+ accum0 = _mm_max_epu8(b, accum0);
+ } else {
+ // Set value of alpha channels to 0xFF.
+ __m128i mask = _mm_set1_epi32(0xff000000);
+ accum0 = _mm_or_si128(accum0, mask);
+ }
+
+ // Store the convolution result (16 bytes) and advance the pixel pointers.
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(out_row), accum0);
+ out_row += 16;
+ }
+
+ // When the width of the output is not divisible by 4, We need to save one
+ // pixel (4 bytes) each time. And also the fourth pixel is always absent.
+ int r = end - out_x;
+ if (r > 0) {
+ // Since accum3 is never used here, we'll use it as a buffer
+ __m128i *buffer = &accum3;
+
+ accum0 = _mm_setzero_si128();
+ accum1 = _mm_setzero_si128();
+ accum2 = _mm_setzero_si128();
+ for (int filter_y = 0; filter_y < filter_length; ++filter_y) {
+ coeff16 = _mm_set1_epi16(filter_values[filter_y]);
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ src = reinterpret_cast<const __m128i*>(
+ &source_data_rows[filter_y][out_x * 4]);
+ memcpy(buffer, src, r * 4);
+ __m128i src8 = _mm_loadu_si128(buffer);
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ __m128i src16 = _mm_unpacklo_epi8(src8, zero);
+ __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a0 b0 g0 r0
+ __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum0 = _mm_add_epi32(accum0, t);
+ // [32] a1 b1 g1 r1
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum1 = _mm_add_epi32(accum1, t);
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ src16 = _mm_unpackhi_epi8(src8, zero);
+ mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a2 b2 g2 r2
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum2 = _mm_add_epi32(accum2, t);
+ }
+
+ accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits);
+ accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits);
+ accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits);
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ accum0 = _mm_packs_epi32(accum0, accum1);
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ accum2 = _mm_packs_epi32(accum2, zero);
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ accum0 = _mm_packus_epi16(accum0, accum2);
+ if (has_alpha) {
+ // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0
+ __m128i a = _mm_srli_epi32(accum0, 8);
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ __m128i b = _mm_max_epu8(a, accum0); // Max of r and g.
+ // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0
+ a = _mm_srli_epi32(accum0, 16);
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ b = _mm_max_epu8(a, b); // Max of r and g and b.
+ // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00
+ b = _mm_slli_epi32(b, 24);
+ accum0 = _mm_max_epu8(b, accum0);
+ } else {
+ __m128i mask = _mm_set1_epi32(0xff000000);
+ accum0 = _mm_or_si128(accum0, mask);
+ }
+
+ for (; out_x < end; out_x++) {
+ *(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum0);
+ accum0 = _mm_srli_si128(accum0, 4);
+ out_row += 4;
+ }
+ }
+}
+
+void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int begin, int end,
+ unsigned char* out_row, bool has_alpha) {
+ if (has_alpha) {
+ ConvolveVertically_SSE2_impl<true>(filter_values, filter_length,
+ source_data_rows, begin, end, out_row);
+ } else {
+ ConvolveVertically_SSE2_impl<false>(filter_values, filter_length,
+ source_data_rows, begin, end, out_row);
+ }
+}
+
+} // namespace skia
diff --git a/gfx/2d/convolverSSE2.h b/gfx/2d/convolverSSE2.h
new file mode 100644
index 000000000..d1ba5d698
--- /dev/null
+++ b/gfx/2d/convolverSSE2.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in
+// the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google, Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this
+// software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+#ifndef SKIA_EXT_CONVOLVER_SSE_H_
+#define SKIA_EXT_CONVOLVER_SSE_H_
+
+#include "convolver.h"
+
+#include <algorithm>
+
+#include "skia/SkTypes.h"
+
+namespace skia {
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+void ConvolveHorizontally_SSE2(const unsigned char* src_data,
+ int begin, int end,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row);
+
+// Convolves horizontally along four rows. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+// The algorithm is almost same as |ConvolveHorizontally_SSE2|. Please
+// refer to that function for detailed comments.
+void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4],
+ int begin, int end,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row[4]);
+
+// Does vertical convolution to produce one output row. The filter values and
+// length are given in the first two parameters. These are applied to each
+// of the rows pointed to in the |source_data_rows| array, with each row
+// being |pixel_width| wide.
+//
+// The output must have room for |pixel_width * 4| bytes.
+void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int begin, int end,
+ unsigned char* out_row, bool has_alpha);
+
+} // namespace skia
+
+#endif // SKIA_EXT_CONVOLVER_SSE_H_
diff --git a/gfx/2d/genshaders.sh b/gfx/2d/genshaders.sh
index 5c399b822..74b1a3d7b 100755
--- a/gfx/2d/genshaders.sh
+++ b/gfx/2d/genshaders.sh
@@ -3,8 +3,8 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
fxc ShadersD2D.fx -nologo -FhShadersD2D.h -Tfx_4_0 -Vn d2deffect
-fxc ShadersD2D1.hlsl -ESampleRadialGradientPS -nologo -Tps_4_0_level_9_3 -Fhtmpfile -VnSampleRadialGradientPS
-cat tmpfile > ShadersD2D1.h
-fxc ShadersD2D1.hlsl -ESampleRadialGradientA0PS -nologo -Tps_4_0_level_9_3 -Fhtmpfile -VnSampleRadialGradientA0PS
-cat tmpfile >> ShadersD2D1.h
-rm tmpfile
+fxc ShadersD2D1.hlsl -ESampleRadialGradientPS -nologo -Tps_4_0_level_9_3 -Fhtmpfile -VnSampleRadialGradientPS
+cat tmpfile > ShadersD2D1.h
+fxc ShadersD2D1.hlsl -ESampleRadialGradientA0PS -nologo -Tps_4_0_level_9_3 -Fhtmpfile -VnSampleRadialGradientA0PS
+cat tmpfile >> ShadersD2D1.h
+rm tmpfile
diff --git a/gfx/2d/gfx2d.vcxproj b/gfx/2d/gfx2d.vcxproj
index dfd0a1f1b..746856a10 100644
--- a/gfx/2d/gfx2d.vcxproj
+++ b/gfx/2d/gfx2d.vcxproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
@@ -42,7 +42,7 @@
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
- <PreprocessorDefinitions>INITGUID;USE_SSE2;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);GFX_LOG_DEBUG;GFX_LOG_WARNING;MFBT_STAND_ALONE;XP_WIN</PreprocessorDefinitions>
+ <PreprocessorDefinitions>USE_SSE2;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);GFX_LOG_DEBUG;GFX_LOG_WARNING;MFBT_STAND_ALONE;XP_WIN</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
@@ -62,7 +62,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
- <PreprocessorDefinitions>INITGUID;USE_SSE2;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>USE_SSE2;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);GFX_LOG_WARNING</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
@@ -92,6 +92,7 @@
<ClInclude Include="Logging.h" />
<ClInclude Include="Matrix.h" />
<ClInclude Include="PathD2D.h" />
+ <ClInclude Include="PathHelpers.h" />
<ClInclude Include="PathRecording.h" />
<ClInclude Include="Point.h" />
<ClInclude Include="RecordedEvent.h" />
@@ -135,4 +136,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/gfx/2d/image_operations.cpp b/gfx/2d/image_operations.cpp
index 532be2d98..59861bb9b 100644
--- a/gfx/2d/image_operations.cpp
+++ b/gfx/2d/image_operations.cpp
@@ -35,7 +35,6 @@
#include "image_operations.h"
-#include "nsAlgorithm.h"
#include "base/stack_container.h"
#include "convolver.h"
#include "skia/SkColorPriv.h"
@@ -45,184 +44,7 @@
namespace skia {
-namespace {
-
-// Returns the ceiling/floor as an integer.
-inline int CeilInt(float val) {
- return static_cast<int>(ceil(val));
-}
-inline int FloorInt(float val) {
- return static_cast<int>(floor(val));
-}
-
-// Filter function computation -------------------------------------------------
-
-// Evaluates the box filter, which goes from -0.5 to +0.5.
-float EvalBox(float x) {
- return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f;
-}
-
-// Evaluates the Lanczos filter of the given filter size window for the given
-// position.
-//
-// |filter_size| is the width of the filter (the "window"), outside of which
-// the value of the function is 0. Inside of the window, the value is the
-// normalized sinc function:
-// lanczos(x) = sinc(x) * sinc(x / filter_size);
-// where
-// sinc(x) = sin(pi*x) / (pi*x);
-float EvalLanczos(int filter_size, float x) {
- if (x <= -filter_size || x >= filter_size)
- return 0.0f; // Outside of the window.
- if (x > -std::numeric_limits<float>::epsilon() &&
- x < std::numeric_limits<float>::epsilon())
- return 1.0f; // Special case the discontinuity at the origin.
- float xpi = x * static_cast<float>(M_PI);
- return (sin(xpi) / xpi) * // sinc(x)
- sin(xpi / filter_size) / (xpi / filter_size); // sinc(x/filter_size)
-}
-
-// Evaluates the Hamming filter of the given filter size window for the given
-// position.
-//
-// The filter covers [-filter_size, +filter_size]. Outside of this window
-// the value of the function is 0. Inside of the window, the value is sinus
-// cardinal multiplied by a recentered Hamming function. The traditional
-// Hamming formula for a window of size N and n ranging in [0, N-1] is:
-// hamming(n) = 0.54 - 0.46 * cos(2 * pi * n / (N-1)))
-// In our case we want the function centered for x == 0 and at its minimum
-// on both ends of the window (x == +/- filter_size), hence the adjusted
-// formula:
-// hamming(x) = (0.54 -
-// 0.46 * cos(2 * pi * (x - filter_size)/ (2 * filter_size)))
-// = 0.54 - 0.46 * cos(pi * x / filter_size - pi)
-// = 0.54 + 0.46 * cos(pi * x / filter_size)
-float EvalHamming(int filter_size, float x) {
- if (x <= -filter_size || x >= filter_size)
- return 0.0f; // Outside of the window.
- if (x > -std::numeric_limits<float>::epsilon() &&
- x < std::numeric_limits<float>::epsilon())
- return 1.0f; // Special case the sinc discontinuity at the origin.
- const float xpi = x * static_cast<float>(M_PI);
-
- return ((sin(xpi) / xpi) * // sinc(x)
- (0.54f + 0.46f * cos(xpi / filter_size))); // hamming(x)
-}
-
-// ResizeFilter ----------------------------------------------------------------
-
-// Encapsulates computation and storage of the filters required for one complete
-// resize operation.
-class ResizeFilter {
- public:
- ResizeFilter(ImageOperations::ResizeMethod method,
- int src_full_width, int src_full_height,
- int dest_width, int dest_height,
- const SkIRect& dest_subset);
-
- // Returns the filled filter values.
- const ConvolutionFilter1D& x_filter() { return x_filter_; }
- const ConvolutionFilter1D& y_filter() { return y_filter_; }
-
- private:
- // Returns the number of pixels that the filer spans, in filter space (the
- // destination image).
- float GetFilterSupport(float scale) {
- switch (method_) {
- case ImageOperations::RESIZE_BOX:
- // The box filter just scales with the image scaling.
- return 0.5f; // Only want one side of the filter = /2.
- case ImageOperations::RESIZE_HAMMING1:
- // The Hamming filter takes as much space in the source image in
- // each direction as the size of the window = 1 for Hamming1.
- return 1.0f;
- case ImageOperations::RESIZE_LANCZOS2:
- // The Lanczos filter takes as much space in the source image in
- // each direction as the size of the window = 2 for Lanczos2.
- return 2.0f;
- case ImageOperations::RESIZE_LANCZOS3:
- // The Lanczos filter takes as much space in the source image in
- // each direction as the size of the window = 3 for Lanczos3.
- return 3.0f;
- default:
- return 1.0f;
- }
- }
-
- // Computes one set of filters either horizontally or vertically. The caller
- // will specify the "min" and "max" rather than the bottom/top and
- // right/bottom so that the same code can be re-used in each dimension.
- //
- // |src_depend_lo| and |src_depend_size| gives the range for the source
- // depend rectangle (horizontally or vertically at the caller's discretion
- // -- see above for what this means).
- //
- // Likewise, the range of destination values to compute and the scale factor
- // for the transform is also specified.
- void ComputeFilters(int src_size,
- int dest_subset_lo, int dest_subset_size,
- float scale, float src_support,
- ConvolutionFilter1D* output);
-
- // Computes the filter value given the coordinate in filter space.
- inline float ComputeFilter(float pos) {
- switch (method_) {
- case ImageOperations::RESIZE_BOX:
- return EvalBox(pos);
- case ImageOperations::RESIZE_HAMMING1:
- return EvalHamming(1, pos);
- case ImageOperations::RESIZE_LANCZOS2:
- return EvalLanczos(2, pos);
- case ImageOperations::RESIZE_LANCZOS3:
- return EvalLanczos(3, pos);
- default:
- return 0;
- }
- }
-
- ImageOperations::ResizeMethod method_;
-
- // Size of the filter support on one side only in the destination space.
- // See GetFilterSupport.
- float x_filter_support_;
- float y_filter_support_;
-
- // Subset of scaled destination bitmap to compute.
- SkIRect out_bounds_;
-
- ConvolutionFilter1D x_filter_;
- ConvolutionFilter1D y_filter_;
-
- DISALLOW_COPY_AND_ASSIGN(ResizeFilter);
-};
-
-ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method,
- int src_full_width, int src_full_height,
- int dest_width, int dest_height,
- const SkIRect& dest_subset)
- : method_(method),
- out_bounds_(dest_subset) {
- // method_ will only ever refer to an "algorithm method".
- SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
- (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
-
- float scale_x = static_cast<float>(dest_width) /
- static_cast<float>(src_full_width);
- float scale_y = static_cast<float>(dest_height) /
- static_cast<float>(src_full_height);
-
- x_filter_support_ = GetFilterSupport(scale_x);
- y_filter_support_ = GetFilterSupport(scale_y);
-
- // Support of the filter in source space.
- float src_x_support = x_filter_support_ / scale_x;
- float src_y_support = y_filter_support_ / scale_y;
-
- ComputeFilters(src_full_width, dest_subset.fLeft, dest_subset.width(),
- scale_x, src_x_support, &x_filter_);
- ComputeFilters(src_full_height, dest_subset.fTop, dest_subset.height(),
- scale_y, src_y_support, &y_filter_);
-}
+namespace resize {
// TODO(egouriou): Take advantage of periods in the convolution.
// Practical resizing filters are periodic outside of the border area.
@@ -235,10 +57,16 @@ ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method,
// Small periods reduce computational load and improve cache usage if
// the coefficients can be shared. For periods of 1 we can consider
// loading the factors only once outside the borders.
-void ResizeFilter::ComputeFilters(int src_size,
- int dest_subset_lo, int dest_subset_size,
- float scale, float src_support,
- ConvolutionFilter1D* output) {
+void ComputeFilters(ImageOperations::ResizeMethod method,
+ int src_size, int dst_size,
+ int dest_subset_lo, int dest_subset_size,
+ ConvolutionFilter1D* output) {
+ // method_ will only ever refer to an "algorithm method".
+ SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
+ (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
+
+ float scale = static_cast<float>(dst_size) / static_cast<float>(src_size);
+
int dest_subset_hi = dest_subset_lo + dest_subset_size; // [lo, hi)
// When we're doing a magnification, the scale will be larger than one. This
@@ -248,6 +76,8 @@ void ResizeFilter::ComputeFilters(int src_size,
// some computations.
float clamped_scale = std::min(1.0f, scale);
+ float src_support = GetFilterSupport(method, clamped_scale) / clamped_scale;
+
// Speed up the divisions below by turning them into multiplies.
float inv_scale = 1.0f / scale;
@@ -294,7 +124,7 @@ void ResizeFilter::ComputeFilters(int src_size,
float dest_filter_dist = src_filter_dist * clamped_scale;
// Compute the filter value at that location.
- float filter_value = ComputeFilter(dest_filter_dist);
+ float filter_value = ComputeFilter(method, dest_filter_dist);
filter_values->push_back(filter_value);
filter_sum += filter_value;
@@ -325,6 +155,8 @@ void ResizeFilter::ComputeFilters(int src_size,
output->PaddingForSIMD(8);
}
+}
+
ImageOperations::ResizeMethod ResizeMethodToAlgorithmMethod(
ImageOperations::ResizeMethod method) {
// Convert any "Quality Method" into an "Algorithm Method"
@@ -336,22 +168,24 @@ ImageOperations::ResizeMethod ResizeMethodToAlgorithmMethod(
// GPU-acceleration in the cases where it is possible. So now we just
// pick the appropriate software method for each resize quality.
switch (method) {
+ // Users of RESIZE_GOOD are willing to trade a lot of quality to
+ // get speed, allowing the use of linear resampling to get hardware
+ // acceleration (SRB). Hence any of our "good" software filters
+ // will be acceptable, and we use the fastest one, Hamming-1.
case ImageOperations::RESIZE_GOOD:
- // In visual tests we see that Hamming-1 is not as good as
+ // Users of RESIZE_BETTER are willing to trade some quality in order
+ // to improve performance, but are guaranteed not to devolve to a linear
+ // resampling. In visual tests we see that Hamming-1 is not as good as
// Lanczos-2, however it is about 40% faster and Lanczos-2 itself is
// about 30% faster than Lanczos-3. The use of Hamming-1 has been deemed
- // an unacceptable trade-off between quality and speed due to the limited
- // pixel space it operates in before switching to HQ scaling becomes
- // necessary to retain fidelity of images.
+ // an acceptable trade-off between quality and speed.
case ImageOperations::RESIZE_BETTER:
- return ImageOperations::RESIZE_LANCZOS2;
+ return ImageOperations::RESIZE_HAMMING1;
default:
return ImageOperations::RESIZE_LANCZOS3;
}
}
-} // namespace
-
// Resize ----------------------------------------------------------------------
// static
@@ -405,9 +239,13 @@ SkBitmap ImageOperations::ResizeSubpixel(const SkBitmap& source,
// Render into subpixels.
SkBitmap result;
- result.setConfig(SkBitmap::kARGB_8888_Config, dest_subset.width(),
- dest_subset.height());
- result.allocPixels();
+ SkImageInfo info = SkImageInfo::Make(dest_subset.width(),
+ dest_subset.height(),
+ kBGRA_8888_SkColorType,
+ kPremul_SkAlphaType);
+
+
+ result.allocPixels(info);
if (!result.readyToDraw())
return img;
@@ -467,7 +305,7 @@ SkBitmap ImageOperations::ResizeSubpixel(const SkBitmap& source,
src_row += h * row_words;
dst_row += result.rowBytes() / 4;
}
- result.setIsOpaque(img.isOpaque());
+ result.setAlphaType(img.alphaType());
return result;
#else
return SkBitmap();
@@ -501,8 +339,11 @@ SkBitmap ImageOperations::ResizeBasic(const SkBitmap& source,
if (!source.readyToDraw())
return SkBitmap();
- ResizeFilter filter(method, source.width(), source.height(),
- dest_width, dest_height, dest_subset);
+ ConvolutionFilter1D x_filter;
+ ConvolutionFilter1D y_filter;
+
+ resize::ComputeFilters(method, source.width(), dest_width, dest_subset.fLeft, dest_subset.width(), &x_filter);
+ resize::ComputeFilters(method, source.height(), dest_height, dest_subset.fTop, dest_subset.height(), &y_filter);
// Get a source bitmap encompassing this touched area. We construct the
// offsets and row strides such that it looks like a new bitmap, while
@@ -512,26 +353,27 @@ SkBitmap ImageOperations::ResizeBasic(const SkBitmap& source,
// Convolve into the result.
SkBitmap result;
- result.setConfig(SkBitmap::kARGB_8888_Config,
- dest_subset.width(), dest_subset.height());
+ SkImageInfo info = SkImageInfo::Make(dest_subset.width(),
+ dest_subset.height(),
+ kBGRA_8888_SkColorType,
+ kPremul_SkAlphaType);
if (dest_pixels) {
- result.setPixels(dest_pixels);
+ result.installPixels(info, dest_pixels, info.minRowBytes());
} else {
- result.allocPixels();
+ result.allocPixels(info);
}
if (!result.readyToDraw())
return SkBitmap();
BGRAConvolve2D(source_subset, static_cast<int>(source.rowBytes()),
- !source.isOpaque(), filter.x_filter(), filter.y_filter(),
+ !source.isOpaque(), x_filter, y_filter,
static_cast<int>(result.rowBytes()),
- static_cast<unsigned char*>(result.getPixels()),
- /* sse = */ false);
+ static_cast<unsigned char*>(result.getPixels()));
// Preserve the "opaque" flag for use as an optimization later.
- result.setIsOpaque(source.isOpaque());
+ result.setAlphaType(source.alphaType());
return result;
}
diff --git a/gfx/2d/image_operations.h b/gfx/2d/image_operations.h
index 4713133d6..b2a792fde 100644
--- a/gfx/2d/image_operations.h
+++ b/gfx/2d/image_operations.h
@@ -31,6 +31,8 @@
#include "skia/SkTypes.h"
#include "Types.h"
+#include "convolver.h"
+#include "skia/SkRect.h"
class SkBitmap;
struct SkIRect;
@@ -152,6 +154,132 @@ class ImageOperations {
const SkIRect& dest_subset);
};
+// Returns the ceiling/floor as an integer.
+inline int CeilInt(float val) {
+ return static_cast<int>(ceil(val));
+}
+inline int FloorInt(float val) {
+ return static_cast<int>(floor(val));
+}
+
+// Filter function computation -------------------------------------------------
+
+// Evaluates the box filter, which goes from -0.5 to +0.5.
+inline float EvalBox(float x) {
+ return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f;
+}
+
+// Evaluates the Lanczos filter of the given filter size window for the given
+// position.
+//
+// |filter_size| is the width of the filter (the "window"), outside of which
+// the value of the function is 0. Inside of the window, the value is the
+// normalized sinc function:
+// lanczos(x) = sinc(x) * sinc(x / filter_size);
+// where
+// sinc(x) = sin(pi*x) / (pi*x);
+inline float EvalLanczos(int filter_size, float x) {
+ if (x <= -filter_size || x >= filter_size)
+ return 0.0f; // Outside of the window.
+ if (x > -std::numeric_limits<float>::epsilon() &&
+ x < std::numeric_limits<float>::epsilon())
+ return 1.0f; // Special case the discontinuity at the origin.
+ float xpi = x * static_cast<float>(M_PI);
+ return (sin(xpi) / xpi) * // sinc(x)
+ sin(xpi / filter_size) / (xpi / filter_size); // sinc(x/filter_size)
+}
+
+// Evaluates the Hamming filter of the given filter size window for the given
+// position.
+//
+// The filter covers [-filter_size, +filter_size]. Outside of this window
+// the value of the function is 0. Inside of the window, the value is sinus
+// cardinal multiplied by a recentered Hamming function. The traditional
+// Hamming formula for a window of size N and n ranging in [0, N-1] is:
+// hamming(n) = 0.54 - 0.46 * cos(2 * pi * n / (N-1)))
+// In our case we want the function centered for x == 0 and at its minimum
+// on both ends of the window (x == +/- filter_size), hence the adjusted
+// formula:
+// hamming(x) = (0.54 -
+// 0.46 * cos(2 * pi * (x - filter_size)/ (2 * filter_size)))
+// = 0.54 - 0.46 * cos(pi * x / filter_size - pi)
+// = 0.54 + 0.46 * cos(pi * x / filter_size)
+inline float EvalHamming(int filter_size, float x) {
+ if (x <= -filter_size || x >= filter_size)
+ return 0.0f; // Outside of the window.
+ if (x > -std::numeric_limits<float>::epsilon() &&
+ x < std::numeric_limits<float>::epsilon())
+ return 1.0f; // Special case the sinc discontinuity at the origin.
+ const float xpi = x * static_cast<float>(M_PI);
+
+ return ((sin(xpi) / xpi) * // sinc(x)
+ (0.54f + 0.46f * cos(xpi / filter_size))); // hamming(x)
+}
+
+// ResizeFilter ----------------------------------------------------------------
+
+// Encapsulates computation and storage of the filters required for one complete
+// resize operation.
+
+namespace resize {
+
+ // Returns the number of pixels that the filer spans, in filter space (the
+ // destination image).
+ inline float GetFilterSupport(ImageOperations::ResizeMethod method,
+ float scale) {
+ switch (method) {
+ case ImageOperations::RESIZE_BOX:
+ // The box filter just scales with the image scaling.
+ return 0.5f; // Only want one side of the filter = /2.
+ case ImageOperations::RESIZE_HAMMING1:
+ // The Hamming filter takes as much space in the source image in
+ // each direction as the size of the window = 1 for Hamming1.
+ return 1.0f;
+ case ImageOperations::RESIZE_LANCZOS2:
+ // The Lanczos filter takes as much space in the source image in
+ // each direction as the size of the window = 2 for Lanczos2.
+ return 2.0f;
+ case ImageOperations::RESIZE_LANCZOS3:
+ // The Lanczos filter takes as much space in the source image in
+ // each direction as the size of the window = 3 for Lanczos3.
+ return 3.0f;
+ default:
+ return 1.0f;
+ }
+ }
+
+ // Computes one set of filters either horizontally or vertically. The caller
+ // will specify the "min" and "max" rather than the bottom/top and
+ // right/bottom so that the same code can be re-used in each dimension.
+ //
+ // |src_depend_lo| and |src_depend_size| gives the range for the source
+ // depend rectangle (horizontally or vertically at the caller's discretion
+ // -- see above for what this means).
+ //
+ // Likewise, the range of destination values to compute and the scale factor
+ // for the transform is also specified.
+ void ComputeFilters(ImageOperations::ResizeMethod method,
+ int src_size, int dst_size,
+ int dest_subset_lo, int dest_subset_size,
+ ConvolutionFilter1D* output);
+
+ // Computes the filter value given the coordinate in filter space.
+ inline float ComputeFilter(ImageOperations::ResizeMethod method, float pos) {
+ switch (method) {
+ case ImageOperations::RESIZE_BOX:
+ return EvalBox(pos);
+ case ImageOperations::RESIZE_HAMMING1:
+ return EvalHamming(1, pos);
+ case ImageOperations::RESIZE_LANCZOS2:
+ return EvalLanczos(2, pos);
+ case ImageOperations::RESIZE_LANCZOS3:
+ return EvalLanczos(3, pos);
+ default:
+ return 0;
+ }
+ }
+}
+
} // namespace skia
#endif // SKIA_EXT_IMAGE_OPERATIONS_H_
diff --git a/gfx/2d/moz.build b/gfx/2d/moz.build
index 96413cbd7..9ce208ec9 100644
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -4,10 +4,13 @@
# 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/.
-MODULE = 'gfx2d'
+EXPORTS.mozilla += [
+ 'GenericRefCounted.h',
+]
EXPORTS.mozilla.gfx += [
'2D.h',
+ 'BaseCoord.h',
'BaseMargin.h',
'BasePoint.h',
'BasePoint3D.h',
@@ -15,12 +18,21 @@ EXPORTS.mozilla.gfx += [
'BaseRect.h',
'BaseSize.h',
'Blur.h',
+ 'BorrowedContext.h',
+ 'Coord.h',
+ 'DataSurfaceHelpers.h',
+ 'DrawTargetTiled.h',
+ 'Filters.h',
+ 'Helpers.h',
+ 'Logging.h',
'Matrix.h',
'PathHelpers.h',
+ 'PatternHelpers.h',
'Point.h',
'Rect.h',
'Scale.h',
'ScaleFactor.h',
+ 'SourceSurfaceCairo.h',
'Tools.h',
'Types.h',
'UserData.h',
@@ -31,67 +43,130 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
'MacIOSurface.h',
'QuartzSupport.h',
]
- CPP_SOURCES += [
- 'SourceSurfaceCG.cpp',
+ UNIFIED_SOURCES += [
'DrawTargetCG.cpp',
'PathCG.cpp',
'ScaledFontMac.cpp',
+ 'SourceSurfaceCG.cpp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
- CPP_SOURCES += [
+ SOURCES += [
'DrawTargetD2D.cpp',
- 'SourceSurfaceD2D.cpp',
- 'SourceSurfaceD2DTarget.cpp',
+ 'DrawTargetD2D1.cpp',
+ 'ExtendInputEffectD2D1.cpp',
+ 'FilterNodeD2D1.cpp',
'PathD2D.cpp',
+ 'RadialGradientEffectD2D1.cpp',
'ScaledFontDWrite.cpp',
+ 'ScaledFontWin.cpp',
+ 'SourceSurfaceD2D.cpp',
+ 'SourceSurfaceD2D1.cpp',
+ 'SourceSurfaceD2DTarget.cpp',
]
- if CONFIG['MOZ_WINSDK_MAXVER'] >= '0x06020000':
- CPP_SOURCES += [
- 'RadialGradientEffectD2D1.cpp'
- ]
- if CONFIG['MOZ_ENABLE_SKIA']:
- CPP_SOURCES += [
- 'ScaledFontWin.cpp',
- ]
+ DEFINES['WIN32'] = True
if CONFIG['MOZ_ENABLE_SKIA']:
- CPP_SOURCES += [
- 'SourceSurfaceSkia.cpp',
+ UNIFIED_SOURCES += [
+ 'convolver.cpp',
'DrawTargetSkia.cpp',
'PathSkia.cpp',
- 'convolver.cpp',
- 'image_operations.cpp',
+ 'SourceSurfaceSkia.cpp',
]
-
-if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3', 'gonk', 'qt'):
- CPP_SOURCES += [
- 'ScaledFontFreetype.cpp',
+ SOURCES += [
+ 'image_operations.cpp', # Uses _USE_MATH_DEFINES
+ ]
+ EXPORTS.mozilla.gfx += [
+ 'HelpersSkia.h',
]
# Are we targeting x86 or x64? If so, build SSE2 files.
if CONFIG['INTEL_ARCHITECTURE']:
- # VC2005 doesn't support _mm_castsi128_ps, so SSE2 is turned off
- if CONFIG['_MSC_VER'] != '1400':
- CPP_SOURCES += [
- 'ImageScalingSSE2.cpp',
- 'BlurSSE2.cpp',
+ SOURCES += [
+ 'BlurSSE2.cpp',
+ 'FilterProcessingSSE2.cpp',
+ 'ImageScalingSSE2.cpp',
+ ]
+ if CONFIG['MOZ_ENABLE_SKIA']:
+ SOURCES += [
+ 'convolverSSE2.cpp',
]
+ DEFINES['USE_SSE2'] = True
+ # The file uses SSE2 intrinsics, so it needs special compile flags on some
+ # compilers.
+ SOURCES['BlurSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+ SOURCES['FilterProcessingSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+ SOURCES['ImageScalingSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+ if CONFIG['MOZ_ENABLE_SKIA']:
+ SOURCES['convolverSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
-CPP_SOURCES += [
+UNIFIED_SOURCES += [
'Blur.cpp',
+ 'DataSourceSurface.cpp',
+ 'DataSurfaceHelpers.cpp',
'DrawEventRecorder.cpp',
+ 'DrawTarget.cpp',
'DrawTargetCairo.cpp',
+ 'DrawTargetCapture.cpp',
'DrawTargetDual.cpp',
'DrawTargetRecording.cpp',
'Factory.cpp',
+ 'FilterNodeSoftware.cpp',
+ 'FilterProcessing.cpp',
+ 'FilterProcessingScalar.cpp',
'ImageScaling.cpp',
'Matrix.cpp',
+ 'Path.cpp',
'PathCairo.cpp',
+ 'PathHelpers.cpp',
'PathRecording.cpp',
'RecordedEvent.cpp',
'Scale.cpp',
'ScaledFontBase.cpp',
+ 'ScaledFontCairo.cpp',
'SourceSurfaceCairo.cpp',
'SourceSurfaceRawData.cpp',
]
+SOURCES += [
+ 'DrawTargetTiled.cpp',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'MacIOSurface.cpp',
+ 'QuartzSupport.mm',
+ ]
+
+if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
+ SOURCES += ['BlurNEON.cpp']
+ SOURCES['BlurNEON.cpp'].flags += ['-mfpu=neon']
+
+FAIL_ON_WARNINGS = True
+
+MSVC_ENABLE_PGO = True
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+for var in ('USE_CAIRO', 'MOZ2D_HAS_MOZ_CAIRO'):
+ DEFINES[var] = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3', 'gonk', 'qt'):
+ DEFINES['MOZ_ENABLE_FREETYPE'] = True
+
+if CONFIG['MOZ_DEBUG']:
+ DEFINES['GFX_LOG_DEBUG'] = True
+ DEFINES['GFX_LOG_WARNING'] = True
+
+# Define the GFX_LOG_WARNING in release builds (available, but controlled by a
+# preference), though we may want to consider only doing it in the nightly
+# build, if the size of gfxWarning() code ends up making a difference.
+# See bug 1074952.
+# if CONFIG['NIGHTLY_BUILD']:
+DEFINES['GFX_LOG_WARNING'] = True
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3', 'gonk', 'qt'):
+ CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
diff --git a/gfx/2d/unittest/Main.cpp b/gfx/2d/unittest/Main.cpp
index 8439c76f4..46a1af5b6 100644
--- a/gfx/2d/unittest/Main.cpp
+++ b/gfx/2d/unittest/Main.cpp
@@ -6,6 +6,7 @@
#include "SanityChecks.h"
#include "TestPoint.h"
#include "TestScaling.h"
+#include "TestBugs.h"
#ifdef WIN32
#include "TestDrawTargetD2D.h"
#endif
@@ -32,6 +33,7 @@ main()
#endif
{ new TestPoint(), "Point Tests" },
{ new TestScaling(), "Scaling Tests" }
+ { new TestBugs(), "Bug Tests" }
};
int totalFailures = 0;
diff --git a/gfx/2d/unittest/SanityChecks.cpp b/gfx/2d/unittest/SanityChecks.cpp
index 428c19854..7f109facd 100644
--- a/gfx/2d/unittest/SanityChecks.cpp
+++ b/gfx/2d/unittest/SanityChecks.cpp
@@ -2,18 +2,18 @@
* 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/. */
-
-#include "SanityChecks.h"
-
-SanityChecks::SanityChecks()
-{
- REGISTER_TEST(SanityChecks, AlwaysPasses);
-}
-
-void
-SanityChecks::AlwaysPasses()
-{
- bool testMustPass = true;
-
- VERIFY(testMustPass);
-}
+
+#include "SanityChecks.h"
+
+SanityChecks::SanityChecks()
+{
+ REGISTER_TEST(SanityChecks, AlwaysPasses);
+}
+
+void
+SanityChecks::AlwaysPasses()
+{
+ bool testMustPass = true;
+
+ VERIFY(testMustPass);
+}
diff --git a/gfx/2d/unittest/SanityChecks.h b/gfx/2d/unittest/SanityChecks.h
index 365c8bf1c..f2a4a0b59 100644
--- a/gfx/2d/unittest/SanityChecks.h
+++ b/gfx/2d/unittest/SanityChecks.h
@@ -2,15 +2,15 @@
* 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/. */
-
-#pragma once
-
-#include "TestBase.h"
-
-class SanityChecks : public TestBase
-{
-public:
- SanityChecks();
-
- void AlwaysPasses();
-};
+
+#pragma once
+
+#include "TestBase.h"
+
+class SanityChecks : public TestBase
+{
+public:
+ SanityChecks();
+
+ void AlwaysPasses();
+};
diff --git a/gfx/2d/unittest/TestBase.cpp b/gfx/2d/unittest/TestBase.cpp
index be8a8e04d..5818a7299 100644
--- a/gfx/2d/unittest/TestBase.cpp
+++ b/gfx/2d/unittest/TestBase.cpp
@@ -2,47 +2,47 @@
* 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/. */
-
-#include "TestBase.h"
-
-#include <sstream>
-
-using namespace std;
-
-int
-TestBase::RunTests(int *aFailures)
-{
- int testsRun = 0;
- *aFailures = 0;
-
- for(unsigned int i = 0; i < mTests.size(); i++) {
- stringstream stream;
- stream << "Test (" << mTests[i].name << "): ";
- LogMessage(stream.str());
- stream.str("");
-
- mTestFailed = false;
-
- // Don't try this at home! We know these are actually pointers to members
- // of child clases, so we reinterpret cast those child class pointers to
- // TestBase and then call the functions. Because the compiler believes
- // these function calls are members of TestBase.
- ((*reinterpret_cast<TestBase*>((mTests[i].implPointer))).*(mTests[i].funcCall))();
-
- if (!mTestFailed) {
- LogMessage("PASSED\n");
- } else {
- LogMessage("FAILED\n");
- (*aFailures)++;
- }
- testsRun++;
- }
-
- return testsRun;
-}
-
-void
-TestBase::LogMessage(string aMessage)
-{
- printf("%s", aMessage.c_str());
-}
+
+#include "TestBase.h"
+
+#include <sstream>
+
+using namespace std;
+
+int
+TestBase::RunTests(int *aFailures)
+{
+ int testsRun = 0;
+ *aFailures = 0;
+
+ for(unsigned int i = 0; i < mTests.size(); i++) {
+ stringstream stream;
+ stream << "Test (" << mTests[i].name << "): ";
+ LogMessage(stream.str());
+ stream.str("");
+
+ mTestFailed = false;
+
+ // Don't try this at home! We know these are actually pointers to members
+ // of child clases, so we reinterpret cast those child class pointers to
+ // TestBase and then call the functions. Because the compiler believes
+ // these function calls are members of TestBase.
+ ((*reinterpret_cast<TestBase*>((mTests[i].implPointer))).*(mTests[i].funcCall))();
+
+ if (!mTestFailed) {
+ LogMessage("PASSED\n");
+ } else {
+ LogMessage("FAILED\n");
+ (*aFailures)++;
+ }
+ testsRun++;
+ }
+
+ return testsRun;
+}
+
+void
+TestBase::LogMessage(string aMessage)
+{
+ printf("%s", aMessage.c_str());
+}
diff --git a/gfx/2d/unittest/TestBase.h b/gfx/2d/unittest/TestBase.h
index 97f37883e..a57d6a730 100644
--- a/gfx/2d/unittest/TestBase.h
+++ b/gfx/2d/unittest/TestBase.h
@@ -26,7 +26,7 @@ class TestBase
public:
TestBase() {}
- typedef void (TestBase::*TestCall)();
+ typedef void (TestBase::*TestCall)();
int RunTests(int *aFailures);
diff --git a/gfx/2d/unittest/TestBugs.cpp b/gfx/2d/unittest/TestBugs.cpp
new file mode 100644
index 000000000..f127eed8b
--- /dev/null
+++ b/gfx/2d/unittest/TestBugs.cpp
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#include "TestBugs.h"
+#include "2D.h"
+#include <string.h>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+TestBugs::TestBugs()
+{
+ REGISTER_TEST(TestBugs, CairoClip918671);
+ REGISTER_TEST(TestBugs, PushPopClip950550);
+}
+
+void
+TestBugs::CairoClip918671()
+{
+ RefPtr<DrawTarget> dt = Factory::CreateDrawTarget(BackendType::CAIRO,
+ IntSize(100, 100),
+ SurfaceFormat::B8G8R8A8);
+ RefPtr<DrawTarget> ref = Factory::CreateDrawTarget(BackendType::CAIRO,
+ IntSize(100, 100),
+ SurfaceFormat::B8G8R8A8);
+ // Create a path that extends around the center rect but doesn't intersect it.
+ RefPtr<PathBuilder> pb1 = dt->CreatePathBuilder();
+ pb1->MoveTo(Point(10, 10));
+ pb1->LineTo(Point(90, 10));
+ pb1->LineTo(Point(90, 20));
+ pb1->LineTo(Point(10, 20));
+ pb1->Close();
+ pb1->MoveTo(Point(90, 90));
+ pb1->LineTo(Point(91, 90));
+ pb1->LineTo(Point(91, 91));
+ pb1->LineTo(Point(91, 90));
+ pb1->Close();
+
+ RefPtr<Path> path1 = pb1->Finish();
+ dt->PushClip(path1);
+
+ // This center rect must NOT be rectilinear!
+ RefPtr<PathBuilder> pb2 = dt->CreatePathBuilder();
+ pb2->MoveTo(Point(50, 50));
+ pb2->LineTo(Point(55, 51));
+ pb2->LineTo(Point(54, 55));
+ pb2->LineTo(Point(50, 56));
+ pb2->Close();
+
+ RefPtr<Path> path2 = pb2->Finish();
+ dt->PushClip(path2);
+
+ dt->FillRect(Rect(0, 0, 100, 100), ColorPattern(Color(1,0,0)));
+
+ RefPtr<SourceSurface> surf1 = dt->Snapshot();
+ RefPtr<SourceSurface> surf2 = ref->Snapshot();
+
+ RefPtr<DataSourceSurface> dataSurf1 = surf1->GetDataSurface();
+ RefPtr<DataSourceSurface> dataSurf2 = surf2->GetDataSurface();
+
+ for (int y = 0; y < dt->GetSize().height; y++) {
+ VERIFY(memcmp(dataSurf1->GetData() + y * dataSurf1->Stride(),
+ dataSurf2->GetData() + y * dataSurf2->Stride(),
+ dataSurf1->GetSize().width * 4) == 0);
+ }
+
+}
+
+void
+TestBugs::PushPopClip950550()
+{
+ RefPtr<DrawTarget> dt = Factory::CreateDrawTarget(BackendType::CAIRO,
+ IntSize(500, 500),
+ SurfaceFormat::B8G8R8A8);
+ dt->PushClipRect(Rect(0, 0, 100, 100));
+ Matrix m(1, 0, 0, 1, 45, -100);
+ dt->SetTransform(m);
+ dt->PopClip();
+
+ // We fail the test if we assert in this call because our draw target's
+ // transforms are out of sync.
+ dt->FillRect(Rect(50, 50, 50, 50), ColorPattern(Color(0.5f, 0, 0, 1.0f)));
+}
+
diff --git a/gfx/2d/unittest/TestBugs.h b/gfx/2d/unittest/TestBugs.h
new file mode 100644
index 000000000..0c715df44
--- /dev/null
+++ b/gfx/2d/unittest/TestBugs.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#pragma once
+
+#include "TestBase.h"
+
+class TestBugs : public TestBase
+{
+public:
+ TestBugs();
+
+ void CairoClip918671();
+ void PushPopClip950550();
+};
+
diff --git a/gfx/2d/unittest/TestDrawTargetBase.cpp b/gfx/2d/unittest/TestDrawTargetBase.cpp
index 020da024a..2a0d95ed6 100644
--- a/gfx/2d/unittest/TestDrawTargetBase.cpp
+++ b/gfx/2d/unittest/TestDrawTargetBase.cpp
@@ -2,108 +2,108 @@
* 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/. */
-
-#include "TestDrawTargetBase.h"
-#include <sstream>
-
-using namespace mozilla;
-using namespace mozilla::gfx;
-using namespace std;
-
-TestDrawTargetBase::TestDrawTargetBase()
-{
- REGISTER_TEST(TestDrawTargetBase, Initialized);
- REGISTER_TEST(TestDrawTargetBase, FillCompletely);
- REGISTER_TEST(TestDrawTargetBase, FillRect);
-}
-
-void
-TestDrawTargetBase::Initialized()
-{
- VERIFY(mDT);
-}
-
-void
-TestDrawTargetBase::FillCompletely()
-{
- mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f)));
-
- RefreshSnapshot();
-
- VerifyAllPixels(Color(0, 0.5f, 0, 1.0f));
-}
-
-void
-TestDrawTargetBase::FillRect()
-{
- mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f)));
- mDT->FillRect(Rect(50, 50, 50, 50), ColorPattern(Color(0.5f, 0, 0, 1.0f)));
-
- RefreshSnapshot();
-
- VerifyPixel(IntPoint(49, 49), Color(0, 0.5f, 0, 1.0f));
- VerifyPixel(IntPoint(50, 50), Color(0.5f, 0, 0, 1.0f));
- VerifyPixel(IntPoint(99, 99), Color(0.5f, 0, 0, 1.0f));
- VerifyPixel(IntPoint(100, 100), Color(0, 0.5f, 0, 1.0f));
-}
-
-void
-TestDrawTargetBase::RefreshSnapshot()
-{
- RefPtr<SourceSurface> snapshot = mDT->Snapshot();
- mDataSnapshot = snapshot->GetDataSurface();
-}
-
-void
-TestDrawTargetBase::VerifyAllPixels(const Color &aColor)
-{
- uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData();
-
- uint32_t expected = RGBAPixelFromColor(aColor);
-
- for (int y = 0; y < DT_HEIGHT; y++) {
- for (int x = 0; x < DT_WIDTH; x++) {
- if (colVal[y * (mDataSnapshot->Stride() / 4) + x] != expected) {
- LogMessage("VerifyAllPixels Failed\n");
- mTestFailed = true;
- return;
- }
- }
- }
-}
-
-void
-TestDrawTargetBase::VerifyPixel(const IntPoint &aPoint, mozilla::gfx::Color &aColor)
-{
- uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData();
-
- uint32_t expected = RGBAPixelFromColor(aColor);
- uint32_t rawActual = colVal[aPoint.y * (mDataSnapshot->Stride() / 4) + aPoint.x];
-
- if (rawActual != expected) {
- stringstream message;
- uint32_t actb = rawActual & 0xFF;
- uint32_t actg = (rawActual & 0xFF00) >> 8;
- uint32_t actr = (rawActual & 0xFF0000) >> 16;
- uint32_t acta = (rawActual & 0xFF000000) >> 24;
- uint32_t expb = expected & 0xFF;
- uint32_t expg = (expected & 0xFF00) >> 8;
- uint32_t expr = (expected & 0xFF0000) >> 16;
- uint32_t expa = (expected & 0xFF000000) >> 24;
-
- message << "Verify Pixel (" << aPoint.x << "x" << aPoint.y << ") Failed."
- " Expected (" << expr << "," << expg << "," << expb << "," << expa << ") "
- " Got (" << actr << "," << actg << "," << actb << "," << acta << ")\n";
-
- LogMessage(message.str());
- mTestFailed = true;
- return;
- }
-}
-
-uint32_t
-TestDrawTargetBase::RGBAPixelFromColor(const Color &aColor)
-{
- return uint8_t((aColor.b * 255) + 0.5f) | uint8_t((aColor.g * 255) + 0.5f) << 8 |
- uint8_t((aColor.r * 255) + 0.5f) << 16 | uint8_t((aColor.a * 255) + 0.5f) << 24;
-}
+
+#include "TestDrawTargetBase.h"
+#include <sstream>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace std;
+
+TestDrawTargetBase::TestDrawTargetBase()
+{
+ REGISTER_TEST(TestDrawTargetBase, Initialized);
+ REGISTER_TEST(TestDrawTargetBase, FillCompletely);
+ REGISTER_TEST(TestDrawTargetBase, FillRect);
+}
+
+void
+TestDrawTargetBase::Initialized()
+{
+ VERIFY(mDT);
+}
+
+void
+TestDrawTargetBase::FillCompletely()
+{
+ mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f)));
+
+ RefreshSnapshot();
+
+ VerifyAllPixels(Color(0, 0.5f, 0, 1.0f));
+}
+
+void
+TestDrawTargetBase::FillRect()
+{
+ mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f)));
+ mDT->FillRect(Rect(50, 50, 50, 50), ColorPattern(Color(0.5f, 0, 0, 1.0f)));
+
+ RefreshSnapshot();
+
+ VerifyPixel(IntPoint(49, 49), Color(0, 0.5f, 0, 1.0f));
+ VerifyPixel(IntPoint(50, 50), Color(0.5f, 0, 0, 1.0f));
+ VerifyPixel(IntPoint(99, 99), Color(0.5f, 0, 0, 1.0f));
+ VerifyPixel(IntPoint(100, 100), Color(0, 0.5f, 0, 1.0f));
+}
+
+void
+TestDrawTargetBase::RefreshSnapshot()
+{
+ RefPtr<SourceSurface> snapshot = mDT->Snapshot();
+ mDataSnapshot = snapshot->GetDataSurface();
+}
+
+void
+TestDrawTargetBase::VerifyAllPixels(const Color &aColor)
+{
+ uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData();
+
+ uint32_t expected = RGBAPixelFromColor(aColor);
+
+ for (int y = 0; y < DT_HEIGHT; y++) {
+ for (int x = 0; x < DT_WIDTH; x++) {
+ if (colVal[y * (mDataSnapshot->Stride() / 4) + x] != expected) {
+ LogMessage("VerifyAllPixels Failed\n");
+ mTestFailed = true;
+ return;
+ }
+ }
+ }
+}
+
+void
+TestDrawTargetBase::VerifyPixel(const IntPoint &aPoint, mozilla::gfx::Color &aColor)
+{
+ uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData();
+
+ uint32_t expected = RGBAPixelFromColor(aColor);
+ uint32_t rawActual = colVal[aPoint.y * (mDataSnapshot->Stride() / 4) + aPoint.x];
+
+ if (rawActual != expected) {
+ stringstream message;
+ uint32_t actb = rawActual & 0xFF;
+ uint32_t actg = (rawActual & 0xFF00) >> 8;
+ uint32_t actr = (rawActual & 0xFF0000) >> 16;
+ uint32_t acta = (rawActual & 0xFF000000) >> 24;
+ uint32_t expb = expected & 0xFF;
+ uint32_t expg = (expected & 0xFF00) >> 8;
+ uint32_t expr = (expected & 0xFF0000) >> 16;
+ uint32_t expa = (expected & 0xFF000000) >> 24;
+
+ message << "Verify Pixel (" << aPoint.x << "x" << aPoint.y << ") Failed."
+ " Expected (" << expr << "," << expg << "," << expb << "," << expa << ") "
+ " Got (" << actr << "," << actg << "," << actb << "," << acta << ")\n";
+
+ LogMessage(message.str());
+ mTestFailed = true;
+ return;
+ }
+}
+
+uint32_t
+TestDrawTargetBase::RGBAPixelFromColor(const Color &aColor)
+{
+ return uint8_t((aColor.b * 255) + 0.5f) | uint8_t((aColor.g * 255) + 0.5f) << 8 |
+ uint8_t((aColor.r * 255) + 0.5f) << 16 | uint8_t((aColor.a * 255) + 0.5f) << 24;
+}
diff --git a/gfx/2d/unittest/TestDrawTargetBase.h b/gfx/2d/unittest/TestDrawTargetBase.h
index d373666b8..1e51f3082 100644
--- a/gfx/2d/unittest/TestDrawTargetBase.h
+++ b/gfx/2d/unittest/TestDrawTargetBase.h
@@ -2,37 +2,37 @@
* 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/. */
-
-#pragma once
-
-#include "2D.h"
-#include "TestBase.h"
-
-#define DT_WIDTH 500
-#define DT_HEIGHT 500
-
-/* This general DrawTarget test class can be reimplemented by a child class
- * with optional additional drawtarget-specific tests. And is intended to run
- * on a 500x500 32 BPP drawtarget.
- */
-class TestDrawTargetBase : public TestBase
-{
-public:
- void Initialized();
- void FillCompletely();
- void FillRect();
-
-protected:
- TestDrawTargetBase();
-
- void RefreshSnapshot();
-
- void VerifyAllPixels(const mozilla::gfx::Color &aColor);
- void VerifyPixel(const mozilla::gfx::IntPoint &aPoint,
- mozilla::gfx::Color &aColor);
-
- uint32_t RGBAPixelFromColor(const mozilla::gfx::Color &aColor);
-
- mozilla::RefPtr<mozilla::gfx::DrawTarget> mDT;
- mozilla::RefPtr<mozilla::gfx::DataSourceSurface> mDataSnapshot;
-};
+
+#pragma once
+
+#include "2D.h"
+#include "TestBase.h"
+
+#define DT_WIDTH 500
+#define DT_HEIGHT 500
+
+/* This general DrawTarget test class can be reimplemented by a child class
+ * with optional additional drawtarget-specific tests. And is intended to run
+ * on a 500x500 32 BPP drawtarget.
+ */
+class TestDrawTargetBase : public TestBase
+{
+public:
+ void Initialized();
+ void FillCompletely();
+ void FillRect();
+
+protected:
+ TestDrawTargetBase();
+
+ void RefreshSnapshot();
+
+ void VerifyAllPixels(const mozilla::gfx::Color &aColor);
+ void VerifyPixel(const mozilla::gfx::IntPoint &aPoint,
+ mozilla::gfx::Color &aColor);
+
+ uint32_t RGBAPixelFromColor(const mozilla::gfx::Color &aColor);
+
+ mozilla::RefPtr<mozilla::gfx::DrawTarget> mDT;
+ mozilla::RefPtr<mozilla::gfx::DataSourceSurface> mDataSnapshot;
+};
diff --git a/gfx/2d/unittest/TestDrawTargetD2D.cpp b/gfx/2d/unittest/TestDrawTargetD2D.cpp
index fd93e93c8..27c25767d 100644
--- a/gfx/2d/unittest/TestDrawTargetD2D.cpp
+++ b/gfx/2d/unittest/TestDrawTargetD2D.cpp
@@ -2,12 +2,12 @@
* 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/. */
-
-#include "TestDrawTargetD2D.h"
-
-using namespace mozilla::gfx;
-TestDrawTargetD2D::TestDrawTargetD2D()
-{
+
+#include "TestDrawTargetD2D.h"
+
+using namespace mozilla::gfx;
+TestDrawTargetD2D::TestDrawTargetD2D()
+{
::D3D10CreateDevice1(nullptr,
D3D10_DRIVER_TYPE_HARDWARE,
nullptr,
@@ -15,9 +15,9 @@ TestDrawTargetD2D::TestDrawTargetD2D()
D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
D3D10_FEATURE_LEVEL_10_0,
D3D10_1_SDK_VERSION,
- byRef(mDevice));
-
- Factory::SetDirect3D10Device(mDevice);
-
- mDT = Factory::CreateDrawTarget(BACKEND_DIRECT2D, IntSize(DT_WIDTH, DT_HEIGHT), FORMAT_B8G8R8A8);
-}
+ byRef(mDevice));
+
+ Factory::SetDirect3D10Device(mDevice);
+
+ mDT = Factory::CreateDrawTarget(BackendType::DIRECT2D, IntSize(DT_WIDTH, DT_HEIGHT), SurfaceFormat::B8G8R8A8);
+}
diff --git a/gfx/2d/unittest/TestDrawTargetD2D.h b/gfx/2d/unittest/TestDrawTargetD2D.h
index 40f5f83b9..e4647ba74 100644
--- a/gfx/2d/unittest/TestDrawTargetD2D.h
+++ b/gfx/2d/unittest/TestDrawTargetD2D.h
@@ -2,18 +2,18 @@
* 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/. */
-
-#pragma once
-
-#include "TestDrawTargetBase.h"
-
-#include <d3d10_1.h>
-
-class TestDrawTargetD2D : public TestDrawTargetBase
-{
-public:
- TestDrawTargetD2D();
-
-private:
- mozilla::RefPtr<ID3D10Device1> mDevice;
+
+#pragma once
+
+#include "TestDrawTargetBase.h"
+
+#include <d3d10_1.h>
+
+class TestDrawTargetD2D : public TestDrawTargetBase
+{
+public:
+ TestDrawTargetD2D();
+
+private:
+ mozilla::RefPtr<ID3D10Device1> mDevice;
};
diff --git a/gfx/2d/unittest/TestPoint.cpp b/gfx/2d/unittest/TestPoint.cpp
index 21d9dfeb2..6aa2b6a35 100644
--- a/gfx/2d/unittest/TestPoint.cpp
+++ b/gfx/2d/unittest/TestPoint.cpp
@@ -2,45 +2,45 @@
* 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/. */
-
-#include "TestPoint.h"
-
-#include "Point.h"
-
-using namespace mozilla::gfx;
-
-TestPoint::TestPoint()
-{
- REGISTER_TEST(TestPoint, Addition);
- REGISTER_TEST(TestPoint, Subtraction);
-}
-
-void
-TestPoint::Addition()
-{
- Point a, b;
- a.x = 2;
- a.y = 2;
- b.x = 5;
- b.y = -5;
-
- a += b;
-
- VERIFY(a.x == 7);
- VERIFY(a.y == -3);
-}
-
-void
-TestPoint::Subtraction()
-{
- Point a, b;
- a.x = 2;
- a.y = 2;
- b.x = 5;
- b.y = -5;
-
- a -= b;
-
- VERIFY(a.x == -3);
- VERIFY(a.y == 7);
-}
+
+#include "TestPoint.h"
+
+#include "Point.h"
+
+using namespace mozilla::gfx;
+
+TestPoint::TestPoint()
+{
+ REGISTER_TEST(TestPoint, Addition);
+ REGISTER_TEST(TestPoint, Subtraction);
+}
+
+void
+TestPoint::Addition()
+{
+ Point a, b;
+ a.x = 2;
+ a.y = 2;
+ b.x = 5;
+ b.y = -5;
+
+ a += b;
+
+ VERIFY(a.x == 7.f);
+ VERIFY(a.y == -3.f);
+}
+
+void
+TestPoint::Subtraction()
+{
+ Point a, b;
+ a.x = 2;
+ a.y = 2;
+ b.x = 5;
+ b.y = -5;
+
+ a -= b;
+
+ VERIFY(a.x == -3.f);
+ VERIFY(a.y == 7.f);
+}
diff --git a/gfx/2d/unittest/TestPoint.h b/gfx/2d/unittest/TestPoint.h
index c62e0afdf..813d66c2f 100644
--- a/gfx/2d/unittest/TestPoint.h
+++ b/gfx/2d/unittest/TestPoint.h
@@ -2,16 +2,16 @@
* 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/. */
-
-#pragma once
-
-#include "TestBase.h"
-
-class TestPoint : public TestBase
-{
-public:
- TestPoint();
-
- void Addition();
- void Subtraction();
-};
+
+#pragma once
+
+#include "TestBase.h"
+
+class TestPoint : public TestBase
+{
+public:
+ TestPoint();
+
+ void Addition();
+ void Subtraction();
+};
diff --git a/gfx/2d/unittest/TestScaling.cpp b/gfx/2d/unittest/TestScaling.cpp
index 1515c13fe..60e1acbf0 100644
--- a/gfx/2d/unittest/TestScaling.cpp
+++ b/gfx/2d/unittest/TestScaling.cpp
@@ -2,248 +2,248 @@
* 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/. */
-
-#include "TestScaling.h"
-
-#include "ImageScaling.h"
-
-using namespace mozilla::gfx;
-
-TestScaling::TestScaling()
-{
- REGISTER_TEST(TestScaling, BasicHalfScale);
- REGISTER_TEST(TestScaling, DoubleHalfScale);
- REGISTER_TEST(TestScaling, UnevenHalfScale);
- REGISTER_TEST(TestScaling, OddStrideHalfScale);
- REGISTER_TEST(TestScaling, VerticalHalfScale);
- REGISTER_TEST(TestScaling, HorizontalHalfScale);
- REGISTER_TEST(TestScaling, MixedHalfScale);
-}
-
-void
-TestScaling::BasicHalfScale()
-{
- std::vector<uint8_t> data;
- data.resize(500 * 500 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y += 2) {
- for (int x = 0; x < 500; x += 2) {
- pixels[y * 500 + x] = 0xff00ff00;
- pixels[y * 500 + x + 1] = 0xff00ffff;
- pixels[(y + 1) * 500 + x] = 0xff000000;
- pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
- }
- }
- ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
-
- scaler.ScaleForSize(IntSize(220, 240));
-
- VERIFY(scaler.GetSize().width == 250);
- VERIFY(scaler.GetSize().height == 250);
-
- pixels = (uint32_t*)scaler.GetScaledData();
-
- for (int y = 0; y < 250; y++) {
- for (int x = 0; x < 250; x++) {
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
- }
- }
-}
-
-void
-TestScaling::DoubleHalfScale()
-{
- std::vector<uint8_t> data;
- data.resize(500 * 500 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y += 2) {
- for (int x = 0; x < 500; x += 2) {
- pixels[y * 500 + x] = 0xff00ff00;
- pixels[y * 500 + x + 1] = 0xff00ffff;
- pixels[(y + 1) * 500 + x] = 0xff000000;
- pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
- }
- }
- ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
-
- scaler.ScaleForSize(IntSize(120, 110));
- VERIFY(scaler.GetSize().width == 125);
- VERIFY(scaler.GetSize().height == 125);
-
- pixels = (uint32_t*)scaler.GetScaledData();
-
- for (int y = 0; y < 125; y++) {
- for (int x = 0; x < 125; x++) {
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
- }
- }
-}
-
-void
-TestScaling::UnevenHalfScale()
-{
- std::vector<uint8_t> data;
- // Use a 16-byte aligned stride still, we test none-aligned strides
- // separately.
- data.resize(499 * 500 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y += 2) {
- for (int x = 0; x < 500; x += 2) {
- pixels[y * 500 + x] = 0xff00ff00;
- if (x < 498) {
- pixels[y * 500 + x + 1] = 0xff00ffff;
- }
- if (y < 498) {
- pixels[(y + 1) * 500 + x] = 0xff000000;
- if (x < 498) {
- pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
- }
- }
- }
- }
- ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(499, 499));
-
- scaler.ScaleForSize(IntSize(220, 220));
- VERIFY(scaler.GetSize().width == 249);
- VERIFY(scaler.GetSize().height == 249);
-
- pixels = (uint32_t*)scaler.GetScaledData();
-
- for (int y = 0; y < 249; y++) {
- for (int x = 0; x < 249; x++) {
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
- }
- }
-}
-
-void
-TestScaling::OddStrideHalfScale()
-{
- std::vector<uint8_t> data;
- // Use a 4-byte aligned stride to test if that doesn't cause any issues.
- data.resize(499 * 499 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y += 2) {
- for (int x = 0; x < 500; x += 2) {
- pixels[y * 499 + x] = 0xff00ff00;
- if (x < 498) {
- pixels[y * 499 + x + 1] = 0xff00ffff;
- }
- if (y < 498) {
- pixels[(y + 1) * 499 + x] = 0xff000000;
- if (x < 498) {
- pixels[(y + 1) * 499 + x + 1] = 0xff0000ff;
- }
- }
- }
- }
- ImageHalfScaler scaler(&data.front(), 499 * 4, IntSize(499, 499));
-
- scaler.ScaleForSize(IntSize(220, 220));
- VERIFY(scaler.GetSize().width == 249);
- VERIFY(scaler.GetSize().height == 249);
-
- pixels = (uint32_t*)scaler.GetScaledData();
-
- for (int y = 0; y < 249; y++) {
- for (int x = 0; x < 249; x++) {
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
- }
- }
-}
-void
-TestScaling::VerticalHalfScale()
-{
- std::vector<uint8_t> data;
- data.resize(500 * 500 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y += 2) {
- for (int x = 0; x < 500; x += 2) {
- pixels[y * 500 + x] = 0xff00ff00;
- pixels[y * 500 + x + 1] = 0xff00ffff;
- pixels[(y + 1) * 500 + x] = 0xff000000;
- pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
- }
- }
- ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
-
- scaler.ScaleForSize(IntSize(400, 240));
- VERIFY(scaler.GetSize().width == 500);
- VERIFY(scaler.GetSize().height == 250);
-
- pixels = (uint32_t*)scaler.GetScaledData();
-
- for (int y = 0; y < 250; y++) {
- for (int x = 0; x < 500; x += 2) {
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f00);
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 1] == 0xff007fff);
- }
- }
-}
-
-void
-TestScaling::HorizontalHalfScale()
-{
- std::vector<uint8_t> data;
- data.resize(520 * 500 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y ++) {
- for (int x = 0; x < 520; x += 8) {
- pixels[y * 520 + x] = 0xff00ff00;
- pixels[y * 520 + x + 1] = 0xff00ffff;
- pixels[y * 520 + x + 2] = 0xff000000;
- pixels[y * 520 + x + 3] = 0xff0000ff;
- pixels[y * 520 + x + 4] = 0xffff00ff;
- pixels[y * 520 + x + 5] = 0xff0000ff;
- pixels[y * 520 + x + 6] = 0xffffffff;
- pixels[y * 520 + x + 7] = 0xff0000ff;
- }
- }
- ImageHalfScaler scaler(&data.front(), 520 * 4, IntSize(520, 500));
-
- scaler.ScaleForSize(IntSize(240, 400));
- VERIFY(scaler.GetSize().width == 260);
- VERIFY(scaler.GetSize().height == 500);
-
- pixels = (uint32_t*)scaler.GetScaledData();
-
- for (int y = 0; y < 500; y++) {
- for (int x = 0; x < 260; x += 4) {
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff00ff7f);
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 1] == 0xff00007f);
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 2] == 0xff7f00ff);
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 3] == 0xff7f7fff);
- }
- }
-}
-
-void
-TestScaling::MixedHalfScale()
-{
- std::vector<uint8_t> data;
- data.resize(500 * 500 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y += 2) {
- for (int x = 0; x < 500; x += 2) {
- pixels[y * 500 + x] = 0xff00ff00;
- pixels[y * 500 + x + 1] = 0xff00ffff;
- pixels[(y + 1) * 500 + x] = 0xff000000;
- pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
- }
- }
- ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
-
- scaler.ScaleForSize(IntSize(120, 240));
- VERIFY(scaler.GetSize().width == 125);
- VERIFY(scaler.GetSize().height == 250);
- scaler.ScaleForSize(IntSize(240, 120));
- VERIFY(scaler.GetSize().width == 250);
- VERIFY(scaler.GetSize().height == 125);
-}
+
+#include "TestScaling.h"
+
+#include "ImageScaling.h"
+
+using namespace mozilla::gfx;
+
+TestScaling::TestScaling()
+{
+ REGISTER_TEST(TestScaling, BasicHalfScale);
+ REGISTER_TEST(TestScaling, DoubleHalfScale);
+ REGISTER_TEST(TestScaling, UnevenHalfScale);
+ REGISTER_TEST(TestScaling, OddStrideHalfScale);
+ REGISTER_TEST(TestScaling, VerticalHalfScale);
+ REGISTER_TEST(TestScaling, HorizontalHalfScale);
+ REGISTER_TEST(TestScaling, MixedHalfScale);
+}
+
+void
+TestScaling::BasicHalfScale()
+{
+ std::vector<uint8_t> data;
+ data.resize(500 * 500 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y += 2) {
+ for (int x = 0; x < 500; x += 2) {
+ pixels[y * 500 + x] = 0xff00ff00;
+ pixels[y * 500 + x + 1] = 0xff00ffff;
+ pixels[(y + 1) * 500 + x] = 0xff000000;
+ pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
+
+ scaler.ScaleForSize(IntSize(220, 240));
+
+ VERIFY(scaler.GetSize().width == 250);
+ VERIFY(scaler.GetSize().height == 250);
+
+ pixels = (uint32_t*)scaler.GetScaledData();
+
+ for (int y = 0; y < 250; y++) {
+ for (int x = 0; x < 250; x++) {
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
+ }
+ }
+}
+
+void
+TestScaling::DoubleHalfScale()
+{
+ std::vector<uint8_t> data;
+ data.resize(500 * 500 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y += 2) {
+ for (int x = 0; x < 500; x += 2) {
+ pixels[y * 500 + x] = 0xff00ff00;
+ pixels[y * 500 + x + 1] = 0xff00ffff;
+ pixels[(y + 1) * 500 + x] = 0xff000000;
+ pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
+
+ scaler.ScaleForSize(IntSize(120, 110));
+ VERIFY(scaler.GetSize().width == 125);
+ VERIFY(scaler.GetSize().height == 125);
+
+ pixels = (uint32_t*)scaler.GetScaledData();
+
+ for (int y = 0; y < 125; y++) {
+ for (int x = 0; x < 125; x++) {
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
+ }
+ }
+}
+
+void
+TestScaling::UnevenHalfScale()
+{
+ std::vector<uint8_t> data;
+ // Use a 16-byte aligned stride still, we test none-aligned strides
+ // separately.
+ data.resize(499 * 500 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y += 2) {
+ for (int x = 0; x < 500; x += 2) {
+ pixels[y * 500 + x] = 0xff00ff00;
+ if (x < 498) {
+ pixels[y * 500 + x + 1] = 0xff00ffff;
+ }
+ if (y < 498) {
+ pixels[(y + 1) * 500 + x] = 0xff000000;
+ if (x < 498) {
+ pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
+ }
+ }
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(499, 499));
+
+ scaler.ScaleForSize(IntSize(220, 220));
+ VERIFY(scaler.GetSize().width == 249);
+ VERIFY(scaler.GetSize().height == 249);
+
+ pixels = (uint32_t*)scaler.GetScaledData();
+
+ for (int y = 0; y < 249; y++) {
+ for (int x = 0; x < 249; x++) {
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
+ }
+ }
+}
+
+void
+TestScaling::OddStrideHalfScale()
+{
+ std::vector<uint8_t> data;
+ // Use a 4-byte aligned stride to test if that doesn't cause any issues.
+ data.resize(499 * 499 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y += 2) {
+ for (int x = 0; x < 500; x += 2) {
+ pixels[y * 499 + x] = 0xff00ff00;
+ if (x < 498) {
+ pixels[y * 499 + x + 1] = 0xff00ffff;
+ }
+ if (y < 498) {
+ pixels[(y + 1) * 499 + x] = 0xff000000;
+ if (x < 498) {
+ pixels[(y + 1) * 499 + x + 1] = 0xff0000ff;
+ }
+ }
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 499 * 4, IntSize(499, 499));
+
+ scaler.ScaleForSize(IntSize(220, 220));
+ VERIFY(scaler.GetSize().width == 249);
+ VERIFY(scaler.GetSize().height == 249);
+
+ pixels = (uint32_t*)scaler.GetScaledData();
+
+ for (int y = 0; y < 249; y++) {
+ for (int x = 0; x < 249; x++) {
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
+ }
+ }
+}
+void
+TestScaling::VerticalHalfScale()
+{
+ std::vector<uint8_t> data;
+ data.resize(500 * 500 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y += 2) {
+ for (int x = 0; x < 500; x += 2) {
+ pixels[y * 500 + x] = 0xff00ff00;
+ pixels[y * 500 + x + 1] = 0xff00ffff;
+ pixels[(y + 1) * 500 + x] = 0xff000000;
+ pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
+
+ scaler.ScaleForSize(IntSize(400, 240));
+ VERIFY(scaler.GetSize().width == 500);
+ VERIFY(scaler.GetSize().height == 250);
+
+ pixels = (uint32_t*)scaler.GetScaledData();
+
+ for (int y = 0; y < 250; y++) {
+ for (int x = 0; x < 500; x += 2) {
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f00);
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 1] == 0xff007fff);
+ }
+ }
+}
+
+void
+TestScaling::HorizontalHalfScale()
+{
+ std::vector<uint8_t> data;
+ data.resize(520 * 500 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y ++) {
+ for (int x = 0; x < 520; x += 8) {
+ pixels[y * 520 + x] = 0xff00ff00;
+ pixels[y * 520 + x + 1] = 0xff00ffff;
+ pixels[y * 520 + x + 2] = 0xff000000;
+ pixels[y * 520 + x + 3] = 0xff0000ff;
+ pixels[y * 520 + x + 4] = 0xffff00ff;
+ pixels[y * 520 + x + 5] = 0xff0000ff;
+ pixels[y * 520 + x + 6] = 0xffffffff;
+ pixels[y * 520 + x + 7] = 0xff0000ff;
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 520 * 4, IntSize(520, 500));
+
+ scaler.ScaleForSize(IntSize(240, 400));
+ VERIFY(scaler.GetSize().width == 260);
+ VERIFY(scaler.GetSize().height == 500);
+
+ pixels = (uint32_t*)scaler.GetScaledData();
+
+ for (int y = 0; y < 500; y++) {
+ for (int x = 0; x < 260; x += 4) {
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff00ff7f);
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 1] == 0xff00007f);
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 2] == 0xff7f00ff);
+ VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 3] == 0xff7f7fff);
+ }
+ }
+}
+
+void
+TestScaling::MixedHalfScale()
+{
+ std::vector<uint8_t> data;
+ data.resize(500 * 500 * 4);
+
+ uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
+ for (int y = 0; y < 500; y += 2) {
+ for (int x = 0; x < 500; x += 2) {
+ pixels[y * 500 + x] = 0xff00ff00;
+ pixels[y * 500 + x + 1] = 0xff00ffff;
+ pixels[(y + 1) * 500 + x] = 0xff000000;
+ pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
+ }
+ }
+ ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
+
+ scaler.ScaleForSize(IntSize(120, 240));
+ VERIFY(scaler.GetSize().width == 125);
+ VERIFY(scaler.GetSize().height == 250);
+ scaler.ScaleForSize(IntSize(240, 120));
+ VERIFY(scaler.GetSize().width == 250);
+ VERIFY(scaler.GetSize().height == 125);
+}
diff --git a/gfx/2d/unittest/TestScaling.h b/gfx/2d/unittest/TestScaling.h
index 928dda45c..e9bd1a8e0 100644
--- a/gfx/2d/unittest/TestScaling.h
+++ b/gfx/2d/unittest/TestScaling.h
@@ -2,21 +2,21 @@
* 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/. */
-
-#pragma once
-
-#include "TestBase.h"
-
-class TestScaling : public TestBase
-{
-public:
- TestScaling();
-
- void BasicHalfScale();
- void DoubleHalfScale();
- void UnevenHalfScale();
- void OddStrideHalfScale();
- void VerticalHalfScale();
- void HorizontalHalfScale();
- void MixedHalfScale();
-};
+
+#pragma once
+
+#include "TestBase.h"
+
+class TestScaling : public TestBase
+{
+public:
+ TestScaling();
+
+ void BasicHalfScale();
+ void DoubleHalfScale();
+ void UnevenHalfScale();
+ void OddStrideHalfScale();
+ void VerticalHalfScale();
+ void HorizontalHalfScale();
+ void MixedHalfScale();
+};