summaryrefslogtreecommitdiffstats
path: root/gfx/2d
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /gfx/2d
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'gfx/2d')
-rw-r--r--gfx/2d/2D.h1526
-rw-r--r--gfx/2d/AutoHelpersWin.h86
-rw-r--r--gfx/2d/BaseCoord.h110
-rw-r--r--gfx/2d/BaseMargin.h152
-rw-r--r--gfx/2d/BasePoint.h121
-rw-r--r--gfx/2d/BasePoint3D.h122
-rw-r--r--gfx/2d/BasePoint4D.h131
-rw-r--r--gfx/2d/BaseRect.h586
-rw-r--r--gfx/2d/BaseSize.h100
-rw-r--r--gfx/2d/BezierUtils.cpp339
-rw-r--r--gfx/2d/BezierUtils.h185
-rw-r--r--gfx/2d/BigEndianInts.h80
-rw-r--r--gfx/2d/Blur.cpp770
-rw-r--r--gfx/2d/Blur.h186
-rw-r--r--gfx/2d/BlurLS3.cpp588
-rw-r--r--gfx/2d/BlurNEON.cpp288
-rw-r--r--gfx/2d/BlurSSE2.cpp315
-rw-r--r--gfx/2d/BorrowedContext.h217
-rw-r--r--gfx/2d/CGTextDrawing.h144
-rw-r--r--gfx/2d/Coord.h151
-rw-r--r--gfx/2d/CriticalSection.h80
-rw-r--r--gfx/2d/DataSourceSurface.cpp21
-rw-r--r--gfx/2d/DataSourceSurfaceWrapper.h39
-rw-r--r--gfx/2d/DataSurfaceHelpers.cpp374
-rw-r--r--gfx/2d/DataSurfaceHelpers.h147
-rw-r--r--gfx/2d/DrawCommand.h594
-rw-r--r--gfx/2d/DrawEventRecorder.cpp117
-rw-r--r--gfx/2d/DrawEventRecorder.h148
-rw-r--r--gfx/2d/DrawTarget.cpp56
-rw-r--r--gfx/2d/DrawTargetCairo.cpp2374
-rw-r--r--gfx/2d/DrawTargetCairo.h262
-rw-r--r--gfx/2d/DrawTargetCapture.cpp201
-rw-r--r--gfx/2d/DrawTargetCapture.h168
-rw-r--r--gfx/2d/DrawTargetD2D1.cpp1931
-rw-r--r--gfx/2d/DrawTargetD2D1.h297
-rw-r--r--gfx/2d/DrawTargetDual.cpp223
-rw-r--r--gfx/2d/DrawTargetDual.h178
-rw-r--r--gfx/2d/DrawTargetRecording.cpp737
-rw-r--r--gfx/2d/DrawTargetRecording.h337
-rw-r--r--gfx/2d/DrawTargetSkia.cpp2135
-rw-r--r--gfx/2d/DrawTargetSkia.h214
-rw-r--r--gfx/2d/DrawTargetTiled.cpp337
-rw-r--r--gfx/2d/DrawTargetTiled.h221
-rw-r--r--gfx/2d/DrawingJob.cpp120
-rw-r--r--gfx/2d/DrawingJob.h158
-rw-r--r--gfx/2d/ExtendInputEffectD2D1.cpp204
-rw-r--r--gfx/2d/ExtendInputEffectD2D1.h88
-rw-r--r--gfx/2d/Factory.cpp985
-rw-r--r--gfx/2d/FilterNodeD2D1.cpp1102
-rw-r--r--gfx/2d/FilterNodeD2D1.h133
-rw-r--r--gfx/2d/FilterNodeSoftware.cpp3689
-rw-r--r--gfx/2d/FilterNodeSoftware.h724
-rw-r--r--gfx/2d/FilterProcessing.cpp262
-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.h132
-rw-r--r--gfx/2d/GradientStopsD2D.h40
-rw-r--r--gfx/2d/Helpers.h97
-rw-r--r--gfx/2d/HelpersCairo.h352
-rw-r--r--gfx/2d/HelpersD2D.h967
-rw-r--r--gfx/2d/HelpersSkia.h396
-rw-r--r--gfx/2d/HelpersWinFonts.h61
-rw-r--r--gfx/2d/ImageScaling.cpp251
-rw-r--r--gfx/2d/ImageScaling.h76
-rw-r--r--gfx/2d/ImageScalingSSE2.cpp330
-rw-r--r--gfx/2d/IterableArena.h193
-rw-r--r--gfx/2d/JobScheduler.cpp288
-rw-r--r--gfx/2d/JobScheduler.h257
-rw-r--r--gfx/2d/JobScheduler_posix.cpp194
-rw-r--r--gfx/2d/JobScheduler_posix.h142
-rw-r--r--gfx/2d/JobScheduler_win32.cpp148
-rw-r--r--gfx/2d/JobScheduler_win32.h99
-rw-r--r--gfx/2d/Logging.h706
-rw-r--r--gfx/2d/LoggingConstants.h30
-rw-r--r--gfx/2d/MMIHelpers.h196
-rw-r--r--gfx/2d/MacIOSurface.cpp615
-rw-r--r--gfx/2d/MacIOSurface.h222
-rw-r--r--gfx/2d/Matrix.cpp134
-rw-r--r--gfx/2d/Matrix.h1676
-rw-r--r--gfx/2d/MatrixFwd.h26
-rw-r--r--gfx/2d/NativeFontResourceDWrite.cpp288
-rw-r--r--gfx/2d/NativeFontResourceDWrite.h57
-rw-r--r--gfx/2d/NativeFontResourceGDI.cpp63
-rw-r--r--gfx/2d/NativeFontResourceGDI.h55
-rw-r--r--gfx/2d/NativeFontResourceMac.cpp67
-rw-r--r--gfx/2d/NativeFontResourceMac.h43
-rw-r--r--gfx/2d/NumericTools.h43
-rw-r--r--gfx/2d/Path.cpp550
-rw-r--r--gfx/2d/PathAnalysis.h57
-rw-r--r--gfx/2d/PathCG.cpp435
-rw-r--r--gfx/2d/PathCG.h114
-rw-r--r--gfx/2d/PathCairo.cpp331
-rw-r--r--gfx/2d/PathCairo.h95
-rw-r--r--gfx/2d/PathD2D.cpp523
-rw-r--r--gfx/2d/PathD2D.h117
-rw-r--r--gfx/2d/PathHelpers.cpp277
-rw-r--r--gfx/2d/PathHelpers.h424
-rw-r--r--gfx/2d/PathRecording.cpp120
-rw-r--r--gfx/2d/PathRecording.h142
-rw-r--r--gfx/2d/PathSkia.cpp237
-rw-r--r--gfx/2d/PathSkia.h92
-rw-r--r--gfx/2d/PatternHelpers.h137
-rw-r--r--gfx/2d/Point.h343
-rw-r--r--gfx/2d/Polygon.h295
-rw-r--r--gfx/2d/QuartzSupport.h98
-rw-r--r--gfx/2d/QuartzSupport.mm625
-rw-r--r--gfx/2d/Quaternion.cpp57
-rw-r--r--gfx/2d/Quaternion.h111
-rw-r--r--gfx/2d/RadialGradientEffectD2D1.cpp398
-rw-r--r--gfx/2d/RadialGradientEffectD2D1.h101
-rw-r--r--gfx/2d/RecordedEvent.cpp1883
-rw-r--r--gfx/2d/RecordedEvent.h1281
-rw-r--r--gfx/2d/RecordingTypes.h41
-rw-r--r--gfx/2d/Rect.h334
-rw-r--r--gfx/2d/SFNTData.cpp251
-rw-r--r--gfx/2d/SFNTData.h95
-rw-r--r--gfx/2d/SFNTNameTable.cpp357
-rw-r--r--gfx/2d/SFNTNameTable.h73
-rw-r--r--gfx/2d/SIMD.h1180
-rw-r--r--gfx/2d/SSEHelpers.h17
-rw-r--r--gfx/2d/SVGTurbulenceRenderer-inl.h360
-rw-r--r--gfx/2d/Scale.cpp44
-rw-r--r--gfx/2d/Scale.h36
-rw-r--r--gfx/2d/ScaleFactor.h85
-rw-r--r--gfx/2d/ScaleFactors2D.h135
-rw-r--r--gfx/2d/ScaledFontBase.cpp266
-rw-r--r--gfx/2d/ScaledFontBase.h72
-rw-r--r--gfx/2d/ScaledFontCairo.cpp38
-rw-r--r--gfx/2d/ScaledFontCairo.h31
-rw-r--r--gfx/2d/ScaledFontDWrite.cpp293
-rw-r--r--gfx/2d/ScaledFontDWrite.h84
-rw-r--r--gfx/2d/ScaledFontFontconfig.cpp47
-rw-r--r--gfx/2d/ScaledFontFontconfig.h37
-rw-r--r--gfx/2d/ScaledFontMac.cpp247
-rw-r--r--gfx/2d/ScaledFontMac.h79
-rw-r--r--gfx/2d/ScaledFontWin.cpp103
-rw-r--r--gfx/2d/ScaledFontWin.h49
-rw-r--r--gfx/2d/ShadersD2D.fx746
-rw-r--r--gfx/2d/ShadersD2D.h16012
-rw-r--r--gfx/2d/ShadersD2D1.h1419
-rw-r--r--gfx/2d/ShadersD2D1.hlsl117
-rw-r--r--gfx/2d/SourceSurfaceCairo.cpp164
-rw-r--r--gfx/2d/SourceSurfaceCairo.h70
-rw-r--r--gfx/2d/SourceSurfaceD2D1.cpp241
-rw-r--r--gfx/2d/SourceSurfaceD2D1.h95
-rw-r--r--gfx/2d/SourceSurfaceDual.h47
-rw-r--r--gfx/2d/SourceSurfaceRawData.cpp81
-rw-r--r--gfx/2d/SourceSurfaceRawData.h160
-rw-r--r--gfx/2d/SourceSurfaceSkia.cpp172
-rw-r--r--gfx/2d/SourceSurfaceSkia.h61
-rw-r--r--gfx/2d/StackArray.h30
-rw-r--r--gfx/2d/Tools.h246
-rw-r--r--gfx/2d/Triangle.h66
-rw-r--r--gfx/2d/Types.h407
-rw-r--r--gfx/2d/UserData.h128
-rw-r--r--gfx/2d/convolver.cpp562
-rw-r--r--gfx/2d/convolver.h201
-rw-r--r--gfx/2d/convolverLS3.cpp927
-rw-r--r--gfx/2d/convolverLS3.h75
-rw-r--r--gfx/2d/convolverSSE2.cpp471
-rw-r--r--gfx/2d/convolverSSE2.h68
-rw-r--r--gfx/2d/genshaders.sh10
-rw-r--r--gfx/2d/gfx2d.sln29
-rw-r--r--gfx/2d/gfx2d.vcxproj139
-rw-r--r--gfx/2d/image_operations.cpp390
-rw-r--r--gfx/2d/image_operations.h285
-rw-r--r--gfx/2d/moz.build230
-rw-r--r--gfx/2d/ssse3-scaler.c561
-rw-r--r--gfx/2d/ssse3-scaler.h22
-rw-r--r--gfx/2d/u16string.h24
-rw-r--r--gfx/2d/unittest/Main.cpp56
-rw-r--r--gfx/2d/unittest/SanityChecks.cpp19
-rw-r--r--gfx/2d/unittest/SanityChecks.h16
-rw-r--r--gfx/2d/unittest/TestBase.cpp48
-rw-r--r--gfx/2d/unittest/TestBase.h54
-rw-r--r--gfx/2d/unittest/TestBugs.cpp86
-rw-r--r--gfx/2d/unittest/TestBugs.h18
-rw-r--r--gfx/2d/unittest/TestCairo.cpp96
-rw-r--r--gfx/2d/unittest/TestDrawTargetBase.cpp109
-rw-r--r--gfx/2d/unittest/TestDrawTargetBase.h38
-rw-r--r--gfx/2d/unittest/TestDrawTargetD2D.cpp23
-rw-r--r--gfx/2d/unittest/TestDrawTargetD2D.h19
-rw-r--r--gfx/2d/unittest/TestPoint.cpp46
-rw-r--r--gfx/2d/unittest/TestPoint.h17
-rw-r--r--gfx/2d/unittest/TestScaling.cpp249
-rw-r--r--gfx/2d/unittest/TestScaling.h22
-rw-r--r--gfx/2d/unittest/unittest.vcxproj94
190 files changed, 73650 insertions, 0 deletions
diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h
new file mode 100644
index 000000000..c1fba3463
--- /dev/null
+++ b/gfx/2d/2D.h
@@ -0,0 +1,1526 @@
+/* -*- 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_H
+#define _MOZILLA_GFX_2D_H
+
+#include "Types.h"
+#include "Point.h"
+#include "Rect.h"
+#include "Matrix.h"
+#include "Quaternion.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
+
+#include "gfxPrefs.h"
+
+struct _cairo_surface;
+typedef _cairo_surface cairo_surface_t;
+
+struct _cairo_scaled_font;
+typedef _cairo_scaled_font cairo_scaled_font_t;
+
+struct _FcPattern;
+typedef _FcPattern FcPattern;
+
+struct ID3D11Texture2D;
+struct ID3D11Device;
+struct ID2D1Device;
+struct IDWriteFactory;
+struct IDWriteRenderingParams;
+struct IDWriteFontFace;
+
+class GrContext;
+class SkCanvas;
+struct gfxFontStyle;
+
+struct CGContext;
+typedef struct CGContext *CGContextRef;
+
+namespace mozilla {
+
+namespace gfx {
+
+class SourceSurface;
+class DataSourceSurface;
+class DrawTarget;
+class DrawEventRecorder;
+class FilterNode;
+class LogForwarder;
+
+struct NativeSurface {
+ NativeSurfaceType mType;
+ SurfaceFormat mFormat;
+ gfx::IntSize mSize;
+ void *mSurface;
+};
+
+struct NativeFont {
+ NativeFontType mType;
+ void *mFont;
+};
+
+/**
+ * This structure is used to send draw options that are universal to all drawing
+ * operations.
+ */
+struct DrawOptions {
+ /// 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)
+ {}
+
+ 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.
+ */
+struct StrokeOptions {
+ /// 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)
+ , mDashLength(aDashLength)
+ , mDashOffset(aDashOffset)
+ , mLineJoin(aLineJoin)
+ , mLineCap(aLineCap)
+ {
+ MOZ_ASSERT(aDashLength == 0 || aDashPattern);
+ }
+
+ 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.
+ */
+struct DrawSurfaceOptions {
+ /// For constructor parameter description, see member data documentation.
+ explicit DrawSurfaceOptions(SamplingFilter aSamplingFilter = SamplingFilter::LINEAR,
+ SamplingBounds aSamplingBounds = SamplingBounds::UNBOUNDED)
+ : mSamplingFilter(aSamplingFilter)
+ , mSamplingBounds(aSamplingBounds)
+ { }
+
+ SamplingFilter mSamplingFilter; /**< SamplingFilter 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.
+ */
+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
+ * example the gradient stops on a gradient pattern can be backend specific.
+ */
+class Pattern
+{
+public:
+ virtual ~Pattern() {}
+
+ virtual PatternType GetType() const = 0;
+
+protected:
+ Pattern() {}
+};
+
+class ColorPattern : public Pattern
+{
+public:
+ // Explicit because consumers should generally use ToDeviceColor when
+ // creating a ColorPattern.
+ explicit ColorPattern(const Color &aColor)
+ : mColor(aColor)
+ {}
+
+ 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.
+ */
+class LinearGradientPattern : public Pattern
+{
+public:
+ /// For constructor parameter description, see member data documentation.
+ LinearGradientPattern(const Point &aBegin,
+ const Point &aEnd,
+ GradientStops *aStops,
+ const Matrix &aMatrix = Matrix())
+ : mBegin(aBegin)
+ , mEnd(aEnd)
+ , mStops(aStops)
+ , mMatrix(aMatrix)
+ {
+ }
+
+ virtual PatternType GetType() const override
+ {
+ return PatternType::LINEAR_GRADIENT;
+ }
+
+ 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.
+ */
+class RadialGradientPattern : public Pattern
+{
+public:
+ /// For constructor parameter description, see member data documentation.
+ RadialGradientPattern(const Point &aCenter1,
+ const Point &aCenter2,
+ Float aRadius1,
+ Float aRadius2,
+ GradientStops *aStops,
+ const Matrix &aMatrix = Matrix())
+ : mCenter1(aCenter1)
+ , mCenter2(aCenter2)
+ , mRadius1(aRadius1)
+ , mRadius2(aRadius2)
+ , mStops(aStops)
+ , mMatrix(aMatrix)
+ {
+ }
+
+ virtual PatternType GetType() const override
+ {
+ return PatternType::RADIAL_GRADIENT;
+ }
+
+ 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:
+ /// For constructor parameter description, see member data documentation.
+ SurfacePattern(SourceSurface *aSourceSurface, ExtendMode aExtendMode,
+ const Matrix &aMatrix = Matrix(),
+ SamplingFilter aSamplingFilter = SamplingFilter::GOOD,
+ const IntRect &aSamplingRect = IntRect())
+ : mSurface(aSourceSurface)
+ , mExtendMode(aExtendMode)
+ , mSamplingFilter(aSamplingFilter)
+ , mMatrix(aMatrix)
+ , mSamplingRect(aSamplingRect)
+ {}
+
+ 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 */
+ SamplingFilter mSamplingFilter; //!< Resampling filter for resampling the image.
+ Matrix mMatrix; //!< Transforms the pattern into user space
+
+ 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 or a DrawSurface call.
+ * They cannot be drawn to directly.
+ *
+ * Although SourceSurface has thread-safe refcount, some SourceSurface cannot
+ * be used on random threads at the same time. Only DataSourceSurface can be
+ * used on random threads now. This will be fixed in the future. Eventually
+ * all SourceSurface should be thread-safe.
+ */
+class SourceSurface : public external::AtomicRefCounted<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
+ * 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 already_AddRefed<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:
+ 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
+ };
+
+ /**
+ * This is a scoped version of Map(). Map() is called in the constructor and
+ * Unmap() in the destructor. Use this for automatic unmapping of your data
+ * surfaces.
+ *
+ * Use IsMapped() to verify whether Map() succeeded or not.
+ */
+ class ScopedMap {
+ public:
+ explicit ScopedMap(DataSourceSurface* aSurface, MapType aType)
+ : mSurface(aSurface)
+ , mIsMapped(aSurface->Map(aType, &mMap)) {}
+
+ virtual ~ScopedMap()
+ {
+ if (mIsMapped) {
+ mSurface->Unmap();
+ }
+ }
+
+ uint8_t* GetData() const
+ {
+ MOZ_ASSERT(mIsMapped);
+ return mMap.mData;
+ }
+
+ int32_t GetStride() const
+ {
+ MOZ_ASSERT(mIsMapped);
+ return mMap.mStride;
+ }
+
+ const MappedSurface* GetMappedSurface() const
+ {
+ MOZ_ASSERT(mIsMapped);
+ return &mMap;
+ }
+
+ bool IsMapped() const { return mIsMapped; }
+
+ private:
+ RefPtr<DataSourceSurface> mSurface;
+ MappedSurface mMap;
+ bool mIsMapped;
+ };
+
+ 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;
+
+ /**
+ * The caller is responsible for ensuring aMappedSurface is not null.
+ */
+ virtual bool Map(MapType, MappedSurface *aMappedSurface)
+ {
+ aMappedSurface->mData = GetData();
+ aMappedSurface->mStride = Stride();
+ mIsMapped = !!aMappedSurface->mData;
+ return mIsMapped;
+ }
+
+ 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 already_AddRefed<DataSourceSurface> GetDataSurface() override;
+
+protected:
+ bool mIsMapped;
+};
+
+/** 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
+ * 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 */
+ virtual void LineTo(const Point &aPoint) = 0;
+ /** 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 */
+ virtual void QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2) = 0;
+ /** 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 */
+ 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
+ * 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
+ * filled or stroked to a DrawTarget
+ */
+class Path : public RefCounted<Path>
+{
+public:
+ 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 path and is still writable.
+ */
+ inline already_AddRefed<PathBuilder> CopyToBuilder() const {
+ return CopyToBuilder(GetFillRule());
+ }
+ inline already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform) const {
+ return TransformedCopyToBuilder(aTransform, GetFillRule());
+ }
+ /** This returns a PathBuilder object that contains a copy of the contents of
+ * this path, converted to use the specified FillRule, and still writable.
+ */
+ virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const = 0;
+ virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
+ FillRule aFillRule) const = 0;
+
+ /** 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
+ * specified strokeoptions. It allows passing a transform that will transform
+ * the path to the coordinate space in which aPoint is given.
+ */
+ virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const = 0;
+
+ /** 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
+ * specified strokeoptions. These bounds are not guaranteed to be tight.
+ * A transform may be specified that gives the bounds after application of
+ * the transform.
+ */
+ virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform = Matrix()) const = 0;
+
+ /** 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
+ * pathbuilder it may no longer be written to.
+ */
+class PathBuilder : public PathSink
+{
+public:
+ 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 already_AddRefed<Path> Finish() = 0;
+
+ virtual BackendType GetBackendType() const = 0;
+};
+
+struct Glyph
+{
+ uint32_t mIndex;
+ Point mPosition;
+};
+
+/** 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
+{
+ const Glyph *mGlyphs; //!< A pointer to a buffer of glyphs. Managed by the caller.
+ uint32_t mNumGlyphs; //!< Number of glyphs mGlyphs points to.
+};
+
+struct GlyphMetrics
+{
+ // Horizontal distance from the origin to the leftmost side of the bounding
+ // box of the drawn glyph. This can be negative!
+ Float mXBearing;
+ // Horizontal distance from the origin of this glyph to the origin of the
+ // next glyph.
+ Float mXAdvance;
+ // Vertical distance from the origin to the topmost side of the bounding box
+ // of the drawn glyph.
+ Float mYBearing;
+ // Vertical distance from the origin of this glyph to the origin of the next
+ // glyph, this is used when drawing vertically and will typically be 0.
+ Float mYAdvance;
+ // Width of the glyph's black box.
+ Float mWidth;
+ // Height of the glyph's black box.
+ Float mHeight;
+};
+
+/** 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);
+ typedef void (*FontInstanceDataOutput)(const uint8_t* aData, uint32_t aLength, void* aBaton);
+ typedef void (*FontDescriptorOutput)(const uint8_t *aData, uint32_t aLength, Float aFontSize, void *aBaton);
+
+ virtual FontType GetType() const = 0;
+ virtual AntialiasMode GetDefaultAAMode() {
+ if (gfxPrefs::DisableAllTextAA()) {
+ return AntialiasMode::NONE;
+ }
+
+ return AntialiasMode::DEFAULT;
+ }
+
+ /** 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 already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) = 0;
+
+ /** 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, const Matrix *aTransformHint = nullptr) = 0;
+
+ /* This gets the metrics of a set of glyphs for the current font face.
+ */
+ virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics) = 0;
+
+ virtual bool GetFontFileData(FontFileDataOutput, void *) { return false; }
+
+ virtual bool GetFontInstanceData(FontInstanceDataOutput, void *) { return false; }
+
+ virtual bool GetFontDescriptor(FontDescriptorOutput, void *) { return false; }
+
+ void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
+ mUserData.Add(key, userData, destroy);
+ }
+ void *GetUserData(UserDataKey *key) {
+ return mUserData.Get(key);
+ }
+
+protected:
+ ScaledFont() {}
+
+ UserData mUserData;
+};
+
+/**
+ * Derived classes hold a native font resource from which to create
+ * ScaledFonts.
+ */
+class NativeFontResource : public RefCounted<NativeFontResource>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResource)
+
+ /**
+ * Creates a ScaledFont using the font corresponding to the index and
+ * the given glyph size.
+ *
+ * @param aIndex index for the font within the resource.
+ * @param aGlyphSize the size of ScaledFont required.
+ * @param aInstanceData pointer to read-only buffer of any available instance data.
+ * @param aInstanceDataLength the size of the instance data.
+ * @return an already_addrefed ScaledFont, containing nullptr if failed.
+ */
+ virtual already_AddRefed<ScaledFont>
+ CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
+ const uint8_t* aInstanceData, uint32_t aInstanceDataLength) = 0;
+
+ virtual ~NativeFontResource() {};
+};
+
+/** 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
+ * parameters.
+ */
+class GlyphRenderingOptions : public RefCounted<GlyphRenderingOptions>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptions)
+ virtual ~GlyphRenderingOptions() {}
+
+ virtual FontType GetType() const = 0;
+
+protected:
+ GlyphRenderingOptions() {}
+};
+
+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.
+ */
+class DrawTarget : public RefCounted<DrawTarget>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTarget)
+ DrawTarget() : mTransformDirty(false), mPermitSubpixelAA(false) {}
+ virtual ~DrawTarget() {}
+
+ virtual bool IsValid() const { return true; };
+ virtual DrawTargetType GetType() const = 0;
+
+ virtual BackendType GetBackendType() const = 0;
+
+ virtual bool IsRecording() const { return false; }
+
+ /**
+ * 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
+ * normally return the same SourceSurface object.
+ */
+ virtual already_AddRefed<SourceSurface> Snapshot() = 0;
+ virtual IntSize GetSize() = 0;
+
+ /**
+ * 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,
+ IntPoint* aOrigin = nullptr) { 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.
+ *
+ * @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,
+ const Rect &aSource,
+ 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!
+ *
+ * @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,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator) = 0;
+
+ /**
+ * Clear a rectangle on the draw target to transparent black. This will
+ * respect the clipping region and transform.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /**
+ * Stroke a path on the draw target with a certain source pattern.
+ *
+ * @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.
+ *
+ * @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 = 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.
+ *
+ * @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.
+ *
+ * @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;
+
+ /**
+ * Draw aSurface using the 3D transform aMatrix. The DrawTarget's transform
+ * and clip are applied after the 3D transform.
+ *
+ * If the transform fails (i.e. because aMatrix is singular), false is returned and nothing is drawn.
+ */
+ virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
+ const Matrix4x4& aMatrix);
+
+ /**
+ * Push a clip to the DrawTarget.
+ *
+ * @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.
+ *
+ * @param aRect The rect to clip to
+ */
+ virtual void PushClipRect(const Rect &aRect) = 0;
+
+ /**
+ * Push a clip region specifed by the union of axis-aligned rectangular
+ * clips to the DrawTarget. These rectangles are specified in device space.
+ * This must be balanced by a corresponding call to PopClip within a layer.
+ *
+ * @param aRects The rects to clip to
+ * @param aCount The number of rectangles
+ */
+ virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount);
+
+ /** Pop a clip from the DrawTarget. A pop without a corresponding push will
+ * be ignored.
+ */
+ virtual void PopClip() = 0;
+
+ /**
+ * Push a 'layer' to the DrawTarget, a layer is a temporary surface that all
+ * drawing will be redirected to, this is used for example to support group
+ * opacity or the masking of groups. Clips must be balanced within a layer,
+ * i.e. between a matching PushLayer/PopLayer pair there must be as many
+ * PushClip(Rect) calls as there are PopClip calls.
+ *
+ * @param aOpaque Whether the layer will be opaque
+ * @param aOpacity Opacity of the layer
+ * @param aMask Mask applied to the layer
+ * @param aMaskTransform Transform applied to the layer mask
+ * @param aBounds Optional bounds in device space to which the layer is
+ * limited in size.
+ * @param aCopyBackground Whether to copy the background into the layer, this
+ * is only supported when aOpaque is true.
+ */
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) { MOZ_CRASH("GFX: PushLayer"); }
+
+ /**
+ * This balances a call to PushLayer and proceeds to blend the layer back
+ * onto the background. This blend will blend the temporary surface back
+ * onto the target in device space using POINT sampling and operator over.
+ */
+ virtual void PopLayer() { MOZ_CRASH("GFX: PopLayer"); }
+
+ /**
+ * Create a SourceSurface optimized for use with this DrawTarget from
+ * existing bitmap data in memory.
+ *
+ * The SourceSurface does not take ownership of aData, and may be freed at any time.
+ */
+ virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const = 0;
+
+ /**
+ * 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 already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const = 0;
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurfaceForUnknownAlpha(SourceSurface *aSurface) const {
+ return OptimizeSourceSurface(aSurface);
+ }
+
+ /**
+ * 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. If this succeeds, the SourceSurface takes the ownersip of the NativeSurface.
+ */
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const = 0;
+
+ /**
+ * Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
+ */
+ virtual already_AddRefed<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 already_AddRefed<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
+ * shadow. Also note that this doesn't affect the size of the allocated
+ * surface, the caller is still responsible for including the shadow area in
+ * its size.
+ */
+ virtual already_AddRefed<DrawTarget>
+ CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
+ float aSigma) const
+ {
+ 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 already_AddRefed<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.
+ *
+ * @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 already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const = 0;
+
+ /**
+ * 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 already_AddRefed<FilterNode> CreateFilter(FilterType aType) = 0;
+
+ Matrix GetTransform() const { return mTransform; }
+
+ /*
+ * Get the metrics of a glyph, including any additional spacing that is taken
+ * during rasterization to this backends (for example because of antialiasing
+ * filters.
+ *
+ * aScaledFont The scaled font used when drawing.
+ * aGlyphIndices An array of indices for the glyphs whose the metrics are wanted
+ * aNumGlyphs The amount of elements in aGlyphIndices
+ * aGlyphMetrics The glyph metrics
+ */
+ virtual void GetGlyphRasterizationMetrics(ScaledFont *aScaledFont, const uint16_t* aGlyphIndices,
+ uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics)
+ {
+ aScaledFont->GetGlyphDesignMetrics(aGlyphIndices, aNumGlyphs, aGlyphMetrics);
+ }
+
+ /**
+ * 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; }
+
+ inline void ConcatTransform(const Matrix &aTransform)
+ { SetTransform(aTransform * Matrix(GetTransform())); }
+
+ 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 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) 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
+ * 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.
+ */
+ void SetOpaqueRect(const IntRect &aRect) {
+ mOpaqueRect = aRect;
+ }
+
+ const IntRect &GetOpaqueRect() const {
+ return mOpaqueRect;
+ }
+
+ virtual bool IsCurrentGroupOpaque() {
+ return GetFormat() == SurfaceFormat::B8G8R8X8;
+ }
+
+ virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) {
+ mPermitSubpixelAA = aPermitSubpixelAA;
+ }
+
+ bool GetPermitSubpixelAA() {
+ return mPermitSubpixelAA;
+ }
+
+ /**
+ * Ensures that no snapshot is still pointing to this DrawTarget's surface data.
+ *
+ * This can be useful if the DrawTarget is wrapped around data that it does not
+ * own, and for some reason the owner of the data has to make it temporarily
+ * unavailable without the DrawTarget knowing about it.
+ * This can cause costly surface copies, so it should not be used without a
+ * a good reason.
+ */
+ virtual void DetachAllSnapshots() = 0;
+
+#ifdef USE_SKIA_GPU
+ virtual bool InitWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat)
+ {
+ MOZ_CRASH("GFX: InitWithGrContext");
+ }
+#endif
+
+protected:
+ UserData mUserData;
+ Matrix mTransform;
+ IntRect mOpaqueRect;
+ bool mTransformDirty : 1;
+ bool mPermitSubpixelAA : 1;
+
+ 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();
+
+ /**
+ * Returns false if any of the following are true:
+ *
+ * - the width/height of |sz| are less than or equal to zero
+ * - the width/height of |sz| are greater than |limit|
+ * - the number of bytes that need to be allocated for the surface is too
+ * big to fit in an int32_t, or bigger than |allocLimit|, if specifed
+ *
+ * To calculate the number of bytes that need to be allocated for the surface
+ * this function makes the conservative assumption that there need to be
+ * 4 bytes-per-pixel, and the stride alignment is 16 bytes.
+ *
+ * The reason for using int32_t rather than uint32_t is again to be
+ * conservative; some code has in the past and may in the future use signed
+ * integers to store buffer lengths etc.
+ */
+ static bool CheckSurfaceSize(const IntSize &sz,
+ int32_t limit = 0,
+ int32_t allocLimit = 0);
+
+ /**
+ * Make sure that the given buffer size doesn't exceed the allocation limit.
+ */
+ static bool CheckBufferSize(int32_t bufSize);
+
+ /** 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 already_AddRefed<DrawTarget> CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr);
+
+ static already_AddRefed<SourceSurface> CreateSourceSurfaceForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat aFormat);
+
+ static already_AddRefed<DrawTarget>
+ CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat);
+
+ static already_AddRefed<DrawTarget>
+ CreateRecordingDrawTarget(DrawEventRecorder *aRecorder, DrawTarget *aDT);
+
+ static already_AddRefed<DrawTarget>
+ CreateDrawTargetForData(BackendType aBackend, unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false);
+
+ static already_AddRefed<ScaledFont>
+ CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize);
+
+#ifdef MOZ_WIDGET_GTK
+ static already_AddRefed<ScaledFont>
+ CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize);
+#endif
+
+ /**
+ * This creates a NativeFontResource from TrueType data.
+ *
+ * @param aData Pointer to the data
+ * @param aSize Size of the TrueType data
+ * @param aType Type of NativeFontResource that should be created.
+ * @return a NativeFontResource of nullptr if failed.
+ */
+ static already_AddRefed<NativeFontResource>
+ CreateNativeFontResource(uint8_t *aData, uint32_t aSize, 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.
+ */
+ static already_AddRefed<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. The caller is responsible for handing the case where nullptr
+ * is returned. The surface is not zeroed unless requested.
+ */
+ static already_AddRefed<DataSourceSurface>
+ 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 already_AddRefed<DataSourceSurface>
+ CreateDataSourceSurfaceWithStride(const IntSize &aSize, SurfaceFormat aFormat, int32_t aStride, bool aZero = false);
+
+ typedef void (*SourceSurfaceDeallocator)(void* aClosure);
+
+ /**
+ * This creates a simple data source surface for some existing data. It will
+ * wrap this data and the data for this source surface.
+ *
+ * We can provide a custom destroying function for |aData|. This will be
+ * called in the surface dtor using |aDeallocator| and the |aClosure|. If
+ * there are errors during construction(return a nullptr surface), the caller
+ * is responsible for the deallocation.
+ *
+ * If there is no destroying function, the caller is responsible for
+ * deallocating the aData memory only after destruction of this
+ * DataSourceSurface.
+ */
+ static already_AddRefed<DataSourceSurface>
+ CreateWrappingDataSourceSurface(uint8_t *aData,
+ int32_t aStride,
+ const IntSize &aSize,
+ SurfaceFormat aFormat,
+ SourceSurfaceDeallocator aDeallocator = nullptr,
+ void* aClosure = nullptr);
+
+ static void
+ CopyDataSourceSurface(DataSourceSurface* aSource,
+ DataSourceSurface* aDest);
+
+
+ static already_AddRefed<DrawEventRecorder>
+ CreateEventRecorderForFile(const char *aFilename);
+
+ static void SetGlobalEventRecorder(DrawEventRecorder *aRecorder);
+
+ static uint32_t GetMaxSurfaceSize(BackendType aType);
+
+ static LogForwarder* GetLogForwarder() { return sConfig ? sConfig->mLogForwarder : nullptr; }
+
+private:
+ static Config* sConfig;
+public:
+
+#ifdef USE_SKIA_GPU
+ static already_AddRefed<DrawTarget>
+ CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat);
+#endif
+
+ static void PurgeAllCaches();
+
+ static already_AddRefed<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 already_AddRefed<DrawTarget> CreateTiledDrawTarget(const TileSet& aTileSet);
+
+ static bool DoesBackendSupportDataDrawtarget(BackendType aType);
+
+#ifdef USE_SKIA
+ static already_AddRefed<DrawTarget> CreateDrawTargetWithSkCanvas(SkCanvas* aCanvas);
+#endif
+
+#ifdef XP_DARWIN
+ static already_AddRefed<GlyphRenderingOptions>
+ CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor);
+#endif
+
+#ifdef WIN32
+ static already_AddRefed<DrawTarget> CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat);
+
+ /*
+ * Attempts to create and install a D2D1 device from the supplied Direct3D11 device.
+ * Returns true on success, or false on failure and leaves the D2D1/Direct3D11 devices unset.
+ */
+ static bool SetDirect3D11Device(ID3D11Device *aDevice);
+ static bool SetDWriteFactory(IDWriteFactory *aFactory);
+ static ID3D11Device *GetDirect3D11Device();
+ static ID2D1Device *GetD2D1Device();
+ static IDWriteFactory *GetDWriteFactory();
+ static bool SupportsD2D1();
+
+ static already_AddRefed<GlyphRenderingOptions>
+ CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams);
+
+ static uint64_t GetD2DVRAMUsageDrawTarget();
+ static uint64_t GetD2DVRAMUsageSourceSurface();
+ static void D2DCleanup();
+
+ static already_AddRefed<ScaledFont>
+ CreateScaledFontForDWriteFont(IDWriteFontFace* aFontFace,
+ const gfxFontStyle* aStyle,
+ Float aSize,
+ bool aUseEmbeddedBitmap,
+ bool aForceGDIMode);
+
+private:
+ static ID2D1Device *mD2D1Device;
+ static ID3D11Device *mD3D11Device;
+ static IDWriteFactory *mDWriteFactory;
+#endif
+
+ static DrawEventRecorder *mRecorder;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_2D_H
diff --git a/gfx/2d/AutoHelpersWin.h b/gfx/2d/AutoHelpersWin.h
new file mode 100644
index 000000000..744d0d500
--- /dev/null
+++ b/gfx/2d/AutoHelpersWin.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+* 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_AutoHelpersWin_h
+#define mozilla_gfx_AutoHelpersWin_h
+
+#include <windows.h>
+
+namespace mozilla {
+namespace gfx {
+
+// Get the global device context, and auto-release it on destruction.
+class AutoDC
+{
+public:
+ AutoDC() {
+ mDC = ::GetDC(nullptr);
+ }
+
+ ~AutoDC() {
+ ::ReleaseDC(nullptr, mDC);
+ }
+
+ HDC GetDC() {
+ return mDC;
+ }
+
+private:
+ HDC mDC;
+};
+
+// Select a font into the given DC, and auto-restore.
+class AutoSelectFont
+{
+public:
+ AutoSelectFont(HDC aDC, LOGFONTW *aLogFont)
+ : mOwnsFont(false)
+ {
+ mFont = ::CreateFontIndirectW(aLogFont);
+ if (mFont) {
+ mOwnsFont = true;
+ mDC = aDC;
+ mOldFont = (HFONT)::SelectObject(aDC, mFont);
+ } else {
+ mOldFont = nullptr;
+ }
+ }
+
+ AutoSelectFont(HDC aDC, HFONT aFont)
+ : mOwnsFont(false)
+ {
+ mDC = aDC;
+ mFont = aFont;
+ mOldFont = (HFONT)::SelectObject(aDC, aFont);
+ }
+
+ ~AutoSelectFont() {
+ if (mOldFont) {
+ ::SelectObject(mDC, mOldFont);
+ if (mOwnsFont) {
+ ::DeleteObject(mFont);
+ }
+ }
+ }
+
+ bool IsValid() const {
+ return mFont != nullptr;
+ }
+
+ HFONT GetFont() const {
+ return mFont;
+ }
+
+private:
+ HDC mDC;
+ HFONT mFont;
+ HFONT mOldFont;
+ bool mOwnsFont;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_AutoHelpersWin_h
diff --git a/gfx/2d/BaseCoord.h b/gfx/2d/BaseCoord.h
new file mode 100644
index 000000000..61caf9889
--- /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
+ constexpr BaseCoord() : value(0) {}
+ explicit 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);
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASECOORD_H_ */
diff --git a/gfx/2d/BaseMargin.h b/gfx/2d/BaseMargin.h
new file mode 100644
index 000000000..76f0a5296
--- /dev/null
+++ b/gfx/2d/BaseMargin.h
@@ -0,0 +1,152 @@
+/* -*- 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_BASEMARGIN_H_
+#define MOZILLA_GFX_BASEMARGIN_H_
+
+#include <ostream>
+
+#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) != 0; }
+ bool Right() const { return (mBits & eSideBitsRight) != 0; }
+ bool Bottom() const { return (mBits & eSideBitsBottom) != 0; }
+ bool Left() const { return (mBits & eSideBitsLeft) != 0; }
+ 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 {
+
+/**
+ * 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>
+struct BaseMargin {
+ 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.
+ T top, right, bottom, left;
+
+ // Constructors
+ BaseMargin() : top(0), right(0), bottom(0), left(0) {}
+ BaseMargin(T aTop, T aRight, T aBottom, T aLeft) :
+ top(aTop), right(aRight), bottom(aBottom), left(aLeft) {}
+
+ void SizeTo(T aTop, T aRight, T aBottom, T aLeft)
+ {
+ top = aTop; right = aRight; bottom = aBottom; left = aLeft;
+ }
+
+ T LeftRight() const { return left + right; }
+ T TopBottom() const { return top + bottom; }
+
+ T& Side(SideT aSide) {
+ // This is ugly!
+ return *(&top + int(aSide));
+ }
+ T Side(SideT aSide) const {
+ // This is ugly!
+ return *(&top + int(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
+ // compiler generated default assignment operator
+ bool operator==(const Sub& aMargin) const {
+ return top == aMargin.top && right == aMargin.right &&
+ bottom == aMargin.bottom && left == aMargin.left;
+ }
+ bool operator!=(const Sub& aMargin) const {
+ return !(*this == aMargin);
+ }
+ Sub operator+(const Sub& aMargin) const {
+ return Sub(top + aMargin.top, right + aMargin.right,
+ bottom + aMargin.bottom, left + aMargin.left);
+ }
+ Sub operator-(const Sub& aMargin) const {
+ return Sub(top - aMargin.top, right - aMargin.right,
+ bottom - aMargin.bottom, left - aMargin.left);
+ }
+ Sub& operator+=(const Sub& aMargin) {
+ top += aMargin.top;
+ right += aMargin.right;
+ bottom += aMargin.bottom;
+ left += aMargin.left;
+ return *static_cast<Sub*>(this);
+ }
+
+ friend std::ostream& operator<<(std::ostream& aStream,
+ const BaseMargin& aMargin) {
+ return aStream << '(' << aMargin.top << ',' << aMargin.right << ','
+ << aMargin.bottom << ',' << aMargin.left << ')';
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASEMARGIN_H_ */
diff --git a/gfx/2d/BasePoint.h b/gfx/2d/BasePoint.h
new file mode 100644
index 000000000..8e4c5fc56
--- /dev/null
+++ b/gfx/2d/BasePoint.h
@@ -0,0 +1,121 @@
+/* -*- 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_BASEPOINT_H_
+#define MOZILLA_GFX_BASEPOINT_H_
+
+#include <cmath>
+#include <ostream>
+#include "mozilla/Attributes.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/TypeTraits.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, class Coord = T>
+struct BasePoint {
+ union {
+ struct {
+ T x, y;
+ };
+ T components[2];
+ };
+
+ // Constructors
+ constexpr BasePoint() : x(0), y(0) {}
+ 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; }
+
+ // Note that '=' isn't defined so we'll get the
+ // compiler generated default assignment operator
+
+ bool operator==(const Sub& aPoint) const {
+ return x == aPoint.x && y == aPoint.y;
+ }
+ bool operator!=(const Sub& aPoint) const {
+ return x != aPoint.x || y != aPoint.y;
+ }
+
+ Sub operator+(const Sub& aPoint) const {
+ return Sub(x + aPoint.x, y + aPoint.y);
+ }
+ Sub operator-(const Sub& aPoint) const {
+ return Sub(x - aPoint.x, y - aPoint.y);
+ }
+ Sub& operator+=(const Sub& aPoint) {
+ x += aPoint.x;
+ y += aPoint.y;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(const Sub& aPoint) {
+ x -= aPoint.x;
+ y -= aPoint.y;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub operator*(T aScale) const {
+ return Sub(x * aScale, y * aScale);
+ }
+ Sub operator/(T aScale) const {
+ return Sub(x / aScale, y / aScale);
+ }
+
+ Sub operator-() const {
+ return Sub(-x, -y);
+ }
+
+ T DotProduct(const Sub& aPoint) const {
+ return x * aPoint.x + y * aPoint.y;
+ }
+
+ Coord Length() const {
+ return hypot(x, y);
+ }
+
+ T LengthSquare() const {
+ return x * x + y * 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;
+ }
+
+ void Clamp(T aMaxAbsValue)
+ {
+ x = std::max(std::min(x, aMaxAbsValue), -aMaxAbsValue);
+ y = std::max(std::min(y, aMaxAbsValue), -aMaxAbsValue);
+ }
+
+ friend std::ostream& operator<<(std::ostream& stream, const BasePoint<T, Sub, Coord>& aPoint) {
+ return stream << '(' << aPoint.x << ',' << aPoint.y << ')';
+ }
+
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASEPOINT_H_ */
diff --git a/gfx/2d/BasePoint3D.h b/gfx/2d/BasePoint3D.h
new file mode 100644
index 000000000..41e387576
--- /dev/null
+++ b/gfx/2d/BasePoint3D.h
@@ -0,0 +1,122 @@
+/* -*- 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_BASEPOINT3D_H_
+#define MOZILLA_BASEPOINT3D_H_
+
+#include "mozilla/Assertions.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 BasePoint3D {
+ union {
+ struct {
+ T x, y, z;
+ };
+ T components[3];
+ };
+
+ // Constructors
+ BasePoint3D() : x(0), y(0), z(0) {}
+ BasePoint3D(T aX, T aY, T aZ) : x(aX), y(aY), z(aZ) {}
+
+ void MoveTo(T aX, T aY, T aZ) { x = aX; y = aY; z = aZ; }
+ void MoveBy(T aDx, T aDy, T aDz) { x += aDx; y += aDy; z += aDz; }
+
+ // Note that '=' isn't defined so we'll get the
+ // compiler generated default assignment operator
+
+ T& operator[](int aIndex) {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 2);
+ return *((&x)+aIndex);
+ }
+
+ const T& operator[](int aIndex) const {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 2);
+ return *((&x)+aIndex);
+ }
+
+ bool operator==(const Sub& aPoint) const {
+ return x == aPoint.x && y == aPoint.y && z == aPoint.z;
+ }
+ bool operator!=(const Sub& aPoint) const {
+ return x != aPoint.x || y != aPoint.y || z != aPoint.z;
+ }
+
+ Sub operator+(const Sub& aPoint) const {
+ return Sub(x + aPoint.x, y + aPoint.y, z + aPoint.z);
+ }
+ Sub operator-(const Sub& aPoint) const {
+ return Sub(x - aPoint.x, y - aPoint.y, z - aPoint.z);
+ }
+ Sub& operator+=(const Sub& aPoint) {
+ x += aPoint.x;
+ y += aPoint.y;
+ z += aPoint.z;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(const Sub& aPoint) {
+ x -= aPoint.x;
+ y -= aPoint.y;
+ z -= aPoint.z;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub operator*(T aScale) const {
+ return Sub(x * aScale, y * aScale, z * aScale);
+ }
+ Sub operator/(T aScale) const {
+ return Sub(x / aScale, y / aScale, z / aScale);
+ }
+
+ Sub& operator*=(T aScale) {
+ x *= aScale;
+ y *= aScale;
+ z *= aScale;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub& operator/=(T aScale) {
+ x /= aScale;
+ y /= aScale;
+ z /= aScale;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub operator-() const {
+ return Sub(-x, -y, -z);
+ }
+
+ Sub CrossProduct(const Sub& aPoint) const {
+ return Sub(y * aPoint.z - aPoint.y * z,
+ z * aPoint.x - aPoint.z * x,
+ x * aPoint.y - aPoint.x * y);
+ }
+
+ T DotProduct(const Sub& aPoint) const {
+ return x * aPoint.x + y * aPoint.y + z * aPoint.z;
+ }
+
+ T Length() const {
+ return sqrt(x*x + y*y + z*z);
+ }
+
+ // Invalid for points with distance from origin of 0.
+ void Normalize() {
+ *this /= Length();
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_BASEPOINT3D_H_ */
diff --git a/gfx/2d/BasePoint4D.h b/gfx/2d/BasePoint4D.h
new file mode 100644
index 000000000..3f4d71011
--- /dev/null
+++ b/gfx/2d/BasePoint4D.h
@@ -0,0 +1,131 @@
+/* -*- 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_BASEPOINT4D_H_
+#define MOZILLA_BASEPOINT4D_H_
+
+#include "mozilla/Assertions.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 BasePoint4D {
+ union {
+ struct {
+ T x, y, z, w;
+ };
+ T components[4];
+ };
+
+ // Constructors
+ BasePoint4D() : x(0), y(0), z(0), w(0) {}
+ BasePoint4D(T aX, T aY, T aZ, T aW) : x(aX), y(aY), z(aZ), w(aW) {}
+
+ void MoveTo(T aX, T aY, T aZ, T aW) { x = aX; y = aY; z = aZ; w = aW; }
+ void MoveBy(T aDx, T aDy, T aDz, T aDw) { x += aDx; y += aDy; z += aDz; w += aDw; }
+
+ // Note that '=' isn't defined so we'll get the
+ // compiler generated default assignment operator
+
+ bool operator==(const Sub& aPoint) const {
+ return x == aPoint.x && y == aPoint.y &&
+ z == aPoint.z && w == aPoint.w;
+ }
+ bool operator!=(const Sub& aPoint) const {
+ return x != aPoint.x || y != aPoint.y ||
+ z != aPoint.z || w != aPoint.w;
+ }
+
+ Sub operator+(const Sub& aPoint) const {
+ return Sub(x + aPoint.x, y + aPoint.y, z + aPoint.z, w + aPoint.w);
+ }
+ Sub operator-(const Sub& aPoint) const {
+ return Sub(x - aPoint.x, y - aPoint.y, z - aPoint.z, w - aPoint.w);
+ }
+ Sub& operator+=(const Sub& aPoint) {
+ x += aPoint.x;
+ y += aPoint.y;
+ z += aPoint.z;
+ w += aPoint.w;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(const Sub& aPoint) {
+ x -= aPoint.x;
+ y -= aPoint.y;
+ z -= aPoint.z;
+ w -= aPoint.w;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub operator*(T aScale) const {
+ return Sub(x * aScale, y * aScale, z * aScale, w * aScale);
+ }
+ Sub operator/(T aScale) const {
+ return Sub(x / aScale, y / aScale, z / aScale, w / aScale);
+ }
+
+ Sub& operator*=(T aScale) {
+ x *= aScale;
+ y *= aScale;
+ z *= aScale;
+ w *= aScale;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub& operator/=(T aScale) {
+ x /= aScale;
+ y /= aScale;
+ z /= aScale;
+ w /= aScale;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub operator-() const {
+ return Sub(-x, -y, -z, -w);
+ }
+
+ T& operator[](int aIndex) {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid array index");
+ return *((&x)+aIndex);
+ }
+
+ const T& operator[](int aIndex) const {
+ MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid array index");
+ return *((&x)+aIndex);
+ }
+
+ T DotProduct(const Sub& aPoint) const {
+ return x * aPoint.x + y * aPoint.y + z * aPoint.z + w * aPoint.w;
+ }
+
+ // Ignores the 4th component!
+ Sub CrossProduct(const Sub& aPoint) const {
+ return Sub(y * aPoint.z - aPoint.y * z,
+ z * aPoint.x - aPoint.z * x,
+ x * aPoint.y - aPoint.x * y,
+ 0);
+ }
+
+ T Length() const {
+ return sqrt(x*x + y*y + z*z + w*w);
+ }
+
+ void Normalize() {
+ *this /= Length();
+ }
+
+ bool HasPositiveWCoord() { return w > 0; }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_BASEPOINT4D_H_ */
diff --git a/gfx/2d/BaseRect.h b/gfx/2d/BaseRect.h
new file mode 100644
index 000000000..57d01ba09
--- /dev/null
+++ b/gfx/2d/BaseRect.h
@@ -0,0 +1,586 @@
+/* -*- 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_BASERECT_H_
+#define MOZILLA_GFX_BASERECT_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 {
+
+/**
+ * Rectangles have two interpretations: a set of (zero-size) points,
+ * and a rectangular area of the plane. Most rectangle operations behave
+ * the same no matter what interpretation is being used, but some operations
+ * differ:
+ * -- Equality tests behave differently. When a rectangle represents an area,
+ * all zero-width and zero-height rectangles are equal to each other since they
+ * represent the empty area. But when a rectangle represents a set of
+ * mathematical points, zero-width and zero-height rectangles can be unequal.
+ * -- The union operation can behave differently. When rectangles represent
+ * areas, taking the union of a zero-width or zero-height rectangle with
+ * another rectangle can just ignore the empty rectangle. But when rectangles
+ * represent sets of mathematical points, we may need to extend the latter
+ * rectangle to include the points of a zero-width or zero-height rectangle.
+ *
+ * To ensure that these interpretations are explicitly disambiguated, we
+ * deny access to the == and != operators and require use of IsEqualEdges and
+ * IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
+ * methods.
+ *
+ * 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 MarginT>
+struct BaseRect {
+ T x, y, width, height;
+
+ // Constructors
+ BaseRect() : x(0), y(0), width(0), height(0) {}
+ BaseRect(const Point& aOrigin, const SizeT &aSize) :
+ x(aOrigin.x), y(aOrigin.y), width(aSize.width), height(aSize.height)
+ {
+ }
+ BaseRect(T aX, T aY, T aWidth, T aHeight) :
+ x(aX), y(aY), width(aWidth), height(aHeight)
+ {
+ }
+
+ // Emptiness. An empty rect is one that has no area, i.e. its height or width
+ // is <= 0
+ bool IsEmpty() const { return height <= 0 || width <= 0; }
+ void SetEmpty() { width = height = 0; }
+
+ // "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)) &&
+ mozilla::IsFinite(FloatType(width)) &&
+ mozilla::IsFinite(FloatType(height)));
+ }
+
+ // Returns true if this rectangle contains the interior of aRect. Always
+ // returns true if aRect is empty, and always returns false is aRect is
+ // nonempty but this rect is empty.
+ bool Contains(const Sub& aRect) const
+ {
+ return aRect.IsEmpty() ||
+ (x <= aRect.x && aRect.XMost() <= XMost() &&
+ y <= aRect.y && aRect.YMost() <= YMost());
+ }
+ // 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 < XMost() &&
+ y <= aY && aY < YMost();
+ }
+ // 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
+ // intersection with aRect's area, and FALSE otherwise.
+ // Always returns false if aRect is empty or 'this' is empty.
+ bool Intersects(const Sub& aRect) const
+ {
+ 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
+ // (including edges) of *this and aRect. If there are no points in that
+ // intersection, returns an empty rectangle with x/y set to the std::max of the x/y
+ // of *this and aRect.
+ MOZ_MUST_USE Sub Intersect(const Sub& aRect) const
+ {
+ Sub result;
+ result.x = std::max<T>(x, aRect.x);
+ result.y = std::max<T>(y, aRect.y);
+ result.width = std::min<T>(x - result.x + width, aRect.x - result.x + aRect.width);
+ result.height = std::min<T>(y - result.y + height, aRect.y - result.y + aRect.height);
+ if (result.width < 0 || result.height < 0) {
+ result.SizeTo(0, 0);
+ }
+ return result;
+ }
+ // Sets *this to be the rectangle containing the intersection of the points
+ // (including edges) of *this and aRect. If there are no points in that
+ // intersection, sets *this to be an empty rectangle with x/y set to the std::max
+ // of the x/y of *this and aRect.
+ //
+ // 'this' can be the same object as either aRect1 or aRect2
+ bool IntersectRect(const Sub& aRect1, const Sub& aRect2)
+ {
+ *static_cast<Sub*>(this) = aRect1.Intersect(aRect2);
+ return !IsEmpty();
+ }
+
+ // Returns the smallest rectangle that contains both the area of both
+ // this and aRect2.
+ // Thus, empty input rectangles are ignored.
+ // If both rectangles are empty, returns this.
+ // WARNING! This is not safe against overflow, prefer using SafeUnion instead
+ // when dealing with int-based rects.
+ MOZ_MUST_USE Sub Union(const Sub& aRect) const
+ {
+ if (IsEmpty()) {
+ return aRect;
+ } else if (aRect.IsEmpty()) {
+ return *static_cast<const Sub*>(this);
+ } else {
+ return UnionEdges(aRect);
+ }
+ }
+ // Returns the smallest rectangle that contains both the points (including
+ // edges) of both aRect1 and aRect2.
+ // Thus, empty input rectangles are allowed to affect the result.
+ // WARNING! This is not safe against overflow, prefer using SafeUnionEdges
+ // instead when dealing with int-based rects.
+ MOZ_MUST_USE Sub UnionEdges(const Sub& aRect) const
+ {
+ Sub result;
+ result.x = std::min(x, aRect.x);
+ result.y = std::min(y, aRect.y);
+ result.width = std::max(XMost(), aRect.XMost()) - result.x;
+ result.height = std::max(YMost(), aRect.YMost()) - result.y;
+ return result;
+ }
+ // Computes the smallest rectangle that contains both the area of both
+ // aRect1 and aRect2, and fills 'this' with the result.
+ // Thus, empty input rectangles are ignored.
+ // If both rectangles are empty, sets 'this' to aRect2.
+ //
+ // 'this' can be the same object as either aRect1 or aRect2
+ void UnionRect(const Sub& aRect1, const Sub& aRect2)
+ {
+ *static_cast<Sub*>(this) = aRect1.Union(aRect2);
+ }
+
+ // Computes the smallest rectangle that contains both the points (including
+ // edges) of both aRect1 and aRect2.
+ // Thus, empty input rectangles are allowed to affect the result.
+ //
+ // 'this' can be the same object as either aRect1 or aRect2
+ void UnionRectEdges(const Sub& aRect1, const Sub& aRect2)
+ {
+ *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;
+ }
+ void SetRect(const Point& aPt, const SizeT& aSize)
+ {
+ SetRect(aPt.x, aPt.y, aSize.width, aSize.height);
+ }
+ void MoveTo(T aX, T aY) { x = aX; y = aY; }
+ void MoveTo(const Point& aPoint) { x = aPoint.x; y = aPoint.y; }
+ void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
+ void MoveBy(const Point& aPoint) { x += aPoint.x; y += aPoint.y; }
+ void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
+ void SizeTo(const SizeT& aSize) { width = aSize.width; height = aSize.height; }
+
+ void Inflate(T aD) { Inflate(aD, aD); }
+ void Inflate(T aDx, T aDy)
+ {
+ x -= aDx;
+ y -= aDy;
+ width += 2 * aDx;
+ height += 2 * aDy;
+ }
+ void Inflate(const MarginT& aMargin)
+ {
+ x -= aMargin.left;
+ y -= aMargin.top;
+ width += aMargin.LeftRight();
+ height += aMargin.TopBottom();
+ }
+ void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); }
+
+ void Deflate(T aD) { Deflate(aD, aD); }
+ void Deflate(T aDx, T aDy)
+ {
+ x += aDx;
+ y += aDy;
+ width = std::max(T(0), width - 2 * aDx);
+ height = std::max(T(0), height - 2 * aDy);
+ }
+ void Deflate(const MarginT& aMargin)
+ {
+ x += aMargin.left;
+ y += aMargin.top;
+ width = std::max(T(0), width - aMargin.LeftRight());
+ height = std::max(T(0), height - aMargin.TopBottom());
+ }
+ void Deflate(const SizeT& aSize) { Deflate(aSize.width, aSize.height); }
+
+ // Return true if the rectangles contain the same set of points, including
+ // points on the edges.
+ // Use when we care about the exact x/y/width/height values being
+ // equal (i.e. we care about differences in empty rectangles).
+ bool IsEqualEdges(const Sub& aRect) const
+ {
+ return x == aRect.x && y == aRect.y &&
+ width == aRect.width && height == aRect.height;
+ }
+ // Return true if the rectangles contain the same area of the plane.
+ // Use when we do not care about differences in empty rectangles.
+ bool IsEqualInterior(const Sub& aRect) const
+ {
+ return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty());
+ }
+
+ friend Sub operator+(Sub aSub, const Point& aPoint)
+ {
+ aSub += aPoint;
+ return aSub;
+ }
+ friend Sub operator-(Sub aSub, const Point& aPoint)
+ {
+ aSub -= aPoint;
+ return aSub;
+ }
+ friend Sub operator+(Sub aSub, const SizeT& aSize)
+ {
+ aSub += aSize;
+ return aSub;
+ }
+ friend Sub operator-(Sub aSub, const SizeT& aSize)
+ {
+ aSub -= aSize;
+ return aSub;
+ }
+ Sub& operator+=(const Point& aPoint)
+ {
+ MoveBy(aPoint);
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(const Point& aPoint)
+ {
+ 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
+ MarginT operator-(const Sub& aRect) const
+ {
+ return MarginT(aRect.y - y,
+ XMost() - aRect.XMost(),
+ YMost() - aRect.YMost(),
+ aRect.x - x);
+ }
+
+ // Helpers for accessing the vertices
+ Point TopLeft() const { return Point(x, y); }
+ 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("GFX: 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("GFX: 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("GFX: Incomplete switch");
+ }
+ Point Center() const { return Point(x, y) + Point(width, height)/2; }
+ SizeT Size() const { return SizeT(width, height); }
+
+ T Area() const { return width * height; }
+
+ // Helper methods for computing the extents
+ T X() const { return x; }
+ T Y() const { return y; }
+ T Width() const { return width; }
+ T Height() const { return height; }
+ T XMost() const { return x + width; }
+ T YMost() const { return y + height; }
+
+ // Get the coordinate of the edge on the given side.
+ T Edge(mozilla::Side aSide) const
+ {
+ switch (aSide) {
+ case NS_SIDE_TOP: return Y();
+ case NS_SIDE_RIGHT: return XMost();
+ case NS_SIDE_BOTTOM: return YMost();
+ case NS_SIDE_LEFT: return X();
+ }
+ MOZ_CRASH("GFX: Incomplete switch");
+ }
+
+ // Moves one edge of the rect without moving the opposite edge.
+ void SetLeftEdge(T aX) {
+ MOZ_ASSERT(aX <= XMost());
+ width = XMost() - aX;
+ x = aX;
+ }
+ void SetRightEdge(T aXMost) {
+ MOZ_ASSERT(aXMost >= x);
+ width = aXMost - x;
+ }
+ void SetTopEdge(T aY) {
+ MOZ_ASSERT(aY <= YMost());
+ height = YMost() - aY;
+ y = aY;
+ }
+ void SetBottomEdge(T aYMost) {
+ MOZ_ASSERT(aYMost >= y);
+ height = aYMost - y;
+ }
+
+ // Round the rectangle edges to integer coordinates, such that the rounded
+ // rectangle has the same set of pixel centers as the original rectangle.
+ // Edges at offset 0.5 round up.
+ // Suitable for most places where integral device coordinates
+ // are needed, but note that any translation should be applied first to
+ // avoid pixel rounding errors.
+ // Note that this 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
+ // If you need similar method which is using NS_round(), you should create
+ // new |RoundAwayFromZero()| method.
+ void Round()
+ {
+ T x0 = static_cast<T>(floor(T(X()) + 0.5));
+ T y0 = static_cast<T>(floor(T(Y()) + 0.5));
+ T x1 = static_cast<T>(floor(T(XMost()) + 0.5));
+ T y1 = static_cast<T>(floor(T(YMost()) + 0.5));
+
+ x = x0;
+ y = y0;
+
+ width = x1 - x0;
+ height = y1 - y0;
+ }
+
+ // Snap the rectangle edges to integer coordinates, such that the
+ // original rectangle contains the resulting rectangle.
+ void RoundIn()
+ {
+ T x0 = static_cast<T>(ceil(T(X())));
+ T y0 = static_cast<T>(ceil(T(Y())));
+ T x1 = static_cast<T>(floor(T(XMost())));
+ T y1 = static_cast<T>(floor(T(YMost())));
+
+ x = x0;
+ y = y0;
+
+ width = x1 - x0;
+ height = y1 - y0;
+ }
+
+ // Snap the rectangle edges to integer coordinates, such that the
+ // resulting rectangle contains the original rectangle.
+ void RoundOut()
+ {
+ T x0 = static_cast<T>(floor(T(X())));
+ T y0 = static_cast<T>(floor(T(Y())));
+ T x1 = static_cast<T>(ceil(T(XMost())));
+ T y1 = static_cast<T>(ceil(T(YMost())));
+
+ x = x0;
+ y = y0;
+
+ width = x1 - x0;
+ height = y1 - y0;
+ }
+
+ // Scale 'this' by aScale without doing any rounding.
+ void Scale(T aScale) { Scale(aScale, aScale); }
+ // Scale 'this' by aXScale and aYScale, without doing any rounding.
+ void Scale(T aXScale, T aYScale)
+ {
+ T right = XMost() * aXScale;
+ T bottom = YMost() * aYScale;
+ x = x * aXScale;
+ y = y * aYScale;
+ width = right - x;
+ height = bottom - y;
+ }
+ // Scale 'this' by aScale, converting coordinates to integers so that the result is
+ // the smallest integer-coordinate rectangle containing the unrounded result.
+ // Note: this can turn an empty rectangle into a non-empty rectangle
+ void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); }
+ // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
+ // that the result is the smallest integer-coordinate rectangle containing the
+ // unrounded result.
+ // Note: this can turn an empty rectangle into a non-empty rectangle
+ void ScaleRoundOut(double aXScale, double aYScale)
+ {
+ T right = static_cast<T>(ceil(double(XMost()) * aXScale));
+ T bottom = static_cast<T>(ceil(double(YMost()) * aYScale));
+ x = static_cast<T>(floor(double(x) * aXScale));
+ y = static_cast<T>(floor(double(y) * aYScale));
+ width = right - x;
+ height = bottom - y;
+ }
+ // Scale 'this' by aScale, converting coordinates to integers so that the result is
+ // the largest integer-coordinate rectangle contained by the unrounded result.
+ void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); }
+ // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
+ // that the result is the largest integer-coordinate rectangle contained by the
+ // unrounded result.
+ void ScaleRoundIn(double aXScale, double aYScale)
+ {
+ T right = static_cast<T>(floor(double(XMost()) * aXScale));
+ T bottom = static_cast<T>(floor(double(YMost()) * aYScale));
+ x = static_cast<T>(ceil(double(x) * aXScale));
+ y = static_cast<T>(ceil(double(y) * aYScale));
+ width = std::max<T>(0, right - x);
+ height = std::max<T>(0, bottom - y);
+ }
+ // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
+ // the smallest integer-coordinate rectangle containing the unrounded result.
+ // Note: this can turn an empty rectangle into a non-empty rectangle
+ void ScaleInverseRoundOut(double aScale) { ScaleInverseRoundOut(aScale, aScale); }
+ // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
+ // that the result is the smallest integer-coordinate rectangle containing the
+ // unrounded result.
+ // Note: this can turn an empty rectangle into a non-empty rectangle
+ void ScaleInverseRoundOut(double aXScale, double aYScale)
+ {
+ T right = static_cast<T>(ceil(double(XMost()) / aXScale));
+ T bottom = static_cast<T>(ceil(double(YMost()) / aYScale));
+ x = static_cast<T>(floor(double(x) / aXScale));
+ y = static_cast<T>(floor(double(y) / aYScale));
+ width = right - x;
+ height = bottom - y;
+ }
+ // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
+ // the largest integer-coordinate rectangle contained by the unrounded result.
+ void ScaleInverseRoundIn(double aScale) { ScaleInverseRoundIn(aScale, aScale); }
+ // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
+ // that the result is the largest integer-coordinate rectangle contained by the
+ // unrounded result.
+ void ScaleInverseRoundIn(double aXScale, double aYScale)
+ {
+ T right = static_cast<T>(floor(double(XMost()) / aXScale));
+ T bottom = static_cast<T>(floor(double(YMost()) / aYScale));
+ x = static_cast<T>(ceil(double(x) / aXScale));
+ y = static_cast<T>(ceil(double(y) / aYScale));
+ width = std::max<T>(0, right - x);
+ height = std::max<T>(0, bottom - y);
+ }
+
+ /**
+ * Clamp aPoint to this rectangle. It is allowed to end up on any
+ * edge of the rectangle.
+ */
+ MOZ_MUST_USE Point ClampPoint(const Point& aPoint) const
+ {
+ return Point(std::max(x, std::min(XMost(), aPoint.x)),
+ std::max(y, std::min(YMost(), aPoint.y)));
+ }
+
+ /**
+ * Translate this rectangle to be inside aRect. If it doesn't fit inside
+ * aRect then the dimensions that don't fit will be shrunk so that they
+ * do fit. The resulting rect is returned.
+ */
+ MOZ_MUST_USE Sub MoveInsideAndClamp(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(), aRect.XMost()) - rect.width;
+ rect.y = std::min(rect.YMost(), aRect.YMost()) - rect.height;
+ return rect;
+ }
+
+ // Returns the largest rectangle that can be represented with 32-bit
+ // signed integers, centered around a point at 0,0. As BaseRect's represent
+ // the dimensions as a top-left point with a width and height, the width
+ // and height will be the largest positive 32-bit value. The top-left
+ // position coordinate is divided by two to center the rectangle around a
+ // point at 0,0.
+ static Sub MaxIntRect()
+ {
+ return Sub(
+ static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
+ static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
+ static_cast<T>(std::numeric_limits<int32_t>::max()),
+ static_cast<T>(std::numeric_limits<int32_t>::max())
+ );
+ };
+
+ 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.
+ bool operator==(const Sub& aRect) const { return false; }
+ bool operator!=(const Sub& aRect) const { return false; }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASERECT_H_ */
diff --git a/gfx/2d/BaseSize.h b/gfx/2d/BaseSize.h
new file mode 100644
index 000000000..7bcccc629
--- /dev/null
+++ b/gfx/2d/BaseSize.h
@@ -0,0 +1,100 @@
+/* -*- 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_BASESIZE_H_
+#define MOZILLA_GFX_BASESIZE_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 BaseSize {
+ union {
+ struct {
+ T width, height;
+ };
+ T components[2];
+ };
+
+ // Constructors
+ constexpr BaseSize() : width(0), height(0) {}
+ constexpr BaseSize(T aWidth, T aHeight) : width(aWidth), height(aHeight) {}
+
+ void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
+
+ bool IsEmpty() const {
+ return width <= 0 || height <= 0;
+ }
+
+ bool IsSquare() const {
+ return width == height;
+ }
+
+ // Note that '=' isn't defined so we'll get the
+ // compiler generated default assignment operator
+
+ bool operator==(const Sub& aSize) const {
+ return width == aSize.width && height == aSize.height;
+ }
+ bool operator!=(const Sub& aSize) const {
+ return width != aSize.width || height != aSize.height;
+ }
+ bool operator<=(const Sub& aSize) const {
+ return width <= aSize.width && height <= aSize.height;
+ }
+ bool operator<(const Sub& aSize) const {
+ return *this <= aSize && *this != aSize;
+ }
+
+ Sub operator+(const Sub& aSize) const {
+ return Sub(width + aSize.width, height + aSize.height);
+ }
+ Sub operator-(const Sub& aSize) const {
+ return Sub(width - aSize.width, height - aSize.height);
+ }
+ Sub& operator+=(const Sub& aSize) {
+ width += aSize.width;
+ height += aSize.height;
+ return *static_cast<Sub*>(this);
+ }
+ Sub& operator-=(const Sub& aSize) {
+ width -= aSize.width;
+ height -= aSize.height;
+ return *static_cast<Sub*>(this);
+ }
+
+ Sub operator*(T aScale) const {
+ return Sub(width * aScale, height * aScale);
+ }
+ Sub operator/(T aScale) const {
+ return Sub(width / aScale, height / aScale);
+ }
+ friend Sub operator*(T aScale, const Sub& aSize) {
+ return Sub(aScale * aSize.width, aScale * aSize.height);
+ }
+ void Scale(T aXScale, T aYScale) {
+ width *= aXScale;
+ height *= aYScale;
+ }
+
+ Sub operator*(const Sub& aSize) const {
+ return Sub(width * aSize.width, height * aSize.height);
+ }
+ Sub operator/(const Sub& aSize) const {
+ return Sub(width / aSize.width, height / aSize.height);
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASESIZE_H_ */
diff --git a/gfx/2d/BezierUtils.cpp b/gfx/2d/BezierUtils.cpp
new file mode 100644
index 000000000..292c6356b
--- /dev/null
+++ b/gfx/2d/BezierUtils.cpp
@@ -0,0 +1,339 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=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 "BezierUtils.h"
+
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+Point
+GetBezierPoint(const Bezier& aBezier, Float t)
+{
+ Float s = 1.0f - t;
+
+ return Point(
+ aBezier.mPoints[0].x * s * s * s +
+ 3.0f * aBezier.mPoints[1].x * t * s * s +
+ 3.0f * aBezier.mPoints[2].x * t * t * s +
+ aBezier.mPoints[3].x * t * t * t,
+ aBezier.mPoints[0].y * s * s * s +
+ 3.0f * aBezier.mPoints[1].y * t * s * s +
+ 3.0f * aBezier.mPoints[2].y * t * t * s +
+ aBezier.mPoints[3].y * t * t * t
+ );
+}
+
+Point
+GetBezierDifferential(const Bezier& aBezier, Float t)
+{
+ // Return P'(t).
+
+ Float s = 1.0f - t;
+
+ return Point(
+ -3.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s * s +
+ 2.0f * (aBezier.mPoints[1].x - aBezier.mPoints[2].x) * t * s +
+ (aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t * t),
+ -3.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s * s +
+ 2.0f * (aBezier.mPoints[1].y - aBezier.mPoints[2].y) * t * s+
+ (aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t * t)
+ );
+}
+
+Point
+GetBezierDifferential2(const Bezier& aBezier, Float t)
+{
+ // Return P''(t).
+
+ Float s = 1.0f - t;
+
+ return Point(
+ 6.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s -
+ (aBezier.mPoints[1].x - aBezier.mPoints[2].x) * (s - t) -
+ (aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t),
+ 6.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s -
+ (aBezier.mPoints[1].y - aBezier.mPoints[2].y) * (s - t) -
+ (aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t)
+ );
+}
+
+Float
+GetBezierLength(const Bezier& aBezier, Float a, Float b)
+{
+ if (a < 0.5f && b > 0.5f) {
+ // To increase the accuracy, split into two parts.
+ return GetBezierLength(aBezier, a, 0.5f) +
+ GetBezierLength(aBezier, 0.5f, b);
+ }
+
+ // Calculate length of simple bezier curve with Simpson's rule.
+ // _
+ // / b
+ // length = | |P'(x)| dx
+ // _/ a
+ //
+ // b - a a + b
+ // = ----- [ |P'(a)| + 4 |P'(-----)| + |P'(b)| ]
+ // 6 2
+
+ Float fa = GetBezierDifferential(aBezier, a).Length();
+ Float fab = GetBezierDifferential(aBezier, (a + b) / 2.0f).Length();
+ Float fb = GetBezierDifferential(aBezier, b).Length();
+
+ return (b - a) / 6.0f * (fa + 4.0f * fab + fb);
+}
+
+static void
+SplitBezierA(Bezier* aSubBezier, const Bezier& aBezier, Float t)
+{
+ // Split bezier curve into [0,t] and [t,1] parts, and return [0,t] part.
+
+ Float s = 1.0f - t;
+
+ Point tmp1;
+ Point tmp2;
+
+ aSubBezier->mPoints[0] = aBezier.mPoints[0];
+
+ aSubBezier->mPoints[1] = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
+ tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
+ tmp2 = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
+
+ aSubBezier->mPoints[2] = aSubBezier->mPoints[1] * s + tmp1 * t;
+ tmp1 = tmp1 * s + tmp2 * t;
+
+ aSubBezier->mPoints[3] = aSubBezier->mPoints[2] * s + tmp1 * t;
+}
+
+static void
+SplitBezierB(Bezier* aSubBezier, const Bezier& aBezier, Float t)
+{
+ // Split bezier curve into [0,t] and [t,1] parts, and return [t,1] part.
+
+ Float s = 1.0f - t;
+
+ Point tmp1;
+ Point tmp2;
+
+ aSubBezier->mPoints[3] = aBezier.mPoints[3];
+
+ aSubBezier->mPoints[2] = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
+ tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
+ tmp2 = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
+
+ aSubBezier->mPoints[1] = tmp1 * s + aSubBezier->mPoints[2] * t;
+ tmp1 = tmp2 * s + tmp1 * t;
+
+ aSubBezier->mPoints[0] = tmp1 * s + aSubBezier->mPoints[1] * t;
+}
+
+void
+GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier, Float t1, Float t2)
+{
+ Bezier tmp;
+ SplitBezierB(&tmp, aBezier, t1);
+
+ Float range = 1.0f - t1;
+ if (range == 0.0f) {
+ *aSubBezier = tmp;
+ } else {
+ SplitBezierA(aSubBezier, tmp, (t2 - t1) / range);
+ }
+}
+
+static Point
+BisectBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
+ Float* aT)
+{
+ // Find a nearest point on bezier curve with Binary search.
+ // Called from FindBezierNearestPoint.
+
+ Float lower = 0.0f;
+ Float upper = 1.0f;
+ Float t;
+
+ Point P, lastP;
+ const size_t MAX_LOOP = 32;
+ const Float DIST_MARGIN = 0.1f;
+ const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
+ const Float DIFF = 0.0001f;
+ for (size_t i = 0; i < MAX_LOOP; i++) {
+ t = (upper + lower) / 2.0f;
+ P = GetBezierPoint(aBezier, t);
+
+ // Check if it converged.
+ if (i > 0 && (lastP - P).LengthSquare() < DIST_MARGIN_SQUARE) {
+ break;
+ }
+
+ Float distSquare = (P - aTarget).LengthSquare();
+ if ((GetBezierPoint(aBezier, t + DIFF) - aTarget).LengthSquare() <
+ distSquare) {
+ lower = t;
+ } else if ((GetBezierPoint(aBezier, t - DIFF) - aTarget).LengthSquare() <
+ distSquare) {
+ upper = t;
+ } else {
+ break;
+ }
+
+ lastP = P;
+ }
+
+ if (aT) {
+ *aT = t;
+ }
+
+ return P;
+}
+
+Point
+FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
+ Float aInitialT, Float* aT)
+{
+ // Find a nearest point on bezier curve with Newton's method.
+ // It converges within 4 iterations in most cases.
+ //
+ // f(t_n)
+ // t_{n+1} = t_n - ---------
+ // f'(t_n)
+ //
+ // d 2
+ // f(t) = ---- | P(t) - aTarget |
+ // dt
+
+ Float t = aInitialT;
+ Point P;
+ Point lastP = GetBezierPoint(aBezier, t);
+
+ const size_t MAX_LOOP = 4;
+ const Float DIST_MARGIN = 0.1f;
+ const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
+ for (size_t i = 0; i <= MAX_LOOP; i++) {
+ Point dP = GetBezierDifferential(aBezier, t);
+ Point ddP = GetBezierDifferential2(aBezier, t);
+ Float f = 2.0f * (lastP.DotProduct(dP) - aTarget.DotProduct(dP));
+ Float df = 2.0f * (dP.DotProduct(dP) + lastP.DotProduct(ddP) -
+ aTarget.DotProduct(ddP));
+ t = t - f / df;
+ P = GetBezierPoint(aBezier, t);
+ if ((P - lastP).LengthSquare() < DIST_MARGIN_SQUARE) {
+ break;
+ }
+ lastP = P;
+
+ if (i == MAX_LOOP) {
+ // If aInitialT is too bad, it won't converge in a few iterations,
+ // fallback to binary search.
+ return BisectBezierNearestPoint(aBezier, aTarget, aT);
+ }
+ }
+
+ if (aT) {
+ *aT = t;
+ }
+
+ return P;
+}
+
+void
+GetBezierPointsForCorner(Bezier* aBezier, mozilla::css::Corner aCorner,
+ const Point& aCornerPoint, const Size& aCornerSize)
+{
+ // Calculate bezier control points for elliptic arc.
+
+ const Float signsList[4][2] = {
+ { +1.0f, +1.0f },
+ { -1.0f, +1.0f },
+ { -1.0f, -1.0f },
+ { +1.0f, -1.0f }
+ };
+ const Float (& signs)[2] = signsList[aCorner];
+
+ aBezier->mPoints[0] = aCornerPoint;
+ aBezier->mPoints[0].x += signs[0] * aCornerSize.width;
+
+ aBezier->mPoints[1] = aBezier->mPoints[0];
+ aBezier->mPoints[1].x -= signs[0] * aCornerSize.width * kKappaFactor;
+
+ aBezier->mPoints[3] = aCornerPoint;
+ aBezier->mPoints[3].y += signs[1] * aCornerSize.height;
+
+ aBezier->mPoints[2] = aBezier->mPoints[3];
+ aBezier->mPoints[2].y -= signs[1] * aCornerSize.height * kKappaFactor;
+}
+
+Float
+GetQuarterEllipticArcLength(Float a, Float b)
+{
+ // Calculate the approximate length of a quarter elliptic arc formed by radii
+ // (a, b), by Ramanujan's approximation of the perimeter p of an ellipse.
+ // _ _
+ // | 2 |
+ // | 3 * (a - b) |
+ // p = PI | (a + b) + ------------------------------------------- |
+ // | 2 2 |
+ // |_ 10 * (a + b) + sqrt(a + 14 * a * b + b ) _|
+ //
+ // _ _
+ // | 2 |
+ // | 3 * (a - b) |
+ // = PI | (a + b) + -------------------------------------------------- |
+ // | 2 2 |
+ // |_ 10 * (a + b) + sqrt(4 * (a + b) - 3 * (a - b) ) _|
+ //
+ // _ _
+ // | 2 |
+ // | 3 * S |
+ // = PI | A + -------------------------------------- |
+ // | 2 2 |
+ // |_ 10 * A + sqrt(4 * A - 3 * S ) _|
+ //
+ // where A = a + b, S = a - b
+
+ Float A = a + b, S = a - b;
+ Float A2 = A * A, S2 = S * S;
+ Float p = M_PI * (A + 3.0f * S2 / (10.0f * A + sqrt(4.0f * A2 - 3.0f * S2)));
+ return p / 4.0f;
+}
+
+Float
+CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
+ const Point& origin, Float width, Float height)
+{
+ // Solve following equations with n and return smaller n.
+ //
+ // / (x, y) = P + n * normal
+ // |
+ // < _ _ 2 _ _ 2
+ // | | x - origin.x | | y - origin.y |
+ // | | ------------ | + | ------------ | = 1
+ // \ |_ width _| |_ height _|
+
+ Float a = (P.x - origin.x) / width;
+ Float b = normal.x / width;
+ Float c = (P.y - origin.y) / height;
+ Float d = normal.y / height;
+
+ Float A = b * b + d * d;
+ Float B = a * b + c * d;
+ Float C = a * a + c * c - 1;
+
+ Float S = sqrt(B * B - A * C);
+
+ Float n1 = (- B + S) / A;
+ Float n2 = (- B - S) / A;
+
+ MOZ_ASSERT(n1 >= 0);
+ MOZ_ASSERT(n2 >= 0);
+
+ return n1 < n2 ? n1 : n2;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/BezierUtils.h b/gfx/2d/BezierUtils.h
new file mode 100644
index 000000000..7871f0284
--- /dev/null
+++ b/gfx/2d/BezierUtils.h
@@ -0,0 +1,185 @@
+/* -*- 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_BezierUtils_h_
+#define mozilla_BezierUtils_h_
+
+#include "mozilla/gfx/2D.h"
+#include "gfxRect.h"
+
+namespace mozilla {
+namespace gfx {
+
+// Control points for bezier curve
+//
+// mPoints[2]
+// +-----___---+ mPoints[3]
+// __--
+// _--
+// /
+// /
+// mPoints[1] + |
+// | |
+// ||
+// ||
+// |
+// |
+// |
+// |
+// mPoints[0] +
+struct Bezier {
+ Point mPoints[4];
+};
+
+// Calculate a point or it's differential of a bezier curve formed by
+// aBezier and parameter t.
+//
+// GetBezierPoint = P(t)
+// GetBezierDifferential = P'(t)
+// GetBezierDifferential2 = P''(t)
+//
+// mPoints[2]
+// +-----___---+ mPoints[3]
+// __-- P(1)
+// _--
+// +
+// / P(t)
+// mPoints[1] + |
+// | |
+// ||
+// ||
+// |
+// |
+// |
+// |
+// mPoints[0] + P(0)
+Point GetBezierPoint(const Bezier& aBezier, Float t);
+Point GetBezierDifferential(const Bezier& aBezier, Float t);
+Point GetBezierDifferential2(const Bezier& aBezier, Float t);
+
+// Calculate length of a simple bezier curve formed by aBezier and range [a, b].
+Float GetBezierLength(const Bezier& aBezier, Float a, Float b);
+
+// Split bezier curve formed by aBezier into [0,t1], [t1,t2], [t2,1] parts, and
+// stores control points for [t1,t2] to aSubBezier.
+//
+// ___---+
+// __+- P(1)
+// _-- P(t2)
+// -
+// / <-- aSubBezier
+// |
+// |
+// +
+// | P(t1)
+// |
+// |
+// |
+// |
+// + P(0)
+void GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier,
+ Float t1, Float t2);
+
+// Find a nearest point on bezier curve formed by aBezier to a point aTarget.
+// aInitialT is a hint to find the parameter t for the nearest point.
+// If aT is non-null, parameter for the nearest point is stored to *aT.
+// This function expects a bezier curve to be an approximation of elliptic arc.
+// Otherwise it will return wrong point.
+//
+// aTarget
+// + ___---+
+// __--
+// _--
+// +
+// / nearest point = P(t = *aT)
+// |
+// |
+// |
+// + P(aInitialT)
+// |
+// |
+// |
+// |
+// +
+Point FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
+ Float aInitialT, Float* aT=nullptr);
+
+// Calculate control points for a bezier curve that is an approximation of
+// an elliptic arc.
+//
+// aCornerSize.width
+// |<----------------->|
+// | |
+// aCornerPoint| mPoints[2] |
+// -------------+-------+-----___---+ mPoints[3]
+// ^ | __--
+// | | _--
+// | | -
+// | | /
+// aCornerSize.height | mPoints[1] + |
+// | | |
+// | ||
+// | ||
+// | |
+// | |
+// | |
+// v mPoints[0] |
+// -------------+
+void GetBezierPointsForCorner(Bezier* aBezier, mozilla::css::Corner aCorner,
+ const Point& aCornerPoint,
+ const Size& aCornerSize);
+
+// Calculate the approximate length of a quarter elliptic arc formed by radii
+// (a, b).
+//
+// a
+// |<----------------->|
+// | |
+// ---+-------------___---+
+// ^ | __--
+// | | _--
+// | | -
+// | | /
+// b | | |
+// | | |
+// | ||
+// | ||
+// | |
+// | |
+// | |
+// v |
+// ---+
+Float GetQuarterEllipticArcLength(Float a, Float b);
+
+// Calculate the distance between an elliptic arc formed by (origin, width,
+// height), and a point P, along a line formed by |P + n * normal|.
+// P should be outside of the ellipse, and the line should cross with the
+// ellipse twice at n > 0 points.
+//
+// width
+// |<----------------->|
+// origin | |
+// -----------+-------------___---+
+// ^ normal | __--
+// | P +->__ | _--
+// | --__ -
+// | | --+
+// height | | |
+// | | |
+// | ||
+// | ||
+// | |
+// | |
+// | |
+// v |
+// -----------+
+Float CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
+ const Point& origin,
+ Float width, Float height);
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* mozilla_BezierUtils_h_ */
diff --git a/gfx/2d/BigEndianInts.h b/gfx/2d/BigEndianInts.h
new file mode 100644
index 000000000..aa4f0dfb2
--- /dev/null
+++ b/gfx/2d/BigEndianInts.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_BigEndianInts_h
+#define mozilla_BigEndianInts_h
+
+#include "mozilla/EndianUtils.h"
+
+namespace mozilla {
+
+#pragma pack(push, 1)
+
+struct BigEndianUint16
+{
+#ifdef __SUNPRO_CC
+ BigEndianUint16& operator=(const uint16_t aValue)
+ {
+ value = NativeEndian::swapToBigEndian(aValue);
+ return *this;
+ }
+#else
+ MOZ_IMPLICIT BigEndianUint16(const uint16_t aValue)
+ {
+ value = NativeEndian::swapToBigEndian(aValue);
+ }
+#endif
+
+ operator uint16_t() const
+ {
+ return NativeEndian::swapFromBigEndian(value);
+ }
+
+ friend inline bool
+ operator==(const BigEndianUint16& lhs, const BigEndianUint16& rhs)
+ {
+ return lhs.value == rhs.value;
+ }
+
+ friend inline bool
+ operator!=(const BigEndianUint16& lhs, const BigEndianUint16& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+private:
+ uint16_t value;
+};
+
+struct BigEndianUint32
+{
+#ifdef __SUNPRO_CC
+ BigEndianUint32& operator=(const uint32_t aValue)
+ {
+ value = NativeEndian::swapToBigEndian(aValue);
+ return *this;
+ }
+#else
+ MOZ_IMPLICIT BigEndianUint32(const uint32_t aValue)
+ {
+ value = NativeEndian::swapToBigEndian(aValue);
+ }
+#endif
+
+ operator uint32_t() const
+ {
+ return NativeEndian::swapFromBigEndian(value);
+ }
+
+private:
+ uint32_t value;
+};
+
+#pragma pack(pop)
+
+} // mozilla
+
+#endif // mozilla_BigEndianInts_h
diff --git a/gfx/2d/Blur.cpp b/gfx/2d/Blur.cpp
new file mode 100644
index 000000000..f3f41c3af
--- /dev/null
+++ b/gfx/2d/Blur.cpp
@@ -0,0 +1,770 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 <algorithm>
+#include <math.h>
+#include <string.h>
+
+#include "mozilla/CheckedInt.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 {
+
+/**
+ * Box blur involves looking at one pixel, and setting its value to the average
+ * of its neighbouring pixels.
+ * @param aInput The input buffer.
+ * @param aOutput The output buffer.
+ * @param aLeftLobe The number of pixels to blend on the left.
+ * @param aRightLobe The number of pixels to blend on the right.
+ * @param aWidth The number of columns in the buffers.
+ * @param aRows The number of rows in the buffers.
+ * @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,
+ int32_t aLeftLobe,
+ int32_t aRightLobe,
+ int32_t aWidth,
+ int32_t aRows,
+ const IntRect& aSkipRect)
+{
+ MOZ_ASSERT(aWidth > 0);
+
+ int32_t boxSize = aLeftLobe + aRightLobe + 1;
+ bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
+ aWidth <= aSkipRect.XMost();
+ if (boxSize == 1) {
+ memcpy(aOutput, aInput, aWidth*aRows);
+ return;
+ }
+ uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
+
+ for (int32_t y = 0; y < aRows; y++) {
+ // Check whether the skip rect intersects this row. If the skip
+ // rect covers the whole surface in this row, we can avoid
+ // this row entirely (and any others along the skip rect).
+ bool inSkipRectY = y >= aSkipRect.y &&
+ y < aSkipRect.YMost();
+ if (inSkipRectY && skipRectCoversWholeRow) {
+ y = aSkipRect.YMost() - 1;
+ continue;
+ }
+
+ uint32_t alphaSum = 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
+ // valid position to clamp to.
+ pos = max(pos, 0);
+ pos = min(pos, aWidth - 1);
+ alphaSum += aInput[aWidth * y + pos];
+ }
+ 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.
+ if (inSkipRectY && x >= aSkipRect.x &&
+ x < aSkipRect.XMost()) {
+ x = aSkipRect.XMost();
+ if (x >= aWidth)
+ break;
+
+ // Recalculate the neighbouring alpha values for
+ // our new point on the surface.
+ alphaSum = 0;
+ for (int32_t i = 0; i < boxSize; i++) {
+ int32_t pos = x + i - aLeftLobe;
+ // See assertion above; if aWidth is zero, then we would have no
+ // valid position to clamp to.
+ pos = max(pos, 0);
+ pos = min(pos, aWidth - 1);
+ alphaSum += aInput[aWidth * y + pos];
+ }
+ }
+ int32_t tmp = x - aLeftLobe;
+ int32_t last = max(tmp, 0);
+ int32_t next = min(tmp + boxSize, aWidth - 1);
+
+ aOutput[aWidth * y + x] = (uint64_t(alphaSum) * reciprocal) >> 32;
+
+ alphaSum += aInput[aWidth * y + next] -
+ aInput[aWidth * y + last];
+ }
+ }
+}
+
+/**
+ * Identical to BoxBlurHorizontal, except it blurs top and bottom instead of
+ * left and right.
+ * XXX shouldn't we pass stride in separately here?
+ */
+static void
+BoxBlurVertical(unsigned char* aInput,
+ unsigned char* aOutput,
+ int32_t aTopLobe,
+ int32_t aBottomLobe,
+ int32_t aWidth,
+ int32_t aRows,
+ const IntRect& aSkipRect)
+{
+ MOZ_ASSERT(aRows > 0);
+
+ int32_t boxSize = aTopLobe + aBottomLobe + 1;
+ bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
+ aRows <= aSkipRect.YMost();
+ if (boxSize == 1) {
+ memcpy(aOutput, aInput, aWidth*aRows);
+ return;
+ }
+ uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
+
+ for (int32_t x = 0; x < aWidth; x++) {
+ bool inSkipRectX = x >= aSkipRect.x &&
+ x < aSkipRect.XMost();
+ if (inSkipRectX && skipRectCoversWholeColumn) {
+ x = aSkipRect.XMost() - 1;
+ continue;
+ }
+
+ uint32_t alphaSum = 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
+ // valid position to clamp to.
+ pos = max(pos, 0);
+ pos = min(pos, aRows - 1);
+ alphaSum += aInput[aWidth * pos + x];
+ }
+ for (int32_t y = 0; y < aRows; y++) {
+ if (inSkipRectX && y >= aSkipRect.y &&
+ y < aSkipRect.YMost()) {
+ y = aSkipRect.YMost();
+ if (y >= aRows)
+ break;
+
+ alphaSum = 0;
+ for (int32_t i = 0; i < boxSize; i++) {
+ int32_t pos = y + i - aTopLobe;
+ // See assertion above; if aRows is zero, then we would have no
+ // valid position to clamp to.
+ pos = max(pos, 0);
+ pos = min(pos, aRows - 1);
+ alphaSum += aInput[aWidth * pos + x];
+ }
+ }
+ int32_t tmp = y - aTopLobe;
+ int32_t last = max(tmp, 0);
+ int32_t next = min(tmp + boxSize, aRows - 1);
+
+ aOutput[aWidth * y + x] = (uint64_t(alphaSum) * reciprocal) >> 32;
+
+ alphaSum += aInput[aWidth * next + x] -
+ aInput[aWidth * last + x];
+ }
+ }
+}
+
+static void ComputeLobes(int32_t aRadius, int32_t aLobes[3][2])
+{
+ int32_t major, minor, final;
+
+ /* See http://www.w3.org/TR/SVG/filters.html#feGaussianBlur for
+ * some notes about approximating the Gaussian blur with box-blurs.
+ * The comments below are in the terminology of that page.
+ */
+ int32_t z = aRadius / 3;
+ switch (aRadius % 3) {
+ case 0:
+ // aRadius = z*3; choose d = 2*z + 1
+ major = minor = final = z;
+ break;
+ case 1:
+ // aRadius = z*3 + 1
+ // This is a tricky case since there is no value of d which will
+ // yield a radius of exactly aRadius. If d is odd, i.e. d=2*k + 1
+ // for some integer k, then the radius will be 3*k. If d is even,
+ // i.e. d=2*k, then the radius will be 3*k - 1.
+ // So we have to choose values that don't match the standard
+ // algorithm.
+ major = z + 1;
+ minor = final = z;
+ break;
+ case 2:
+ // aRadius = z*3 + 2; choose d = 2*z + 2
+ major = final = z + 1;
+ minor = z;
+ break;
+ default:
+ // Mathematical impossibility!
+ MOZ_ASSERT(false);
+ major = minor = final = 0;
+ }
+ MOZ_ASSERT(major + minor + final == aRadius);
+
+ aLobes[0][0] = major;
+ aLobes[0][1] = minor;
+ aLobes[1][0] = minor;
+ aLobes[1][1] = major;
+ aLobes[2][0] = final;
+ aLobes[2][1] = final;
+}
+
+static void
+SpreadHorizontal(unsigned char* aInput,
+ unsigned char* aOutput,
+ int32_t aRadius,
+ int32_t aWidth,
+ int32_t aRows,
+ int32_t aStride,
+ const IntRect& aSkipRect)
+{
+ if (aRadius == 0) {
+ memcpy(aOutput, aInput, aStride * aRows);
+ return;
+ }
+
+ bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
+ aWidth <= aSkipRect.XMost();
+ for (int32_t y = 0; y < aRows; y++) {
+ // Check whether the skip rect intersects this row. If the skip
+ // rect covers the whole surface in this row, we can avoid
+ // this row entirely (and any others along the skip rect).
+ bool inSkipRectY = y >= aSkipRect.y &&
+ y < aSkipRect.YMost();
+ if (inSkipRectY && skipRectCoversWholeRow) {
+ y = aSkipRect.YMost() - 1;
+ continue;
+ }
+
+ 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.
+ if (inSkipRectY && x >= aSkipRect.x &&
+ x < aSkipRect.XMost()) {
+ x = aSkipRect.XMost();
+ if (x >= aWidth)
+ break;
+ }
+
+ int32_t sMin = max(x - aRadius, 0);
+ int32_t sMax = min(x + aRadius, aWidth - 1);
+ int32_t v = 0;
+ for (int32_t s = sMin; s <= sMax; ++s) {
+ v = max<int32_t>(v, aInput[aStride * y + s]);
+ }
+ aOutput[aStride * y + x] = v;
+ }
+ }
+}
+
+static void
+SpreadVertical(unsigned char* aInput,
+ unsigned char* aOutput,
+ int32_t aRadius,
+ int32_t aWidth,
+ int32_t aRows,
+ int32_t aStride,
+ const IntRect& aSkipRect)
+{
+ if (aRadius == 0) {
+ memcpy(aOutput, aInput, aStride * aRows);
+ return;
+ }
+
+ bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
+ aRows <= aSkipRect.YMost();
+ for (int32_t x = 0; x < aWidth; x++) {
+ bool inSkipRectX = x >= aSkipRect.x &&
+ x < aSkipRect.XMost();
+ if (inSkipRectX && skipRectCoversWholeColumn) {
+ x = aSkipRect.XMost() - 1;
+ continue;
+ }
+
+ 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.
+ if (inSkipRectX && y >= aSkipRect.y &&
+ y < aSkipRect.YMost()) {
+ y = aSkipRect.YMost();
+ if (y >= aRows)
+ break;
+ }
+
+ int32_t sMin = max(y - aRadius, 0);
+ int32_t sMax = min(y + aRadius, aRows - 1);
+ int32_t v = 0;
+ for (int32_t s = sMin; s <= sMax; ++s) {
+ v = max<int32_t>(v, aInput[aStride * s + x]);
+ }
+ aOutput[aStride * y + x] = v;
+ }
+ }
+}
+
+CheckedInt<int32_t>
+AlphaBoxBlur::RoundUpToMultipleOf4(int32_t aVal)
+{
+ CheckedInt<int32_t> val(aVal);
+
+ val += 3;
+ val /= 4;
+ val *= 4;
+
+ return val;
+}
+
+AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
+ const IntSize& aSpreadRadius,
+ const IntSize& aBlurRadius,
+ const Rect* aDirtyRect,
+ const Rect* aSkipRect)
+ : mSpreadRadius(aSpreadRadius),
+ mBlurRadius(aBlurRadius),
+ mSurfaceAllocationSize(0)
+{
+ Rect rect(aRect);
+ rect.Inflate(Size(aBlurRadius + aSpreadRadius));
+ rect.RoundOut();
+
+ if (aDirtyRect) {
+ // If we get passed a dirty rect from layout, we can minimize the
+ // shadow size and make painting faster.
+ mHasDirtyRect = true;
+ mDirtyRect = *aDirtyRect;
+ Rect requiredBlurArea = mDirtyRect.Intersect(rect);
+ requiredBlurArea.Inflate(Size(aBlurRadius + aSpreadRadius));
+ rect = requiredBlurArea.Intersect(rect);
+ } else {
+ mHasDirtyRect = false;
+ }
+
+ mRect = IntRect(int32_t(rect.x), int32_t(rect.y),
+ int32_t(rect.width), int32_t(rect.height));
+ if (mRect.IsEmpty()) {
+ return;
+ }
+
+ if (aSkipRect) {
+ // If we get passed a skip rect, we can lower the amount of
+ // blurring/spreading we need to do. We convert it to IntRect to avoid
+ // expensive int<->float conversions if we were to use Rect instead.
+ Rect skipRect = *aSkipRect;
+ skipRect.RoundIn();
+ skipRect.Deflate(Size(aBlurRadius + aSpreadRadius));
+ mSkipRect = IntRect(int32_t(skipRect.x), int32_t(skipRect.y),
+ int32_t(skipRect.width), int32_t(skipRect.height));
+
+ mSkipRect = mSkipRect.Intersect(mRect);
+ if (mSkipRect.IsEqualInterior(mRect))
+ return;
+
+ mSkipRect -= mRect.TopLeft();
+ } else {
+ mSkipRect = IntRect(0, 0, 0, 0);
+ }
+
+ CheckedInt<int32_t> stride = RoundUpToMultipleOf4(mRect.width);
+ if (stride.isValid()) {
+ mStride = stride.value();
+
+ // We need to leave room for an additional 3 bytes for a potential overrun
+ // in our blurring code.
+ size_t size = BufferSizeFromStrideAndHeight(mStride, mRect.height, 3);
+ if (size != 0) {
+ mSurfaceAllocationSize = size;
+ }
+ }
+}
+
+AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
+ int32_t aStride,
+ 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(aSigmaX, aSigmaY))),
+ mStride(aStride),
+ mSurfaceAllocationSize(0)
+{
+ IntRect intRect;
+ if (aRect.ToIntRect(&intRect)) {
+ size_t minDataSize = BufferSizeFromStrideAndHeight(intRect.width, intRect.height);
+ if (minDataSize != 0) {
+ mSurfaceAllocationSize = minDataSize;
+ }
+ }
+}
+
+
+AlphaBoxBlur::~AlphaBoxBlur()
+{
+}
+
+IntSize
+AlphaBoxBlur::GetSize()
+{
+ IntSize size(mRect.width, mRect.height);
+ return size;
+}
+
+int32_t
+AlphaBoxBlur::GetStride()
+{
+ return mStride;
+}
+
+IntRect
+AlphaBoxBlur::GetRect()
+{
+ return mRect;
+}
+
+Rect*
+AlphaBoxBlur::GetDirtyRect()
+{
+ if (mHasDirtyRect) {
+ return &mDirtyRect;
+ }
+
+ return nullptr;
+}
+
+size_t
+AlphaBoxBlur::GetSurfaceAllocationSize() const
+{
+ return mSurfaceAllocationSize;
+}
+
+void
+AlphaBoxBlur::Blur(uint8_t* aData)
+{
+ if (!aData) {
+ return;
+ }
+
+ // no need to do all this if not blurring or spreading
+ if (mBlurRadius != IntSize(0,0) || mSpreadRadius != IntSize(0,0)) {
+ int32_t stride = GetStride();
+
+ IntSize size = GetSize();
+
+ if (mSpreadRadius.width > 0 || mSpreadRadius.height > 0) {
+ // No need to use CheckedInt here - we have validated it in the constructor.
+ size_t szB = stride * size.height;
+ unsigned char* tmpData = new (std::nothrow) uint8_t[szB];
+
+ if (!tmpData) {
+ return;
+ }
+
+ memset(tmpData, 0, szB);
+
+ SpreadHorizontal(aData, tmpData, mSpreadRadius.width, GetSize().width, GetSize().height, stride, mSkipRect);
+ SpreadVertical(tmpData, aData, mSpreadRadius.height, GetSize().width, GetSize().height, stride, mSkipRect);
+
+ delete [] tmpData;
+ }
+
+ int32_t horizontalLobes[3][2];
+ ComputeLobes(mBlurRadius.width, horizontalLobes);
+ int32_t verticalLobes[3][2];
+ ComputeLobes(mBlurRadius.height, verticalLobes);
+
+ // We want to allow for some extra space on the left for alignment reasons.
+ int32_t maxLeftLobe = RoundUpToMultipleOf4(horizontalLobes[0][0] + 1).value();
+
+ IntSize integralImageSize(size.width + maxLeftLobe + horizontalLobes[1][1],
+ size.height + verticalLobes[0][0] + verticalLobes[1][1] + 1);
+
+ if ((integralImageSize.width * integralImageSize.height) > (1 << 24)) {
+ // Fallback to old blurring code when the surface is so large it may
+ // overflow our integral image!
+
+ // No need to use CheckedInt here - we have validated it in the constructor.
+ size_t szB = stride * size.height;
+ uint8_t* tmpData = new (std::nothrow) uint8_t[szB];
+ if (!tmpData) {
+ return;
+ }
+
+ memset(tmpData, 0, szB);
+
+ uint8_t* a = aData;
+ uint8_t* b = tmpData;
+ if (mBlurRadius.width > 0) {
+ BoxBlurHorizontal(a, b, horizontalLobes[0][0], horizontalLobes[0][1], stride, GetSize().height, mSkipRect);
+ BoxBlurHorizontal(b, a, horizontalLobes[1][0], horizontalLobes[1][1], stride, GetSize().height, mSkipRect);
+ BoxBlurHorizontal(a, b, horizontalLobes[2][0], horizontalLobes[2][1], stride, GetSize().height, mSkipRect);
+ } else {
+ a = tmpData;
+ b = aData;
+ }
+ // The result is in 'b' here.
+ if (mBlurRadius.height > 0) {
+ BoxBlurVertical(b, a, verticalLobes[0][0], verticalLobes[0][1], stride, GetSize().height, mSkipRect);
+ BoxBlurVertical(a, b, verticalLobes[1][0], verticalLobes[1][1], stride, GetSize().height, mSkipRect);
+ BoxBlurVertical(b, a, verticalLobes[2][0], verticalLobes[2][1], stride, GetSize().height, mSkipRect);
+ } else {
+ a = b;
+ }
+ // The result is in 'a' here.
+ if (a == tmpData) {
+ memcpy(aData, tmpData, szB);
+ }
+ delete [] tmpData;
+ } else {
+ size_t integralImageStride = GetAlignedStride<16>(integralImageSize.width, 4);
+ if (integralImageStride == 0) {
+ return;
+ }
+
+ // We need to leave room for an additional 12 bytes for a maximum overrun
+ // of 3 pixels in the blurring code.
+ 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],
+ verticalLobes[0][1], integralImage, integralImageStride);
+ BoxBlur_SSE2(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
+ verticalLobes[1][1], integralImage, integralImageStride);
+ BoxBlur_SSE2(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
+ 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
+ {
+#ifdef _MIPS_ARCH_LOONGSON3A
+ BoxBlur_LS3(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
+ verticalLobes[0][1], integralImage, integralImageStride);
+ BoxBlur_LS3(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
+ verticalLobes[1][1], integralImage, integralImageStride);
+ BoxBlur_LS3(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
+ verticalLobes[2][1], integralImage, integralImageStride);
+#else
+ BoxBlur_C(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
+ verticalLobes[0][1], integralImage, integralImageStride);
+ BoxBlur_C(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
+ verticalLobes[1][1], integralImage, integralImageStride);
+ BoxBlur_C(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
+ verticalLobes[2][1], integralImage, integralImageStride);
+#endif
+ }
+ }
+ }
+}
+
+MOZ_ALWAYS_INLINE void
+GenerateIntegralRow(uint32_t *aDest, const uint8_t *aSource, uint32_t *aPreviousRow,
+ const uint32_t &aSourceWidth, const uint32_t &aLeftInflation, const uint32_t &aRightInflation)
+{
+ uint32_t currentRowSum = 0;
+ uint32_t pixel = aSource[0];
+ for (uint32_t x = 0; x < aLeftInflation; x++) {
+ currentRowSum += pixel;
+ *aDest++ = currentRowSum + *aPreviousRow++;
+ }
+ for (uint32_t x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x += 4) {
+ uint32_t alphaValues = *(uint32_t*)(aSource + (x - aLeftInflation));
+#if defined WORDS_BIGENDIAN || defined IS_BIG_ENDIAN || defined __BIG_ENDIAN__
+ currentRowSum += (alphaValues >> 24) & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+ currentRowSum += (alphaValues >> 16) & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+ currentRowSum += (alphaValues >> 8) & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+ currentRowSum += alphaValues & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+#else
+ currentRowSum += alphaValues & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+ alphaValues >>= 8;
+ currentRowSum += alphaValues & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+ alphaValues >>= 8;
+ currentRowSum += alphaValues & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+ alphaValues >>= 8;
+ currentRowSum += alphaValues & 0xff;
+ *aDest++ = *aPreviousRow++ + currentRowSum;
+#endif
+ }
+ pixel = aSource[aSourceWidth - 1];
+ for (uint32_t x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
+ currentRowSum += pixel;
+ *aDest++ = currentRowSum + *aPreviousRow++;
+ }
+}
+
+MOZ_ALWAYS_INLINE void
+GenerateIntegralImage_C(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)
+{
+ uint32_t stride32bit = aIntegralImageStride / 4;
+
+ IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
+ aSize.height + aTopInflation + aBottomInflation);
+
+ memset(aIntegralImage, 0, aIntegralImageStride);
+
+ GenerateIntegralRow(aIntegralImage, aSource, aIntegralImage,
+ aSize.width, aLeftInflation, aRightInflation);
+ for (int y = 1; y < aTopInflation + 1; y++) {
+ GenerateIntegralRow(aIntegralImage + (y * stride32bit), aSource, aIntegralImage + (y - 1) * stride32bit,
+ aSize.width, aLeftInflation, aRightInflation);
+ }
+
+ for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
+ GenerateIntegralRow(aIntegralImage + (y * stride32bit), aSource + aSourceStride * (y - aTopInflation),
+ aIntegralImage + (y - 1) * stride32bit, aSize.width, aLeftInflation, aRightInflation);
+ }
+
+ if (aBottomInflation) {
+ for (int y = (aSize.height + aTopInflation); y < integralImageSize.height; y++) {
+ GenerateIntegralRow(aIntegralImage + (y * stride32bit), aSource + ((aSize.height - 1) * aSourceStride),
+ aIntegralImage + (y - 1) * stride32bit,
+ aSize.width, aLeftInflation, aRightInflation);
+ }
+ }
+}
+
+/**
+ * Attempt to do an in-place box blur using an integral image.
+ */
+void
+AlphaBoxBlur::BoxBlur_C(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.width > 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;
+ }
+
+ int32_t stride32bit = aIntegralImageStride / 4;
+
+ int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
+
+ GenerateIntegralImage_C(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
+ aIntegralImage, aIntegralImageStride, aData,
+ mStride, size);
+
+ uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
+
+ uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
+
+ // Storing these locally makes this about 30% faster! Presumably the compiler
+ // can't be sure we're not altering the member variables in this loop.
+ IntRect skipRect = mSkipRect;
+ uint8_t *data = aData;
+ int32_t stride = mStride;
+ for (int32_t y = 0; y < size.height; y++) {
+ bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
+
+ uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * stride32bit - aLeftLobe);
+ uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * stride32bit + aRightLobe);
+ uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * stride32bit + aRightLobe);
+ uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * stride32bit - aLeftLobe);
+
+ for (int32_t x = 0; x < size.width; x++) {
+ if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
+ x = skipRect.XMost() - 1;
+ // Trigger early jump on coming loop iterations, this will be reset
+ // next line anyway.
+ inSkipRectY = false;
+ continue;
+ }
+ int32_t topLeft = topLeftBase[x];
+ int32_t topRight = topRightBase[x];
+ int32_t bottomRight = bottomRightBase[x];
+ int32_t bottomLeft = bottomLeftBase[x];
+
+ uint32_t value = bottomRight - topRight - bottomLeft;
+ value += topLeft;
+
+ data[stride * y + x] = (uint64_t(reciprocal) * value + (uint64_t(1) << 31)) >> 32;
+ }
+ }
+}
+
+/**
+ * Compute the box blur size (which we're calling the blur radius) from
+ * the standard deviation.
+ *
+ * Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for
+ * approximating a Gaussian using box blurs. This yields quite a good
+ * approximation for a Gaussian. Then we multiply this by 1.5 since our
+ * code wants the radius of the entire triple-box-blur kernel instead of
+ * the diameter of an individual box blur. For more details, see:
+ * http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19
+ */
+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.5f)),
+ static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5f)));
+
+ return size;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/Blur.h b/gfx/2d/Blur.h
new file mode 100644
index 000000000..8464a57fe
--- /dev/null
+++ b/gfx/2d/Blur.h
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_BLUR_H_
+#define MOZILLA_GFX_BLUR_H_
+
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/CheckedInt.h"
+
+namespace mozilla {
+namespace gfx {
+
+#ifdef _MSC_VER
+#pragma warning( disable : 4251 )
+#endif
+
+/**
+ * Implementation of a triple box blur approximation of a Gaussian blur.
+ *
+ * A Gaussian blur is good for blurring because, when done independently
+ * in the horizontal and vertical directions, it matches the result that
+ * would be obtained using a different (rotated) set of axes. A triple
+ * box blur is a very close approximation of a Gaussian.
+ *
+ * This is a "service" class; the constructors set up all the information
+ * based on the values and compute the minimum size for an 8-bit alpha
+ * channel context.
+ * The callers are responsible for creating and managing the backing surface
+ * and passing the pointer to the data to the Blur() method. This class does
+ * not retain the pointer to the data outside of the Blur() call.
+ *
+ * A spread N makes each output pixel the maximum value of all source
+ * pixels within a square of side length 2N+1 centered on the output pixel.
+ */
+class GFX2D_API AlphaBoxBlur
+{
+public:
+
+ /** Constructs a box blur and computes the backing surface size.
+ *
+ * @param aRect The coordinates of the surface to create in device units.
+ *
+ * @param aBlurRadius The blur radius in pixels. This is the radius of the
+ * entire (triple) kernel function. Each individual box blur has radius
+ * approximately 1/3 this value, or diameter approximately 2/3 this value.
+ * This parameter should nearly always be computed using CalculateBlurRadius,
+ * below.
+ *
+ * @param aDirtyRect A pointer to a dirty rect, measured in device units, if
+ * available. This will be used for optimizing the blur operation. It is
+ * safe to pass nullptr here.
+ *
+ * @param aSkipRect A pointer to a rect, measured in device units, that
+ * represents an area where blurring is unnecessary and shouldn't be done for
+ * speed reasons. It is safe to pass nullptr here.
+ */
+ AlphaBoxBlur(const Rect& aRect,
+ const IntSize& aSpreadRadius,
+ const IntSize& aBlurRadius,
+ const Rect* aDirtyRect,
+ const Rect* aSkipRect);
+
+ AlphaBoxBlur(const Rect& aRect,
+ int32_t aStride,
+ float aSigmaX,
+ float aSigmaY);
+
+ ~AlphaBoxBlur();
+
+ /**
+ * Return the size, in pixels, of the 8-bit alpha surface we'd use.
+ */
+ IntSize GetSize();
+
+ /**
+ * Return the stride, in bytes, of the 8-bit alpha surface we'd use.
+ */
+ int32_t GetStride();
+
+ /**
+ * Returns the device-space rectangle the 8-bit alpha surface covers.
+ */
+ IntRect GetRect();
+
+ /**
+ * Return a pointer to a dirty rect, as passed in to the constructor, or nullptr
+ * if none was passed in.
+ */
+ Rect* GetDirtyRect();
+
+ /**
+ * Return the minimum buffer size that should be given to Blur() method. If
+ * 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.
+ */
+ size_t GetSurfaceAllocationSize() const;
+
+ /**
+ * Perform the blur in-place on the surface backed by specified 8-bit
+ * alpha surface data. The size must be at least that returned by
+ * GetSurfaceAllocationSize() or bad things will happen.
+ */
+ void Blur(uint8_t* aData);
+
+ /**
+ * Calculates a blur radius that, when used with box blur, approximates a
+ * Gaussian blur with the given standard deviation. The result of this
+ * function should be used as the aBlurRadius parameter to AlphaBoxBlur's
+ * constructor, above.
+ */
+ static IntSize CalculateBlurRadius(const Point& aStandardDeviation);
+
+private:
+
+ void BoxBlur_C(uint8_t* aData,
+ int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
+ int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
+ 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
+#ifdef _MIPS_ARCH_LOONGSON3A
+ void BoxBlur_LS3(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);
+
+ /**
+ * A rect indicating the area where blurring is unnecessary, and the blur
+ * algorithm should skip over it.
+ */
+ IntRect mSkipRect;
+
+ /**
+ * The device-space rectangle the the backing 8-bit alpha surface covers.
+ */
+ IntRect mRect;
+
+ /**
+ * A copy of the dirty rect passed to the constructor. This will only be valid if
+ * mHasDirtyRect is true.
+ */
+ Rect mDirtyRect;
+
+ /**
+ * The spread radius, in pixels.
+ */
+ IntSize mSpreadRadius;
+
+ /**
+ * The blur radius, in pixels.
+ */
+ IntSize mBlurRadius;
+
+ /**
+ * The stride of the data passed to Blur()
+ */
+ int32_t mStride;
+
+ /**
+ * The minimum size of the buffer needed for the Blur() operation.
+ */
+ size_t mSurfaceAllocationSize;
+
+ /**
+ * Whether mDirtyRect contains valid data.
+ */
+ bool mHasDirtyRect;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BLUR_H_ */
diff --git a/gfx/2d/BlurLS3.cpp b/gfx/2d/BlurLS3.cpp
new file mode 100644
index 000000000..20c28b37e
--- /dev/null
+++ b/gfx/2d/BlurLS3.cpp
@@ -0,0 +1,588 @@
+/* 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 <string.h>
+
+#ifdef _MIPS_ARCH_LOONGSON3A
+
+#include "MMIHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+typedef struct { double l; double h; } __m128i;
+
+MOZ_ALWAYS_INLINE
+__m128i loadUnaligned128(__m128i *p)
+{
+ __m128i v;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gsldlc1 %[vh], 0xf(%[p]) \n\t"
+ "gsldrc1 %[vh], 0x8(%[p]) \n\t"
+ "gsldlc1 %[vl], 0x7(%[p]) \n\t"
+ "gsldrc1 %[vl], 0x0(%[p]) \n\t"
+ ".set pop \n\t"
+ :[vh]"=f"(v.h), [vl]"=f"(v.l)
+ :[p]"r"(p)
+ :"memory"
+ );
+
+ return v;
+}
+
+MOZ_ALWAYS_INLINE
+__m128i Divide(__m128i aValues, __m128i aDivisor)
+{
+ uint64_t tmp;
+ double srl32;
+ __m128i mask, ra, p4321, t1, t2;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "li %[tmp], 0x80000000 \n\t"
+ "mtc1 %[tmp], %[ral] \n\t"
+ "xor %[maskl], %[maskl], %[maskl] \n\t"
+ "mov.d %[rah], %[ral] \n\t"
+ "li %[tmp], 0xffffffff \n\t"
+ "mthc1 %[tmp], %[maskl] \n\t"
+ "mov.d %[maskh], %[maskl] \n\t"
+ ".set pop \n\t"
+ :[rah]"=f"(ra.h), [ral]"=f"(ra.l),
+ [maskh]"=f"(mask.h), [maskl]"=f"(mask.l),
+ [tmp]"=&r"(tmp)
+ );
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "ori %[tmp], $0, 32 \n\t"
+ "mtc1 %[tmp], %[srl32] \n\t"
+ _mm_pmuluw(t1, av, ad)
+ _mm_psrld(t2, av, srl32)
+ _mm_pmuluw(t2, t2, ad)
+ // Add 1 << 31 before shifting or masking the lower 32 bits away, so that the
+ // result is rounded.
+ _mm_paddd(t1, t1, ra)
+ _mm_psrld(t1, t1, srl32)
+ _mm_paddd(t2, t2, ra)
+ _mm_and(t2, t2, mask)
+ _mm_or(p4321, t1, t2)
+ ".set pop \n\t"
+ :[p4321h]"=&f"(p4321.h), [p4321l]"=&f"(p4321.l),
+ [t1h]"=&f"(t1.h), [t1l]"=&f"(t1.l),
+ [t2h]"=&f"(t2.h), [t2l]"=&f"(t2.l),
+ [srl32]"=&f"(srl32), [tmp]"=&r"(tmp)
+ :[rah]"f"(ra.h), [ral]"f"(ra.l),
+ [maskh]"f"(mask.h), [maskl]"f"(mask.l),
+ [avh]"f"(aValues.h), [avl]"f"(aValues.l),
+ [adh]"f"(aDivisor.h), [adl]"f"(aDivisor.l)
+ );
+
+ return p4321;
+}
+
+MOZ_ALWAYS_INLINE
+__m128i BlurFourPixels(const __m128i& aTopLeft, const __m128i& aTopRight,
+ const __m128i& aBottomRight, const __m128i& aBottomLeft,
+ const __m128i& aDivisor)
+{
+ __m128i values;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_psubw(val, abr, atr)
+ _mm_psubw(val, val, abl)
+ _mm_paddw(val, val, atl)
+ ".set pop \n\t"
+ :[valh]"=&f"(values.h), [vall]"=&f"(values.l)
+ :[abrh]"f"(aBottomRight.h), [abrl]"f"(aBottomRight.l),
+ [atrh]"f"(aTopRight.h), [atrl]"f"(aTopRight.l),
+ [ablh]"f"(aBottomLeft.h), [abll]"f"(aBottomLeft.l),
+ [atlh]"f"(aTopLeft.h), [atll]"f"(aTopLeft.l)
+ );
+
+ 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;
+ }
+}
+
+// This function calculates an integral of four pixels stored in the 4
+// 32-bit integers on aPixels. i.e. for { 30, 50, 80, 100 } this returns
+// { 30, 80, 160, 260 }. This seems to be the fastest way to do this after
+// much testing.
+MOZ_ALWAYS_INLINE
+__m128i AccumulatePixelSums(__m128i aPixels)
+{
+ uint64_t tr;
+ double tmp, s4, s64;
+ __m128i sumPixels, currentPixels, zero;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_xor(z, z, z)
+ "li %[tr], 64 \n\t"
+ "mtc1 %[tr], %[s64] \n\t"
+ "li %[tr], 32 \n\t"
+ "mtc1 %[tr], %[s4] \n\t"
+ _mm_psllq(cp, ap, s4, s64, t)
+ _mm_paddw(sp, ap, cp)
+ _mm_punpckldq(cp, z, sp)
+ _mm_paddw(sp, sp, cp)
+ ".set pop \n\t"
+ :[sph]"=&f"(sumPixels.h), [spl]"=&f"(sumPixels.l),
+ [cph]"=&f"(currentPixels.h), [cpl]"=&f"(currentPixels.l),
+ [zh]"=&f"(zero.h), [zl]"=&f"(zero.l),
+ [s4]"=&f"(s4), [s64]"=&f"(s64), [t]"=&f"(tmp), [tr]"=&r"(tr)
+ :[aph]"f"(aPixels.h), [apl]"f"(aPixels.l)
+ );
+
+ return sumPixels;
+}
+
+MOZ_ALWAYS_INLINE
+void GenerateIntegralImage_LS3(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) {
+ __m128i firstRow, previousRow;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gslqc1 %[frh], %[frl], (%[fr]) \n\t"
+ "gslqc1 %[prh], %[prl], (%[pr]) \n\t"
+ _mm_paddw(fr, fr, pr)
+ "gssqc1 %[frh], %[frl], (%[r]) \n\t"
+ ".set pop \n\t"
+ :[frh]"=&f"(firstRow.h), [frl]"=&f"(firstRow.l),
+ [prh]"=&f"(previousRow.h), [prl]"=&f"(previousRow.l)
+ :[fr]"r"(intFirstRow + x), [pr]"r"(intPrevRow + x),
+ [r]"r"(intRow + x)
+ :"memory"
+ );
+ }
+ }
+
+ uint64_t tmp;
+ double s44, see;
+ __m128i zero;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "li %[tmp], 0xee \n\t"
+ "mtc1 %[tmp], %[see] \n\t"
+ "li %[tmp], 0x44 \n\t"
+ "mtc1 %[tmp], %[s44] \n\t"
+ _mm_xor(zero, zero, zero)
+ ".set pop \n\t"
+ :[tmp]"=&r"(tmp), [s44]"=f"(s44), [see]"=f"(see),
+ [zeroh]"=f"(zero.h), [zerol]"=f"(zero.l)
+ );
+ for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
+ __m128i currentRowSum;
+ 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];
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_xor(cr, cr, cr)
+ ".set pop \n\t"
+ :[crh]"=f"(currentRowSum.h), [crl]"=f"(currentRowSum.l)
+ );
+ for (int x = 0; x < aLeftInflation; x += 4) {
+ __m128i sumPixels, t;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "mtc1 %[pix], %[spl] \n\t"
+ "punpcklwd %[spl], %[spl], %[spl] \n\t"
+ "mov.d %[sph], %[spl] \n\t"
+ "pshufh %[sph], %[spl], %[s44] \n\t"
+ "pshufh %[spl], %[spl], %[s44] \n\t"
+ ".set pop \n\t"
+ :[sph]"=&f"(sumPixels.h), [spl]"=&f"(sumPixels.l)
+ :[pix]"r"(pixel), [s44]"f"(s44)
+ );
+ sumPixels = AccumulatePixelSums(sumPixels);
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_paddw(sp, sp, cr)
+ "pshufh %[crh], %[sph], %[see] \n\t"
+ "pshufh %[crl], %[sph], %[see] \n\t"
+ "gslqc1 %[th], %[tl], (%[pr]) \n\t"
+ _mm_paddw(t, sp, t)
+ "gssqc1 %[th], %[tl], (%[r]) \n\t"
+ ".set pop \n\t"
+ :[th]"=&f"(t.h), [tl]"=&f"(t.l),
+ [sph]"+f"(sumPixels.h), [spl]"+f"(sumPixels.l),
+ [crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
+ :[r]"r"(intRow + x), [pr]"r"(intPrevRow + x), [see]"f"(see)
+ :"memory"
+ );
+ }
+ for (int x = aLeftInflation; x < (aSize.width + aLeftInflation); x += 4) {
+ uint32_t pixels = *(uint32_t*)(sourceRow + (x - aLeftInflation));
+ __m128i sumPixels, t;
+
+ // 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.
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "pshufh %[crl], %[crh], %[see] \n\t"
+ "pshufh %[crh], %[crh], %[see] \n\t"
+ "mtc1 %[pix], %[spl] \n\t"
+ "punpcklwd %[spl], %[spl], %[spl] \n\t"
+ "mov.d %[sph], %[spl] \n\t"
+ _mm_punpcklbh(sp, sp, zero)
+ _mm_punpcklhw(sp, sp, zero)
+ ".set pop \n\t"
+ :[sph]"=&f"(sumPixels.h), [spl]"=&f"(sumPixels.l),
+ [crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
+ :[pix]"r"(pixels), [see]"f"(see),
+ [zeroh]"f"(zero.h), [zerol]"f"(zero.l)
+ );
+ sumPixels = AccumulatePixelSums(sumPixels);
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_paddw(sp, sp, cr)
+ "mov.d %[crh], %[sph] \n\t"
+ "mov.d %[crl], %[spl] \n\t"
+ "gslqc1 %[th], %[tl], (%[pr]) \n\t"
+ _mm_paddw(t, sp, t)
+ "gssqc1 %[th], %[tl], (%[r]) \n\t"
+ ".set pop \n\t"
+ :[th]"=&f"(t.h), [tl]"=&f"(t.l),
+ [sph]"+f"(sumPixels.h), [spl]"+f"(sumPixels.l),
+ [crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
+ :[r]"r"(intRow + x), [pr]"r"(intPrevRow + x)
+ :"memory"
+ );
+ }
+
+ 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!
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "mtc1 %[cr], %[crl] \n\t"
+ "punpcklwd %[crl], %[crl], %[crl] \n\t"
+ "mov.d %[crh], %[crl] \n\t"
+ ".set pop \n\t"
+ :[crh]"=f"(currentRowSum.h), [crl]"=f"(currentRowSum.l)
+ :[cr]"r"(intCurrentRowSum)
+ );
+ break;
+ }
+ intCurrentRowSum += pixel;
+ intRow[x] = intPrevRow[x] + intCurrentRowSum;
+ }
+ } else {
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "pshufh %[crl], %[crh], %[see] \n\t"
+ "pshufh %[crh], %[crh], %[see] \n\t"
+ ".set pop \n\t"
+ :[crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
+ :[see]"f"(see)
+ );
+ }
+ for (; x < integralImageSize.width; x += 4) {
+ __m128i sumPixels, t;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "mtc1 %[pix], %[spl] \n\t"
+ "punpcklwd %[spl], %[spl], %[spl] \n\t"
+ "mov.d %[sph], %[spl] \n\t"
+ ".set pop \n\t"
+ :[sph]"=f"(sumPixels.h), [spl]"=f"(sumPixels.l)
+ :[pix]"r"(pixel)
+ );
+ sumPixels = AccumulatePixelSums(sumPixels);
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_paddw(sp, sp, cr)
+ "pshufh %[crh], %[sph], %[see] \n\t"
+ "pshufh %[crl], %[sph], %[see] \n\t"
+ "gslqc1 %[th], %[tl], (%[pr]) \n\t"
+ _mm_paddw(t, sp, t)
+ "gssqc1 %[th], %[tl], (%[r]) \n\t"
+ ".set pop \n\t"
+ :[th]"=&f"(t.h), [tl]"=&f"(t.l),
+ [sph]"+f"(sumPixels.h), [spl]"+f"(sumPixels.l),
+ [crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
+ :[r]"r"(intRow + x), [pr]"r"(intPrevRow + x), [see]"f"(see)
+ :"memory"
+ );
+ }
+ }
+
+ 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++) {
+ __m128i *intRow = (__m128i*)(aIntegralImage + (y * stride32bit));
+ __m128i *intPrevRow = (__m128i*)(aIntegralImage + (y - 1) * stride32bit);
+ __m128i *intLastRow = (__m128i*)(aIntegralImage + (integralImageSize.height - 1) * stride32bit);
+
+ for (int x = 0; x < integralImageSize.width; x += 4) {
+ __m128i t1, t2;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gslqc1 %[t1h], %[t1l], (%[lr]) \n\t"
+ "gslqc1 %[t2h], %[t2l], (%[pr]) \n\t"
+ _mm_paddw(t1, t1, t2)
+ "gssqc1 %[t1h], %[t1l], (%[r]) \n\t"
+ ".set pop \n\t"
+ :[t1h]"=&f"(t1.h), [t1l]"=&f"(t1.l),
+ [t2h]"=&f"(t2.h), [t2l]"=&f"(t2.l)
+ :[r]"r"(intRow + (x / 4)),
+ [lr]"r"(intLastRow + (x / 4)),
+ [pr]"r"(intPrevRow + (x / 4))
+ :"memory"
+ );
+ }
+ }
+ }
+}
+
+/**
+ * Attempt to do an in-place box blur using an integral image.
+ */
+void
+AlphaBoxBlur::BoxBlur_LS3(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_LS3(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
+ aIntegralImage, aIntegralImageStride, aData,
+ mStride, size);
+
+ __m128i divisor, zero;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "mtc1 %[rec], %[divl] \n\t"
+ "punpcklwd %[divl], %[divl], %[divl] \n\t"
+ "mov.d %[divh], %[divl] \n\t"
+ _mm_xor(zero, zero, zero)
+ ".set pop \n\t"
+ :[divh]"=f"(divisor.h), [divl]"=f"(divisor.l),
+ [zeroh]"=f"(zero.h), [zerol]"=f"(zero.l)
+ :[rec]"r"(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;
+ }
+
+ __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);
+
+ double t;
+ __m128i final;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_packsswh(r3, r3, r4, t)
+ _mm_packsswh(f, r1, r2, t)
+ _mm_packushb(f, f, r3, t)
+ "gssdlc1 %[fh], 0xf(%[d]) \n\t"
+ "gssdrc1 %[fh], 0x8(%[d]) \n\t"
+ "gssdlc1 %[fl], 0x7(%[d]) \n\t"
+ "gssdrc1 %[fl], 0x0(%[d]) \n\t"
+ ".set pop \n\t"
+ :[fh]"=&f"(final.h), [fl]"=&f"(final.l),
+ [r3h]"+f"(result3.h), [r3l]"+f"(result3.l),
+ [t]"=&f"(t)
+ :[r1h]"f"(result1.h), [r1l]"f"(result1.l),
+ [r2h]"f"(result2.h), [r2l]"f"(result2.l),
+ [r4h]"f"(result4.h), [r4l]"f"(result4.l),
+ [d]"r"(data + stride * y + x)
+ :"memory"
+ );
+ }
+
+ // 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;
+ }
+ __m128i topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
+ __m128i topRight = loadUnaligned128((__m128i*)(topRightBase + x));
+ __m128i bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
+ __m128i bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
+
+ __m128i result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
+
+ double t;
+ __m128i final;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_packsswh(f, r, zero, t)
+ _mm_packushb(f, f, zero, t)
+ "swc1 %[fl], (%[d]) \n\t"
+ ".set pop \n\t"
+ :[fh]"=&f"(final.h), [fl]"=&f"(final.l),
+ [t]"=&f"(t)
+ :[d]"r"(data + stride * y + x),
+ [rh]"f"(result.h), [rl]"f"(result.l),
+ [zeroh]"f"(zero.h), [zerol]"f"(zero.l)
+ :"memory"
+ );
+ }
+ }
+
+}
+
+}
+}
+
+#endif /* _MIPS_ARCH_LOONGSON3A */
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
new file mode 100644
index 000000000..d652325b9
--- /dev/null
+++ b/gfx/2d/BlurSSE2.cpp
@@ -0,0 +1,315 @@
+/* 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 "SSEHelpers.h"
+
+#include <string.h>
+
+namespace mozilla {
+namespace gfx {
+
+MOZ_ALWAYS_INLINE
+__m128i Divide(__m128i aValues, __m128i aDivisor)
+{
+ 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;
+}
+
+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
+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;
+ }
+}
+
+// This function calculates an integral of four pixels stored in the 4
+// 32-bit integers on aPixels. i.e. for { 30, 50, 80, 100 } this returns
+// { 30, 80, 160, 260 }. This seems to be the fastest way to do this after
+// much testing.
+MOZ_ALWAYS_INLINE
+__m128i AccumulatePixelSums(__m128i aPixels)
+{
+ __m128i sumPixels = aPixels;
+ __m128i currentPixels = _mm_slli_si128(aPixels, 4);
+ sumPixels = _mm_add_epi32(sumPixels, currentPixels);
+ currentPixels = _mm_unpacklo_epi64(_mm_setzero_si128(), sumPixels);
+
+ return _mm_add_epi32(sumPixels, currentPixels);
+}
+
+MOZ_ALWAYS_INLINE void
+GenerateIntegralImage_SSE2(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) {
+ __m128i firstRow = _mm_load_si128((__m128i*)(intFirstRow + x));
+ __m128i previousRow = _mm_load_si128((__m128i*)(intPrevRow + x));
+ _mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(firstRow, previousRow));
+ }
+ }
+
+ for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
+ __m128i currentRowSum = _mm_setzero_si128();
+ 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) {
+ __m128i sumPixels = AccumulatePixelSums(_mm_shuffle_epi32(_mm_set1_epi32(pixel), _MM_SHUFFLE(0, 0, 0, 0)));
+
+ sumPixels = _mm_add_epi32(sumPixels, currentRowSum);
+
+ currentRowSum = _mm_shuffle_epi32(sumPixels, _MM_SHUFFLE(3, 3, 3, 3));
+
+ _mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(sumPixels, _mm_load_si128((__m128i*)(intPrevRow + x))));
+ }
+ for (int x = aLeftInflation; x < (aSize.width + aLeftInflation); x += 4) {
+ uint32_t pixels = *(uint32_t*)(sourceRow + (x - aLeftInflation));
+
+ // 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 = _mm_shuffle_epi32(currentRowSum, _MM_SHUFFLE(3, 3, 3, 3));
+
+ __m128i sumPixels = AccumulatePixelSums(_mm_unpacklo_epi16(_mm_unpacklo_epi8( _mm_set1_epi32(pixels), _mm_setzero_si128()), _mm_setzero_si128()));
+ sumPixels = _mm_add_epi32(sumPixels, currentRowSum);
+
+ currentRowSum = sumPixels;
+
+ _mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(sumPixels, _mm_load_si128((__m128i*)(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 = _mm_set1_epi32(intCurrentRowSum);
+ break;
+ }
+ intCurrentRowSum += pixel;
+ intRow[x] = intPrevRow[x] + intCurrentRowSum;
+ }
+ } else {
+ currentRowSum = _mm_shuffle_epi32(currentRowSum, _MM_SHUFFLE(3, 3, 3, 3));
+ }
+ for (; x < integralImageSize.width; x += 4) {
+ __m128i sumPixels = AccumulatePixelSums(_mm_set1_epi32(pixel));
+
+ sumPixels = _mm_add_epi32(sumPixels, currentRowSum);
+
+ currentRowSum = _mm_shuffle_epi32(sumPixels, _MM_SHUFFLE(3, 3, 3, 3));
+
+ _mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(sumPixels, _mm_load_si128((__m128i*)(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++) {
+ __m128i *intRow = (__m128i*)(aIntegralImage + (y * stride32bit));
+ __m128i *intPrevRow = (__m128i*)(aIntegralImage + (y - 1) * stride32bit);
+ __m128i *intLastRow = (__m128i*)(aIntegralImage + (integralImageSize.height - 1) * stride32bit);
+
+ for (int x = 0; x < integralImageSize.width; x += 4) {
+ _mm_store_si128(intRow + (x / 4),
+ _mm_add_epi32(_mm_load_si128(intLastRow + (x / 4)),
+ _mm_load_si128(intPrevRow + (x / 4))));
+ }
+ }
+ }
+}
+
+/**
+ * Attempt to do an in-place box blur using an integral image.
+ */
+void
+AlphaBoxBlur::BoxBlur_SSE2(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_SSE2(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
+ aIntegralImage, aIntegralImageStride, aData,
+ mStride, size);
+
+ __m128i divisor = _mm_set1_epi32(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;
+ }
+
+ __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
+ // next line anyway.
+ inSkipRectY = false;
+ continue;
+ }
+ __m128i topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
+ __m128i topRight = loadUnaligned128((__m128i*)(topRightBase + x));
+ __m128i bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
+ __m128i bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
+
+ __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) = _mm_cvtsi128_si32(final);
+ }
+ }
+
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/BorrowedContext.h b/gfx/2d/BorrowedContext.h
new file mode 100644
index 000000000..edb923b1e
--- /dev/null
+++ b/gfx/2d/BorrowedContext.h
@@ -0,0 +1,217 @@
+/* -*- 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"
+
+#ifdef MOZ_X11
+#include <X11/extensions/Xrender.h>
+#include <X11/Xlib.h>
+#include "X11UndefineNone.h"
+#endif
+
+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 MOZ_X11
+/* This is a helper class that let's you borrow an Xlib drawable from
+ * a DrawTarget. This is used for drawing themed widgets.
+ *
+ * Callers should check the Xlib drawable after constructing the object
+ * to see if it succeeded. The DrawTarget should not be used while
+ * the drawable is borrowed. */
+class BorrowedXlibDrawable
+{
+public:
+ BorrowedXlibDrawable()
+ : mDT(nullptr),
+ mDisplay(nullptr),
+ mDrawable(X11None),
+ mScreen(nullptr),
+ mVisual(nullptr),
+ mXRenderFormat(nullptr)
+ {}
+
+ explicit BorrowedXlibDrawable(DrawTarget *aDT)
+ : mDT(nullptr),
+ mDisplay(nullptr),
+ mDrawable(X11None),
+ mScreen(nullptr),
+ mVisual(nullptr),
+ mXRenderFormat(nullptr)
+ {
+ Init(aDT);
+ }
+
+ // We can optionally Init after construction in
+ // case we don't know what the DT will be at construction
+ // time.
+ bool Init(DrawTarget *aDT);
+
+ // The caller needs to call Finish if drawable is non-zero 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();
+
+ ~BorrowedXlibDrawable() {
+ MOZ_ASSERT(!mDrawable);
+ }
+
+ Display *GetDisplay() const { return mDisplay; }
+ Drawable GetDrawable() const { return mDrawable; }
+ Screen *GetScreen() const { return mScreen; }
+ Visual *GetVisual() const { return mVisual; }
+ IntSize GetSize() const { return mSize; }
+ Point GetOffset() const { return mOffset; }
+
+ XRenderPictFormat* GetXRenderFormat() const { return mXRenderFormat; }
+
+private:
+ DrawTarget *mDT;
+ Display *mDisplay;
+ Drawable mDrawable;
+ Screen *mScreen;
+ Visual *mVisual;
+ XRenderPictFormat *mXRenderFormat;
+ IntSize mSize;
+ Point mOffset;
+};
+#endif
+
+#ifdef XP_DARWIN
+/* 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:
+#ifdef USE_SKIA
+ static CGContextRef BorrowCGContextFromDrawTarget(DrawTarget *aDT);
+ static void ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg);
+#else
+ static CGContextRef BorrowCGContextFromDrawTarget(DrawTarget *aDT) {
+ MOZ_CRASH("Not supported without Skia");
+ }
+
+ static void ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg) {
+ MOZ_CRASH("not supported without Skia");
+ }
+#endif
+ DrawTarget *mDT;
+};
+#endif
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_BORROWED_CONTEXT_H
diff --git a/gfx/2d/CGTextDrawing.h b/gfx/2d/CGTextDrawing.h
new file mode 100644
index 000000000..b9e3b374a
--- /dev/null
+++ b/gfx/2d/CGTextDrawing.h
@@ -0,0 +1,144 @@
+/* -*- 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_SKIACGPOPUPDRAWER_H
+#define _MOZILLA_GFX_SKIACGPOPUPDRAWER_H
+
+#include <ApplicationServices/ApplicationServices.h>
+#include "nsDebug.h"
+#include "mozilla/Vector.h"
+#include "ScaledFontMac.h"
+#include "PathCG.h"
+#include <dlfcn.h>
+
+// This is used when we explicitly need CG to draw text to support things such
+// as vibrancy and subpixel AA on transparent backgrounds. The current use cases
+// are really only to enable Skia to support drawing text in those situations.
+
+namespace mozilla {
+namespace gfx {
+
+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;
+}
+
+static CGColorRef
+ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColor)
+{
+ CGFloat components[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
+ return CGColorCreate(aColorSpace, components);
+}
+
+static bool
+SetFontSmoothingBackgroundColor(CGContextRef aCGContext, CGColorSpaceRef aColorSpace,
+ const GlyphRenderingOptions* aRenderingOptions)
+{
+ if (aRenderingOptions) {
+ Color fontSmoothingBackgroundColor =
+ static_cast<const GlyphRenderingOptionsCG*>(aRenderingOptions)->FontSmoothingBackgroundColor();
+ if (fontSmoothingBackgroundColor.a > 0) {
+ CGContextSetFontSmoothingBackgroundColorFunc setFontSmoothingBGColorFunc =
+ GetCGContextSetFontSmoothingBackgroundColorFunc();
+ if (setFontSmoothingBGColorFunc) {
+ CGColorRef color = ColorToCGColor(aColorSpace, fontSmoothingBackgroundColor);
+ setFontSmoothingBGColorFunc(aCGContext, color);
+ CGColorRelease(color);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// 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.)
+static void
+EnsureValidPremultipliedData(CGContextRef aContext,
+ CGRect aTextBounds = CGRectInfinite)
+{
+ if (CGBitmapContextGetBitsPerPixel(aContext) != 32 ||
+ CGBitmapContextGetAlphaInfo(aContext) != kCGImageAlphaPremultipliedFirst) {
+ return;
+ }
+
+ uint8_t* bitmapData = (uint8_t*)CGBitmapContextGetData(aContext);
+ CGRect bitmapBounds = CGRectMake(0, 0, CGBitmapContextGetWidth(aContext), CGBitmapContextGetHeight(aContext));
+ int stride = CGBitmapContextGetBytesPerRow(aContext);
+
+ CGRect bounds = CGRectIntersection(bitmapBounds, aTextBounds);
+ int startX = bounds.origin.x;
+ int endX = startX + bounds.size.width;
+ MOZ_ASSERT(endX <= bitmapBounds.size.width);
+
+
+ // CGRect assume that our origin is the bottom left.
+ // The data assumes that the origin is the top left.
+ // Have to switch the Y axis so that our coordinates are correct
+ int startY = bitmapBounds.size.height - (bounds.origin.y + bounds.size.height);
+ int endY = startY + bounds.size.height;
+ MOZ_ASSERT(endY <= (int)CGBitmapContextGetHeight(aContext));
+
+ for (int y = startY; y < endY; y++) {
+ for (int x = startX; x < endX; x++) {
+ int i = y * stride + x * 4;
+ uint8_t a = bitmapData[i + 3];
+
+ bitmapData[i + 0] = std::min(a, bitmapData[i+0]);
+ bitmapData[i + 1] = std::min(a, bitmapData[i+1]);
+ bitmapData[i + 2] = std::min(a, bitmapData[i+2]);
+ }
+ }
+}
+
+static 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 = std::min(x1, bboxes[i].origin.x + positions[i].x);
+ y1 = std::min(y1, bboxes[i].origin.y + positions[i].y);
+ x2 = std::max(x2, bboxes[i].origin.x + positions[i].x + scale*bboxes[i].size.width);
+ y2 = std::max(y2, bboxes[i].origin.y + positions[i].y + scale*bboxes[i].size.height);
+ }
+
+ CGRect extents = {{x1, y1}, {x2-x1, y2-y1}};
+ return extents;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/gfx/2d/Coord.h b/gfx/2d/Coord.h
new file mode 100644
index 000000000..0d2cdd6a6
--- /dev/null
+++ b/gfx/2d/Coord.h
@@ -0,0 +1,151 @@
+/* -*- 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 "mozilla/TypeTraits.h" // For IsSame
+#include "Types.h"
+#include "BaseCoord.h"
+
+#include <cmath>
+
+namespace mozilla {
+
+template <typename> struct IsPixel;
+
+namespace gfx {
+
+template <class units> struct IntCoordTyped;
+template <class units, class F = Float> 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 F, class primitive>
+struct CommonType<CoordTyped<units, F>, primitive> {
+ typedef decltype(F() + 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 <bool B, class coord, class primitive>
+struct CoordOperatorsHelper {
+ // Using SFINAE (Substitution Failure Is Not An Error) to suppress redundant
+ // operators
+};
+
+template <class coord, class primitive>
+struct CoordOperatorsHelper<true, coord, primitive> {
+ 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< true, IntCoordTyped<units>, float >,
+ public CoordOperatorsHelper< true, IntCoordTyped<units>, double > {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseCoord< int32_t, IntCoordTyped<units> > Super;
+
+ constexpr IntCoordTyped() : Super() {}
+ constexpr MOZ_IMPLICIT IntCoordTyped(int32_t aValue) : Super(aValue) {}
+};
+
+template<class units, class F>
+struct CoordTyped :
+ public BaseCoord< F, CoordTyped<units, F> >,
+ public CoordOperatorsHelper< !IsSame<F, int32_t>::value, CoordTyped<units, F>, int32_t >,
+ public CoordOperatorsHelper< !IsSame<F, uint32_t>::value, CoordTyped<units, F>, uint32_t >,
+ public CoordOperatorsHelper< !IsSame<F, double>::value, CoordTyped<units, F>, double >,
+ public CoordOperatorsHelper< !IsSame<F, float>::value, CoordTyped<units, F>, float > {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseCoord< F, CoordTyped<units, F> > Super;
+
+ constexpr CoordTyped() : Super() {}
+ constexpr MOZ_IMPLICIT CoordTyped(F aValue) : Super(aValue) {}
+ explicit constexpr CoordTyped(const IntCoordTyped<units>& aCoord) : Super(F(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));
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_COORD_H_ */
diff --git a/gfx/2d/CriticalSection.h b/gfx/2d/CriticalSection.h
new file mode 100644
index 000000000..d1eb69abc
--- /dev/null
+++ b/gfx/2d/CriticalSection.h
@@ -0,0 +1,80 @@
+/* -*- 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_CRITICALSECTION_H_
+#define MOZILLA_GFX_CRITICALSECTION_H_
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <pthread.h>
+#include "mozilla/DebugOnly.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+#ifdef WIN32
+
+class CriticalSection {
+public:
+ CriticalSection() { ::InitializeCriticalSection(&mCriticalSection); }
+
+ ~CriticalSection() { ::DeleteCriticalSection(&mCriticalSection); }
+
+ void Enter() { ::EnterCriticalSection(&mCriticalSection); }
+
+ void Leave() { ::LeaveCriticalSection(&mCriticalSection); }
+
+protected:
+ CRITICAL_SECTION mCriticalSection;
+};
+
+#else
+// posix
+
+class PosixCondvar;
+class CriticalSection {
+public:
+ CriticalSection() {
+ DebugOnly<int> err = pthread_mutex_init(&mMutex, nullptr);
+ MOZ_ASSERT(!err);
+ }
+
+ ~CriticalSection() {
+ DebugOnly<int> err = pthread_mutex_destroy(&mMutex);
+ MOZ_ASSERT(!err);
+ }
+
+ void Enter() {
+ DebugOnly<int> err = pthread_mutex_lock(&mMutex);
+ MOZ_ASSERT(!err);
+ }
+
+ void Leave() {
+ DebugOnly<int> err = pthread_mutex_unlock(&mMutex);
+ MOZ_ASSERT(!err);
+ }
+
+protected:
+ pthread_mutex_t mMutex;
+ friend class PosixCondVar;
+};
+
+#endif
+
+/// RAII helper.
+struct CriticalSectionAutoEnter {
+ explicit CriticalSectionAutoEnter(CriticalSection* aSection) : mSection(aSection) { mSection->Enter(); }
+ ~CriticalSectionAutoEnter() { mSection->Leave(); }
+protected:
+ CriticalSection* mSection;
+};
+
+
+} // namespace
+} // namespace
+
+#endif
diff --git a/gfx/2d/DataSourceSurface.cpp b/gfx/2d/DataSourceSurface.cpp
new file mode 100644
index 000000000..75d843506
--- /dev/null
+++ b/gfx/2d/DataSourceSurface.cpp
@@ -0,0 +1,21 @@
+/* -*- 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 "DataSourceSurfaceWrapper.h"
+
+namespace mozilla {
+namespace gfx {
+
+already_AddRefed<DataSourceSurface>
+DataSourceSurface::GetDataSurface()
+{
+ RefPtr<DataSourceSurface> surface =
+ (GetType() == SurfaceType::DATA) ? this : new DataSourceSurfaceWrapper(this);
+ return surface.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/DataSourceSurfaceWrapper.h b/gfx/2d/DataSourceSurfaceWrapper.h
new file mode 100644
index 000000000..d1112b57c
--- /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;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_DATASOURCESURFACEWRAPPER_H_ */
diff --git a/gfx/2d/DataSurfaceHelpers.cpp b/gfx/2d/DataSurfaceHelpers.cpp
new file mode 100644
index 000000000..87ef00fcd
--- /dev/null
+++ b/gfx/2d/DataSurfaceHelpers.cpp
@@ -0,0 +1,374 @@
+/* -*- 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 {
+
+already_AddRefed<DataSourceSurface>
+CreateDataSourceSurfaceFromData(const IntSize& aSize,
+ SurfaceFormat aFormat,
+ const uint8_t* aData,
+ int32_t aDataStride)
+{
+ RefPtr<DataSourceSurface> srcSurface =
+ Factory::CreateWrappingDataSourceSurface(const_cast<uint8_t*>(aData),
+ aDataStride,
+ aSize,
+ aFormat);
+ RefPtr<DataSourceSurface> destSurface =
+ Factory::CreateDataSourceSurface(aSize, aFormat, false);
+
+ if (!srcSurface || !destSurface) {
+ return nullptr;
+ }
+
+ if (CopyRect(srcSurface,
+ destSurface,
+ IntRect(IntPoint(), srcSurface->GetSize()),
+ IntPoint())) {
+ return destSurface.forget();
+ }
+
+ return nullptr;
+}
+
+already_AddRefed<DataSourceSurface>
+CreateDataSourceSurfaceWithStrideFromData(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ int32_t aStride,
+ const uint8_t* aData,
+ int32_t aDataStride)
+{
+ RefPtr<DataSourceSurface> srcSurface =
+ Factory::CreateWrappingDataSourceSurface(const_cast<uint8_t*>(aData),
+ aDataStride,
+ aSize,
+ aFormat);
+ RefPtr<DataSourceSurface> destSurface =
+ Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat, aStride, false);
+
+ if (!srcSurface || !destSurface) {
+ return nullptr;
+ }
+
+ if (CopyRect(srcSurface,
+ destSurface,
+ IntRect(IntPoint(), srcSurface->GetSize()),
+ IntPoint())) {
+ return destSurface.forget();
+ }
+
+ return nullptr;
+}
+
+uint8_t*
+DataAtOffset(DataSourceSurface* aSurface,
+ const DataSourceSurface::MappedSurface* aMap,
+ IntPoint aPoint)
+{
+ if (!SurfaceContainsPoint(aSurface, aPoint)) {
+ MOZ_CRASH("GFX: 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 = aMap->mData + aPoint.y * aMap->mStride +
+ aPoint.x * BytesPerPixel(aSurface->GetFormat());
+
+ if (data < aMap->mData) {
+ MOZ_CRASH("GFX: 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.width; ++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;
+ }
+}
+
+UniquePtr<uint8_t[]>
+SurfaceToPackedBGRA(DataSourceSurface *aSurface)
+{
+ SurfaceFormat format = aSurface->GetFormat();
+ if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) {
+ return nullptr;
+ }
+
+ IntSize size = aSurface->GetSize();
+
+ UniquePtr<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)) {
+ return nullptr;
+ }
+
+ CopySurfaceDataToPackedArray(map.mData, imageBuffer.get(), size,
+ map.mStride, 4 * sizeof(uint8_t));
+
+ aSurface->Unmap();
+
+ if (format == SurfaceFormat::B8G8R8X8) {
+ // Convert BGRX to BGRA by setting a to 255.
+ ConvertBGRXToBGRA(imageBuffer.get(), 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) || MOZ_UNLIKELY(aStride <= 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 " << aStride << ", " << aHeight << ", " << aExtraBytes;
+ return 0;
+ }
+ return requiredBytes.value();
+}
+
+size_t
+BufferSizeFromDimensions(int32_t aWidth,
+ int32_t aHeight,
+ int32_t aDepth,
+ int32_t aExtraBytes)
+{
+ if (MOZ_UNLIKELY(aHeight <= 0) ||
+ MOZ_UNLIKELY(aWidth <= 0) ||
+ MOZ_UNLIKELY(aDepth <= 0)) {
+ return 0;
+ }
+
+ // Similar to BufferSizeFromStrideAndHeight, but with an extra parameter.
+
+ CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * CheckedInt32(aDepth) + CheckedInt32(aExtraBytes);
+ if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
+ gfxWarning() << "Buffer size too big; returning zero " << aWidth << ", " << aHeight << ", " << aDepth << ", " << aExtraBytes;
+ return 0;
+ }
+ return requiredBytes.value();
+}
+
+/**
+ * aSrcRect: Rect relative to the aSrc surface
+ * aDestPoint: Point inside aDest surface
+ */
+bool
+CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
+ IntRect aSrcRect, IntPoint aDestPoint)
+{
+ if (aSrcRect.Overflows() ||
+ IntRect(aDestPoint, aSrcRect.Size()).Overflows()) {
+ MOZ_CRASH("GFX: we should never be getting invalid rects at this point");
+ }
+
+ MOZ_RELEASE_ASSERT(aSrc->GetFormat() == aDest->GetFormat(),
+ "GFX: different surface formats");
+ MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect),
+ "GFX: source rect too big for source surface");
+ MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(IntRect(aDestPoint, aSrcRect.Size())),
+ "GFX: dest surface too small");
+
+ if (aSrcRect.IsEmpty()) {
+ return false;
+ }
+
+ DataSourceSurface::ScopedMap srcMap(aSrc, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap destMap(aDest, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!srcMap.IsMapped() || !destMap.IsMapped())) {
+ return false;
+ }
+
+ uint8_t* sourceData = DataAtOffset(aSrc, srcMap.GetMappedSurface(), aSrcRect.TopLeft());
+ uint32_t sourceStride = srcMap.GetStride();
+ uint8_t* destData = DataAtOffset(aDest, destMap.GetMappedSurface(), aDestPoint);
+ uint32_t destStride = destMap.GetStride();
+
+ 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;
+ }
+ }
+
+ return true;
+}
+
+already_AddRefed<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();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/DataSurfaceHelpers.h b/gfx/2d/DataSurfaceHelpers.h
new file mode 100644
index 000000000..b0fdf2983
--- /dev/null
+++ b/gfx/2d/DataSurfaceHelpers.h
@@ -0,0 +1,147 @@
+/* -*- 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"
+
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Create a DataSourceSurface and init the surface with the |aData|. The stride
+ * of this source surface might be different from the input data's |aDataStride|.
+ * System will try to use the optimal one.
+ */
+already_AddRefed<DataSourceSurface>
+CreateDataSourceSurfaceFromData(const IntSize& aSize,
+ SurfaceFormat aFormat,
+ const uint8_t* aData,
+ int32_t aDataStride);
+
+/**
+ * Similar to CreateDataSourceSurfaceFromData(), but could setup the stride for
+ * this surface.
+ */
+already_AddRefed<DataSourceSurface>
+CreateDataSourceSurfaceWithStrideFromData(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ int32_t aStride,
+ const uint8_t* aData,
+ int32_t aDataStride);
+
+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.
+ */
+UniquePtr<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);
+
+/**
+ * Multiplies aWidth, aHeight, aDepth 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 dimensions.
+ *
+ * @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
+BufferSizeFromDimensions(int32_t aWidth,
+ int32_t aHeight,
+ int32_t aDepth,
+ int32_t aExtraBytes = 0);
+/**
+ * Copy aSrcRect from aSrc to aDest starting at aDestPoint.
+ * @returns false if the copy is not successful or the aSrc's size is empty.
+ */
+bool
+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.
+ */
+already_AddRefed<DataSourceSurface>
+CreateDataSourceSurfaceByCloning(DataSourceSurface* aSource);
+
+/**
+ * Return the byte at aPoint.
+ */
+uint8_t*
+DataAtOffset(DataSourceSurface* aSurface,
+ const DataSourceSurface::MappedSurface* aMap,
+ 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);
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_DATASURFACEHELPERS_H
diff --git a/gfx/2d/DrawCommand.h b/gfx/2d/DrawCommand.h
new file mode 100644
index 000000000..eb415c70a
--- /dev/null
+++ b/gfx/2d/DrawCommand.h
@@ -0,0 +1,594 @@
+/* -*- 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 <math.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,
+ FLUSH
+};
+
+class DrawingCommand
+{
+public:
+ virtual ~DrawingCommand() {}
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aTransform = nullptr) const = 0;
+
+ virtual bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const { return false; }
+
+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 (mSurface)SurfacePattern(*static_cast<const SurfacePattern*>(&aPattern));
+ surfPat->mSurface->GuaranteePersistance();
+ return;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ new (mLinear)LinearGradientPattern(*static_cast<const LinearGradientPattern*>(&aPattern));
+ return;
+ case PatternType::RADIAL_GRADIENT:
+ new (mRadial)RadialGradientPattern(*static_cast<const RadialGradientPattern*>(&aPattern));
+ return;
+ }
+ }
+
+ ~StoredPattern()
+ {
+ reinterpret_cast<Pattern*>(mPattern)->~Pattern();
+ }
+
+ operator Pattern&()
+ {
+ return *reinterpret_cast<Pattern*>(mPattern);
+ }
+
+ operator const Pattern&() const
+ {
+ return *reinterpret_cast<const Pattern*>(mPattern);
+ }
+
+ 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 mPattern[sizeof(Pattern)];
+ 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*) const
+ {
+ 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*) const
+ {
+ 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*) const
+ {
+ 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) const
+ {
+ MOZ_ASSERT(!aTransform || !aTransform->HasNonIntegerTranslation());
+ Point dest(Float(mDestination.x), Float(mDestination.y));
+ if (aTransform) {
+ dest = aTransform->TransformPoint(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*) const
+ {
+ aDT->FillRect(mRect, mPattern, mOptions);
+ }
+
+ bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const
+ {
+ aDeviceRect = aTransform.TransformBounds(mRect);
+ return true;
+ }
+
+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)
+ {
+ if (aStrokeOptions.mDashLength) {
+ mDashes.resize(aStrokeOptions.mDashLength);
+ mStrokeOptions.mDashPattern = &mDashes.front();
+ memcpy(&mDashes.front(), aStrokeOptions.mDashPattern, mStrokeOptions.mDashLength * sizeof(Float));
+ }
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->StrokeRect(mRect, mPattern, mStrokeOptions, mOptions);
+ }
+
+private:
+ Rect mRect;
+ StoredPattern mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+ std::vector<Float> mDashes;
+};
+
+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*) const
+ {
+ 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*) const
+ {
+ aDT->Fill(mPath, mPattern, mOptions);
+ }
+
+ bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const
+ {
+ aDeviceRect = mPath->GetBounds(aTransform);
+ return true;
+ }
+
+private:
+ RefPtr<Path> mPath;
+ StoredPattern mPattern;
+ DrawOptions mOptions;
+};
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880
+#endif
+
+#ifndef M_SQRT1_2
+#define M_SQRT1_2 0.707106781186547524400844362104849039
+#endif
+
+// 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;
+}
+
+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)
+ {
+ if (aStrokeOptions.mDashLength) {
+ mDashes.resize(aStrokeOptions.mDashLength);
+ mStrokeOptions.mDashPattern = &mDashes.front();
+ memcpy(&mDashes.front(), aStrokeOptions.mDashPattern, mStrokeOptions.mDashLength * sizeof(Float));
+ }
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->Stroke(mPath, mPattern, mStrokeOptions, mOptions);
+ }
+
+ bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const
+ {
+ aDeviceRect = PathExtentsToMaxStrokeExtents(mStrokeOptions, mPath->GetBounds(aTransform), aTransform);
+ return true;
+ }
+
+private:
+ RefPtr<Path> mPath;
+ StoredPattern mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+ std::vector<Float> mDashes;
+};
+
+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*) const
+ {
+ 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*) const
+ {
+ 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*) const
+ {
+ 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*) const
+ {
+ 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*) const
+ {
+ aDT->PushClipRect(mRect);
+ }
+
+private:
+ Rect mRect;
+};
+
+class PopClipCommand : public DrawingCommand
+{
+public:
+ PopClipCommand()
+ : DrawingCommand(CommandType::POPCLIP)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ 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) const
+ {
+ if (aMatrix) {
+ aDT->SetTransform(mTransform * (*aMatrix));
+ } else {
+ aDT->SetTransform(mTransform);
+ }
+ }
+
+private:
+ Matrix mTransform;
+};
+
+class FlushCommand : public DrawingCommand
+{
+public:
+ explicit FlushCommand()
+ : DrawingCommand(CommandType::FLUSH)
+ {
+ }
+
+ virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
+ {
+ aDT->Flush();
+ }
+};
+
+} // namespace gfx
+
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_DRAWCOMMAND_H_ */
diff --git a/gfx/2d/DrawEventRecorder.cpp b/gfx/2d/DrawEventRecorder.cpp
new file mode 100644
index 000000000..0e20b8b5a
--- /dev/null
+++ b/gfx/2d/DrawEventRecorder.cpp
@@ -0,0 +1,117 @@
+/* -*- 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 "DrawEventRecorder.h"
+#include "PathRecording.h"
+#include "RecordingTypes.h"
+
+namespace mozilla {
+namespace gfx {
+
+using namespace std;
+
+DrawEventRecorderPrivate::DrawEventRecorderPrivate(std::ostream *aStream)
+ : mOutputStream(aStream)
+{
+}
+
+void
+DrawEventRecorderPrivate::WriteHeader()
+{
+ WriteElement(*mOutputStream, kMagicInt);
+ WriteElement(*mOutputStream, kMajorRevision);
+ WriteElement(*mOutputStream, kMinorRevision);
+}
+
+void
+DrawEventRecorderPrivate::RecordEvent(const RecordedEvent &aEvent)
+{
+ WriteElement(*mOutputStream, aEvent.mType);
+
+ aEvent.RecordToStream(*mOutputStream);
+
+ Flush();
+}
+
+DrawEventRecorderFile::DrawEventRecorderFile(const char *aFilename)
+ : DrawEventRecorderPrivate(nullptr)
+ , mOutputFile(aFilename, ofstream::binary)
+{
+ mOutputStream = &mOutputFile;
+
+ WriteHeader();
+}
+
+DrawEventRecorderFile::~DrawEventRecorderFile()
+{
+ mOutputFile.close();
+}
+
+void
+DrawEventRecorderFile::Flush()
+{
+ mOutputFile.flush();
+}
+
+bool
+DrawEventRecorderFile::IsOpen()
+{
+ return mOutputFile.is_open();
+}
+
+void
+DrawEventRecorderFile::OpenNew(const char *aFilename)
+{
+ MOZ_ASSERT(!mOutputFile.is_open());
+
+ mOutputFile.open(aFilename, ofstream::binary);
+ WriteHeader();
+}
+
+void
+DrawEventRecorderFile::Close()
+{
+ MOZ_ASSERT(mOutputFile.is_open());
+
+ mOutputFile.close();
+}
+
+DrawEventRecorderMemory::DrawEventRecorderMemory()
+ : DrawEventRecorderPrivate(nullptr)
+{
+ mOutputStream = &mMemoryStream;
+
+ WriteHeader();
+}
+
+void
+DrawEventRecorderMemory::Flush()
+{
+ mOutputStream->flush();
+}
+
+size_t
+DrawEventRecorderMemory::RecordingSize()
+{
+ return mMemoryStream.tellp();
+}
+
+bool
+DrawEventRecorderMemory::CopyRecording(char* aBuffer, size_t aBufferLen)
+{
+ return !!mMemoryStream.read(aBuffer, aBufferLen);
+}
+
+void
+DrawEventRecorderMemory::WipeRecording()
+{
+ mMemoryStream.str(std::string());
+ mMemoryStream.clear();
+
+ WriteHeader();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/DrawEventRecorder.h b/gfx/2d/DrawEventRecorder.h
new file mode 100644
index 000000000..a789379d2
--- /dev/null
+++ b/gfx/2d/DrawEventRecorder.h
@@ -0,0 +1,148 @@
+/* -*- 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_DRAWEVENTRECORDER_H_
+#define MOZILLA_GFX_DRAWEVENTRECORDER_H_
+
+#include "2D.h"
+#include "RecordedEvent.h"
+#include <ostream>
+#include <fstream>
+
+#if defined(_MSC_VER)
+#include <unordered_set>
+#else
+#include <set>
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+class PathRecording;
+
+class DrawEventRecorderPrivate : public DrawEventRecorder
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPrivate)
+ explicit DrawEventRecorderPrivate(std::ostream *aStream);
+ virtual ~DrawEventRecorderPrivate() { }
+
+ void WriteHeader();
+
+ void RecordEvent(const RecordedEvent &aEvent);
+ void WritePath(const PathRecording *aPath);
+
+ void AddStoredObject(const ReferencePtr aObject) {
+ mStoredObjects.insert(aObject);
+ }
+
+ void RemoveStoredObject(const ReferencePtr aObject) {
+ mStoredObjects.erase(aObject);
+ }
+
+ bool HasStoredObject(const ReferencePtr aObject) {
+ return mStoredObjects.find(aObject) != mStoredObjects.end();
+ }
+
+ void AddStoredFontData(const uint64_t aFontDataKey) {
+ mStoredFontData.insert(aFontDataKey);
+ }
+
+ bool HasStoredFontData(const uint64_t aFontDataKey) {
+ return mStoredFontData.find(aFontDataKey) != mStoredFontData.end();
+ }
+
+protected:
+ std::ostream *mOutputStream;
+
+ virtual void Flush() = 0;
+
+#if defined(_MSC_VER)
+ typedef std::unordered_set<const void*> ObjectSet;
+ typedef std::unordered_set<uint64_t> Uint64Set;
+#else
+ typedef std::set<const void*> ObjectSet;
+ typedef std::set<uint64_t> Uint64Set;
+#endif
+
+ ObjectSet mStoredObjects;
+ Uint64Set mStoredFontData;
+};
+
+class DrawEventRecorderFile : public DrawEventRecorderPrivate
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderFile)
+ explicit DrawEventRecorderFile(const char *aFilename);
+ ~DrawEventRecorderFile();
+
+ /**
+ * Returns whether a recording file is currently open.
+ */
+ bool IsOpen();
+
+ /**
+ * Opens new file with the provided name. The recorder does NOT forget which
+ * objects it has recorded. This can be used with Close, so that a recording
+ * can be processed in chunks. The file must not already be open.
+ */
+ void OpenNew(const char *aFilename);
+
+ /**
+ * Closes the file so that it can be processed. The recorder does NOT forget
+ * which objects it has recorded. This can be used with OpenNew, so that a
+ * recording can be processed in chunks. The file must be open.
+ */
+ void Close();
+
+private:
+ virtual void Flush();
+
+ std::ofstream mOutputFile;
+};
+
+class DrawEventRecorderMemory final : public DrawEventRecorderPrivate
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderMemory)
+
+ /**
+ * Constructs a DrawEventRecorder that stores the recording in memory.
+ */
+ DrawEventRecorderMemory();
+
+ /**
+ * @return the current size of the recording (in chars).
+ */
+ size_t RecordingSize();
+
+ /**
+ * Copies at most aBufferLen chars of the recording into aBuffer.
+ *
+ * @param aBuffer buffer to receive the recording chars
+ * @param aBufferLen length of aBuffer
+ * @return true if copied successfully
+ */
+ bool CopyRecording(char* aBuffer, size_t aBufferLen);
+
+ /**
+ * Wipes the internal recording buffer, but the recorder does NOT forget which
+ * objects it has recorded. This can be used so that a recording can be copied
+ * and processed in chunks, releasing memory as it goes.
+ */
+ void WipeRecording();
+
+private:
+ ~DrawEventRecorderMemory() {};
+
+ void Flush() final;
+
+ std::stringstream mMemoryStream;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_DRAWEVENTRECORDER_H_ */
diff --git a/gfx/2d/DrawTarget.cpp b/gfx/2d/DrawTarget.cpp
new file mode 100644
index 000000000..72e070dc3
--- /dev/null
+++ b/gfx/2d/DrawTarget.cpp
@@ -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/. */
+
+#include "2D.h"
+#include "Logging.h"
+#include "PathHelpers.h"
+
+#include "DrawTargetCapture.h"
+
+namespace mozilla {
+namespace gfx {
+
+already_AddRefed<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.forget();
+}
+
+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);
+}
+
+void
+DrawTarget::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
+{
+ Matrix oldTransform = GetTransform();
+ SetTransform(Matrix());
+
+ RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
+ for (uint32_t i = 0; i < aCount; i++) {
+ AppendRectToPath(pathBuilder, Rect(aRects[i]));
+ }
+ RefPtr<Path> path = pathBuilder->Finish();
+ PushClip(path);
+
+ SetTransform(oldTransform);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp
new file mode 100644
index 000000000..c0e4f0af2
--- /dev/null
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -0,0 +1,2374 @@
+/* -*- 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 "DrawTargetCairo.h"
+
+#include "SourceSurfaceCairo.h"
+#include "PathCairo.h"
+#include "HelpersCairo.h"
+#include "ScaledFontBase.h"
+#include "BorrowedContext.h"
+#include "FilterNodeSoftware.h"
+#include "mozilla/Scoped.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+
+#include "cairo.h"
+#include "cairo-tee.h"
+#include <string.h>
+
+#include "Blur.h"
+#include "Logging.h"
+#include "Tools.h"
+
+#ifdef CAIRO_HAS_QUARTZ_SURFACE
+#include "cairo-quartz.h"
+#ifdef MOZ_WIDGET_COCOA
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+#endif
+
+#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
+
+#define PIXMAN_DONT_DEFINE_STDINT
+#include "pixman.h"
+
+#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
+// restores the context on construction/destruction.
+class AutoPrepareForDrawing
+{
+public:
+ AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx)
+ : mCtx(ctx)
+ {
+ dt->PrepareForDrawing(ctx);
+ cairo_save(mCtx);
+ MOZ_ASSERT(cairo_status(mCtx) || dt->GetTransform() == GetTransform());
+ }
+
+ AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx, const Path* path)
+ : mCtx(ctx)
+ {
+ dt->PrepareForDrawing(ctx, path);
+ cairo_save(mCtx);
+ MOZ_ASSERT(cairo_status(mCtx) || dt->GetTransform() == GetTransform());
+ }
+
+ ~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
+ Matrix GetTransform()
+ {
+ cairo_matrix_t mat;
+ cairo_get_matrix(mCtx, &mat);
+ return Matrix(mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0);
+ }
+#endif
+
+ 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
+SupportsSelfCopy(cairo_surface_t* surface)
+{
+ switch (cairo_surface_get_type(surface))
+ {
+#ifdef CAIRO_HAS_QUARTZ_SURFACE
+ case CAIRO_SURFACE_TYPE_QUARTZ:
+ 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;
+ }
+}
+
+static bool
+PatternIsCompatible(const Pattern& aPattern)
+{
+ switch (aPattern.GetType())
+ {
+ case PatternType::LINEAR_GRADIENT:
+ {
+ const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
+ return pattern.mStops->GetBackendType() == BackendType::CAIRO;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
+ return pattern.mStops->GetBackendType() == BackendType::CAIRO;
+ }
+ default:
+ return true;
+ }
+}
+
+static cairo_user_data_key_t surfaceDataKey;
+
+void
+ReleaseData(void* aData)
+{
+ 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)) {
+ gfxWarning() << "Invalid surface DTC " << 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;
+}
+
+/**
+ * Returns cairo surface for the given SourceSurface.
+ * If possible, it will use the cairo_surface associated with aSurface,
+ * otherwise, it will create a new cairo_surface.
+ * In either case, the caller must call cairo_surface_destroy on the
+ * result when it is done with it.
+ */
+cairo_surface_t*
+GetCairoSurfaceForSourceSurface(SourceSurface *aSurface,
+ bool aExistingOnly = false,
+ const IntRect& aSubImage = IntRect())
+{
+ if (!aSurface) {
+ return nullptr;
+ }
+
+ 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();
+ if (aSubImage.IsEmpty()) {
+ cairo_surface_reference(surf);
+ } else {
+ surf = ExtractSubImage(surf, subimage, aSurface->GetFormat());
+ }
+ return surf;
+ }
+
+ if (aSurface->GetType() == SurfaceType::CAIRO_IMAGE) {
+ cairo_surface_t* surf =
+ static_cast<const DataSourceSurfaceCairo*>(aSurface)->GetSurface();
+ 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 =
+ 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 (!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().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)
+ , mX(0)
+ , mY(0)
+ {
+ 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;
+};
+
+static inline void
+CairoPatternAddGradientStop(cairo_pattern_t* aPattern,
+ const GradientStop &aStop,
+ Float aNudge = 0)
+{
+ cairo_pattern_add_color_stop_rgba(aPattern, aStop.offset + aNudge,
+ aStop.color.r, aStop.color.g, aStop.color.b,
+ aStop.color.a);
+
+}
+
+// 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
+// caller.
+// As the cairo_pattern_t returned may depend on the Pattern passed in, the
+// lifetime of the cairo_pattern_t returned must not exceed the lifetime of the
+// Pattern passed in.
+static cairo_pattern_t*
+GfxPatternToCairoPattern(const Pattern& aPattern,
+ Float aAlpha,
+ const Matrix& aTransform)
+{
+ cairo_pattern_t* pat;
+ const Matrix* matrix = nullptr;
+
+ switch (aPattern.GetType())
+ {
+ 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 PatternType::SURFACE:
+ {
+ const SurfacePattern& pattern = static_cast<const SurfacePattern&>(aPattern);
+ cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(pattern.mSurface,
+ false,
+ pattern.mSamplingRect);
+ if (!surf)
+ return nullptr;
+
+ pat = cairo_pattern_create_for_surface(surf);
+
+ matrix = &pattern.mMatrix;
+
+ cairo_pattern_set_filter(pat, GfxSamplingFilterToCairoFilter(pattern.mSamplingFilter));
+ cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(pattern.mExtendMode));
+
+ cairo_surface_destroy(surf);
+ break;
+ }
+ 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() == 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) {
+ CairoPatternAddGradientStop(pat, stops[i]);
+ }
+
+ break;
+ }
+ 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() == 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) {
+ CairoPatternAddGradientStop(pat, stops[i]);
+ }
+
+ break;
+ }
+ default:
+ {
+ // We should support all pattern types!
+ MOZ_ASSERT(false);
+ }
+ }
+
+ // 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;
+}
+
+static bool
+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() == PatternType::COLOR)
+ return false;
+
+ if (aOptions.mAlpha == 1.0)
+ return false;
+
+ return true;
+}
+
+DrawTargetCairo::DrawTargetCairo()
+ : mContext(nullptr)
+ , mSurface(nullptr)
+ , mTransformSingular(false)
+ , mLockedBits(nullptr)
+ , mFontOptions(nullptr)
+{
+}
+
+DrawTargetCairo::~DrawTargetCairo()
+{
+ cairo_destroy(mContext);
+ if (mSurface) {
+ cairo_surface_destroy(mSurface);
+ mSurface = nullptr;
+ }
+ if (mFontOptions) {
+ cairo_font_options_destroy(mFontOptions);
+ mFontOptions = nullptr;
+ }
+ MOZ_ASSERT(!mLockedBits);
+}
+
+bool
+DrawTargetCairo::IsValid() const
+{
+ return mSurface && !cairo_surface_status(mSurface) &&
+ mContext && !cairo_surface_status(cairo_get_group_target(mContext));
+}
+
+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_FALLTHROUGH_ASSERT("Can't determine actual DrawTargetType for DrawTargetCairo - assuming SOFTWARE_RASTER");
+ 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:
+ case CAIRO_SURFACE_TYPE_TEE: // included to silence warning about unhandled enum value
+ return DrawTargetType::SOFTWARE_RASTER;
+ default:
+ MOZ_CRASH("GFX: Unsupported cairo surface type");
+ }
+ }
+ MOZ_ASSERT(false, "Could not determine DrawTargetType for DrawTargetCairo");
+ return DrawTargetType::SOFTWARE_RASTER;
+}
+
+IntSize
+DrawTargetCairo::GetSize()
+{
+ return mSize;
+}
+
+SurfaceFormat
+GfxFormatForCairoSurface(cairo_surface_t* surface)
+{
+ cairo_surface_type_t type = cairo_surface_get_type(surface);
+ if (type == CAIRO_SURFACE_TYPE_IMAGE) {
+ return CairoFormatToGfxFormat(cairo_image_surface_get_format(surface));
+ }
+#ifdef CAIRO_HAS_XLIB_SURFACE
+ // xlib is currently the only Cairo backend that creates 16bpp surfaces
+ if (type == CAIRO_SURFACE_TYPE_XLIB &&
+ cairo_xlib_surface_get_depth(surface) == 16) {
+ return SurfaceFormat::R5G6B5_UINT16;
+ }
+#endif
+ return CairoContentToGfxFormat(cairo_surface_get_content(surface));
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetCairo::Snapshot()
+{
+ if (!IsValid()) {
+ gfxCriticalNote << "DrawTargetCairo::Snapshot with bad surface " << cairo_surface_status(mSurface);
+ return nullptr;
+ }
+ if (mSnapshot) {
+ RefPtr<SourceSurface> snapshot(mSnapshot);
+ return snapshot.forget();
+ }
+
+ IntSize size = GetSize();
+
+ mSnapshot = new SourceSurfaceCairo(mSurface,
+ size,
+ GfxFormatForCairoSurface(mSurface),
+ this);
+ RefPtr<SourceSurface> snapshot(mSnapshot);
+ return snapshot.forget();
+}
+
+bool
+DrawTargetCairo::LockBits(uint8_t** aData, IntSize* aSize,
+ int32_t* aStride, SurfaceFormat* aFormat,
+ IntPoint* aOrigin)
+{
+ cairo_surface_t* target = cairo_get_group_target(mContext);
+ cairo_surface_t* surf = target;
+#ifdef CAIRO_HAS_WIN32_SURFACE
+ if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_WIN32) {
+ cairo_surface_t* imgsurf = cairo_win32_surface_get_image(surf);
+ if (imgsurf) {
+ surf = imgsurf;
+ }
+ }
+#endif
+ if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_IMAGE &&
+ cairo_surface_status(surf) == CAIRO_STATUS_SUCCESS) {
+ PointDouble offset;
+ cairo_surface_get_device_offset(target, &offset.x, &offset.y);
+ // verify the device offset can be converted to integers suitable for a bounds rect
+ IntPoint origin(int32_t(-offset.x), int32_t(-offset.y));
+ if (-PointDouble(origin) != offset ||
+ (!aOrigin && origin != IntPoint())) {
+ return false;
+ }
+
+ WillChange();
+ Flush();
+
+ mLockedBits = cairo_image_surface_get_data(surf);
+ *aData = mLockedBits;
+ *aSize = IntSize(cairo_image_surface_get_width(surf),
+ cairo_image_surface_get_height(surf));
+ *aStride = cairo_image_surface_get_stride(surf);
+ *aFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(surf));
+ if (aOrigin) {
+ *aOrigin = origin;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void
+DrawTargetCairo::ReleaseBits(uint8_t* aData)
+{
+ MOZ_ASSERT(mLockedBits == aData);
+ mLockedBits = nullptr;
+ cairo_surface_t* surf = cairo_get_group_target(mContext);
+#ifdef CAIRO_HAS_WIN32_SURFACE
+ if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_WIN32) {
+ cairo_surface_t* imgsurf = cairo_win32_surface_get_image(surf);
+ if (imgsurf) {
+ cairo_surface_mark_dirty(imgsurf);
+ }
+ }
+#endif
+ cairo_surface_mark_dirty(surf);
+}
+
+void
+DrawTargetCairo::Flush()
+{
+ cairo_surface_t* surf = cairo_get_group_target(mContext);
+ cairo_surface_flush(surf);
+}
+
+void
+DrawTargetCairo::PrepareForDrawing(cairo_t* aContext, const Path* aPath /* = nullptr */)
+{
+ WillChange(aPath);
+}
+
+cairo_surface_t*
+DrawTargetCairo::GetDummySurface()
+{
+ if (mDummySurface) {
+ return mDummySurface;
+ }
+
+ mDummySurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
+
+ return mDummySurface;
+}
+
+static void
+PaintWithAlpha(cairo_t* aContext, const DrawOptions& aOptions)
+{
+ if (aOptions.mCompositionOp == CompositionOp::OP_SOURCE) {
+ // Cairo treats the source operator like a lerp when alpha is < 1.
+ // Approximate the desired operator by: out = 0; out += src*alpha;
+ if (aOptions.mAlpha == 1) {
+ cairo_set_operator(aContext, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(aContext);
+ } else {
+ cairo_set_operator(aContext, CAIRO_OPERATOR_CLEAR);
+ cairo_paint(aContext);
+ cairo_set_operator(aContext, CAIRO_OPERATOR_ADD);
+ cairo_paint_with_alpha(aContext, aOptions.mAlpha);
+ }
+ } else {
+ cairo_set_operator(aContext, GfxOpToCairoOp(aOptions.mCompositionOp));
+ cairo_paint_with_alpha(aContext, aOptions.mAlpha);
+ }
+}
+
+void
+DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions)
+{
+ if (mTransformSingular || aDest.IsEmpty()) {
+ return;
+ }
+
+ if (!IsValid() || !aSurface) {
+ gfxCriticalNote << "DrawSurface with bad surface " << cairo_surface_status(cairo_get_group_target(mContext));
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clear(aSurface);
+
+ float sx = aSource.Width() / aDest.Width();
+ float sy = aSource.Height() / aDest.Height();
+
+ cairo_matrix_t src_mat;
+ cairo_matrix_init_translate(&src_mat, aSource.X(), aSource.Y());
+ cairo_matrix_scale(&src_mat, sx, sy);
+
+ cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface);
+ if (!surf) {
+ gfxWarning() << "Failed to create cairo surface for DrawTargetCairo::DrawSurface";
+ return;
+ }
+ cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf);
+ cairo_surface_destroy(surf);
+
+ cairo_pattern_set_matrix(pat, &src_mat);
+ cairo_pattern_set_filter(pat, GfxSamplingFilterToCairoFilter(aSurfOptions.mSamplingFilter));
+ 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 (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);
+ }
+
+ PaintWithAlpha(mContext, aOptions);
+
+ cairo_pattern_destroy(pat);
+}
+
+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,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator)
+{
+ if (aSurface->GetType() != SurfaceType::CAIRO) {
+ return;
+ }
+
+ AutoClearDeviceOffset clear(aSurface);
+
+ Float width = Float(aSurface->GetSize().width);
+ Float height = Float(aSurface->GetSize().height);
+
+ SourceSurfaceCairo* source = static_cast<SourceSurfaceCairo*>(aSurface);
+ cairo_surface_t* sourcesurf = source->GetSurface();
+ cairo_surface_t* blursurf;
+ cairo_surface_t* surf;
+
+ // We only use the A8 surface for blurred shadows. Unblurred shadows can just
+ // use the RGBA surface directly.
+ if (cairo_surface_get_type(sourcesurf) == CAIRO_SURFACE_TYPE_TEE) {
+ blursurf = cairo_tee_surface_index(sourcesurf, 0);
+ surf = cairo_tee_surface_index(sourcesurf, 1);
+
+ MOZ_ASSERT(cairo_surface_get_type(blursurf) == CAIRO_SURFACE_TYPE_IMAGE);
+ Rect extents(0, 0, width, height);
+ AlphaBoxBlur blur(extents,
+ cairo_image_surface_get_stride(blursurf),
+ aSigma, aSigma);
+ blur.Blur(cairo_image_surface_get_data(blursurf));
+ } else {
+ blursurf = sourcesurf;
+ surf = sourcesurf;
+ }
+
+ WillChange();
+ ClearSurfaceForUnboundedSource(aOperator);
+
+ cairo_save(mContext);
+ cairo_set_operator(mContext, GfxOpToCairoOp(aOperator));
+ cairo_identity_matrix(mContext);
+ cairo_translate(mContext, aDest.x, aDest.y);
+
+ if (IsOperatorBoundByMask(aOperator)){
+ cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
+ cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
+
+ // Now that the shadow has been drawn, we can draw the surface on top.
+ cairo_set_source_surface(mContext, surf, 0, 0);
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, 0, 0, width, height);
+ cairo_fill(mContext);
+ } else {
+ cairo_push_group(mContext);
+ cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
+ cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
+
+ // Now that the shadow has been drawn, we can draw the surface on top.
+ cairo_set_source_surface(mContext, surf, 0, 0);
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, 0, 0, width, height);
+ cairo_fill(mContext);
+ cairo_pop_group_to_source(mContext);
+ cairo_paint(mContext);
+ }
+
+ cairo_restore(mContext);
+}
+
+void
+DrawTargetCairo::DrawPattern(const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions,
+ DrawPatternType aDrawType,
+ bool aPathBoundsClip)
+{
+ if (!PatternIsCompatible(aPattern)) {
+ return;
+ }
+
+ AutoClearDeviceOffset clear(aPattern);
+
+ cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha, GetTransform());
+ 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) && !aPathBoundsClip)) {
+ 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);
+
+ if (aDrawType == DRAW_STROKE) {
+ SetCairoStrokeOptions(mContext, aStrokeOptions);
+ cairo_stroke_preserve(mContext);
+ } else {
+ cairo_fill_preserve(mContext);
+ }
+
+ cairo_pop_group_to_source(mContext);
+
+ // Now draw the content using the desired operator
+ PaintWithAlpha(mContext, aOptions);
+ } else {
+ cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
+
+ if (aDrawType == DRAW_STROKE) {
+ SetCairoStrokeOptions(mContext, aStrokeOptions);
+ cairo_stroke_preserve(mContext);
+ } else {
+ cairo_fill_preserve(mContext);
+ }
+ }
+
+ cairo_pattern_destroy(pat);
+}
+
+void
+DrawTargetCairo::FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ 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, r.x, r.y, r.Width(), r.Height());
+
+ 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" << cairo_surface_status(aSurface);
+ 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
+DrawTargetCairo::CopySurface(SourceSurface *aSurface,
+ const IntRect &aSource,
+ const IntPoint &aDest)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clear(aSurface);
+
+ if (!aSurface) {
+ gfxWarning() << "Unsupported surface type specified";
+ return;
+ }
+
+ cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface);
+ if (!surf) {
+ gfxWarning() << "Unsupported surface type specified";
+ return;
+ }
+
+ CopySurfaceInternal(surf, aSource, aDest);
+ cairo_surface_destroy(surf);
+}
+
+void
+DrawTargetCairo::CopyRect(const IntRect &aSource,
+ const IntPoint &aDest)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, 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
+DrawTargetCairo::ClearRect(const Rect& aRect)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+
+ if (!mContext || aRect.Width() < 0 || aRect.Height() < 0 ||
+ !IsFinite(aRect.X()) || !IsFinite(aRect.Width()) ||
+ !IsFinite(aRect.Y()) || !IsFinite(aRect.Height())) {
+ gfxCriticalNote << "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(),
+ aRect.Width(), aRect.Height());
+ cairo_fill(mContext);
+}
+
+void
+DrawTargetCairo::StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
+ const DrawOptions &aOptions /* = DrawOptions() */)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height());
+
+ DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
+}
+
+void
+DrawTargetCairo::StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
+ const DrawOptions &aOptions /* = DrawOptions() */)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+
+ cairo_new_path(mContext);
+ cairo_move_to(mContext, aStart.x, aStart.y);
+ cairo_line_to(mContext, aEnd.x, aEnd.y);
+
+ DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
+}
+
+void
+DrawTargetCairo::Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
+ const DrawOptions &aOptions /* = DrawOptions() */)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext, aPath);
+
+ if (aPath->GetBackendType() != BackendType::CAIRO)
+ return;
+
+ PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
+ path->SetPathOnContext(mContext);
+
+ DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
+}
+
+void
+DrawTargetCairo::Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions /* = DrawOptions() */)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext, aPath);
+
+ if (aPath->GetBackendType() != BackendType::CAIRO)
+ return;
+
+ PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
+ path->SetPathOnContext(mContext);
+
+ DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
+}
+
+bool
+DrawTargetCairo::IsCurrentGroupOpaque()
+{
+ cairo_surface_t* surf = cairo_get_group_target(mContext);
+
+ if (!surf) {
+ return false;
+ }
+
+ return cairo_surface_get_content(surf) == CAIRO_CONTENT_COLOR;
+}
+
+void
+DrawTargetCairo::SetFontOptions()
+{
+ // This will attempt to detect if the currently set scaled font on the
+ // context has enabled subpixel AA. If it is not permitted, then it will
+ // downgrade to grayscale AA.
+ // This only currently works effectively for the cairo-ft backend relative
+ // to system defaults, as only cairo-ft reflect system defaults in the scaled
+ // font state. However, this will work for cairo-ft on both tree Cairo and
+ // system Cairo.
+ // Other backends leave the CAIRO_ANTIALIAS_DEFAULT setting untouched while
+ // potentially interpreting it as subpixel or even other types of AA that
+ // can't be safely equivocated with grayscale AA. For this reason we don't
+ // try to also detect and modify the default AA setting, only explicit
+ // subpixel AA. These other backends must instead rely on tree Cairo's
+ // cairo_surface_set_subpixel_antialiasing extension.
+
+ // If allowing subpixel AA, then leave Cairo's default AA state.
+ if (mPermitSubpixelAA) {
+ return;
+ }
+
+ if (!mFontOptions) {
+ mFontOptions = cairo_font_options_create();
+ if (!mFontOptions) {
+ gfxWarning() << "Failed allocating Cairo font options";
+ return;
+ }
+ }
+
+ // If the current font requests subpixel AA, force it to gray since we don't
+ // allow subpixel AA.
+ cairo_get_font_options(mContext, mFontOptions);
+ cairo_antialias_t antialias = cairo_font_options_get_antialias(mFontOptions);
+ if (antialias == CAIRO_ANTIALIAS_SUBPIXEL) {
+ cairo_font_options_set_antialias(mFontOptions, CAIRO_ANTIALIAS_GRAY);
+ cairo_set_font_options(mContext, mFontOptions);
+ }
+}
+
+void
+DrawTargetCairo::SetPermitSubpixelAA(bool aPermitSubpixelAA)
+{
+ DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA);
+#ifdef MOZ_TREE_CAIRO
+ cairo_surface_set_subpixel_antialiasing(cairo_get_group_target(mContext),
+ aPermitSubpixelAA ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
+#endif
+}
+
+void
+DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions,
+ const GlyphRenderingOptions*)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ if (!IsValid()) {
+ gfxDebug() << "FillGlyphs bad surface " << cairo_surface_status(cairo_get_group_target(mContext));
+ return;
+ }
+
+ if (!aFont) {
+ gfxDevCrash(LogReason::InvalidFont) << "Invalid scaled font";
+ return;
+ }
+
+ 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, GetTransform());
+ if (!pat)
+ return;
+
+ cairo_set_source(mContext, pat);
+ cairo_pattern_destroy(pat);
+
+ cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
+
+ // Override any font-specific options as necessary.
+ SetFontOptions();
+
+ // 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)) {
+ gfxDevCrash(LogReason::GlyphAllocFailedCairo) << "glyphs allocation failed";
+ return;
+ }
+ 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_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs);
+
+ if (cairo_surface_status(cairo_get_group_target(mContext))) {
+ gfxDebug() << "Ending FillGlyphs with a bad surface " << cairo_surface_status(cairo_get_group_target(mContext));
+ }
+}
+
+void
+DrawTargetCairo::Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions /* = DrawOptions() */)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ 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, GetTransform());
+ if (!source) {
+ return;
+ }
+
+ cairo_pattern_t* mask = GfxPatternToCairoPattern(aMask, aOptions.mAlpha, GetTransform());
+ 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_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
+ cairo_mask(mContext, mask);
+
+ cairo_pattern_destroy(mask);
+ cairo_pattern_destroy(source);
+}
+
+void
+DrawTargetCairo::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ if (mTransformSingular) {
+ return;
+ }
+
+ AutoPrepareForDrawing prep(this, mContext);
+ AutoClearDeviceOffset clearSource(aSource);
+ AutoClearDeviceOffset clearMask(aMask);
+
+ if (!PatternIsCompatible(aSource)) {
+ return;
+ }
+
+ cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
+
+ cairo_pattern_t* pat = GfxPatternToCairoPattern(aSource, aOptions.mAlpha, GetTransform());
+ if (!pat) {
+ return;
+ }
+
+ if (cairo_pattern_status(pat)) {
+ cairo_pattern_destroy(pat);
+ gfxWarning() << "Invalid pattern";
+ return;
+ }
+
+ 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);
+
+ 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));
+
+ cairo_mask(mContext, mask);
+
+ cairo_surface_destroy(surf);
+ cairo_pattern_destroy(mask);
+ cairo_pattern_destroy(pat);
+}
+
+void
+DrawTargetCairo::PushClip(const Path *aPath)
+{
+ if (aPath->GetBackendType() != BackendType::CAIRO) {
+ return;
+ }
+
+ WillChange(aPath);
+ cairo_save(mContext);
+
+ PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
+
+ if (mTransformSingular) {
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, 0, 0, 0, 0);
+ } else {
+ path->SetPathOnContext(mContext);
+ }
+ cairo_clip_preserve(mContext);
+}
+
+void
+DrawTargetCairo::PushClipRect(const Rect& aRect)
+{
+ WillChange();
+ cairo_save(mContext);
+
+ cairo_new_path(mContext);
+ if (mTransformSingular) {
+ cairo_rectangle(mContext, 0, 0, 0, 0);
+ } else {
+ cairo_rectangle(mContext, aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
+ }
+ cairo_clip_preserve(mContext);
+}
+
+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);
+}
+
+void
+DrawTargetCairo::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+ const Matrix& aMaskTransform, const IntRect& aBounds,
+ bool aCopyBackground)
+{
+ cairo_content_t content = CAIRO_CONTENT_COLOR_ALPHA;
+
+ if (mFormat == SurfaceFormat::A8) {
+ content = CAIRO_CONTENT_ALPHA;
+ } else if (aOpaque) {
+ content = CAIRO_CONTENT_COLOR;
+ }
+
+ if (aCopyBackground) {
+ cairo_surface_t* source = cairo_get_group_target(mContext);
+ cairo_push_group_with_content(mContext, content);
+ cairo_surface_t* dest = cairo_get_group_target(mContext);
+ cairo_t* ctx = cairo_create(dest);
+ cairo_set_source_surface(ctx, source, 0, 0);
+ cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(ctx);
+ cairo_destroy(ctx);
+ } else {
+ cairo_push_group_with_content(mContext, content);
+ }
+
+ PushedLayer layer(aOpacity, mPermitSubpixelAA);
+
+ if (aMask) {
+ cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask);
+ if (surf) {
+ layer.mMaskPattern = cairo_pattern_create_for_surface(surf);
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(aMaskTransform, mat);
+ cairo_matrix_invert(&mat);
+ cairo_pattern_set_matrix(layer.mMaskPattern, &mat);
+ cairo_surface_destroy(surf);
+ } else {
+ gfxCriticalError() << "Failed to get cairo surface for mask surface!";
+ }
+ }
+
+ mPushedLayers.push_back(layer);
+
+ SetPermitSubpixelAA(aOpaque);
+}
+
+void
+DrawTargetCairo::PopLayer()
+{
+ MOZ_ASSERT(mPushedLayers.size());
+
+ cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
+
+ cairo_pop_group_to_source(mContext);
+
+ PushedLayer layer = mPushedLayers.back();
+ mPushedLayers.pop_back();
+
+ if (!layer.mMaskPattern) {
+ cairo_paint_with_alpha(mContext, layer.mOpacity);
+ } else {
+ if (layer.mOpacity != Float(1.0)) {
+ cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
+
+ // Now draw the content using the desired operator
+ cairo_paint_with_alpha(mContext, layer.mOpacity);
+
+ cairo_pop_group_to_source(mContext);
+ }
+ cairo_mask(mContext, layer.mMaskPattern);
+ }
+
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(mTransform, mat);
+ cairo_set_matrix(mContext, &mat);
+
+ cairo_pattern_destroy(layer.mMaskPattern);
+ SetPermitSubpixelAA(layer.mWasPermittingSubpixelAA);
+}
+
+already_AddRefed<PathBuilder>
+DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FillRule::FILL_WINDING */) const
+{
+ return MakeAndAddRef<PathBuilderCairo>(aFillRule);
+}
+
+void
+DrawTargetCairo::ClearSurfaceForUnboundedSource(const CompositionOp &aOperator)
+{
+ 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
+ // isn't bounded by the source and the mask covers the entire clip
+ // region.
+ cairo_paint(mContext);
+}
+
+
+already_AddRefed<GradientStops>
+DrawTargetCairo::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops,
+ ExtendMode aExtendMode) const
+{
+ return MakeAndAddRef<GradientStopsCairo>(aStops, aNumStops, aExtendMode);
+}
+
+already_AddRefed<FilterNode>
+DrawTargetCairo::CreateFilter(FilterType aType)
+{
+ return FilterNodeSoftware::Create(aType);
+}
+
+void
+DrawTargetCairo::GetGlyphRasterizationMetrics(ScaledFont *aScaledFont, const uint16_t* aGlyphIndices,
+ uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics)
+{
+ for (uint32_t i = 0; i < aNumGlyphs; i++) {
+ cairo_glyph_t glyph;
+ cairo_text_extents_t extents;
+ glyph.index = aGlyphIndices[i];
+ glyph.x = 0;
+ glyph.y = 0;
+ cairo_glyph_extents(mContext, &glyph, 1, &extents);
+
+ aGlyphMetrics[i].mXBearing = extents.x_bearing;
+ aGlyphMetrics[i].mXAdvance = extents.x_advance;
+ aGlyphMetrics[i].mYBearing = extents.y_bearing;
+ aGlyphMetrics[i].mYAdvance = extents.y_advance;
+ aGlyphMetrics[i].mWidth = extents.width;
+ aGlyphMetrics[i].mHeight = extents.height;
+ }
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+{
+ if (!aData) {
+ gfxWarning() << "DrawTargetCairo::CreateSourceSurfaceFromData null aData";
+ return nullptr;
+ }
+
+ 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.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
+
+already_AddRefed<SourceSurface>
+DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
+{
+ RefPtr<SourceSurface> surface(aSurface);
+#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 surface.forget();
+ }
+
+ if (ctype != CAIRO_SURFACE_TYPE_XLIB) {
+ return surface.forget();
+ }
+
+ IntSize size = aSurface->GetSize();
+ if (!size.width || !size.height) {
+ return surface.forget();
+ }
+
+ // 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 surface.forget();
+ }
+
+ SurfaceFormat format = aSurface->GetFormat();
+ Screen *screen = cairo_xlib_surface_get_screen(mSurface);
+ Display *dpy = DisplayOfScreen(screen);
+ XRenderPictFormat* xrenderFormat = nullptr;
+ switch (format) {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardARGB32);
+ break;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardRGB24);
+ break;
+ case SurfaceFormat::A8:
+ xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardA8);
+ break;
+ default:
+ return surface.forget();
+ }
+ if (!xrenderFormat) {
+ return surface.forget();
+ }
+
+ Drawable pixmap = XCreatePixmap(dpy, RootWindowOfScreen(screen),
+ size.width, size.height,
+ xrenderFormat->depth);
+ if (!pixmap) {
+ return surface.forget();
+ }
+
+ auto closure = MakeUnique<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 surface.forget();
+ }
+
+ cairo_surface_set_user_data(csurf, &gDestroyPixmapKey,
+ closure.release(), DestroyPixmap);
+
+ RefPtr<DrawTargetCairo> dt = new DrawTargetCairo();
+ if (!dt->Init(csurf, size, &format)) {
+ return surface.forget();
+ }
+
+ dt->CopySurface(aSurface,
+ IntRect(0, 0, size.width, size.height),
+ IntPoint(0, 0));
+ dt->Flush();
+
+ surface = new SourceSurfaceCairo(csurf, size, format);
+#endif
+
+ return surface.forget();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+{
+ return nullptr;
+}
+
+already_AddRefed<DrawTarget>
+DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+ if (cairo_surface_status(cairo_get_group_target(mContext))) {
+ RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
+ if (target->Init(aSize, aFormat)) {
+ return target.forget();
+ }
+ }
+
+ cairo_surface_t* similar;
+ switch (cairo_surface_get_type(mSurface)) {
+#ifdef CAIRO_HAS_WIN32_SURFACE
+ case CAIRO_SURFACE_TYPE_WIN32:
+ similar = cairo_win32_surface_create_with_dib(
+ GfxFormatToCairoFormat(aFormat), aSize.width, aSize.height);
+ break;
+#endif
+#ifdef CAIRO_HAS_QUARTZ_SURFACE
+ case CAIRO_SURFACE_TYPE_QUARTZ:
+ similar = cairo_quartz_surface_create_cg_layer(
+ mSurface, GfxFormatToCairoContent(aFormat), aSize.width, aSize.height);
+ break;
+#endif
+ default:
+ similar = cairo_surface_create_similar(mSurface,
+ GfxFormatToCairoContent(aFormat),
+ aSize.width, aSize.height);
+ break;
+ }
+
+ if (!cairo_surface_status(similar)) {
+ RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
+ if (target->InitAlreadyReferenced(similar, aSize)) {
+ return target.forget();
+ }
+ }
+
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "Failed to create similar cairo surface! Size: " << aSize << " Status: " << cairo_surface_status(similar) << cairo_surface_status(cairo_get_group_target(mContext)) << " format " << (int)aFormat;
+ cairo_surface_destroy(similar);
+
+ return nullptr;
+}
+
+bool
+DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
+{
+ if (cairo_surface_status(aSurface)) {
+ gfxCriticalNote
+ << "Attempt to create DrawTarget for invalid surface. "
+ << aSize << " Cairo Status: " << cairo_surface_status(aSurface);
+ cairo_surface_destroy(aSurface);
+ return false;
+ }
+
+ mContext = cairo_create(aSurface);
+ mSurface = aSurface;
+ mSize = aSize;
+ 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 == SurfaceFormat::A8R8G8B8_UINT32 ||
+ mFormat == SurfaceFormat::R8G8B8A8) {
+ SetPermitSubpixelAA(false);
+ } else {
+ SetPermitSubpixelAA(true);
+ }
+
+ return true;
+}
+
+already_AddRefed<DrawTarget>
+DrawTargetCairo::CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
+ float aSigma) const
+{
+ cairo_surface_t* similar = cairo_surface_create_similar(cairo_get_target(mContext),
+ GfxFormatToCairoContent(aFormat),
+ aSize.width, aSize.height);
+
+ if (cairo_surface_status(similar)) {
+ return nullptr;
+ }
+
+ // If we don't have a blur then we can use the RGBA mask and keep all the
+ // operations in graphics memory.
+ if (aSigma == 0.0F) {
+ RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
+ if (target->InitAlreadyReferenced(similar, aSize)) {
+ return target.forget();
+ } else {
+ return nullptr;
+ }
+ }
+
+ cairo_surface_t* blursurf = cairo_image_surface_create(CAIRO_FORMAT_A8,
+ aSize.width,
+ aSize.height);
+
+ if (cairo_surface_status(blursurf)) {
+ return nullptr;
+ }
+
+ cairo_surface_t* tee = cairo_tee_surface_create(blursurf);
+ cairo_surface_destroy(blursurf);
+ if (cairo_surface_status(tee)) {
+ cairo_surface_destroy(similar);
+ return nullptr;
+ }
+
+ cairo_tee_surface_add(tee, similar);
+ cairo_surface_destroy(similar);
+
+ RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
+ if (target->InitAlreadyReferenced(tee, aSize)) {
+ return target.forget();
+ }
+ return nullptr;
+}
+
+static inline pixman_format_code_t
+GfxFormatToPixmanFormat(SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ return PIXMAN_a8r8g8b8;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ return PIXMAN_x8r8g8b8;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return PIXMAN_r5g6b5;
+ case SurfaceFormat::A8:
+ return PIXMAN_a8;
+ default:
+ // Allow both BGRA and ARGB formats to be passed through unmodified,
+ // even though even though we are actually rendering to A8R8G8B8_UINT32.
+ if (aFormat == SurfaceFormat::B8G8R8A8 ||
+ aFormat == SurfaceFormat::A8R8G8B8) {
+ return PIXMAN_a8r8g8b8;
+ }
+ return (pixman_format_code_t)0;
+ }
+}
+
+static inline bool
+GfxMatrixToPixmanTransform(const Matrix4x4 &aMatrix, pixman_transform* aResult)
+{
+ pixman_f_transform fTransform = {{
+ { aMatrix._11, aMatrix._21, aMatrix._41 },
+ { aMatrix._12, aMatrix._22, aMatrix._42 },
+ { aMatrix._14, aMatrix._24, aMatrix._44 }
+ }};
+ return pixman_transform_from_pixman_f_transform(aResult, &fTransform);
+}
+
+#ifndef USE_SKIA
+bool
+DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
+{
+ // Composite the 3D transform with the DT's transform.
+ Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
+ // Transform the surface bounds and clip to this DT.
+ IntRect xformBounds =
+ RoundedOut(
+ fullMat.TransformAndClipBounds(Rect(Point(0, 0), Size(aSurface->GetSize())),
+ Rect(Point(0, 0), Size(GetSize()))));
+ if (xformBounds.IsEmpty()) {
+ return true;
+ }
+ // Offset the matrix by the transformed origin.
+ fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+ // Invert the matrix into a pattern matrix for pixman.
+ if (!fullMat.Invert()) {
+ return false;
+ }
+ pixman_transform xform;
+ if (!GfxMatrixToPixmanTransform(fullMat, &xform)) {
+ return false;
+ }
+
+ // Read in the source data.
+ RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+ pixman_format_code_t srcFormat = GfxFormatToPixmanFormat(srcSurf->GetFormat());
+ if (!srcFormat) {
+ return false;
+ }
+ DataSourceSurface::ScopedMap srcMap(srcSurf, DataSourceSurface::READ);
+ if (!srcMap.IsMapped()) {
+ return false;
+ }
+
+ // Set up an intermediate destination surface only the size of the transformed bounds.
+ // Try to pass through the source's format unmodified in both the BGRA and ARGB cases.
+ RefPtr<DataSourceSurface> dstSurf =
+ Factory::CreateDataSourceSurface(xformBounds.Size(),
+ srcFormat == PIXMAN_a8r8g8b8 ?
+ srcSurf->GetFormat() : SurfaceFormat::A8R8G8B8_UINT32);
+ if (!dstSurf) {
+ return false;
+ }
+
+ // Wrap the surfaces in pixman images and do the transform.
+ pixman_image_t* dst =
+ pixman_image_create_bits(PIXMAN_a8r8g8b8,
+ xformBounds.width, xformBounds.height,
+ (uint32_t*)dstSurf->GetData(), dstSurf->Stride());
+ if (!dst) {
+ return false;
+ }
+ pixman_image_t* src =
+ pixman_image_create_bits(srcFormat,
+ srcSurf->GetSize().width, srcSurf->GetSize().height,
+ (uint32_t*)srcMap.GetData(), srcMap.GetStride());
+ if (!src) {
+ pixman_image_unref(dst);
+ return false;
+ }
+
+ pixman_image_set_filter(src, PIXMAN_FILTER_BILINEAR, nullptr, 0);
+ pixman_image_set_transform(src, &xform);
+
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ src, nullptr, dst,
+ 0, 0, 0, 0, 0, 0,
+ xformBounds.width, xformBounds.height);
+
+ pixman_image_unref(dst);
+ pixman_image_unref(src);
+
+ // Temporarily reset the DT's transform, since it has already been composed above.
+ Matrix origTransform = mTransform;
+ SetTransform(Matrix());
+
+ // Draw the transformed surface within the transformed bounds.
+ DrawSurface(dstSurf, Rect(xformBounds), Rect(Point(0, 0), Size(xformBounds.Size())));
+
+ SetTransform(origTransform);
+
+ return true;
+}
+#endif
+
+#ifdef CAIRO_HAS_XLIB_SURFACE
+static bool gXRenderInitialized = false;
+static bool gXRenderHasTransform = false;
+
+static bool
+SupportsXRender(cairo_surface_t* surface)
+{
+ if (!surface ||
+ cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_XLIB ||
+ !cairo_xlib_surface_get_xrender_format(surface)) {
+ return false;
+ }
+
+ if (gXRenderInitialized) {
+ return true;
+ }
+ gXRenderInitialized = true;
+
+ cairo_device_t* device = cairo_surface_get_device(surface);
+ if (cairo_device_acquire(device) != CAIRO_STATUS_SUCCESS) {
+ return false;
+ }
+
+ Display* display = cairo_xlib_surface_get_display(surface);
+ int major, minor;
+ if (XRenderQueryVersion(display, &major, &minor)) {
+ if (major > 0 || (major == 0 && minor >= 6)) {
+ gXRenderHasTransform = true;
+ }
+ }
+
+ cairo_device_release(device);
+
+ return true;
+}
+#endif
+
+bool
+DrawTargetCairo::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
+{
+#if CAIRO_HAS_XLIB_SURFACE
+ cairo_surface_t* srcSurf =
+ aSurface->GetType() == SurfaceType::CAIRO ?
+ static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface() : nullptr;
+ if (!SupportsXRender(srcSurf) || !gXRenderHasTransform) {
+ return DrawTarget::Draw3DTransformedSurface(aSurface, aMatrix);
+ }
+
+ Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
+ IntRect xformBounds =
+ RoundedOut(
+ fullMat.TransformAndClipBounds(Rect(Point(0, 0), Size(aSurface->GetSize())),
+ Rect(Point(0, 0), Size(GetSize()))));
+ if (xformBounds.IsEmpty()) {
+ return true;
+ }
+ fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+ if (!fullMat.Invert()) {
+ return false;
+ }
+ pixman_transform xform;
+ if (!GfxMatrixToPixmanTransform(fullMat, &xform)) {
+ return false;
+ }
+
+ cairo_surface_t* xformSurf =
+ cairo_surface_create_similar(srcSurf, CAIRO_CONTENT_COLOR_ALPHA,
+ xformBounds.width, xformBounds.height);
+ if (!SupportsXRender(xformSurf)) {
+ cairo_surface_destroy(xformSurf);
+ return false;
+ }
+ cairo_device_t* device = cairo_surface_get_device(xformSurf);
+ if (cairo_device_acquire(device) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(xformSurf);
+ return false;
+ }
+
+ Display* display = cairo_xlib_surface_get_display(xformSurf);
+
+ Picture srcPict = XRenderCreatePicture(display,
+ cairo_xlib_surface_get_drawable(srcSurf),
+ cairo_xlib_surface_get_xrender_format(srcSurf),
+ 0, nullptr);
+ XRenderSetPictureFilter(display, srcPict, FilterBilinear, nullptr, 0);
+ XRenderSetPictureTransform(display, srcPict, (XTransform*)&xform);
+
+ Picture dstPict = XRenderCreatePicture(display,
+ cairo_xlib_surface_get_drawable(xformSurf),
+ cairo_xlib_surface_get_xrender_format(xformSurf),
+ 0, nullptr);
+
+ XRenderComposite(display, PictOpSrc,
+ srcPict, X11None, dstPict,
+ 0, 0, 0, 0, 0, 0,
+ xformBounds.width, xformBounds.height);
+
+ XRenderFreePicture(display, srcPict);
+ XRenderFreePicture(display, dstPict);
+
+ cairo_device_release(device);
+ cairo_surface_mark_dirty(xformSurf);
+
+ AutoPrepareForDrawing(this, mContext);
+
+ cairo_identity_matrix(mContext);
+
+ cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
+ cairo_set_antialias(mContext, CAIRO_ANTIALIAS_DEFAULT);
+ cairo_set_source_surface(mContext, xformSurf, xformBounds.x, xformBounds.y);
+
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, xformBounds.x, xformBounds.y, xformBounds.width, xformBounds.height);
+ cairo_fill(mContext);
+
+ cairo_surface_destroy(xformSurf);
+
+ return true;
+#else
+ return DrawTarget::Draw3DTransformedSurface(aSurface, aMatrix);
+#endif
+}
+
+bool
+DrawTargetCairo::Init(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
+{
+ cairo_surface_reference(aSurface);
+ 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 == NativeSurfaceType::CAIRO_CONTEXT) {
+ return mContext;
+ }
+
+ return nullptr;
+}
+
+void
+DrawTargetCairo::MarkSnapshotIndependent()
+{
+ if (mSnapshot) {
+ if (mSnapshot->refCount() > 1) {
+ // We only need to worry about snapshots that someone else knows about
+ mSnapshot->DrawTargetWillChange();
+ }
+ mSnapshot = nullptr;
+ }
+}
+
+void
+DrawTargetCairo::WillChange(const Path* aPath /* = nullptr */)
+{
+ MarkSnapshotIndependent();
+ MOZ_ASSERT(!mLockedBits);
+}
+
+void
+DrawTargetCairo::SetTransform(const Matrix& aTransform)
+{
+ DrawTarget::SetTransform(aTransform);
+
+ mTransformSingular = aTransform.IsSingular();
+ if (!mTransformSingular) {
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(mTransform, mat);
+ 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;
+}
+
+#ifdef MOZ_X11
+bool
+BorrowedXlibDrawable::Init(DrawTarget* aDT)
+{
+ MOZ_ASSERT(aDT, "Caller should check for nullptr");
+ MOZ_ASSERT(!mDT, "Can't initialize twice!");
+ mDT = aDT;
+ mDrawable = X11None;
+
+#ifdef CAIRO_HAS_XLIB_SURFACE
+ if (aDT->GetBackendType() != BackendType::CAIRO ||
+ aDT->IsDualDrawTarget() ||
+ aDT->IsTiledDrawTarget()) {
+ return false;
+ }
+
+ DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
+ cairo_surface_t* surf = cairo_get_group_target(cairoDT->mContext);
+ if (cairo_surface_get_type(surf) != CAIRO_SURFACE_TYPE_XLIB) {
+ return false;
+ }
+ cairo_surface_flush(surf);
+
+ cairoDT->WillChange();
+
+ mDisplay = cairo_xlib_surface_get_display(surf);
+ mDrawable = cairo_xlib_surface_get_drawable(surf);
+ mScreen = cairo_xlib_surface_get_screen(surf);
+ mVisual = cairo_xlib_surface_get_visual(surf);
+ mXRenderFormat = cairo_xlib_surface_get_xrender_format(surf);
+ mSize.width = cairo_xlib_surface_get_width(surf);
+ mSize.height = cairo_xlib_surface_get_height(surf);
+
+ double x = 0, y = 0;
+ cairo_surface_get_device_offset(surf, &x, &y);
+ mOffset = Point(x, y);
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+void
+BorrowedXlibDrawable::Finish()
+{
+ DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(mDT);
+ cairo_surface_t* surf = cairo_get_group_target(cairoDT->mContext);
+ cairo_surface_mark_dirty(surf);
+ if (mDrawable) {
+ mDrawable = X11None;
+ }
+}
+#endif
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/DrawTargetCairo.h b/gfx/2d/DrawTargetCairo.h
new file mode 100644
index 000000000..bd0415b06
--- /dev/null
+++ b/gfx/2d/DrawTargetCairo.h
@@ -0,0 +1,262 @@
+/* -*- 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_DRAWTARGET_CAIRO_H_
+#define _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
+
+#include "2D.h"
+#include "cairo.h"
+#include "PathCairo.h"
+
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class SourceSurfaceCairo;
+
+class GradientStopsCairo : public GradientStops
+{
+ public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsCairo)
+ GradientStopsCairo(GradientStop* aStops, uint32_t aNumStops,
+ ExtendMode aExtendMode)
+ : mExtendMode(aExtendMode)
+ {
+ for (uint32_t i = 0; i < aNumStops; ++i) {
+ mStops.push_back(aStops[i]);
+ }
+ }
+
+ virtual ~GradientStopsCairo() {}
+
+ const std::vector<GradientStop>& GetStops() const
+ {
+ return mStops;
+ }
+
+ ExtendMode GetExtendMode() const
+ {
+ return mExtendMode;
+ }
+
+ virtual BackendType GetBackendType() const { return BackendType::CAIRO; }
+
+ private:
+ std::vector<GradientStop> mStops;
+ ExtendMode mExtendMode;
+};
+
+class DrawTargetCairo final : public DrawTarget
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetCairo, override)
+ friend class BorrowedCairoContext;
+ friend class BorrowedXlibDrawable;
+
+ DrawTargetCairo();
+ virtual ~DrawTargetCairo();
+
+ virtual bool IsValid() const override;
+ virtual DrawTargetType GetType() const override;
+ virtual BackendType GetBackendType() const override { return BackendType::CAIRO; }
+ virtual already_AddRefed<SourceSurface> Snapshot() override;
+ virtual IntSize GetSize() override;
+
+ virtual bool IsCurrentGroupOpaque() override;
+
+ virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) override;
+
+ virtual bool LockBits(uint8_t** aData, IntSize* aSize,
+ int32_t* aStride, SurfaceFormat* aFormat,
+ IntPoint* aOrigin = nullptr) override;
+ virtual void ReleaseBits(uint8_t* aData) override;
+
+ virtual void Flush() override;
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
+ 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) override;
+
+ virtual void ClearRect(const Rect &aRect) override;
+
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ 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()) 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,
+ const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
+ virtual void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
+ const Matrix4x4& aMatrix) override;
+
+ virtual void PushClip(const Path *aPath) override;
+ virtual void PushClipRect(const Rect &aRect) override;
+ virtual void PopClip() override;
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) override;
+ virtual void PopLayer() override;
+
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
+
+ virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const override;
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+ virtual already_AddRefed<DrawTarget>
+ CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
+ float aSigma) const override;
+
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
+
+ virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
+
+ virtual void GetGlyphRasterizationMetrics(ScaledFont *aScaledFont, const uint16_t* aGlyphIndices,
+ uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics) override;
+
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override;
+
+ 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) override;
+
+ virtual void DetachAllSnapshots() override { MarkSnapshotIndependent(); }
+
+ // 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, SurfaceFormat* aFormat = nullptr);
+ enum DrawPatternType { DRAW_FILL, DRAW_STROKE };
+ void DrawPattern(const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions,
+ 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
+ // one.
+ void WillChange(const Path* aPath = nullptr);
+
+ // Call if there is any reason to disassociate the snapshot from this draw
+ // target; for example, because we're going to be destroyed.
+ void MarkSnapshotIndependent();
+
+ // 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);
+
+ // Set the Cairo context font options according to the current draw target
+ // font state.
+ void SetFontOptions();
+
+private: // data
+ cairo_t* mContext;
+ cairo_surface_t* mSurface;
+ IntSize mSize;
+ bool mTransformSingular;
+
+ uint8_t* mLockedBits;
+
+ cairo_font_options_t* mFontOptions;
+
+ struct PushedLayer
+ {
+ PushedLayer(Float aOpacity, bool aWasPermittingSubpixelAA)
+ : mOpacity(aOpacity)
+ , mMaskPattern(nullptr)
+ , mWasPermittingSubpixelAA(aWasPermittingSubpixelAA)
+ {}
+ Float mOpacity;
+ cairo_pattern_t* mMaskPattern;
+ bool mWasPermittingSubpixelAA;
+ };
+ std::vector<PushedLayer> mPushedLayers;
+
+ // 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;
+ static cairo_surface_t *mDummySurface;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
diff --git a/gfx/2d/DrawTargetCapture.cpp b/gfx/2d/DrawTargetCapture.cpp
new file mode 100644
index 000000000..f0584c427
--- /dev/null
+++ b/gfx/2d/DrawTargetCapture.cpp
@@ -0,0 +1,201 @@
+/* -*- 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;
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetCaptureImpl::Snapshot()
+{
+ RefPtr<DrawTarget> dt = mRefDT->CreateSimilarDrawTarget(mSize, mRefDT->GetFormat());
+
+ ReplayToDrawTarget(dt, Matrix());
+
+ return dt->Snapshot();
+}
+
+void
+DrawTargetCaptureImpl::DetachAllSnapshots()
+{}
+
+#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;
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/DrawTargetCapture.h b/gfx/2d/DrawTargetCapture.h
new file mode 100644
index 000000000..a60e07b56
--- /dev/null
+++ b/gfx/2d/DrawTargetCapture.h
@@ -0,0 +1,168 @@
+/* -*- 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 already_AddRefed<SourceSurface> Snapshot();
+
+ virtual void DetachAllSnapshots();
+
+ 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 already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+ {
+ return mRefDT->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
+ }
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const
+ {
+ return mRefDT->OptimizeSourceSurface(aSurface);
+ }
+
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+ {
+ return mRefDT->CreateSourceSurfaceFromNativeSurface(aSurface);
+ }
+
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+ {
+ return mRefDT->CreateSimilarDrawTarget(aSize, aFormat);
+ }
+
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const
+ {
+ return mRefDT->CreatePathBuilder(aFillRule);
+ }
+
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const
+ {
+ return mRefDT->CreateGradientStops(aStops, aNumStops, aExtendMode);
+ }
+ virtual already_AddRefed<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 gfx
+
+} // namespace mozilla
+
+
+#endif /* MOZILLA_GFX_DRAWTARGETCAPTURE_H_ */
diff --git a/gfx/2d/DrawTargetD2D1.cpp b/gfx/2d/DrawTargetD2D1.cpp
new file mode 100644
index 000000000..d9deb4c10
--- /dev/null
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -0,0 +1,1931 @@
+/* -*- 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 "FilterNodeSoftware.h"
+#include "GradientStopsD2D.h"
+#include "SourceSurfaceD2D1.h"
+#include "RadialGradientEffectD2D1.h"
+
+#include "HelpersD2D.h"
+#include "FilterNodeD2D1.h"
+#include "ExtendInputEffectD2D1.h"
+#include "Tools.h"
+
+using namespace std;
+
+// decltype is not usable for overloaded functions.
+typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
+ D2D1_FACTORY_TYPE factoryType,
+ REFIID iid,
+ CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
+ void **factory
+);
+
+namespace mozilla {
+namespace gfx {
+
+uint64_t DrawTargetD2D1::mVRAMUsageDT;
+uint64_t DrawTargetD2D1::mVRAMUsageSS;
+IDWriteFactory *DrawTargetD2D1::mDWriteFactory;
+ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr;
+
+ID2D1Factory1 *D2DFactory1()
+{
+ return DrawTargetD2D1::factory();
+}
+
+DrawTargetD2D1::DrawTargetD2D1()
+ : mPushedLayers(1)
+ , mUsedCommandListsSincePurge(0)
+ , mDidComplexBlendWithListInList(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.
+ }
+
+ if (mDC) {
+ // The only way mDC can be null is if Init failed, but it can happen and the
+ // destructor is the only place where we need to check for it since the
+ // DrawTarget will destroyed right after Init fails.
+ 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);
+ }
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetD2D1::Snapshot()
+{
+ if (mSnapshot) {
+ RefPtr<SourceSurface> snapshot(mSnapshot);
+ return snapshot.forget();
+ }
+ PopAllClips();
+
+ Flush();
+
+ mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this);
+
+ RefPtr<SourceSurface> snapshot(mSnapshot);
+ return snapshot.forget();
+}
+
+// Command lists are kept around by device contexts until EndDraw is called,
+// this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
+// are expensive though, especially relatively when little work is done, so
+// we try to reduce the amount of times we execute these purges.
+static const uint32_t kPushedLayersBeforePurge = 25;
+
+void
+DrawTargetD2D1::Flush()
+{
+ if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge) &&
+ mPushedLayers.size() == 1) {
+ // It's important to pop all clips as otherwise layers can forget about
+ // their clip when doing an EndDraw. When we have layers pushed we cannot
+ // easily pop all underlying clips to delay the purge until we have no
+ // layers pushed.
+ PopAllClips();
+ mUsedCommandListsSincePurge = 0;
+ mDC->EndDraw();
+ mDC->BeginDraw();
+ } else {
+ 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**)getter_AddRefs(bitmap));
+ }
+
+ if (bitmap && aSurfOptions.mSamplingBounds == SamplingBounds::UNBOUNDED) {
+ mDC->DrawBitmap(bitmap, D2DRect(aDest), aOptions.mAlpha,
+ D2DFilter(aSurfOptions.mSamplingFilter), D2DRect(aSource));
+ } else {
+ // This has issues ignoring the alpha channel on windows 7 with images marked opaque.
+ MOZ_ASSERT(aSurface->GetFormat() != SurfaceFormat::B8G8R8X8);
+
+ // Bug 1275478 - D2D1 cannot draw A8 surface correctly.
+ MOZ_ASSERT(aSurface->GetFormat() != SurfaceFormat::A8);
+
+ mDC->CreateImageBrush(image,
+ D2D1::ImageBrushProperties(samplingBounds,
+ D2D1_EXTEND_MODE_CLAMP,
+ D2D1_EXTEND_MODE_CLAMP,
+ D2DInterpolationMode(aSurfOptions.mSamplingFilter)),
+ D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)),
+ getter_AddRefs(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;
+ HRESULT hr = mDC->CreateEffect(CLSID_D2D1Shadow, getter_AddRefs(shadowEffect));
+ if (FAILED(hr) || !shadowEffect) {
+ gfxWarning() << "Failed to create shadow effect. Code: " << hexa(hr);
+ return;
+ }
+ 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;
+ }
+
+ RefPtr<ID2D1CommandList> list;
+ mUsedCommandListsSincePurge++;
+ mDC->CreateCommandList(getter_AddRefs(list));
+ mDC->SetTarget(list);
+
+ IntRect addClipRect;
+ RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&addClipRect);
+
+ RefPtr<ID2D1SolidColorBrush> brush;
+ mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(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(CurrentTarget());
+ list->Close();
+
+ mDC->DrawImage(list, 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;
+
+ Matrix mat = Matrix::Translation(aOffset);
+ RefPtr<ID2D1Image> image = GetImageForSurface(aMask, mat, ExtendMode::CLAMP, nullptr);
+
+ MOZ_ASSERT(!mat.HasNonTranslation());
+ aOffset.x = mat._31;
+ aOffset.y = mat._32;
+
+ 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);
+
+ image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+ if (!bitmap) {
+ gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces.";
+ return;
+ }
+
+ IntSize size = IntSize::Truncate(bitmap->GetSize().width, bitmap->GetSize().height);
+
+ Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height));
+
+ 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 = Matrix::Translation(aDestination.x - aSourceRect.x, aDestination.y - aSourceRect.y);
+ RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP);
+
+ if (!image) {
+ gfxWarning() << "Couldn't get image for surface.";
+ return;
+ }
+
+ if (mat.HasNonIntegerTranslation()) {
+ gfxDebug() << *this << ": At this point scaled partial uploads are not supported for CopySurface.";
+ return;
+ }
+
+ IntRect sourceRect = aSourceRect;
+ sourceRect.x += (aDestination.x - aSourceRect.x) - mat._31;
+ sourceRect.width -= (aDestination.x - aSourceRect.x) - mat._31;
+ sourceRect.y += (aDestination.y - aSourceRect.y) - mat._32;
+ sourceRect.height -= (aDestination.y - aSourceRect.y) - mat._32;
+
+ RefPtr<ID2D1Bitmap> bitmap;
+ image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+
+ if (bitmap && mFormat == SurfaceFormat::A8) {
+ RefPtr<ID2D1SolidColorBrush> brush;
+ mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White),
+ D2D1::BrushProperties(), getter_AddRefs(brush));
+ mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
+ mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS);
+ mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
+ return;
+ }
+
+ Rect srcRect(Float(sourceRect.x), Float(sourceRect.y),
+ Float(aSourceRect.width), Float(aSourceRect.height));
+
+ Rect dstRect(Float(aDestination.x), Float(aDestination.y),
+ Float(aSourceRect.width), Float(aSourceRect.height));
+
+ if (bitmap) {
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
+ mDC->DrawBitmap(bitmap, D2DRect(dstRect), 1.0f,
+ D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
+ D2DRect(srcRect));
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
+ return;
+ }
+
+ mDC->DrawImage(image, D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)),
+ D2DRect(srcRect), 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 || 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 (!CurrentLayer().mIsOpaque && 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 &&
+ !CurrentLayer().mIsOpaque && !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);
+
+ bool needsRepushedLayers = false;
+ if (forceClearType) {
+ D2D1_RECT_F rect;
+ bool isAligned;
+ needsRepushedLayers = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+
+ // If we have a complex clip in our stack and we have a transparent
+ // background, and subpixel AA is permitted, we need to repush our layer
+ // stack limited by the glyph run bounds initializing our layers for
+ // subpixel AA.
+ if (needsRepushedLayers) {
+ mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
+ DWRITE_MEASURING_MODE_NATURAL, &rect);
+ rect.left = std::floor(rect.left);
+ rect.right = std::ceil(rect.right);
+ rect.top = std::floor(rect.top);
+ rect.bottom = std::ceil(rect.bottom);
+
+ PopAllClips();
+
+ if (!mTransform.IsRectilinear()) {
+ // We must limit the pixels we touch to the -user space- bounds of
+ // the glyphs being drawn. In order not to get transparent pixels
+ // copied up in our pushed layer stack.
+ D2D1_RECT_F userRect;
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
+ DWRITE_MEASURING_MODE_NATURAL, &userRect);
+
+ RefPtr<ID2D1PathGeometry> path;
+ factory()->CreatePathGeometry(getter_AddRefs(path));
+ RefPtr<ID2D1GeometrySink> sink;
+ path->Open(getter_AddRefs(sink));
+ AddRectToSink(sink, userRect);
+ sink->Close();
+
+ mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), path, D2D1_ANTIALIAS_MODE_ALIASED,
+ D2DMatrix(mTransform), 1.0f, nullptr,
+ D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND |
+ D2D1_LAYER_OPTIONS1_IGNORE_ALPHA), nullptr);
+ }
+
+ PushClipsToDC(mDC, true, rect);
+ mDC->SetTransform(D2DMatrix(mTransform));
+ }
+ }
+
+ if (brush) {
+ mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
+ }
+
+ if (needsRepushedLayers) {
+ PopClipsFromDC(mDC);
+
+ if (!mTransform.IsRectilinear()) {
+ mDC->PopLayer();
+ }
+ }
+
+ 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::PushClipGeometry(ID2D1Geometry* aGeometry,
+ const D2D1_MATRIX_3X2_F& aTransform,
+ bool aPixelAligned)
+{
+ mCurrentClippedGeometry = nullptr;
+
+ PushedClip clip;
+ clip.mGeometry = aGeometry;
+ clip.mTransform = aTransform;
+ clip.mIsPixelAligned = aPixelAligned;
+
+ aGeometry->GetBounds(aTransform, &clip.mBounds);
+
+ CurrentLayer().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 (CurrentLayer().mClipsArePushed) {
+ PushD2DLayer(mDC, clip.mGeometry, clip.mTransform, clip.mIsPixelAligned);
+ }
+}
+
+void
+DrawTargetD2D1::PushClip(const Path *aPath)
+{
+ if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
+ gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
+ return;
+ }
+
+ RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
+
+ PushClipGeometry(pathD2D->GetGeometry(), D2DMatrix(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<ID2D1Geometry> geom = ConvertRectToGeometry(D2DRect(aRect));
+ return PushClipGeometry(geom, D2DMatrix(mTransform));
+ }
+
+ 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);
+
+ CurrentLayer().mPushedClips.push_back(clip);
+
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ if (CurrentLayer().mClipsArePushed) {
+ mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ }
+}
+
+void
+DrawTargetD2D1::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
+{
+ // Build a path for the union of the rects.
+ RefPtr<ID2D1PathGeometry> path;
+ factory()->CreatePathGeometry(getter_AddRefs(path));
+ RefPtr<ID2D1GeometrySink> sink;
+ path->Open(getter_AddRefs(sink));
+ sink->SetFillMode(D2D1_FILL_MODE_WINDING);
+ for (uint32_t i = 0; i < aCount; i++) {
+ const IntRect& rect = aRects[i];
+ sink->BeginFigure(D2DPoint(rect.TopLeft()), D2D1_FIGURE_BEGIN_FILLED);
+ D2D1_POINT_2F lines[3] = { D2DPoint(rect.TopRight()), D2DPoint(rect.BottomRight()), D2DPoint(rect.BottomLeft()) };
+ sink->AddLines(lines, 3);
+ sink->EndFigure(D2D1_FIGURE_END_CLOSED);
+ }
+ sink->Close();
+
+ // The path is in device-space, so there is no transform needed,
+ // and all rects are pixel aligned.
+ PushClipGeometry(path, D2D1::IdentityMatrix(), true);
+}
+
+void
+DrawTargetD2D1::PopClip()
+{
+ mCurrentClippedGeometry = nullptr;
+ if (CurrentLayer().mPushedClips.empty()) {
+ gfxDevCrash(LogReason::UnbalancedClipStack) << "DrawTargetD2D1::PopClip: No clip to pop.";
+ return;
+ }
+
+ if (CurrentLayer().mClipsArePushed) {
+ if (CurrentLayer().mPushedClips.back().mGeometry) {
+ mDC->PopLayer();
+ } else {
+ mDC->PopAxisAlignedClip();
+ }
+ }
+ CurrentLayer().mPushedClips.pop_back();
+}
+
+void
+DrawTargetD2D1::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+ const Matrix& aMaskTransform, const IntRect& aBounds,
+ bool aCopyBackground)
+{
+ D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
+
+ if (aOpaque) {
+ options |= D2D1_LAYER_OPTIONS1_IGNORE_ALPHA;
+ }
+ if (aCopyBackground) {
+ options |= D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
+ }
+
+ RefPtr<ID2D1BitmapBrush> mask;
+
+ Matrix maskTransform = aMaskTransform;
+
+ RefPtr<ID2D1PathGeometry> clip;
+ if (aMask) {
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ RefPtr<ID2D1Image> image = GetImageForSurface(aMask, maskTransform, ExtendMode::CLAMP);
+
+ // The mask is given in user space. Our layer will apply it in device space.
+ maskTransform = maskTransform * mTransform;
+
+ if (image) {
+ RefPtr<ID2D1Bitmap> bitmap;
+ image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+
+ mDC->CreateBitmapBrush(bitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(1.0f, D2DMatrix(maskTransform)), getter_AddRefs(mask));
+ MOZ_ASSERT(bitmap); // This should always be true since it was created for a surface.
+
+ factory()->CreatePathGeometry(getter_AddRefs(clip));
+ RefPtr<ID2D1GeometrySink> sink;
+ clip->Open(getter_AddRefs(sink));
+ AddRectToSink(sink, D2D1::RectF(0, 0, aMask->GetSize().width, aMask->GetSize().height));
+ sink->Close();
+ } else {
+ gfxCriticalError() << "Failed to get image for mask surface!";
+ }
+ }
+
+ PushAllClips();
+
+ mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), clip, D2D1_ANTIALIAS_MODE_ALIASED, D2DMatrix(maskTransform), aOpacity, mask, options), nullptr);
+ PushedLayer pushedLayer;
+ pushedLayer.mClipsArePushed = false;
+ pushedLayer.mIsOpaque = aOpaque;
+ pushedLayer.mOldPermitSubpixelAA = mPermitSubpixelAA;
+ mPermitSubpixelAA = aOpaque;
+
+ mDC->CreateCommandList(getter_AddRefs(pushedLayer.mCurrentList));
+ mPushedLayers.push_back(pushedLayer);
+
+ mDC->SetTarget(CurrentTarget());
+
+ mUsedCommandListsSincePurge++;
+}
+
+void
+DrawTargetD2D1::PopLayer()
+{
+ MOZ_ASSERT(CurrentLayer().mPushedClips.size() == 0);
+
+ RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
+ mPermitSubpixelAA = CurrentLayer().mOldPermitSubpixelAA;
+
+ mPushedLayers.pop_back();
+ mDC->SetTarget(CurrentTarget());
+
+ list->Close();
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ DCCommandSink sink(mDC);
+ list->Stream(&sink);
+
+ mDC->PopLayer();
+}
+
+already_AddRefed<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)),
+ getter_AddRefs(bitmap));
+
+ if (FAILED(hr) || !bitmap) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D2D1.1] 1CreateBitmap failure " << aSize << " Code: " << hexa(hr) << " format " << (int)aFormat;
+ return nullptr;
+ }
+
+ return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), mDC, aFormat, aSize);
+}
+
+already_AddRefed<DrawTarget>
+DrawTargetD2D1::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+ RefPtr<DrawTargetD2D1> dt = new DrawTargetD2D1();
+
+ if (!dt->Init(aSize, aFormat)) {
+ return nullptr;
+ }
+
+ return dt.forget();
+}
+
+already_AddRefed<PathBuilder>
+DrawTargetD2D1::CreatePathBuilder(FillRule aFillRule) const
+{
+ RefPtr<ID2D1PathGeometry> path;
+ HRESULT hr = factory()->CreatePathGeometry(getter_AddRefs(path));
+
+ if (FAILED(hr)) {
+ gfxWarning() << *this << ": Failed to create Direct2D Path Geometry. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ RefPtr<ID2D1GeometrySink> sink;
+ hr = path->Open(getter_AddRefs(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 MakeAndAddRef<PathBuilderD2D>(sink, path, aFillRule, BackendType::DIRECT2D1_1);
+}
+
+already_AddRefed<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, Axis::BOTH),
+ getter_AddRefs(stopCollection));
+ delete [] stops;
+
+ if (FAILED(hr)) {
+ gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ return MakeAndAddRef<GradientStopsD2D>(stopCollection, Factory::GetDirect3D11Device());
+}
+
+already_AddRefed<FilterNode>
+DrawTargetD2D1::CreateFilter(FilterType aType)
+{
+ return FilterNodeD2D1::Create(mDC, aType);
+}
+
+void
+DrawTargetD2D1::GetGlyphRasterizationMetrics(ScaledFont *aScaledFont, const uint16_t* aGlyphIndices,
+ uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics)
+{
+ MOZ_ASSERT(aScaledFont->GetType() == FontType::DWRITE);
+
+ aScaledFont->GetGlyphDesignMetrics(aGlyphIndices, aNumGlyphs, aGlyphMetrics);
+
+ // GetDesignGlyphMetrics returns 'ideal' glyph metrics, we need to pad to
+ // account for antialiasing.
+ for (uint32_t i = 0; i < aNumGlyphs; i++) {
+ if (aGlyphMetrics[i].mWidth > 0 && aGlyphMetrics[i].mHeight > 0) {
+ aGlyphMetrics[i].mWidth += 2.0f;
+ aGlyphMetrics[i].mXBearing -= 1.0f;
+ }
+ }
+}
+
+bool
+DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat)
+{
+ HRESULT hr;
+
+ ID2D1Device* device = Factory::GetD2D1Device();
+ if (!device) {
+ gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init(ID3D11Texture2D*, SurfaceFormat).";
+ return false;
+ }
+
+ hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() <<"[D2D1.1] 1Failed to create a DeviceContext, code: " << hexa(hr) << " format " << (int)aFormat;
+ return false;
+ }
+
+ RefPtr<IDXGISurface> dxgiSurface;
+ aTexture->QueryInterface(__uuidof(IDXGISurface),
+ (void**)((IDXGISurface**)getter_AddRefs(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**)getter_AddRefs(mBitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1.1] CreateBitmapFromDxgiSurface failure Code: " << hexa(hr) << " format " << (int)aFormat;
+ return false;
+ }
+
+ mFormat = aFormat;
+ D3D11_TEXTURE2D_DESC desc;
+ aTexture->GetDesc(&desc);
+ mSize.width = desc.Width;
+ mSize.height = desc.Height;
+
+ // This single solid color brush system is not very 'threadsafe', however,
+ // issueing multiple drawing commands simultaneously to a single drawtarget
+ // from multiple threads is unexpected since there's no way to guarantee
+ // ordering in that situation anyway.
+ hr = mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0), getter_AddRefs(mSolidColorBrush));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1.1] Failure creating solid color brush (I1).";
+ return false;
+ }
+
+ mDC->SetTarget(CurrentTarget());
+
+ mDC->BeginDraw();
+
+ CurrentLayer().mIsOpaque = aFormat == SurfaceFormat::B8G8R8X8;
+
+ return true;
+}
+
+bool
+DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
+{
+ HRESULT hr;
+
+ ID2D1Device* device = Factory::GetD2D1Device();
+ if (!device) {
+ gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init(IntSize, SurfaceFormat).";
+ return false;
+ }
+
+ hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() <<"[D2D1.1] 2Failed to create a DeviceContext, code: " << hexa(hr) << " format " << (int)aFormat;
+ return false;
+ }
+
+ if (mDC->GetMaximumBitmapSize() < UINT32(aSize.width) ||
+ mDC->GetMaximumBitmapSize() < UINT32(aSize.height)) {
+ // This is 'ok', so don't assert
+ gfxCriticalNote << "[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**)getter_AddRefs(mBitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1.1] 3CreateBitmap failure " << aSize << " Code: " << hexa(hr) << " format " << (int)aFormat;
+ return false;
+ }
+
+ mDC->SetTarget(CurrentTarget());
+
+ hr = mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0), getter_AddRefs(mSolidColorBrush));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1.1] Failure creating solid color brush (I2).";
+ return false;
+ }
+
+ mDC->BeginDraw();
+
+ CurrentLayer().mIsOpaque = aFormat == SurfaceFormat::B8G8R8X8;
+
+ 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;
+ }
+
+ RefPtr<ID2D1Factory> factory;
+ D2D1CreateFactoryFunc createD2DFactory;
+ HMODULE d2dModule = LoadLibraryW(L"d2d1.dll");
+ createD2DFactory = (D2D1CreateFactoryFunc)
+ GetProcAddress(d2dModule, "D2D1CreateFactory");
+
+ if (!createD2DFactory) {
+ gfxWarning() << "Failed to locate D2D1CreateFactory function.";
+ return nullptr;
+ }
+
+ D2D1_FACTORY_OPTIONS options;
+#ifdef _DEBUG
+ options.debugLevel = D2D1_DEBUG_LEVEL_WARNING;
+#else
+ options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
+#endif
+ //options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
+
+ HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
+ __uuidof(ID2D1Factory),
+ &options,
+ getter_AddRefs(factory));
+
+ if (FAILED(hr) || !factory) {
+ gfxCriticalNote << "Failed to create a D2D1 content device: " << hexa(hr);
+ return nullptr;
+ }
+
+ hr = factory->QueryInterface((ID2D1Factory1**)&mFactory);
+ if (FAILED(hr) || !mFactory) {
+ return nullptr;
+ }
+
+ ExtendInputEffectD2D1::Register(mFactory);
+ RadialGradientEffectD2D1::Register(mFactory);
+
+ return mFactory;
+}
+
+IDWriteFactory*
+DrawTargetD2D1::GetDWriteFactory()
+{
+ if (mDWriteFactory) {
+ return mDWriteFactory;
+ }
+
+ decltype(DWriteCreateFactory)* createDWriteFactory;
+ HMODULE dwriteModule = LoadLibraryW(L"dwrite.dll");
+ createDWriteFactory = (decltype(DWriteCreateFactory)*)
+ GetProcAddress(dwriteModule, "DWriteCreateFactory");
+
+ if (!createDWriteFactory) {
+ gfxWarning() << "Failed to locate DWriteCreateFactory function.";
+ return nullptr;
+ }
+
+ HRESULT hr = createDWriteFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
+ reinterpret_cast<IUnknown**>(&mDWriteFactory));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create DWrite Factory.";
+ }
+
+ return mDWriteFactory;
+}
+
+void
+DrawTargetD2D1::CleanupD2D()
+{
+ if (mFactory) {
+ RadialGradientEffectD2D1::Unregister(mFactory);
+ ExtendInputEffectD2D1::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());
+ }
+}
+
+bool
+DrawTargetD2D1::ShouldClipTemporarySurfaceDrawing(CompositionOp aOp,
+ const Pattern& aPattern,
+ bool aClipIsComplex)
+{
+ bool patternSupported = IsPatternSupportedByD2D(aPattern);
+ return patternSupported && !CurrentLayer().mIsOpaque && D2DSupportsCompositeMode(aOp) &&
+ IsOperatorBoundByMask(aOp) && aClipIsComplex;
+}
+
+void
+DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern)
+{
+ MarkChanged();
+
+ bool patternSupported = IsPatternSupportedByD2D(aPattern);
+
+ if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) {
+ // It's important to do this before FlushTransformToDC! As this will cause
+ // the transform to become dirty.
+ PushAllClips();
+
+ FlushTransformToDC();
+
+ if (aOp != CompositionOp::OP_OVER)
+ mDC->SetPrimitiveBlend(D2DPrimitiveBlendMode(aOp));
+
+ return;
+ }
+
+ HRESULT result = mDC->CreateCommandList(getter_AddRefs(mCommandList));
+ mDC->SetTarget(mCommandList);
+ mUsedCommandListsSincePurge++;
+
+ // This is where we should have a valid command list. If we don't, something is
+ // wrong, and it's likely an OOM.
+ if (!mCommandList) {
+ gfxDevCrash(LogReason::InvalidCommandList) << "Invalid D2D1.1 command list on creation " << mUsedCommandListsSincePurge << ", " << gfx::hexa(result);
+ }
+
+ D2D1_RECT_F rect;
+ bool isAligned;
+ bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+
+ if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
+ PushClipsToDC(mDC);
+ }
+
+ FlushTransformToDC();
+}
+
+void
+DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
+{
+ bool patternSupported = IsPatternSupportedByD2D(aPattern);
+
+ if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) {
+ if (aOp != CompositionOp::OP_OVER)
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
+ return;
+ }
+
+ D2D1_RECT_F rect;
+ bool isAligned;
+ bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+
+ if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
+ PopClipsFromDC(mDC);
+ }
+
+ mDC->SetTarget(CurrentTarget());
+ if (!mCommandList) {
+ gfxDevCrash(LogReason::InvalidCommandList) << "Invalid D21.1 command list on finalize";
+ return;
+ }
+ mCommandList->Close();
+
+ RefPtr<ID2D1CommandList> source = mCommandList;
+ mCommandList = nullptr;
+
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ if (patternSupported) {
+ if (D2DSupportsCompositeMode(aOp)) {
+ RefPtr<ID2D1Image> tmpImage;
+ if (clipIsComplex) {
+ PopAllClips();
+ if (!IsOperatorBoundByMask(aOp)) {
+ tmpImage = GetImageForLayerContent();
+ }
+ }
+ mDC->DrawImage(source, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
+
+ if (tmpImage) {
+ RefPtr<ID2D1ImageBrush> brush;
+ RefPtr<ID2D1Geometry> inverseGeom = GetInverseClippedGeometry();
+ mDC->CreateImageBrush(tmpImage, D2D1::ImageBrushProperties(D2D1::RectF(0, 0, mSize.width, mSize.height)),
+ getter_AddRefs(brush));
+
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
+ mDC->FillGeometry(inverseGeom, brush);
+ mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
+ }
+ return;
+ }
+
+ RefPtr<ID2D1Effect> blendEffect;
+ HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(blendEffect));
+
+ if (FAILED(hr) || !blendEffect) {
+ gfxWarning() << "Failed to create blend effect!";
+ return;
+ }
+
+ // We don't need to preserve the current content of this layer as the output
+ // of the blend effect should completely replace it.
+ RefPtr<ID2D1Image> tmpImage = GetImageForLayerContent(false);
+ if (!tmpImage) {
+ return;
+ }
+
+ blendEffect->SetInput(0, tmpImage);
+ blendEffect->SetInput(1, source);
+ blendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
+
+ mDC->DrawImage(blendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
+
+ // This may seem a little counter intuitive. If this is false, we go through the regular
+ // codepaths and set it to true. When this was true, GetImageForLayerContent will return
+ // a bitmap for the current command list and we will no longer have a complex blend
+ // with a list for tmpImage. Therefore we can set it to false again.
+ mDidComplexBlendWithListInList = !mDidComplexBlendWithListInList;
+
+ return;
+ }
+
+ const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern);
+ if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) {
+ // Draw nothing!
+ return;
+ }
+
+ if (!pat->mStops) {
+ // Draw nothing because of no color stops
+ return;
+ }
+
+ RefPtr<ID2D1Effect> radialGradientEffect;
+
+ HRESULT hr = mDC->CreateEffect(CLSID_RadialGradientEffect, getter_AddRefs(radialGradientEffect));
+ if (FAILED(hr) || !radialGradientEffect) {
+ gfxWarning() << "Failed to create radial gradient effect. Code: " << hexa(hr);
+ return;
+ }
+
+ 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, source);
+
+ 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 (!CurrentLayer().mPushedClips.size()) {
+ return false;
+ }
+
+ aIsPixelAligned = true;
+ aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height);
+ for (auto iter = CurrentLayer().mPushedClips.begin();iter != CurrentLayer().mPushedClips.end(); iter++) {
+ if (iter->mGeometry) {
+ return false;
+ }
+ aClipRect = IntersectRect(aClipRect, iter->mBounds);
+ if (!iter->mIsPixelAligned) {
+ aIsPixelAligned = false;
+ }
+ }
+ return true;
+}
+
+already_AddRefed<ID2D1Image>
+DrawTargetD2D1::GetImageForLayerContent(bool aShouldPreserveContent)
+{
+ PopAllClips();
+
+ if (!CurrentLayer().mCurrentList) {
+ RefPtr<ID2D1Bitmap> tmpBitmap;
+ HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap));
+ if (FAILED(hr)) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat;
+ // If it's a recreate target error, return and handle it elsewhere.
+ if (hr == D2DERR_RECREATE_TARGET) {
+ mDC->Flush();
+ return nullptr;
+ }
+ // For now, crash in other scenarios; this should happen because tmpBitmap is
+ // null and CopyFromBitmap call below dereferences it.
+ }
+ mDC->Flush();
+
+ tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
+ return tmpBitmap.forget();
+ } else {
+ RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
+ mDC->CreateCommandList(getter_AddRefs(CurrentLayer().mCurrentList));
+ mDC->SetTarget(CurrentTarget());
+ list->Close();
+
+ RefPtr<ID2D1Bitmap1> tmpBitmap;
+ if (mDidComplexBlendWithListInList) {
+ D2D1_BITMAP_PROPERTIES1 props =
+ D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET,
+ D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
+ D2D1_ALPHA_MODE_PREMULTIPLIED));
+ mDC->CreateBitmap(mBitmap->GetPixelSize(), nullptr, 0, &props, getter_AddRefs(tmpBitmap));
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mDC->SetTarget(tmpBitmap);
+ mDC->DrawImage(list, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
+ mDC->SetTarget(CurrentTarget());
+ }
+
+ DCCommandSink sink(mDC);
+
+ if (aShouldPreserveContent) {
+ list->Stream(&sink);
+ PushAllClips();
+ }
+
+ if (mDidComplexBlendWithListInList) {
+ return tmpBitmap.forget();
+ }
+
+ return list.forget();
+ }
+}
+
+already_AddRefed<ID2D1Geometry>
+DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
+{
+ if (mCurrentClippedGeometry) {
+ *aClipBounds = mCurrentClipBounds;
+ RefPtr<ID2D1Geometry> clippedGeometry(mCurrentClippedGeometry);
+ return clippedGeometry.forget();
+ }
+
+ MOZ_ASSERT(CurrentLayer().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 = CurrentLayer().mPushedClips.begin();
+
+ if (iter->mGeometry) {
+ pathGeom = GetTransformedGeometry(iter->mGeometry, iter->mTransform);
+ } else {
+ pathRect = iter->mBounds;
+ pathRectIsAxisAligned = iter->mIsPixelAligned;
+ }
+
+ iter++;
+ for (;iter != CurrentLayer().mPushedClips.end(); iter++) {
+ // Do nothing but add it to the current clip bounds.
+ if (!iter->mGeometry && 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->mGeometry) {
+ // See if pathRect needs to go into the path geometry.
+ if (!pathRectIsAxisAligned) {
+ pathGeom = ConvertRectToGeometry(pathRect);
+ } else {
+ pathGeom = GetTransformedGeometry(iter->mGeometry, iter->mTransform);
+ }
+ } else {
+ pathRect = IntersectRect(pathRect, iter->mBounds);
+ pathRectIsAxisAligned = false;
+ continue;
+ }
+ }
+
+ RefPtr<ID2D1PathGeometry> newGeom;
+ factory()->CreatePathGeometry(getter_AddRefs(newGeom));
+
+ RefPtr<ID2D1GeometrySink> currentSink;
+ newGeom->Open(getter_AddRefs(currentSink));
+
+ if (iter->mGeometry) {
+ pathGeom->CombineWithGeometry(iter->mGeometry, 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;
+ RefPtr<ID2D1Geometry> clippedGeometry(mCurrentClippedGeometry);
+ return clippedGeometry.forget();
+}
+
+already_AddRefed<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), getter_AddRefs(rectGeom));
+ factory()->CreatePathGeometry(getter_AddRefs(inverseGeom));
+ RefPtr<ID2D1GeometrySink> sink;
+ inverseGeom->Open(getter_AddRefs(sink));
+ rectGeom->CombineWithGeometry(geom, D2D1_COMBINE_MODE_EXCLUDE, D2D1::IdentityMatrix(), sink);
+ sink->Close();
+
+ return inverseGeom.forget();
+}
+
+void
+DrawTargetD2D1::PopAllClips()
+{
+ if (CurrentLayer().mClipsArePushed) {
+ PopClipsFromDC(mDC);
+
+ CurrentLayer().mClipsArePushed = false;
+ }
+}
+
+void
+DrawTargetD2D1::PushAllClips()
+{
+ if (!CurrentLayer().mClipsArePushed) {
+ PushClipsToDC(mDC);
+
+ CurrentLayer().mClipsArePushed = true;
+ }
+}
+
+void
+DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
+{
+ mDC->SetTransform(D2D1::IdentityMatrix());
+ mTransformDirty = true;
+
+ for (auto iter = CurrentLayer().mPushedClips.begin(); iter != CurrentLayer().mPushedClips.end(); iter++) {
+ if (iter->mGeometry) {
+ PushD2DLayer(aDC, iter->mGeometry, iter->mTransform, iter->mIsPixelAligned, aForceIgnoreAlpha, aMaxRect);
+ } else {
+ mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ }
+ }
+}
+
+void
+DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC)
+{
+ for (int i = CurrentLayer().mPushedClips.size() - 1; i >= 0; i--) {
+ if (CurrentLayer().mPushedClips[i].mGeometry) {
+ aDC->PopLayer();
+ } else {
+ aDC->PopAxisAlignedClip();
+ }
+ }
+}
+
+already_AddRefed<ID2D1Brush>
+DrawTargetD2D1::CreateTransparentBlackBrush()
+{
+ return GetSolidColorBrush(D2D1::ColorF(0, 0));
+}
+
+already_AddRefed<ID2D1SolidColorBrush>
+DrawTargetD2D1::GetSolidColorBrush(const D2D_COLOR_F& aColor)
+{
+ RefPtr<ID2D1SolidColorBrush> brush = mSolidColorBrush;
+ brush->SetColor(aColor);
+ return brush.forget();
+}
+
+already_AddRefed<ID2D1Brush>
+DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
+{
+ if (!IsPatternSupportedByD2D(aPattern)) {
+ return GetSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f));
+ }
+
+ if (aPattern.GetType() == PatternType::COLOR) {
+ Color color = static_cast<const ColorPattern*>(&aPattern)->mColor;
+ return GetSolidColorBrush(D2D1::ColorF(color.r, color.g, color.b, color.a * aAlpha));
+ }
+ 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) {
+ uint32_t stopCount = stops->mStopCollection->GetGradientStopCount();
+ vector<D2D1_GRADIENT_STOP> d2dStops(stopCount);
+ stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount);
+ d2dStops.back().color.a *= aAlpha;
+ return GetSolidColorBrush(d2dStops.back().color);
+ }
+
+ mDC->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin),
+ D2DPoint(pat->mEnd)),
+ D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
+ stops->mStopCollection,
+ getter_AddRefs(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,
+ getter_AddRefs(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;
+
+ MOZ_ASSERT(pat->mSurface->IsValid());
+
+ RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode, !pat->mSamplingRect.IsEmpty() ? &pat->mSamplingRect : nullptr);
+
+ if (pat->mSurface->GetFormat() == SurfaceFormat::A8) {
+ // See bug 1251431, at least FillOpacityMask does not appear to allow a source bitmapbrush
+ // with source format A8. This creates a BGRA surface with the same alpha values that
+ // the A8 surface has.
+ RefPtr<ID2D1Bitmap> bitmap;
+ image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+ if (bitmap) {
+ RefPtr<ID2D1Image> oldTarget;
+ RefPtr<ID2D1Bitmap1> tmpBitmap;
+ mDC->CreateBitmap(D2D1::SizeU(pat->mSurface->GetSize().width, pat->mSurface->GetSize().height), nullptr, 0,
+ D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)),
+ getter_AddRefs(tmpBitmap));
+ mDC->GetTarget(getter_AddRefs(oldTarget));
+ mDC->SetTarget(tmpBitmap);
+
+ RefPtr<ID2D1SolidColorBrush> brush;
+ mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(brush));
+ mDC->FillOpacityMask(bitmap, brush);
+ mDC->SetTarget(oldTarget);
+ image = tmpBitmap;
+ }
+ }
+
+ if (!image) {
+ return CreateTransparentBlackBrush();
+ }
+
+ if (pat->mSamplingRect.IsEmpty()) {
+ RefPtr<ID2D1Bitmap> bitmap;
+ image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+ if (bitmap) {
+ /**
+ * Create the brush with the proper repeat modes.
+ */
+ RefPtr<ID2D1BitmapBrush> bitmapBrush;
+ D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS);
+ D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS);
+
+ mDC->CreateBitmapBrush(bitmap,
+ D2D1::BitmapBrushProperties(xRepeat, yRepeat,
+ D2DFilter(pat->mSamplingFilter)),
+ D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
+ getter_AddRefs(bitmapBrush));
+ if (!bitmapBrush) {
+ gfxWarning() << "Couldn't create bitmap brush!";
+ return CreateTransparentBlackBrush();
+ }
+ return bitmapBrush.forget();
+ }
+ }
+
+ RefPtr<ID2D1ImageBrush> imageBrush;
+ if (pat->mSamplingRect.IsEmpty()) {
+ samplingBounds = D2D1::RectF(0, 0,
+ Float(pat->mSurface->GetSize().width),
+ Float(pat->mSurface->GetSize().height));
+ } else if (pat->mSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
+ samplingBounds = D2DRect(pat->mSamplingRect);
+ mat.PreTranslate(pat->mSamplingRect.x, pat->mSamplingRect.y);
+ } else {
+ // We will do a partial upload of the sampling restricted area from GetImageForSurface.
+ samplingBounds = D2D1::RectF(0, 0, pat->mSamplingRect.width, pat->mSamplingRect.height);
+ }
+
+ D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS);
+ D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS);
+
+ mDC->CreateImageBrush(image,
+ D2D1::ImageBrushProperties(samplingBounds,
+ xRepeat,
+ yRepeat,
+ D2DInterpolationMode(pat->mSamplingFilter)),
+ D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
+ getter_AddRefs(imageBrush));
+
+ if (!imageBrush) {
+ gfxWarning() << "Couldn't create image brush!";
+ return CreateTransparentBlackBrush();
+ }
+
+ return imageBrush.forget();
+ }
+
+ gfxWarning() << "Invalid pattern type detected.";
+ return CreateTransparentBlackBrush();
+}
+
+already_AddRefed<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();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetD2D1::OptimizeSourceSurface(SourceSurface* aSurface) const
+{
+ if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
+ RefPtr<SourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
+
+ RefPtr<ID2D1Bitmap1> bitmap;
+ {
+ DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
+ if (MOZ2D_WARN_IF(!map.IsMapped())) {
+ return nullptr;
+ }
+
+ HRESULT hr = mDC->CreateBitmap(D2DIntSize(data->GetSize()), map.GetData(), map.GetStride(),
+ D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(data->GetFormat())),
+ getter_AddRefs(bitmap));
+
+ if (FAILED(hr)) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(data->GetSize()))) << "[D2D1.1] 4CreateBitmap failure " << data->GetSize() << " Code: " << hexa(hr) << " format " << (int)data->GetFormat();
+ }
+ }
+
+ if (!bitmap) {
+ return data.forget();
+ }
+
+ return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), mDC, data->GetFormat(), data->GetSize());
+}
+
+void
+DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
+ bool aPixelAligned, bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
+{
+ D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
+
+ if (CurrentLayer().mIsOpaque || aForceIgnoreAlpha) {
+ options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
+ }
+
+ D2D1_ANTIALIAS_MODE antialias =
+ aPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
+
+ mDC->PushLayer(D2D1::LayerParameters1(aMaxRect, aGeometry, antialias, aTransform,
+ 1.0, nullptr, options), nullptr);
+}
+
+}
+}
diff --git a/gfx/2d/DrawTargetD2D1.h b/gfx/2d/DrawTargetD2D1.h
new file mode 100644
index 000000000..624fb58cc
--- /dev/null
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -0,0 +1,297 @@
+/* -*- 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;
+
+const int32_t kLayerCacheSize1 = 5;
+
+class DrawTargetD2D1 : public DrawTarget
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetD2D1, override)
+ DrawTargetD2D1();
+ virtual ~DrawTargetD2D1();
+
+ virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; }
+ virtual BackendType GetBackendType() const override { return BackendType::DIRECT2D1_1; }
+ virtual already_AddRefed<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,
+ 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;
+ 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 PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override;
+
+ virtual void PopClip() override;
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) override;
+ virtual void PopLayer() override;
+
+ virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const override;
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
+
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override { return nullptr; }
+
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
+
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
+
+ virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
+
+ virtual bool SupportsRegionClipping() const override { return false; }
+ virtual bool IsCurrentGroupOpaque() override { return CurrentLayer().mIsOpaque; }
+
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override { return nullptr; }
+
+ virtual void DetachAllSnapshots() override { MarkChanged(); }
+
+ virtual void GetGlyphRasterizationMetrics(ScaledFont *aScaledFont, const uint16_t* aGlyphIndices,
+ uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics) override;
+
+ bool Init(const IntSize &aSize, SurfaceFormat aFormat);
+ bool Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat);
+ uint32_t GetByteSize() const;
+
+ // This function will get an image for a surface, it may adjust the source
+ // transform for any transformation of the resulting image relative to the
+ // oritingal SourceSurface.
+ already_AddRefed<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
+ ExtendMode aExtendMode, const IntRect* aSourceRect = nullptr);
+
+ already_AddRefed<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();
+ bool ShouldClipTemporarySurfaceDrawing(CompositionOp aOp, const Pattern& aPattern, bool aClipIsComplex);
+ 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);
+
+ // Must be called with all clips popped and an identity matrix set.
+ already_AddRefed<ID2D1Image> GetImageForLayerContent(bool aShouldPreserveContent = true);
+
+ ID2D1Image* CurrentTarget()
+ {
+ if (CurrentLayer().mCurrentList) {
+ return CurrentLayer().mCurrentList;
+ }
+ return mBitmap;
+ }
+
+ // 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 and
+ // only for clips applied to the -current layer-.
+ already_AddRefed<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
+
+ already_AddRefed<ID2D1Geometry> GetInverseClippedGeometry();
+
+ // This gives the device space clip rect applied to the -current layer-.
+ bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
+
+ void PopAllClips();
+ void PushAllClips();
+ void PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha = false, const D2D1_RECT_F& aMaxRect = D2D1::InfiniteRect());
+ void PopClipsFromDC(ID2D1DeviceContext *aDC);
+
+ already_AddRefed<ID2D1Brush> CreateTransparentBlackBrush();
+ already_AddRefed<ID2D1SolidColorBrush> GetSolidColorBrush(const D2D_COLOR_F& aColor);
+ already_AddRefed<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
+
+ void PushClipGeometry(ID2D1Geometry* aGeometry, const D2D1_MATRIX_3X2_F& aTransform, bool aPixelAligned = false);
+
+ void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
+ bool aPixelAligned = false, bool aForceIgnoreAlpha = false,
+ const D2D1_RECT_F& aLayerRect = D2D1::InfiniteRect());
+
+ 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<ID2D1CommandList> mCommandList;
+
+ RefPtr<ID2D1SolidColorBrush> mSolidColorBrush;
+
+ // We store this to prevent excessive SetTextRenderingParams calls.
+ RefPtr<IDWriteRenderingParams> mTextRenderingParams;
+
+ // List of pushed clips.
+ struct PushedClip
+ {
+ D2D1_RECT_F mBounds;
+ // If mGeometry is non-null, the mTransform member will be used.
+ D2D1_MATRIX_3X2_F mTransform;
+ RefPtr<ID2D1Geometry> mGeometry;
+ // Indicates if mBounds, and when non-null, mGeometry with mTransform
+ // applied, are pixel-aligned.
+ bool mIsPixelAligned;
+ };
+
+ // List of pushed layers.
+ struct PushedLayer
+ {
+ PushedLayer() : mClipsArePushed(false), mIsOpaque(false), mOldPermitSubpixelAA(false) {}
+
+ std::vector<PushedClip> mPushedClips;
+ RefPtr<ID2D1CommandList> mCurrentList;
+ // True if the current clip stack is pushed to the CurrentTarget().
+ bool mClipsArePushed;
+ bool mIsOpaque;
+ bool mOldPermitSubpixelAA;
+ };
+ std::vector<PushedLayer> mPushedLayers;
+ PushedLayer& CurrentLayer()
+ {
+ return mPushedLayers.back();
+ }
+
+ // 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;
+
+ uint32_t mUsedCommandListsSincePurge;
+ // When a BlendEffect has been drawn to a command list, and that command list is
+ // subsequently used -again- as an input to a blend effect for a command list,
+ // this causes an infinite recursion inside D2D as it tries to resolve the bounds.
+ // If we resolve the current command list before this happens
+ // we can avoid the subsequent hang. (See bug 1293586)
+ bool mDidComplexBlendWithListInList;
+
+ static ID2D1Factory1 *mFactory;
+ static IDWriteFactory *mDWriteFactory;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */
diff --git a/gfx/2d/DrawTargetDual.cpp b/gfx/2d/DrawTargetDual.cpp
new file mode 100644
index 000000000..87714f123
--- /dev/null
+++ b/gfx/2d/DrawTargetDual.cpp
@@ -0,0 +1,223 @@
+/* -*- 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 "DrawTargetDual.h"
+#include "Tools.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DualSurface
+{
+public:
+ inline explicit DualSurface(SourceSurface *aSurface)
+ {
+ if (!aSurface) {
+ mA = mB = nullptr;
+ return;
+ }
+
+ if (aSurface->GetType() != SurfaceType::DUAL_DT) {
+ mA = mB = aSurface;
+ return;
+ }
+
+ SourceSurfaceDual *ssDual =
+ static_cast<SourceSurfaceDual*>(aSurface);
+ mA = ssDual->mA;
+ mB = ssDual->mB;
+ }
+
+ SourceSurface *mA;
+ SourceSurface *mB;
+};
+
+/* This only needs to split patterns up for SurfacePatterns. Only in that
+ * case can we be dealing with a 'dual' source (SourceSurfaceDual) and do
+ * we need to pass separate patterns into our destination DrawTargets.
+ */
+class DualPattern
+{
+public:
+ inline explicit DualPattern(const Pattern &aPattern)
+ : mPatternsInitialized(false)
+ {
+ if (aPattern.GetType() != PatternType::SURFACE) {
+ mA = mB = &aPattern;
+ return;
+ }
+
+ const SurfacePattern *surfPat =
+ static_cast<const SurfacePattern*>(&aPattern);
+
+ if (surfPat->mSurface->GetType() != SurfaceType::DUAL_DT) {
+ mA = mB = &aPattern;
+ return;
+ }
+
+ const SourceSurfaceDual *ssDual =
+ static_cast<const SourceSurfaceDual*>(surfPat->mSurface.get());
+ mA = new (mSurfPatA.addr()) SurfacePattern(ssDual->mA, surfPat->mExtendMode,
+ surfPat->mMatrix,
+ surfPat->mSamplingFilter);
+ mB = new (mSurfPatB.addr()) SurfacePattern(ssDual->mB, surfPat->mExtendMode,
+ surfPat->mMatrix,
+ surfPat->mSamplingFilter);
+ mPatternsInitialized = true;
+ }
+
+ inline ~DualPattern()
+ {
+ if (mPatternsInitialized) {
+ mA->~Pattern();
+ mB->~Pattern();
+ }
+ }
+
+ ClassStorage<SurfacePattern> mSurfPatA;
+ ClassStorage<SurfacePattern> mSurfPatB;
+
+ const Pattern *mA;
+ const Pattern *mB;
+
+ bool mPatternsInitialized;
+};
+
+void
+DrawTargetDual::DetachAllSnapshots()
+{
+ mA->DetachAllSnapshots();
+ mB->DetachAllSnapshots();
+}
+
+void
+DrawTargetDual::DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions)
+{
+ DualSurface surface(aSurface);
+ mA->DrawSurface(surface.mA, aDest, aSource, aSurfOptions, aOptions);
+ mB->DrawSurface(surface.mB, aDest, aSource, aSurfOptions, aOptions);
+}
+
+void
+DrawTargetDual::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest,
+ const Color &aColor, const Point &aOffset,
+ Float aSigma, CompositionOp aOp)
+{
+ DualSurface surface(aSurface);
+ mA->DrawSurfaceWithShadow(surface.mA, aDest, aColor, aOffset, aSigma, aOp);
+ mB->DrawSurfaceWithShadow(surface.mB, aDest, aColor, aOffset, aSigma, aOp);
+}
+
+void
+DrawTargetDual::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ DualPattern source(aSource);
+ DualSurface mask(aMask);
+ mA->MaskSurface(*source.mA, mask.mA, aOffset, aOptions);
+ mB->MaskSurface(*source.mB, mask.mB, aOffset, aOptions);
+}
+
+void
+DrawTargetDual::CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect,
+ const IntPoint &aDestination)
+{
+ DualSurface surface(aSurface);
+ mA->CopySurface(surface.mA, aSourceRect, aDestination);
+ mB->CopySurface(surface.mB, aSourceRect, aDestination);
+}
+
+void
+DrawTargetDual::FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions)
+{
+ DualPattern pattern(aPattern);
+ mA->FillRect(aRect, *pattern.mA, aOptions);
+ mB->FillRect(aRect, *pattern.mB, aOptions);
+}
+
+void
+DrawTargetDual::StrokeRect(const Rect &aRect, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
+{
+ DualPattern pattern(aPattern);
+ mA->StrokeRect(aRect, *pattern.mA, aStrokeOptions, aOptions);
+ mB->StrokeRect(aRect, *pattern.mB, aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetDual::StrokeLine(const Point &aStart, const Point &aEnd, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
+{
+ DualPattern pattern(aPattern);
+ mA->StrokeLine(aStart, aEnd, *pattern.mA, aStrokeOptions, aOptions);
+ mB->StrokeLine(aStart, aEnd, *pattern.mB, aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetDual::Stroke(const Path *aPath, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
+{
+ DualPattern pattern(aPattern);
+ mA->Stroke(aPath, *pattern.mA, aStrokeOptions, aOptions);
+ mB->Stroke(aPath, *pattern.mB, aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetDual::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions)
+{
+ DualPattern pattern(aPattern);
+ mA->Fill(aPath, *pattern.mA, aOptions);
+ mB->Fill(aPath, *pattern.mB, aOptions);
+}
+
+void
+DrawTargetDual::FillGlyphs(ScaledFont *aScaledFont, const GlyphBuffer &aBuffer,
+ const Pattern &aPattern, const DrawOptions &aOptions,
+ const GlyphRenderingOptions *aRenderingOptions)
+{
+ DualPattern pattern(aPattern);
+ mA->FillGlyphs(aScaledFont, aBuffer, *pattern.mA, aOptions, aRenderingOptions);
+ mB->FillGlyphs(aScaledFont, aBuffer, *pattern.mB, aOptions, aRenderingOptions);
+}
+
+void
+DrawTargetDual::Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions)
+{
+ DualPattern source(aSource);
+ DualPattern mask(aMask);
+ mA->Mask(*source.mA, *mask.mA, aOptions);
+ mB->Mask(*source.mB, *mask.mB, aOptions);
+}
+
+void
+DrawTargetDual::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+ const Matrix& aMaskTransform, const IntRect& aBounds,
+ bool aCopyBackground)
+{
+ DualSurface mask(aMask);
+ mA->PushLayer(aOpaque, aOpacity, mask.mA, aMaskTransform, aBounds, aCopyBackground);
+ mB->PushLayer(aOpaque, aOpacity, mask.mB, aMaskTransform, aBounds, aCopyBackground);
+}
+
+already_AddRefed<DrawTarget>
+DrawTargetDual::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+ 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 MakeAndAddRef<DrawTargetDual>(dtA, dtB);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/DrawTargetDual.h b/gfx/2d/DrawTargetDual.h
new file mode 100644
index 000000000..190346e44
--- /dev/null
+++ b/gfx/2d/DrawTargetDual.h
@@ -0,0 +1,178 @@
+/* -*- 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_DRAWTARGETDUAL_H_
+#define MOZILLA_GFX_DRAWTARGETDUAL_H_
+
+#include <vector>
+#include <sstream>
+
+#include "SourceSurfaceDual.h"
+
+#include "2D.h"
+#include "Filters.h"
+
+namespace mozilla {
+namespace gfx {
+
+#define FORWARD_FUNCTION(funcName) \
+ virtual void funcName() override { mA->funcName(); mB->funcName(); }
+#define FORWARD_FUNCTION1(funcName, var1Type, 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
+ * dual DrawTarget is used as the source for any surface data. In this case
+ * the snapshot of the first source DrawTarget is used as a source for the call
+ * to the first destination DrawTarget (mA) and the snapshot of the second
+ * source DrawTarget is used at the source for the second destination
+ * DrawTarget (mB). This class facilitates black-background/white-background
+ * drawing for per-component alpha extraction for backends which do not support
+ * native component alpha.
+ */
+class DrawTargetDual : public DrawTarget
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetDual, override)
+ DrawTargetDual(DrawTarget *aA, DrawTarget *aB)
+ : mA(aA)
+ , mB(aB)
+ {
+ mFormat = aA->GetFormat();
+ }
+
+ virtual DrawTargetType GetType() const override { return mA->GetType(); }
+ virtual BackendType GetBackendType() const override { return mA->GetBackendType(); }
+ virtual already_AddRefed<SourceSurface> Snapshot() override {
+ return MakeAndAddRef<SourceSurfaceDual>(mA, mB);
+ }
+ virtual IntSize GetSize() override { return mA->GetSize(); }
+
+ virtual void DetachAllSnapshots() override;
+
+ FORWARD_FUNCTION(Flush)
+ FORWARD_FUNCTION1(PushClip, const Path *, aPath)
+ FORWARD_FUNCTION1(PushClipRect, const Rect &, aRect)
+ FORWARD_FUNCTION(PopClip)
+ FORWARD_FUNCTION(PopLayer)
+ FORWARD_FUNCTION1(ClearRect, const Rect &, aRect)
+
+ 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) 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()) override;
+
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest,
+ const Color &aColor, const Point &aOffset,
+ Float aSigma, CompositionOp aOp) 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) override;
+
+ virtual void StrokeRect(const Rect &aRect, const Pattern &aPattern,
+ 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) override;
+
+ virtual void Stroke(const Path *aPath, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override;
+
+ 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) override;
+
+ virtual void Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions) override;
+
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) override;
+
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const override
+ {
+ return mA->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
+ }
+
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override
+ {
+ return mA->OptimizeSourceSurface(aSurface);
+ }
+
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override
+ {
+ return mA->CreateSourceSurfaceFromNativeSurface(aSurface);
+ }
+
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override
+ {
+ return mA->CreatePathBuilder(aFillRule);
+ }
+
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override
+ {
+ return mA->CreateGradientStops(aStops, aNumStops, aExtendMode);
+ }
+
+ virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override
+ {
+ return mA->CreateFilter(aType);
+ }
+
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override
+ {
+ return nullptr;
+ }
+
+ virtual bool IsDualDrawTarget() const override
+ {
+ return true;
+ }
+
+ virtual bool IsCurrentGroupOpaque() override { return mA->IsCurrentGroupOpaque(); }
+
+private:
+ RefPtr<DrawTarget> mA;
+ RefPtr<DrawTarget> mB;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_DRAWTARGETDUAL_H_ */
diff --git a/gfx/2d/DrawTargetRecording.cpp b/gfx/2d/DrawTargetRecording.cpp
new file mode 100644
index 000000000..b8eda90fe
--- /dev/null
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -0,0 +1,737 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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 "DrawTargetRecording.h"
+#include "PathRecording.h"
+#include <stdio.h>
+
+#include "Logging.h"
+#include "Tools.h"
+#include "Filters.h"
+#include "mozilla/UniquePtr.h"
+#include "RecordingTypes.h"
+
+namespace mozilla {
+namespace gfx {
+
+struct RecordingSourceSurfaceUserData
+{
+ void *refPtr;
+ RefPtr<DrawEventRecorderPrivate> recorder;
+};
+
+void RecordingSourceSurfaceUserDataFunc(void *aUserData)
+{
+ RecordingSourceSurfaceUserData *userData =
+ static_cast<RecordingSourceSurfaceUserData*>(aUserData);
+
+ userData->recorder->RemoveStoredObject(userData->refPtr);
+ userData->recorder->RecordEvent(
+ RecordedSourceSurfaceDestruction(userData->refPtr));
+
+ delete userData;
+}
+
+static void
+StoreSourceSurface(DrawEventRecorderPrivate *aRecorder, SourceSurface *aSurface,
+ DataSourceSurface *aDataSurf, const char *reason)
+{
+ if (!aDataSurf) {
+ gfxWarning() << "Recording failed to record SourceSurface for " << reason;
+ // Insert a bogus source surface.
+ int32_t stride = aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat());
+ UniquePtr<uint8_t[]> sourceData(new uint8_t[stride * aSurface->GetSize().height]());
+ aRecorder->RecordEvent(
+ RecordedSourceSurfaceCreation(aSurface, sourceData.get(), stride,
+ aSurface->GetSize(), aSurface->GetFormat()));
+ } else {
+ DataSourceSurface::ScopedMap map(aDataSurf, DataSourceSurface::READ);
+ aRecorder->RecordEvent(
+ RecordedSourceSurfaceCreation(aSurface, map.GetData(), map.GetStride(),
+ aDataSurf->GetSize(), aDataSurf->GetFormat()));
+ }
+}
+
+static void
+EnsureSurfaceStored(DrawEventRecorderPrivate *aRecorder, SourceSurface *aSurface,
+ const char *reason)
+{
+ if (aRecorder->HasStoredObject(aSurface)) {
+ return;
+ }
+
+ RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
+ StoreSourceSurface(aRecorder, aSurface, dataSurf, reason);
+ aRecorder->AddStoredObject(aSurface);
+
+ RecordingSourceSurfaceUserData *userData = new RecordingSourceSurfaceUserData;
+ userData->refPtr = aSurface;
+ userData->recorder = aRecorder;
+ aSurface->AddUserData(reinterpret_cast<UserDataKey*>(aRecorder),
+ userData, &RecordingSourceSurfaceUserDataFunc);
+ return;
+}
+
+class SourceSurfaceRecording : public SourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceRecording)
+ SourceSurfaceRecording(SourceSurface *aFinalSurface, DrawEventRecorderPrivate *aRecorder)
+ : mFinalSurface(aFinalSurface), mRecorder(aRecorder)
+ {
+ mRecorder->AddStoredObject(this);
+ }
+
+ ~SourceSurfaceRecording()
+ {
+ mRecorder->RemoveStoredObject(this);
+ mRecorder->RecordEvent(RecordedSourceSurfaceDestruction(this));
+ }
+
+ virtual SurfaceType GetType() const { return SurfaceType::RECORDING; }
+ virtual IntSize GetSize() const { return mFinalSurface->GetSize(); }
+ virtual SurfaceFormat GetFormat() const { return mFinalSurface->GetFormat(); }
+ virtual already_AddRefed<DataSourceSurface> GetDataSurface() { return mFinalSurface->GetDataSurface(); }
+
+ RefPtr<SourceSurface> mFinalSurface;
+ RefPtr<DrawEventRecorderPrivate> mRecorder;
+};
+
+class GradientStopsRecording : public GradientStops
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsRecording)
+ GradientStopsRecording(GradientStops *aFinalGradientStops, DrawEventRecorderPrivate *aRecorder)
+ : mFinalGradientStops(aFinalGradientStops), mRecorder(aRecorder)
+ {
+ mRecorder->AddStoredObject(this);
+ }
+
+ ~GradientStopsRecording()
+ {
+ mRecorder->RemoveStoredObject(this);
+ mRecorder->RecordEvent(RecordedGradientStopsDestruction(this));
+ }
+
+ virtual BackendType GetBackendType() const { return BackendType::RECORDING; }
+
+ RefPtr<GradientStops> mFinalGradientStops;
+ RefPtr<DrawEventRecorderPrivate> mRecorder;
+};
+
+static SourceSurface *
+GetSourceSurface(SourceSurface *aSurface)
+{
+ if (aSurface->GetType() != SurfaceType::RECORDING) {
+ return aSurface;
+ }
+
+ return static_cast<SourceSurfaceRecording*>(aSurface)->mFinalSurface;
+}
+
+static GradientStops *
+GetGradientStops(GradientStops *aStops)
+{
+ 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)
+ {
+ mRecorder->AddStoredObject(this);
+ }
+
+ ~FilterNodeRecording()
+ {
+ mRecorder->RemoveStoredObject(this);
+ mRecorder->RecordEvent(RecordedFilterNodeDestruction(this));
+ }
+
+ 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;
+ }
+
+ virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override
+ {
+ EnsureSurfaceStored(mRecorder, aSurface, "SetInput");
+
+ mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aSurface));
+ mFinalFilterNode->SetInput(aIndex, GetSourceSurface(aSurface));
+ }
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override
+ {
+ MOZ_ASSERT(mRecorder->HasStoredObject(aFilter));
+
+ mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aFilter));
+ mFinalFilterNode->SetInput(aIndex, GetFilterNode(aFilter));
+ }
+
+
+#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 Matrix&, MATRIX);
+ 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;
+};
+
+struct AdjustedPattern
+{
+ explicit AdjustedPattern(const Pattern &aPattern)
+ : mPattern(nullptr)
+ {
+ mOrigPattern = const_cast<Pattern*>(&aPattern);
+ }
+
+ ~AdjustedPattern() {
+ if (mPattern) {
+ mPattern->~Pattern();
+ }
+ }
+
+ operator Pattern*()
+ {
+ switch(mOrigPattern->GetType()) {
+ case PatternType::COLOR:
+ return mOrigPattern;
+ case PatternType::SURFACE:
+ {
+ SurfacePattern *surfPat = static_cast<SurfacePattern*>(mOrigPattern);
+ mPattern =
+ new (mSurfPat) SurfacePattern(GetSourceSurface(surfPat->mSurface),
+ surfPat->mExtendMode, surfPat->mMatrix,
+ surfPat->mSamplingFilter,
+ surfPat->mSamplingRect);
+ return mPattern;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ {
+ LinearGradientPattern *linGradPat = static_cast<LinearGradientPattern*>(mOrigPattern);
+ mPattern =
+ new (mLinGradPat) LinearGradientPattern(linGradPat->mBegin, linGradPat->mEnd,
+ GetGradientStops(linGradPat->mStops),
+ linGradPat->mMatrix);
+ return mPattern;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ RadialGradientPattern *radGradPat = static_cast<RadialGradientPattern*>(mOrigPattern);
+ mPattern =
+ new (mRadGradPat) RadialGradientPattern(radGradPat->mCenter1, radGradPat->mCenter2,
+ radGradPat->mRadius1, radGradPat->mRadius2,
+ GetGradientStops(radGradPat->mStops),
+ radGradPat->mMatrix);
+ return mPattern;
+ }
+ default:
+ return new (mColPat) ColorPattern(Color());
+ }
+
+ return mPattern;
+ }
+
+ union {
+ char mColPat[sizeof(ColorPattern)];
+ char mLinGradPat[sizeof(LinearGradientPattern)];
+ char mRadGradPat[sizeof(RadialGradientPattern)];
+ char mSurfPat[sizeof(SurfacePattern)];
+ };
+
+ Pattern *mOrigPattern;
+ Pattern *mPattern;
+};
+
+DrawTargetRecording::DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT, bool aHasData)
+ : mRecorder(static_cast<DrawEventRecorderPrivate*>(aRecorder))
+ , mFinalDT(aDT)
+{
+ RefPtr<SourceSurface> snapshot = aHasData ? mFinalDT->Snapshot() : nullptr;
+ mRecorder->RecordEvent(RecordedDrawTargetCreation(this,
+ mFinalDT->GetBackendType(),
+ mFinalDT->GetSize(),
+ mFinalDT->GetFormat(),
+ aHasData, snapshot));
+ mFormat = mFinalDT->GetFormat();
+}
+
+DrawTargetRecording::DrawTargetRecording(const DrawTargetRecording *aDT,
+ DrawTarget *aSimilarDT)
+ : mRecorder(aDT->mRecorder)
+ , mFinalDT(aSimilarDT)
+{
+ mRecorder->RecordEvent(RecordedCreateSimilarDrawTarget(this,
+ mFinalDT->GetSize(),
+ mFinalDT->GetFormat()));
+ mFormat = mFinalDT->GetFormat();
+}
+
+DrawTargetRecording::~DrawTargetRecording()
+{
+ mRecorder->RecordEvent(RecordedDrawTargetDestruction(this));
+}
+
+void
+DrawTargetRecording::FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ EnsurePatternDependenciesStored(aPattern);
+
+ mRecorder->RecordEvent(RecordedFillRect(this, aRect, aPattern, aOptions));
+ mFinalDT->FillRect(aRect, *AdjustedPattern(aPattern), aOptions);
+}
+
+void
+DrawTargetRecording::StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ EnsurePatternDependenciesStored(aPattern);
+
+ mRecorder->RecordEvent(RecordedStrokeRect(this, aRect, aPattern, aStrokeOptions, aOptions));
+ mFinalDT->StrokeRect(aRect, *AdjustedPattern(aPattern), aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetRecording::StrokeLine(const Point &aBegin,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ EnsurePatternDependenciesStored(aPattern);
+
+ mRecorder->RecordEvent(RecordedStrokeLine(this, aBegin, aEnd, aPattern, aStrokeOptions, aOptions));
+ mFinalDT->StrokeLine(aBegin, aEnd, *AdjustedPattern(aPattern), aStrokeOptions, aOptions);
+}
+
+void
+DrawTargetRecording::Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
+ EnsurePatternDependenciesStored(aPattern);
+
+ mRecorder->RecordEvent(RecordedFill(this, pathRecording, aPattern, aOptions));
+ mFinalDT->Fill(pathRecording->mPath, *AdjustedPattern(aPattern), aOptions);
+}
+
+struct RecordingFontUserData
+{
+ void *refPtr;
+ RefPtr<DrawEventRecorderPrivate> recorder;
+};
+
+void RecordingFontUserDataDestroyFunc(void *aUserData)
+{
+ RecordingFontUserData *userData =
+ static_cast<RecordingFontUserData*>(aUserData);
+
+ userData->recorder->RecordEvent(RecordedScaledFontDestruction(userData->refPtr));
+
+ delete userData;
+}
+
+void
+DrawTargetRecording::FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions,
+ const GlyphRenderingOptions *aRenderingOptions)
+{
+ EnsurePatternDependenciesStored(aPattern);
+
+ if (!aFont->GetUserData(reinterpret_cast<UserDataKey*>(mRecorder.get()))) {
+ RecordedFontData fontData(aFont);
+ RecordedFontDetails fontDetails;
+ if (fontData.GetFontDetails(fontDetails)) {
+ // Try to serialise the whole font, just in case this is a web font that
+ // is not present on the system.
+ if (!mRecorder->HasStoredFontData(fontDetails.fontDataKey)) {
+ mRecorder->RecordEvent(fontData);
+ mRecorder->AddStoredFontData(fontDetails.fontDataKey);
+ }
+ mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, fontDetails));
+ } else {
+ // If that fails, record just the font description and try to load it from
+ // the system on the other side.
+ RecordedFontDescriptor fontDesc(aFont);
+ if (fontDesc.IsValid()) {
+ mRecorder->RecordEvent(fontDesc);
+ } else {
+ gfxWarning() << "DrawTargetRecording::FillGlyphs failed to serialise ScaledFont";
+ }
+ }
+ RecordingFontUserData *userData = new RecordingFontUserData;
+ userData->refPtr = aFont;
+ userData->recorder = mRecorder;
+ aFont->AddUserData(reinterpret_cast<UserDataKey*>(mRecorder.get()), userData,
+ &RecordingFontUserDataDestroyFunc);
+ }
+
+ mRecorder->RecordEvent(RecordedFillGlyphs(this, aFont, aPattern, aOptions, aBuffer.mGlyphs, aBuffer.mNumGlyphs));
+ mFinalDT->FillGlyphs(aFont, aBuffer, *AdjustedPattern(aPattern), aOptions, aRenderingOptions);
+}
+
+void
+DrawTargetRecording::Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions)
+{
+ EnsurePatternDependenciesStored(aSource);
+ EnsurePatternDependenciesStored(aMask);
+
+ mRecorder->RecordEvent(RecordedMask(this, aSource, aMask, aOptions));
+ mFinalDT->Mask(*AdjustedPattern(aSource), *AdjustedPattern(aMask), aOptions);
+}
+
+void
+DrawTargetRecording::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ EnsurePatternDependenciesStored(aSource);
+ EnsureSurfaceStored(mRecorder, aMask, "MaskSurface");
+
+ mRecorder->RecordEvent(RecordedMaskSurface(this, aSource, aMask, aOffset, aOptions));
+ mFinalDT->MaskSurface(*AdjustedPattern(aSource), GetSourceSurface(aMask), aOffset, aOptions);
+}
+
+void
+DrawTargetRecording::Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
+ EnsurePatternDependenciesStored(aPattern);
+
+ mRecorder->RecordEvent(RecordedStroke(this, pathRecording, aPattern, aStrokeOptions, aOptions));
+ mFinalDT->Stroke(pathRecording->mPath, *AdjustedPattern(aPattern), aStrokeOptions, aOptions);
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetRecording::Snapshot()
+{
+ RefPtr<SourceSurface> surf = mFinalDT->Snapshot();
+
+ RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
+
+ mRecorder->RecordEvent(RecordedSnapshot(retSurf, this));
+
+ return retSurf.forget();
+}
+
+void
+DrawTargetRecording::DetachAllSnapshots()
+{
+ mFinalDT->DetachAllSnapshots();
+}
+
+void
+DrawTargetRecording::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions)
+{
+ EnsureSurfaceStored(mRecorder, aSurface, "DrawSurface");
+
+ mRecorder->RecordEvent(RecordedDrawSurface(this, aSurface, aDest, aSource, aSurfOptions, aOptions));
+ mFinalDT->DrawSurface(GetSourceSurface(aSurface), aDest, aSource, aSurfOptions, aOptions);
+}
+
+void
+DrawTargetRecording::DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOp)
+{
+ EnsureSurfaceStored(mRecorder, aSurface, "DrawSurfaceWithShadow");
+
+ mRecorder->RecordEvent(RecordedDrawSurfaceWithShadow(this, aSurface, aDest, aColor, aOffset, aSigma, aOp));
+ mFinalDT->DrawSurfaceWithShadow(GetSourceSurface(aSurface), aDest, aColor, aOffset, aSigma, aOp);
+}
+
+void
+DrawTargetRecording::DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions)
+{
+ MOZ_ASSERT(mRecorder->HasStoredObject(aNode));
+
+ mRecorder->RecordEvent(RecordedDrawFilter(this, aNode, aSourceRect, aDestPoint, aOptions));
+ mFinalDT->DrawFilter(FilterNodeRecording::GetFilterNode(aNode), aSourceRect, aDestPoint, aOptions);
+}
+
+already_AddRefed<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));
+ mFinalDT->ClearRect(aRect);
+}
+
+void
+DrawTargetRecording::CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination)
+{
+ EnsureSurfaceStored(mRecorder, aSurface, "CopySurface");
+
+ mRecorder->RecordEvent(RecordedCopySurface(this, aSurface, aSourceRect, aDestination));
+ mFinalDT->CopySurface(GetSourceSurface(aSurface), aSourceRect, aDestination);
+}
+
+void
+DrawTargetRecording::PushClip(const Path *aPath)
+{
+ RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
+
+ mRecorder->RecordEvent(RecordedPushClip(this, pathRecording));
+ mFinalDT->PushClip(pathRecording->mPath);
+}
+
+void
+DrawTargetRecording::PushClipRect(const Rect &aRect)
+{
+ mRecorder->RecordEvent(RecordedPushClipRect(this, aRect));
+ mFinalDT->PushClipRect(aRect);
+}
+
+void
+DrawTargetRecording::PopClip()
+{
+ mRecorder->RecordEvent(RecordedPopClip(this));
+ mFinalDT->PopClip();
+}
+
+void
+DrawTargetRecording::PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds, bool aCopyBackground)
+{
+ if (aMask) {
+ EnsureSurfaceStored(mRecorder, aMask, "PushLayer");
+ }
+
+ mRecorder->RecordEvent(RecordedPushLayer(this, aOpaque, aOpacity, aMask,
+ aMaskTransform, aBounds,
+ aCopyBackground));
+ mFinalDT->PushLayer(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
+ aCopyBackground);
+}
+
+void
+DrawTargetRecording::PopLayer()
+{
+ mRecorder->RecordEvent(RecordedPopLayer(this));
+ mFinalDT->PopLayer();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetRecording::CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+{
+ RefPtr<SourceSurface> surf = mFinalDT->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
+
+ RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
+
+ mRecorder->RecordEvent(RecordedSourceSurfaceCreation(retSurf, aData, aStride, aSize, aFormat));
+
+ return retSurf.forget();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetRecording::OptimizeSourceSurface(SourceSurface *aSurface) const
+{
+ RefPtr<SourceSurface> surf = mFinalDT->OptimizeSourceSurface(aSurface);
+
+ RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
+
+ RefPtr<DataSourceSurface> dataSurf = surf->GetDataSurface();
+
+ if (!dataSurf) {
+ // Let's try get it off the original surface.
+ dataSurf = aSurface->GetDataSurface();
+ }
+
+ StoreSourceSurface(mRecorder, retSurf, dataSurf, "OptimizeSourceSurface");
+
+ return retSurf.forget();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetRecording::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+{
+ RefPtr<SourceSurface> surf = mFinalDT->CreateSourceSurfaceFromNativeSurface(aSurface);
+
+ RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
+
+ RefPtr<DataSourceSurface> dataSurf = surf->GetDataSurface();
+ StoreSourceSurface(mRecorder, retSurf, dataSurf, "CreateSourceSurfaceFromNativeSurface");
+
+ return retSurf.forget();
+}
+
+already_AddRefed<DrawTarget>
+DrawTargetRecording::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+ RefPtr<DrawTarget> similarDT =
+ mFinalDT->CreateSimilarDrawTarget(aSize, aFormat);
+ if (!similarDT) {
+ return nullptr;
+ }
+
+ similarDT = new DrawTargetRecording(this, similarDT);
+ return similarDT.forget();
+}
+
+already_AddRefed<PathBuilder>
+DrawTargetRecording::CreatePathBuilder(FillRule aFillRule) const
+{
+ RefPtr<PathBuilder> builder = mFinalDT->CreatePathBuilder(aFillRule);
+ return MakeAndAddRef<PathBuilderRecording>(builder, aFillRule);
+}
+
+already_AddRefed<GradientStops>
+DrawTargetRecording::CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode) const
+{
+ RefPtr<GradientStops> stops = mFinalDT->CreateGradientStops(aStops, aNumStops, aExtendMode);
+
+ RefPtr<GradientStops> retStops = new GradientStopsRecording(stops, mRecorder);
+
+ mRecorder->RecordEvent(RecordedGradientStopsCreation(retStops, aStops, aNumStops, aExtendMode));
+
+ return retStops.forget();
+}
+
+void
+DrawTargetRecording::SetTransform(const Matrix &aTransform)
+{
+ mRecorder->RecordEvent(RecordedSetTransform(this, aTransform));
+ DrawTarget::SetTransform(aTransform);
+ mFinalDT->SetTransform(aTransform);
+}
+
+already_AddRefed<PathRecording>
+DrawTargetRecording::EnsurePathStored(const Path *aPath)
+{
+ RefPtr<PathRecording> pathRecording;
+ if (aPath->GetBackendType() == BackendType::RECORDING) {
+ pathRecording = const_cast<PathRecording*>(static_cast<const PathRecording*>(aPath));
+ if (mRecorder->HasStoredObject(aPath)) {
+ return pathRecording.forget();
+ }
+ } else {
+ MOZ_ASSERT(!mRecorder->HasStoredObject(aPath));
+ FillRule fillRule = aPath->GetFillRule();
+ RefPtr<PathBuilder> builder = mFinalDT->CreatePathBuilder(fillRule);
+ RefPtr<PathBuilderRecording> builderRecording =
+ new PathBuilderRecording(builder, fillRule);
+ aPath->StreamToSink(builderRecording);
+ pathRecording = builderRecording->Finish().downcast<PathRecording>();
+ }
+
+ mRecorder->RecordEvent(RecordedPathCreation(pathRecording));
+ mRecorder->AddStoredObject(pathRecording);
+ pathRecording->mStoredRecorders.push_back(mRecorder);
+
+ return pathRecording.forget();
+}
+
+void
+DrawTargetRecording::EnsurePatternDependenciesStored(const Pattern &aPattern)
+{
+ switch (aPattern.GetType()) {
+ case PatternType::COLOR:
+ // No dependencies here.
+ return;
+ case PatternType::LINEAR_GRADIENT:
+ {
+ MOZ_ASSERT(mRecorder->HasStoredObject(static_cast<const LinearGradientPattern*>(&aPattern)->mStops));
+ return;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ MOZ_ASSERT(mRecorder->HasStoredObject(static_cast<const RadialGradientPattern*>(&aPattern)->mStops));
+ return;
+ }
+ case PatternType::SURFACE:
+ {
+ const SurfacePattern *pat = static_cast<const SurfacePattern*>(&aPattern);
+ EnsureSurfaceStored(mRecorder, pat->mSurface, "EnsurePatternDependenciesStored");
+ return;
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/DrawTargetRecording.h b/gfx/2d/DrawTargetRecording.h
new file mode 100644
index 000000000..359d1f6be
--- /dev/null
+++ b/gfx/2d/DrawTargetRecording.h
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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_DRAWTARGETRECORDING_H_
+#define MOZILLA_GFX_DRAWTARGETRECORDING_H_
+
+#include "2D.h"
+#include "DrawEventRecorder.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DrawTargetRecording : public DrawTarget
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetRecording, override)
+ DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT, bool aHasData = false);
+
+ ~DrawTargetRecording();
+
+ virtual DrawTargetType GetType() const override { return mFinalDT->GetType(); }
+ virtual BackendType GetBackendType() const override { return mFinalDT->GetBackendType(); }
+ virtual bool IsRecording() const override { return true; }
+
+ virtual already_AddRefed<SourceSurface> Snapshot() override;
+
+ virtual void DetachAllSnapshots() override;
+
+ 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() override { mFinalDT->Flush(); }
+
+ /*
+ * 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
+ */
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
+ 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
+ * 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
+ */
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator) override;
+
+ /*
+ * Clear a rectangle on the draw target to transparent black. This will
+ * respect the clipping region and transform.
+ *
+ * aRect Rectangle to clear
+ */
+ virtual void ClearRect(const Rect &aRect) override;
+
+ /*
+ * 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
+ */
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination) override;
+
+ /*
+ * 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
+ */
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * 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
+ */
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * 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
+ */
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * 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
+ */
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * 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
+ */
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * 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 = nullptr) override;
+
+ /*
+ * 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
+ */
+ virtual void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ virtual void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions()) override;
+
+ /*
+ * Push a clip to the DrawTarget.
+ *
+ * aPath The path to clip to
+ */
+ virtual void PushClip(const Path *aPath) override;
+
+ /*
+ * Push an axis-aligned rectangular clip to the DrawTarget. This rectangle
+ * is specified in user space.
+ *
+ * aRect The rect to clip to
+ */
+ 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() override;
+
+ /**
+ * Push a 'layer' to the DrawTarget, a layer is a temporary surface that all
+ * drawing will be redirected to, this is used for example to support group
+ * opacity or the masking of groups. Clips must be balanced within a layer,
+ * i.e. between a matching PushLayer/PopLayer pair there must be as many
+ * PushClip(Rect) calls as there are PopClip calls.
+ *
+ * @param aOpaque Whether the layer will be opaque
+ * @param aOpacity Opacity of the layer
+ * @param aMask Mask applied to the layer
+ * @param aMaskTransform Transform applied to the layer mask
+ * @param aBounds Optional bounds in device space to which the layer is
+ * limited in size.
+ * @param aCopyBackground Whether to copy the background into the layer, this
+ * is only supported when aOpaque is true.
+ */
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) override;
+
+ /**
+ * This balances a call to PushLayer and proceeds to blend the layer back
+ * onto the background. This blend will blend the temporary surface back
+ * onto the target in device space using POINT sampling and operator over.
+ */
+ virtual void PopLayer() override;
+
+ /*
+ * Create a SourceSurface optimized for use with this DrawTarget from
+ * existing bitmap data in memory.
+ *
+ * The SourceSurface does not take ownership of aData, and may be freed at any time.
+ */
+ virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ 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 already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
+
+ /*
+ * 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.
+ */
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
+
+ /*
+ * Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
+ */
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+
+ /*
+ * 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 already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
+
+ /*
+ * 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.
+ */
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
+
+ virtual already_AddRefed<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) 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) override { return mFinalDT->GetNativeSurface(aType); }
+
+ virtual bool IsCurrentGroupOpaque() override {
+ return mFinalDT->IsCurrentGroupOpaque();
+ }
+
+private:
+ /**
+ * Used for creating a DrawTargetRecording for a CreateSimilarDrawTarget call.
+ * We have to call CreateSimilarDrawTarget on mFinalDT up front and pass it in
+ * as it can fail.
+ *
+ * @param aDT DrawTargetRecording on which CreateSimilarDrawTarget was called
+ * @param aSimilarDT Similar DrawTarget created from aDT.mFinalDT.
+ */
+ DrawTargetRecording(const DrawTargetRecording *aDT,
+ DrawTarget *aSimilarDT);
+
+ Path *GetPathForPathRecording(const Path *aPath) const;
+ already_AddRefed<PathRecording> EnsurePathStored(const Path *aPath);
+ void EnsurePatternDependenciesStored(const Pattern &aPattern);
+
+ RefPtr<DrawEventRecorderPrivate> mRecorder;
+ RefPtr<DrawTarget> mFinalDT;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_DRAWTARGETRECORDING_H_ */
diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp
new file mode 100644
index 000000000..9eaac5674
--- /dev/null
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -0,0 +1,2135 @@
+/* -*- 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 "DrawTargetSkia.h"
+#include "SourceSurfaceSkia.h"
+#include "ScaledFontBase.h"
+#include "ScaledFontCairo.h"
+#include "skia/include/core/SkBitmapDevice.h"
+#include "FilterNodeSoftware.h"
+#include "HelpersSkia.h"
+
+#include "mozilla/ArrayUtils.h"
+
+#include "skia/include/core/SkSurface.h"
+#include "skia/include/core/SkTypeface.h"
+#include "skia/include/effects/SkGradientShader.h"
+#include "skia/include/core/SkColorFilter.h"
+#include "skia/include/effects/SkBlurImageFilter.h"
+#include "skia/include/effects/SkLayerRasterizer.h"
+#include "skia/src/core/SkSpecialImage.h"
+#include "Blur.h"
+#include "Logging.h"
+#include "Tools.h"
+#include "DataSurfaceHelpers.h"
+#include <algorithm>
+
+#ifdef USE_SKIA_GPU
+#include "GLDefs.h"
+#include "skia/include/gpu/SkGr.h"
+#include "skia/include/gpu/GrContext.h"
+#include "skia/include/gpu/GrDrawContext.h"
+#include "skia/include/gpu/gl/GrGLInterface.h"
+#include "skia/src/image/SkImage_Gpu.h"
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+#include "BorrowedContext.h"
+#include <ApplicationServices/ApplicationServices.h>
+#include "mozilla/Vector.h"
+#include "ScaledFontMac.h"
+#include "CGTextDrawing.h"
+#endif
+
+#ifdef XP_WIN
+#include "ScaledFontDWrite.h"
+#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)
+ {
+ if (mCount == 0) {
+ return;
+ }
+
+ // Skia gradients always require a stop at 0.0 and 1.0, insert these if
+ // we don't have them.
+ uint32_t shift = 0;
+ if (aStops[0].offset != 0) {
+ mCount++;
+ shift = 1;
+ }
+ if (aStops[aNumStops-1].offset != 1) {
+ mCount++;
+ }
+ mColors.resize(mCount);
+ mPositions.resize(mCount);
+ if (aStops[0].offset != 0) {
+ mColors[0] = ColorToSkColor(aStops[0].color, 1.0);
+ mPositions[0] = 0;
+ }
+ for (uint32_t i = 0; i < aNumStops; i++) {
+ mColors[i + shift] = ColorToSkColor(aStops[i].color, 1.0);
+ mPositions[i + shift] = SkFloatToScalar(aStops[i].offset);
+ }
+ if (aStops[aNumStops-1].offset != 1) {
+ mColors[mCount-1] = ColorToSkColor(aStops[aNumStops-1].color, 1.0);
+ mPositions[mCount-1] = SK_Scalar1;
+ }
+ }
+
+ BackendType GetBackendType() const { return BackendType::SKIA; }
+
+ std::vector<SkColor> mColors;
+ std::vector<SkScalar> mPositions;
+ int mCount;
+ ExtendMode mExtendMode;
+};
+
+/**
+ * When constructing a temporary SkImage via GetSkImageForSurface, we may also
+ * have to construct a temporary DataSourceSurface, which must live as long as
+ * the SkImage. We attach this temporary surface to the image's pixelref, so
+ * that it can be released once the pixelref is freed.
+ */
+static void
+ReleaseTemporarySurface(const void* aPixels, void* aContext)
+{
+ DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext);
+ if (surf) {
+ surf->Release();
+ }
+}
+
+#ifdef IS_BIG_ENDIAN
+static const int kARGBAlphaOffset = 0;
+#else
+static const int kARGBAlphaOffset = 3;
+#endif
+
+static void
+WriteRGBXFormat(uint8_t* aData, const IntSize &aSize,
+ const int32_t aStride, SurfaceFormat aFormat)
+{
+ if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
+ return;
+ }
+
+ int height = aSize.height;
+ int width = aSize.width * 4;
+
+ for (int row = 0; row < height; ++row) {
+ for (int column = 0; column < width; column += 4) {
+ aData[column + kARGBAlphaOffset] = 0xFF;
+ }
+ aData += aStride;
+ }
+
+ return;
+}
+
+#ifdef DEBUG
+static bool
+VerifyRGBXFormat(uint8_t* aData, const IntSize &aSize, const int32_t aStride, SurfaceFormat aFormat)
+{
+ if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
+ return true;
+ }
+ // We should've initialized the data to be opaque already
+ // On debug builds, verify that this is actually true.
+ int height = aSize.height;
+ int width = aSize.width * 4;
+
+ for (int row = 0; row < height; ++row) {
+ for (int column = 0; column < width; column += 4) {
+ if (aData[column + kARGBAlphaOffset] != 0xFF) {
+ gfxCriticalError() << "RGBX pixel at (" << column << "," << row << ") in "
+ << width << "x" << height << " surface is not opaque: "
+ << int(aData[column]) << ","
+ << int(aData[column+1]) << ","
+ << int(aData[column+2]) << ","
+ << int(aData[column+3]);
+ }
+ }
+ aData += aStride;
+ }
+
+ return true;
+}
+
+// Since checking every pixel is expensive, this only checks the four corners and center
+// of a surface that their alpha value is 0xFF.
+static bool
+VerifyRGBXCorners(uint8_t* aData, const IntSize &aSize, const int32_t aStride, SurfaceFormat aFormat)
+{
+ if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
+ return true;
+ }
+
+ int height = aSize.height;
+ int width = aSize.width;
+ const int pixelSize = 4;
+ const int strideDiff = aStride - (width * pixelSize);
+ MOZ_ASSERT(width * pixelSize <= aStride);
+
+ const int topLeft = 0;
+ const int topRight = width * pixelSize - pixelSize;
+ const int bottomRight = aStride * height - strideDiff - pixelSize;
+ const int bottomLeft = aStride * height - aStride;
+
+ // Lastly the center pixel
+ int middleRowHeight = height / 2;
+ int middleRowWidth = (width / 2) * pixelSize;
+ const int middle = aStride * middleRowHeight + middleRowWidth;
+
+ const int offsets[] = { topLeft, topRight, bottomRight, bottomLeft, middle };
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(offsets); i++) {
+ int offset = offsets[i];
+ if (aData[offset + kARGBAlphaOffset] != 0xFF) {
+ int row = offset / aStride;
+ int column = (offset % aStride) / pixelSize;
+ gfxCriticalError() << "RGBX corner pixel at (" << column << "," << row << ") in "
+ << width << "x" << height << " surface is not opaque: "
+ << int(aData[offset]) << ","
+ << int(aData[offset+1]) << ","
+ << int(aData[offset+2]) << ","
+ << int(aData[offset+3]);
+ }
+ }
+
+ return true;
+}
+#endif
+
+static sk_sp<SkImage>
+GetSkImageForSurface(SourceSurface* aSurface)
+{
+ if (!aSurface) {
+ gfxDebug() << "Creating null Skia image from null SourceSurface";
+ return nullptr;
+ }
+
+ if (aSurface->GetType() == SurfaceType::SKIA) {
+ return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage();
+ }
+
+ DataSourceSurface* surf = aSurface->GetDataSurface().take();
+ if (!surf) {
+ gfxWarning() << "Failed getting DataSourceSurface for Skia image";
+ return nullptr;
+ }
+
+ SkPixmap pixmap(MakeSkiaImageInfo(surf->GetSize(), surf->GetFormat()),
+ surf->GetData(), surf->Stride());
+ sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, ReleaseTemporarySurface, surf);
+ if (!image) {
+ ReleaseTemporarySurface(nullptr, surf);
+ gfxDebug() << "Failed making Skia raster image for temporary surface";
+ }
+
+ // Skia doesn't support RGBX surfaces so ensure that the alpha value is opaque white.
+ MOZ_ASSERT(VerifyRGBXCorners(surf->GetData(), surf->GetSize(),
+ surf->Stride(), surf->GetFormat()));
+ return image;
+}
+
+DrawTargetSkia::DrawTargetSkia()
+ : mSnapshot(nullptr)
+#ifdef MOZ_WIDGET_COCOA
+ , mCG(nullptr)
+ , mColorSpace(nullptr)
+ , mCanvasData(nullptr)
+ , mCGSize(0, 0)
+#endif
+{
+}
+
+DrawTargetSkia::~DrawTargetSkia()
+{
+#ifdef MOZ_WIDGET_COCOA
+ if (mCG) {
+ CGContextRelease(mCG);
+ mCG = nullptr;
+ }
+
+ if (mColorSpace) {
+ CGColorSpaceRelease(mColorSpace);
+ mColorSpace = nullptr;
+ }
+#endif
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetSkia::Snapshot()
+{
+ RefPtr<SourceSurfaceSkia> snapshot = mSnapshot;
+ if (mSurface && !snapshot) {
+ snapshot = new SourceSurfaceSkia();
+ sk_sp<SkImage> image;
+ // If the surface is raster, making a snapshot may trigger a pixel copy.
+ // Instead, try to directly make a raster image referencing the surface pixels.
+ SkPixmap pixmap;
+ if (mSurface->peekPixels(&pixmap)) {
+ image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
+ } else {
+ image = mSurface->makeImageSnapshot(SkBudgeted::kNo);
+ }
+ if (!snapshot->InitFromImage(image, mFormat, this)) {
+ return nullptr;
+ }
+ mSnapshot = snapshot;
+ }
+
+ return snapshot.forget();
+}
+
+bool
+DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize,
+ int32_t* aStride, SurfaceFormat* aFormat,
+ IntPoint* aOrigin)
+{
+ // Ensure the layer is at the origin if required.
+ SkIPoint origin = mCanvas->getTopDevice()->getOrigin();
+ if (!aOrigin && !origin.isZero()) {
+ return false;
+ }
+
+ /* Test if the canvas' device has accessible pixels first, as actually
+ * accessing the pixels may trigger side-effects, even if it fails.
+ */
+ if (!mCanvas->peekPixels(nullptr)) {
+ return false;
+ }
+
+ SkImageInfo info;
+ size_t rowBytes;
+ void* pixels = mCanvas->accessTopLayerPixels(&info, &rowBytes);
+ if (!pixels) {
+ return false;
+ }
+
+ MarkChanged();
+
+ *aData = reinterpret_cast<uint8_t*>(pixels);
+ *aSize = IntSize(info.width(), info.height());
+ *aStride = int32_t(rowBytes);
+ *aFormat = SkiaColorTypeToGfxFormat(info.colorType(), info.alphaType());
+ if (aOrigin) {
+ *aOrigin = IntPoint(origin.x(), origin.y());
+ }
+ return true;
+}
+
+void
+DrawTargetSkia::ReleaseBits(uint8_t* aData)
+{
+}
+
+static void
+ReleaseImage(const void* aPixels, void* aContext)
+{
+ SkImage* image = static_cast<SkImage*>(aContext);
+ SkSafeUnref(image);
+}
+
+static sk_sp<SkImage>
+ExtractSubset(sk_sp<SkImage> aImage, const IntRect& aRect)
+{
+ SkIRect subsetRect = IntRectToSkIRect(aRect);
+ if (aImage->bounds() == subsetRect) {
+ return aImage;
+ }
+ // makeSubset is slow, so prefer to use SkPixmap::extractSubset where possible.
+ SkPixmap pixmap, subsetPixmap;
+ if (aImage->peekPixels(&pixmap) &&
+ pixmap.extractSubset(&subsetPixmap, subsetRect)) {
+ // Release the original image reference so only the subset image keeps it alive.
+ return SkImage::MakeFromRaster(subsetPixmap, ReleaseImage, aImage.release());
+ }
+ return aImage->makeSubset(subsetRect);
+}
+
+static inline bool
+SkImageIsMask(const sk_sp<SkImage>& aImage)
+{
+ SkPixmap pixmap;
+ if (aImage->peekPixels(&pixmap)) {
+ return pixmap.colorType() == kAlpha_8_SkColorType;
+#ifdef USE_SKIA_GPU
+ } else if (GrTexture* tex = aImage->getTexture()) {
+ return GrPixelConfigIsAlphaOnly(tex->config());
+#endif
+ } else {
+ return false;
+ }
+}
+
+static bool
+ExtractAlphaBitmap(sk_sp<SkImage> aImage, SkBitmap* aResultBitmap)
+{
+ SkImageInfo info = SkImageInfo::MakeA8(aImage->width(), aImage->height());
+ SkBitmap bitmap;
+ if (!bitmap.tryAllocPixels(info, SkAlign4(info.minRowBytes())) ||
+ !aImage->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0)) {
+ gfxWarning() << "Failed reading alpha pixels for Skia bitmap";
+ return false;
+ }
+
+ *aResultBitmap = bitmap;
+ return true;
+}
+
+static sk_sp<SkImage>
+ExtractAlphaForSurface(SourceSurface* aSurface)
+{
+ sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
+ if (!image) {
+ return nullptr;
+ }
+ if (SkImageIsMask(image)) {
+ return image;
+ }
+
+ SkBitmap bitmap;
+ if (!ExtractAlphaBitmap(image, &bitmap)) {
+ return nullptr;
+ }
+
+ // Mark the bitmap immutable so that it will be shared rather than copied.
+ bitmap.setImmutable();
+ return SkImage::MakeFromBitmap(bitmap);
+}
+
+static void
+SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0, Point aOffset = Point(0, 0))
+{
+ switch (aPattern.GetType()) {
+ case PatternType::COLOR: {
+ Color color = static_cast<const ColorPattern&>(aPattern).mColor;
+ aPaint.setColor(ColorToSkColor(color, aAlpha));
+ break;
+ }
+ case PatternType::LINEAR_GRADIENT: {
+ const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern);
+ GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get());
+ if (!stops || stops->mCount < 2 ||
+ !pat.mBegin.IsFinite() || !pat.mEnd.IsFinite()) {
+ aPaint.setColor(SK_ColorTRANSPARENT);
+ } else {
+ SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
+ SkPoint points[2];
+ points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x), SkFloatToScalar(pat.mBegin.y));
+ points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x), SkFloatToScalar(pat.mEnd.y));
+
+ SkMatrix mat;
+ GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
+ mat.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
+ sk_sp<SkShader> shader = SkGradientShader::MakeLinear(points,
+ &stops->mColors.front(),
+ &stops->mPositions.front(),
+ stops->mCount,
+ mode, 0, &mat);
+ aPaint.setShader(shader);
+ }
+ break;
+ }
+ case PatternType::RADIAL_GRADIENT: {
+ const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
+ GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get());
+ if (!stops || stops->mCount < 2 ||
+ !pat.mCenter1.IsFinite() || !IsFinite(pat.mRadius1) ||
+ !pat.mCenter2.IsFinite() || !IsFinite(pat.mRadius2)) {
+ aPaint.setColor(SK_ColorTRANSPARENT);
+ } else {
+ SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
+ SkPoint points[2];
+ points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x), SkFloatToScalar(pat.mCenter1.y));
+ points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x), SkFloatToScalar(pat.mCenter2.y));
+
+ SkMatrix mat;
+ GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
+ mat.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
+ sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(points[0],
+ SkFloatToScalar(pat.mRadius1),
+ points[1],
+ SkFloatToScalar(pat.mRadius2),
+ &stops->mColors.front(),
+ &stops->mPositions.front(),
+ stops->mCount,
+ mode, 0, &mat);
+ aPaint.setShader(shader);
+ }
+ break;
+ }
+ case PatternType::SURFACE: {
+ const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
+ sk_sp<SkImage> image = GetSkImageForSurface(pat.mSurface);
+ if (!image) {
+ aPaint.setColor(SK_ColorTRANSPARENT);
+ break;
+ }
+
+ SkMatrix mat;
+ GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
+ mat.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
+
+ if (!pat.mSamplingRect.IsEmpty()) {
+ image = ExtractSubset(image, pat.mSamplingRect);
+ mat.preTranslate(pat.mSamplingRect.x, pat.mSamplingRect.y);
+ }
+
+ SkShader::TileMode xTileMode = ExtendModeToTileMode(pat.mExtendMode, Axis::X_AXIS);
+ SkShader::TileMode yTileMode = ExtendModeToTileMode(pat.mExtendMode, Axis::Y_AXIS);
+
+ aPaint.setShader(image->makeShader(xTileMode, yTileMode, &mat));
+
+ if (pat.mSamplingFilter == SamplingFilter::POINT) {
+ aPaint.setFilterQuality(kNone_SkFilterQuality);
+ }
+ break;
+ }
+ }
+}
+
+static inline Rect
+GetClipBounds(SkCanvas *aCanvas)
+{
+ // Use a manually transformed getClipDeviceBounds instead of
+ // getClipBounds because getClipBounds inflates the the bounds
+ // by a pixel in each direction to compensate for antialiasing.
+ SkIRect deviceBounds;
+ if (!aCanvas->getClipDeviceBounds(&deviceBounds)) {
+ return Rect();
+ }
+ SkMatrix inverseCTM;
+ if (!aCanvas->getTotalMatrix().invert(&inverseCTM)) {
+ return Rect();
+ }
+ SkRect localBounds;
+ inverseCTM.mapRect(&localBounds, SkRect::Make(deviceBounds));
+ return SkRectToRect(localBounds);
+}
+
+struct AutoPaintSetup {
+ AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Pattern& aPattern, const Rect* aMaskBounds = nullptr, Point aOffset = Point(0, 0))
+ : mNeedsRestore(false), mAlpha(1.0)
+ {
+ Init(aCanvas, aOptions, aMaskBounds, false);
+ SetPaintPattern(mPaint, aPattern, mAlpha, aOffset);
+ }
+
+ AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr, bool aForceGroup = false)
+ : mNeedsRestore(false), mAlpha(1.0)
+ {
+ Init(aCanvas, aOptions, aMaskBounds, aForceGroup);
+ }
+
+ ~AutoPaintSetup()
+ {
+ if (mNeedsRestore) {
+ mCanvas->restore();
+ }
+ }
+
+ void Init(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds, bool aForceGroup)
+ {
+ mPaint.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
+ mCanvas = aCanvas;
+
+ //TODO: Can we set greyscale somehow?
+ if (aOptions.mAntialiasMode != AntialiasMode::NONE) {
+ mPaint.setAntiAlias(true);
+ } else {
+ mPaint.setAntiAlias(false);
+ }
+
+ bool needsGroup = aForceGroup ||
+ (!IsOperatorBoundByMask(aOptions.mCompositionOp) &&
+ (!aMaskBounds || !aMaskBounds->Contains(GetClipBounds(aCanvas))));
+
+ // 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 (needsGroup) {
+ mPaint.setBlendMode(SkBlendMode::kSrcOver);
+ SkPaint temp;
+ temp.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
+ temp.setAlpha(ColorFloatToByte(aOptions.mAlpha));
+ //TODO: Get a rect here
+ mCanvas->saveLayer(nullptr, &temp);
+ mNeedsRestore = true;
+ } else {
+ mPaint.setAlpha(ColorFloatToByte(aOptions.mAlpha));
+ mAlpha = aOptions.mAlpha;
+ }
+ mPaint.setFilterQuality(kLow_SkFilterQuality);
+ }
+
+ // TODO: Maybe add an operator overload to access this easier?
+ SkPaint mPaint;
+ bool mNeedsRestore;
+ SkCanvas* mCanvas;
+ Float mAlpha;
+};
+
+void
+DrawTargetSkia::Flush()
+{
+ mCanvas->flush();
+}
+
+void
+DrawTargetSkia::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions)
+{
+ if (aSource.IsEmpty()) {
+ return;
+ }
+
+ MarkChanged();
+
+ sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
+ if (!image) {
+ return;
+ }
+
+ SkRect destRect = RectToSkRect(aDest);
+ SkRect sourceRect = RectToSkRect(aSource);
+ bool forceGroup = SkImageIsMask(image) &&
+ aOptions.mCompositionOp != CompositionOp::OP_OVER;
+
+ AutoPaintSetup paint(mCanvas.get(), aOptions, &aDest, forceGroup);
+ if (aSurfOptions.mSamplingFilter == SamplingFilter::POINT) {
+ paint.mPaint.setFilterQuality(kNone_SkFilterQuality);
+ }
+
+ mCanvas->drawImageRect(image, sourceRect, destRect, &paint.mPaint);
+}
+
+DrawTargetType
+DrawTargetSkia::GetType() const
+{
+#ifdef USE_SKIA_GPU
+ if (mGrContext) {
+ return DrawTargetType::HARDWARE_RASTER;
+ }
+#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
+DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator)
+{
+ if (aSurface->GetSize().IsEmpty()) {
+ return;
+ }
+
+ MarkChanged();
+
+ sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
+ if (!image) {
+ return;
+ }
+
+ mCanvas->save();
+ mCanvas->resetMatrix();
+
+ SkPaint paint;
+ paint.setBlendMode(GfxOpToSkiaOp(aOperator));
+
+ // bug 1201272
+ // We can't use the SkDropShadowImageFilter here because it applies the xfer
+ // mode first to render the bitmap to a temporary layer, and then implicitly
+ // uses src-over to composite the resulting shadow.
+ // The canvas spec, however, states that the composite op must be used to
+ // composite the resulting shadow, so we must instead use a SkBlurImageFilter
+ // to blur the image ourselves.
+
+ SkPaint shadowPaint;
+ shadowPaint.setBlendMode(GfxOpToSkiaOp(aOperator));
+
+ auto shadowDest = IntPoint::Round(aDest + aOffset);
+
+ SkBitmap blurMask;
+ if (!UsingSkiaGPU() &&
+ ExtractAlphaBitmap(image, &blurMask)) {
+ // Prefer using our own box blur instead of Skia's when we're
+ // not using the GPU. It currently performs much better than
+ // SkBlurImageFilter or SkBlurMaskFilter on the CPU.
+ AlphaBoxBlur blur(Rect(0, 0, blurMask.width(), blurMask.height()),
+ int32_t(blurMask.rowBytes()),
+ aSigma, aSigma);
+ blurMask.lockPixels();
+ blur.Blur(reinterpret_cast<uint8_t*>(blurMask.getPixels()));
+ blurMask.unlockPixels();
+ blurMask.notifyPixelsChanged();
+
+ shadowPaint.setColor(ColorToSkColor(aColor, 1.0f));
+
+ mCanvas->drawBitmap(blurMask, shadowDest.x, shadowDest.y, &shadowPaint);
+ } else {
+ sk_sp<SkImageFilter> blurFilter(SkBlurImageFilter::Make(aSigma, aSigma, nullptr));
+ sk_sp<SkColorFilter> colorFilter(
+ SkColorFilter::MakeModeFilter(ColorToSkColor(aColor, 1.0f), SkBlendMode::kSrcIn));
+
+ shadowPaint.setImageFilter(blurFilter);
+ shadowPaint.setColorFilter(colorFilter);
+
+ mCanvas->drawImage(image, shadowDest.x, shadowDest.y, &shadowPaint);
+ }
+
+ // Composite the original image after the shadow
+ auto dest = IntPoint::Round(aDest);
+ mCanvas->drawImage(image, dest.x, dest.y, &paint);
+
+ mCanvas->restore();
+}
+
+void
+DrawTargetSkia::FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ // The sprite blitting path in Skia can be faster than the shader blitter for
+ // operators other than source (or source-over with opaque surface). So, when
+ // possible/beneficial, route to DrawSurface which will use the sprite blitter.
+ if (aPattern.GetType() == PatternType::SURFACE &&
+ aOptions.mCompositionOp != CompositionOp::OP_SOURCE) {
+ const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
+ // Verify there is a valid surface and a pattern matrix without skew.
+ if (pat.mSurface &&
+ (aOptions.mCompositionOp != CompositionOp::OP_OVER ||
+ GfxFormatToSkiaAlphaType(pat.mSurface->GetFormat()) != kOpaque_SkAlphaType) &&
+ !pat.mMatrix.HasNonAxisAlignedTransform()) {
+ // Bound the sampling to smaller of the bounds or the sampling rect.
+ IntRect srcRect(IntPoint(0, 0), pat.mSurface->GetSize());
+ if (!pat.mSamplingRect.IsEmpty()) {
+ srcRect = srcRect.Intersect(pat.mSamplingRect);
+ }
+ // Transform the destination rectangle by the inverse of the pattern
+ // matrix so that it is in pattern space like the source rectangle.
+ Rect patRect = aRect - pat.mMatrix.GetTranslation();
+ patRect.Scale(1.0f / pat.mMatrix._11, 1.0f / pat.mMatrix._22);
+ // Verify the pattern rectangle will not tile or clamp.
+ if (!patRect.IsEmpty() && srcRect.Contains(RoundedOut(patRect))) {
+ // The pattern is a surface with an axis-aligned source rectangle
+ // fitting entirely in its bounds, so just treat it as a DrawSurface.
+ DrawSurface(pat.mSurface, aRect, patRect,
+ DrawSurfaceOptions(pat.mSamplingFilter),
+ aOptions);
+ return;
+ }
+ }
+ }
+
+ MarkChanged();
+ SkRect rect = RectToSkRect(aRect);
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern, &aRect);
+
+ mCanvas->drawRect(rect, paint.mPaint);
+}
+
+void
+DrawTargetSkia::Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ MOZ_ASSERT(aPath, "Null path");
+ if (aPath->GetBackendType() != BackendType::SKIA) {
+ return;
+ }
+
+ const PathSkia *skiaPath = static_cast<const PathSkia*>(aPath);
+
+
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
+ if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
+ return;
+ }
+
+ if (!skiaPath->GetPath().isFinite()) {
+ return;
+ }
+
+ mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
+}
+
+void
+DrawTargetSkia::StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
+ if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
+ return;
+ }
+
+ mCanvas->drawRect(RectToSkRect(aRect), paint.mPaint);
+}
+
+void
+DrawTargetSkia::StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
+ if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
+ return;
+ }
+
+ mCanvas->drawLine(SkFloatToScalar(aStart.x), SkFloatToScalar(aStart.y),
+ SkFloatToScalar(aEnd.x), SkFloatToScalar(aEnd.y),
+ paint.mPaint);
+}
+
+void
+DrawTargetSkia::Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ if (!aPath || aPath->GetBackendType() != BackendType::SKIA) {
+ return;
+ }
+
+ const PathSkia *skiaPath = static_cast<const PathSkia*>(aPath);
+
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
+
+ if (!skiaPath->GetPath().isFinite()) {
+ return;
+ }
+
+ mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
+}
+
+bool
+DrawTargetSkia::ShouldLCDRenderText(FontType aFontType, AntialiasMode aAntialiasMode)
+{
+ // For non-opaque surfaces, only allow subpixel AA if explicitly permitted.
+ if (!IsOpaque(mFormat) && !mPermitSubpixelAA) {
+ return false;
+ }
+
+ if (aAntialiasMode == AntialiasMode::DEFAULT) {
+ switch (aFontType) {
+ case FontType::MAC:
+ case FontType::GDI:
+ case FontType::DWRITE:
+ case FontType::FONTCONFIG:
+ return true;
+ default:
+ // TODO: Figure out what to do for the other platforms.
+ return false;
+ }
+ }
+ return (aAntialiasMode == AntialiasMode::SUBPIXEL);
+}
+
+#ifdef MOZ_WIDGET_COCOA
+class CGClipApply : public SkCanvas::ClipVisitor {
+public:
+ explicit CGClipApply(CGContextRef aCGContext)
+ : mCG(aCGContext) {}
+ void clipRect(const SkRect& aRect, SkCanvas::ClipOp op, bool antialias) override {
+ CGRect rect = CGRectMake(aRect.x(), aRect.y(), aRect.width(), aRect.height());
+ CGContextClipToRect(mCG, rect);
+ }
+
+ void clipRRect(const SkRRect& rrect, SkCanvas::ClipOp op, bool antialias) override {
+ SkPath path;
+ path.addRRect(rrect);
+ clipPath(path, op, antialias);
+ }
+
+ void clipPath(const SkPath& aPath, SkCanvas::ClipOp, bool antialias) override {
+ SkPath::Iter iter(aPath, true);
+ SkPoint source[4];
+ SkPath::Verb verb;
+ RefPtr<PathBuilderCG> pathBuilder =
+ new PathBuilderCG(GetFillRule(aPath.getFillType()));
+
+ while ((verb = iter.next(source)) != SkPath::kDone_Verb) {
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ {
+ SkPoint dest = source[0];
+ pathBuilder->MoveTo(Point(dest.fX, dest.fY));
+ break;
+ }
+ case SkPath::kLine_Verb:
+ {
+ // The first point should be the end point of whatever
+ // verb we got to get here.
+ SkPoint second = source[1];
+ pathBuilder->LineTo(Point(second.fX, second.fY));
+ break;
+ }
+ case SkPath::kQuad_Verb:
+ {
+ SkPoint second = source[1];
+ SkPoint third = source[2];
+
+ pathBuilder->QuadraticBezierTo(Point(second.fX, second.fY),
+ Point(third.fX, third.fY));
+ break;
+ }
+ case SkPath::kCubic_Verb:
+ {
+ SkPoint second = source[1];
+ SkPoint third = source[2];
+ SkPoint fourth = source[2];
+
+ pathBuilder->BezierTo(Point(second.fX, second.fY),
+ Point(third.fX, third.fY),
+ Point(fourth.fX, fourth.fY));
+ break;
+ }
+ case SkPath::kClose_Verb:
+ {
+ pathBuilder->Close();
+ break;
+ }
+ default:
+ {
+ SkDEBUGFAIL("unknown verb");
+ break;
+ }
+ } // end switch
+ } // end while
+
+ RefPtr<Path> path = pathBuilder->Finish();
+ PathCG* cgPath = static_cast<PathCG*>(path.get());
+
+ // Weirdly, CoreGraphics clips empty paths as all shown
+ // but empty rects as all clipped. We detect this situation and
+ // workaround it appropriately
+ if (CGPathIsEmpty(cgPath->GetPath())) {
+ CGContextClipToRect(mCG, CGRectZero);
+ return;
+ }
+
+ CGContextBeginPath(mCG);
+ CGContextAddPath(mCG, cgPath->GetPath());
+
+ if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD) {
+ CGContextEOClip(mCG);
+ } else {
+ CGContextClip(mCG);
+ }
+ }
+
+private:
+ CGContextRef mCG;
+};
+
+static inline CGAffineTransform
+GfxMatrixToCGAffineTransform(const Matrix &m)
+{
+ CGAffineTransform t;
+ t.a = m._11;
+ t.b = m._12;
+ t.c = m._21;
+ t.d = m._22;
+ t.tx = m._31;
+ t.ty = m._32;
+ return t;
+}
+
+/***
+ * We have to do a lot of work to draw glyphs with CG because
+ * CG assumes that the origin of rects are in the bottom left
+ * while every other DrawTarget assumes the top left is the origin.
+ * This means we have to transform the CGContext to have rects
+ * actually be applied in top left fashion. We do this by:
+ *
+ * 1) Translating the context up by the height of the canvas
+ * 2) Flipping the context by the Y axis so it's upside down.
+ *
+ * These two transforms put the origin in the top left.
+ * Transforms are better understood thinking about them from right to left order (mathematically).
+ *
+ * Consider a point we want to draw at (0, 10) in normal cartesian planes with
+ * a box of (100, 100). in CG terms, this would be at (0, 10).
+ * Positive Y values point up.
+ * In our DrawTarget terms, positive Y values point down, so (0, 10) would be
+ * at (0, 90) in cartesian plane terms. That means our point at (0, 10) in DrawTarget
+ * terms should end up at (0, 90). How does this work with the current transforms?
+ *
+ * Going right to left with the transforms, a CGPoint of (0, 10) has cartesian coordinates
+ * of (0, 10). The first flip of the Y axis puts the point now at (0, -10);
+ * Next, we translate the context up by the size of the canvas (Positive Y values go up in CG
+ * coordinates but down in our draw target coordinates). Since our canvas size is (100, 100),
+ * the resulting coordinate becomes (0, 90), which is what we expect from our DrawTarget code.
+ * These two transforms put the CG context equal to what every other DrawTarget expects.
+ *
+ * Next, we need two more transforms for actual text. IF we left the transforms as is,
+ * the text would be drawn upside down, so we need another flip of the Y axis
+ * to draw the text right side up. However, with only the flip, the text would be drawn
+ * in the wrong place. Thus we also have to invert the Y position of the glyphs to get them
+ * in the right place.
+ *
+ * Thus we have the following transforms:
+ * 1) Translation of the context up
+ * 2) Flipping the context around the Y axis
+ * 3) Flipping the context around the Y axis
+ * 4) Inverting the Y position of each glyph
+ *
+ * We cannot cancel out (2) and (3) as we have to apply the clips and transforms
+ * of DrawTargetSkia between (2) and (3).
+ *
+ * Consider the example letter P, drawn at (0, 20) in CG coordinates in a (100, 100) rect.
+ * Again, going right to left of the transforms. We'd get:
+ *
+ * 1) The letter P drawn at (0, -20) due to the inversion of the Y axis
+ * 2) The letter P upside down (b) at (0, 20) due to the second flip
+ * 3) The letter P right side up at (0, -20) due to the first flip
+ * 4) The letter P right side up at (0, 80) due to the translation
+ *
+ * tl;dr - CGRects assume origin is bottom left, DrawTarget rects assume top left.
+ */
+static bool
+SetupCGContext(DrawTargetSkia* aDT,
+ CGContextRef aCGContext,
+ sk_sp<SkCanvas> aCanvas)
+{
+ // DrawTarget expects the origin to be at the top left, but CG
+ // expects it to be at the bottom left. Transform to set the origin to
+ // the top left. Have to set this before we do anything else.
+ // This is transform (1) up top
+ CGContextTranslateCTM(aCGContext, 0, aDT->GetSize().height);
+
+ // Transform (2) from the comments.
+ CGContextScaleCTM(aCGContext, 1, -1);
+
+ // Want to apply clips BEFORE the transform since the transform
+ // will apply to the clips we apply.
+ // CGClipApply applies clips in device space, so it would be a mistake
+ // to transform these clips.
+ CGClipApply clipApply(aCGContext);
+ aCanvas->replayClips(&clipApply);
+
+ CGContextConcatCTM(aCGContext, GfxMatrixToCGAffineTransform(aDT->GetTransform()));
+ return true;
+}
+
+static bool
+SetupCGGlyphs(CGContextRef aCGContext,
+ const GlyphBuffer& aBuffer,
+ Vector<CGGlyph,32>& aGlyphs,
+ Vector<CGPoint,32>& aPositions)
+{
+ // Flip again so we draw text in right side up. Transform (3) from the top
+ CGContextScaleCTM(aCGContext, 1, -1);
+
+ if (!aGlyphs.resizeUninitialized(aBuffer.mNumGlyphs) ||
+ !aPositions.resizeUninitialized(aBuffer.mNumGlyphs)) {
+ gfxDevCrash(LogReason::GlyphAllocFailedCG) << "glyphs/positions allocation failed";
+ return false;
+ }
+
+ for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+ aGlyphs[i] = aBuffer.mGlyphs[i].mIndex;
+
+ // Flip the y coordinates so that text ends up in the right spot after the (3) flip
+ // Inversion from (4) in the comments.
+ aPositions[i] = CGPointMake(aBuffer.mGlyphs[i].mPosition.x,
+ -aBuffer.mGlyphs[i].mPosition.y);
+ }
+
+ return true;
+}
+// End long comment about transforms. SetupCGContext and SetupCGGlyphs should stay
+// next to each other.
+
+// The context returned from this method will have the origin
+// in the top left and will hvae applied all the neccessary clips
+// and transforms to the CGContext. See the comment above
+// SetupCGContext.
+CGContextRef
+DrawTargetSkia::BorrowCGContext(const DrawOptions &aOptions)
+{
+ int32_t stride;
+ SurfaceFormat format;
+ IntSize size;
+
+ uint8_t* aSurfaceData = nullptr;
+ if (!LockBits(&aSurfaceData, &size, &stride, &format)) {
+ NS_WARNING("Could not lock skia bits to wrap CG around");
+ return nullptr;
+ }
+
+ if ((aSurfaceData == mCanvasData) && mCG && (mCGSize == size)) {
+ // If our canvas data still points to the same data,
+ // we can reuse the CG Context
+ CGContextSaveGState(mCG);
+ CGContextSetAlpha(mCG, aOptions.mAlpha);
+ SetupCGContext(this, mCG, mCanvas);
+ return mCG;
+ }
+
+ if (!mColorSpace) {
+ mColorSpace = (format == SurfaceFormat::A8) ?
+ CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB();
+ }
+
+ if (mCG) {
+ // Release the old CG context since it's no longer valid.
+ CGContextRelease(mCG);
+ }
+
+ mCanvasData = aSurfaceData;
+ mCGSize = size;
+
+ uint32_t bitmapInfo = (format == SurfaceFormat::A8) ?
+ kCGImageAlphaOnly :
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
+
+ mCG = CGBitmapContextCreateWithData(mCanvasData,
+ mCGSize.width,
+ mCGSize.height,
+ 8, /* bits per component */
+ stride,
+ mColorSpace,
+ bitmapInfo,
+ NULL, /* Callback when released */
+ NULL);
+ if (!mCG) {
+ ReleaseBits(mCanvasData);
+ NS_WARNING("Could not create bitmap around skia data\n");
+ return nullptr;
+ }
+
+ CGContextSetAlpha(mCG, aOptions.mAlpha);
+ CGContextSetShouldAntialias(mCG, aOptions.mAntialiasMode != AntialiasMode::NONE);
+ CGContextSetShouldSmoothFonts(mCG, true);
+ CGContextSetTextDrawingMode(mCG, kCGTextFill);
+ CGContextSaveGState(mCG);
+ SetupCGContext(this, mCG, mCanvas);
+ return mCG;
+}
+
+void
+DrawTargetSkia::ReturnCGContext(CGContextRef aCGContext)
+{
+ MOZ_ASSERT(aCGContext == mCG);
+ ReleaseBits(mCanvasData);
+ CGContextRestoreGState(aCGContext);
+}
+
+CGContextRef
+BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget *aDT)
+{
+ DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
+ return skiaDT->BorrowCGContext(DrawOptions());
+}
+
+void
+BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg)
+{
+ DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
+ skiaDT->ReturnCGContext(cg);
+ return;
+}
+
+static void
+SetFontColor(CGContextRef aCGContext, CGColorSpaceRef aColorSpace, const Pattern& aPattern)
+{
+ const Color& color = static_cast<const ColorPattern&>(aPattern).mColor;
+ CGColorRef textColor = ColorToCGColor(aColorSpace, color);
+ CGContextSetFillColorWithColor(aCGContext, textColor);
+ CGColorRelease(textColor);
+}
+
+/***
+ * We need this to support subpixel AA text on OS X in two cases:
+ * text in DrawTargets that are not opaque and text over vibrant backgrounds.
+ * Skia normally doesn't support subpixel AA text on transparent backgrounds.
+ * To get around this, we have to wrap the Skia bytes with a CGContext and ask
+ * CG to draw the text.
+ * In vibrancy cases, we have to use a private API,
+ * CGContextSetFontSmoothingBackgroundColor, which sets the expected
+ * background color the text will draw onto so that CG can render the text
+ * properly. After that, we have to go back and fixup the pixels
+ * such that their alpha values are correct.
+ */
+bool
+DrawTargetSkia::FillGlyphsWithCG(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions,
+ const GlyphRenderingOptions *aRenderingOptions)
+{
+ MOZ_ASSERT(aFont->GetType() == FontType::MAC);
+ MOZ_ASSERT(aPattern.GetType() == PatternType::COLOR);
+
+ CGContextRef cgContext = BorrowCGContext(aOptions);
+ if (!cgContext) {
+ return false;
+ }
+
+ Vector<CGGlyph,32> glyphs;
+ Vector<CGPoint,32> positions;
+ if (!SetupCGGlyphs(cgContext, aBuffer, glyphs, positions)) {
+ ReturnCGContext(cgContext);
+ return false;
+ }
+
+ SetFontSmoothingBackgroundColor(cgContext, mColorSpace, aRenderingOptions);
+ SetFontColor(cgContext, mColorSpace, aPattern);
+
+ ScaledFontMac* macFont = static_cast<ScaledFontMac*>(aFont);
+ if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) {
+ ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, glyphs.begin(),
+ positions.begin(),
+ aBuffer.mNumGlyphs, cgContext);
+ } else {
+ CGContextSetFont(cgContext, macFont->mFont);
+ CGContextSetFontSize(cgContext, macFont->mSize);
+ CGContextShowGlyphsAtPositions(cgContext, glyphs.begin(), positions.begin(),
+ aBuffer.mNumGlyphs);
+ }
+
+ // Calculate the area of the text we just drew
+ CGRect *bboxes = new CGRect[aBuffer.mNumGlyphs];
+ CTFontGetBoundingRectsForGlyphs(macFont->mCTFont, kCTFontDefaultOrientation,
+ glyphs.begin(), bboxes, aBuffer.mNumGlyphs);
+ CGRect extents = ComputeGlyphsExtents(bboxes, positions.begin(), aBuffer.mNumGlyphs, 1.0f);
+ delete[] bboxes;
+
+ CGAffineTransform cgTransform = CGContextGetCTM(cgContext);
+ extents = CGRectApplyAffineTransform(extents, cgTransform);
+
+ // Have to round it out to ensure we fully cover all pixels
+ Rect rect(extents.origin.x, extents.origin.y, extents.size.width, extents.size.height);
+ rect.RoundOut();
+ extents = CGRectMake(rect.x, rect.y, rect.width, rect.height);
+
+ EnsureValidPremultipliedData(cgContext, extents);
+
+ ReturnCGContext(cgContext);
+ return true;
+}
+
+static bool
+HasFontSmoothingBackgroundColor(const GlyphRenderingOptions* aRenderingOptions)
+{
+ // This should generally only be true if we have a popup context menu
+ if (aRenderingOptions && aRenderingOptions->GetType() == FontType::MAC) {
+ Color fontSmoothingBackgroundColor =
+ static_cast<const GlyphRenderingOptionsCG*>(aRenderingOptions)->FontSmoothingBackgroundColor();
+ return fontSmoothingBackgroundColor.a > 0;
+ }
+
+ return false;
+}
+
+static bool
+ShouldUseCGToFillGlyphs(const GlyphRenderingOptions* aOptions, const Pattern& aPattern)
+{
+ return HasFontSmoothingBackgroundColor(aOptions) &&
+ aPattern.GetType() == PatternType::COLOR;
+}
+
+#endif
+
+static bool
+CanDrawFont(ScaledFont* aFont)
+{
+ switch (aFont->GetType()) {
+ case FontType::SKIA:
+ case FontType::CAIRO:
+ case FontType::FONTCONFIG:
+ case FontType::MAC:
+ case FontType::GDI:
+ case FontType::DWRITE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void
+DrawTargetSkia::FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions,
+ const GlyphRenderingOptions *aRenderingOptions)
+{
+ if (!CanDrawFont(aFont)) {
+ return;
+ }
+
+ MarkChanged();
+
+#ifdef MOZ_WIDGET_COCOA
+ if (ShouldUseCGToFillGlyphs(aRenderingOptions, aPattern)) {
+ if (FillGlyphsWithCG(aFont, aBuffer, aPattern, aOptions, aRenderingOptions)) {
+ return;
+ }
+ }
+#endif
+
+ ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
+ SkTypeface* typeface = skiaFont->GetSkTypeface();
+ if (!typeface) {
+ return;
+ }
+
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
+ AntialiasMode aaMode = aFont->GetDefaultAAMode();
+ if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
+ aaMode = aOptions.mAntialiasMode;
+ }
+ bool aaEnabled = aaMode != AntialiasMode::NONE;
+
+ paint.mPaint.setAntiAlias(aaEnabled);
+ paint.mPaint.setTypeface(sk_ref_sp(typeface));
+ paint.mPaint.setTextSize(SkFloatToScalar(skiaFont->mSize));
+ paint.mPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ bool shouldLCDRenderText = ShouldLCDRenderText(aFont->GetType(), aaMode);
+ paint.mPaint.setLCDRenderText(shouldLCDRenderText);
+
+ bool useSubpixelText = true;
+
+ switch (aFont->GetType()) {
+ case FontType::SKIA:
+ case FontType::CAIRO:
+ case FontType::FONTCONFIG:
+ // SkFontHost_cairo does not support subpixel text positioning,
+ // so only enable it for other font hosts.
+ useSubpixelText = false;
+ break;
+ case FontType::MAC:
+ if (aaMode == AntialiasMode::GRAY) {
+ // Normally, Skia enables LCD FontSmoothing which creates thicker fonts
+ // and also enables subpixel AA. CoreGraphics without font smoothing
+ // explicitly creates thinner fonts and grayscale AA.
+ // CoreGraphics doesn't support a configuration that produces thicker
+ // fonts with grayscale AA as LCD Font Smoothing enables or disables both.
+ // However, Skia supports it by enabling font smoothing (producing subpixel AA)
+ // and converts it to grayscale AA. Since Skia doesn't support subpixel AA on
+ // transparent backgrounds, we still want font smoothing for the thicker fonts,
+ // even if it is grayscale AA.
+ //
+ // With explicit Grayscale AA (from -moz-osx-font-smoothing:grayscale),
+ // we want to have grayscale AA with no smoothing at all. This means
+ // disabling the LCD font smoothing behaviour.
+ // To accomplish this we have to explicitly disable hinting,
+ // and disable LCDRenderText.
+ paint.mPaint.setHinting(SkPaint::kNo_Hinting);
+ }
+ break;
+ case FontType::GDI:
+ {
+ if (!shouldLCDRenderText && aaEnabled) {
+ // If we have non LCD GDI text, render the fonts as cleartype and convert them
+ // to grayscale. This seems to be what Chrome and IE are doing on Windows 7.
+ // This also applies if cleartype is disabled system wide.
+ paint.mPaint.setFlags(paint.mPaint.getFlags() | SkPaint::kGenA8FromLCD_Flag);
+ }
+ break;
+ }
+#ifdef XP_WIN
+ case FontType::DWRITE:
+ {
+ ScaledFontDWrite* dwriteFont = static_cast<ScaledFontDWrite*>(aFont);
+ paint.mPaint.setEmbeddedBitmapText(dwriteFont->UseEmbeddedBitmaps());
+
+ if (dwriteFont->ForceGDIMode()) {
+ paint.mPaint.setEmbeddedBitmapText(true);
+ useSubpixelText = false;
+ }
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+
+ paint.mPaint.setSubpixelText(useSubpixelText);
+
+ 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);
+ }
+
+ mCanvas->drawPosText(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), paint.mPaint);
+}
+
+void
+DrawTargetSkia::Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aSource);
+
+ SkPaint maskPaint;
+ SetPaintPattern(maskPaint, aMask);
+
+ SkLayerRasterizer::Builder builder;
+ builder.addLayer(maskPaint);
+ sk_sp<SkLayerRasterizer> raster(builder.detach());
+ paint.mPaint.setRasterizer(raster);
+
+ mCanvas->drawPaint(paint.mPaint);
+}
+
+void
+DrawTargetSkia::MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions)
+{
+ MarkChanged();
+ AutoPaintSetup paint(mCanvas.get(), aOptions, aSource, nullptr, -aOffset);
+
+ sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask);
+ if (!alphaMask) {
+ gfxDebug() << *this << ": MaskSurface() failed to extract alpha for mask";
+ return;
+ }
+
+ mCanvas->drawImage(alphaMask, aOffset.x, aOffset.y, &paint.mPaint);
+}
+
+bool
+DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
+{
+ // Composite the 3D transform with the DT's transform.
+ Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
+ if (fullMat.IsSingular()) {
+ return false;
+ }
+ // Transform the surface bounds and clip to this DT.
+ IntRect xformBounds =
+ RoundedOut(
+ fullMat.TransformAndClipBounds(Rect(Point(0, 0), Size(aSurface->GetSize())),
+ Rect(Point(0, 0), Size(GetSize()))));
+ if (xformBounds.IsEmpty()) {
+ return true;
+ }
+ // Offset the matrix by the transformed origin.
+ fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+
+ // Read in the source data.
+ sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface);
+ if (!srcImage) {
+ return true;
+ }
+
+ // Set up an intermediate destination surface only the size of the transformed bounds.
+ // Try to pass through the source's format unmodified in both the BGRA and ARGB cases.
+ RefPtr<DataSourceSurface> dstSurf =
+ Factory::CreateDataSourceSurface(xformBounds.Size(),
+ !srcImage->isOpaque() ?
+ aSurface->GetFormat() : SurfaceFormat::A8R8G8B8_UINT32,
+ true);
+ if (!dstSurf) {
+ return false;
+ }
+ sk_sp<SkCanvas> dstCanvas(
+ SkCanvas::NewRasterDirect(
+ SkImageInfo::Make(xformBounds.width, xformBounds.height,
+ GfxFormatToSkiaColorType(dstSurf->GetFormat()),
+ kPremul_SkAlphaType),
+ dstSurf->GetData(), dstSurf->Stride()));
+ if (!dstCanvas) {
+ return false;
+ }
+
+ // Do the transform.
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ paint.setBlendMode(SkBlendMode::kSrc);
+
+ SkMatrix xform;
+ GfxMatrixToSkiaMatrix(fullMat, xform);
+ dstCanvas->setMatrix(xform);
+
+ dstCanvas->drawImage(srcImage, 0, 0, &paint);
+ dstCanvas->flush();
+
+ // Temporarily reset the DT's transform, since it has already been composed above.
+ Matrix origTransform = mTransform;
+ SetTransform(Matrix());
+
+ // Draw the transformed surface within the transformed bounds.
+ DrawSurface(dstSurf, Rect(xformBounds), Rect(Point(0, 0), Size(xformBounds.Size())));
+
+ SetTransform(origTransform);
+
+ return true;
+}
+
+bool
+DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
+{
+ if (aMatrix.IsSingular()) {
+ return false;
+ }
+
+ MarkChanged();
+
+ sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
+ if (!image) {
+ return true;
+ }
+
+ mCanvas->save();
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setFilterQuality(kLow_SkFilterQuality);
+
+ SkMatrix xform;
+ GfxMatrixToSkiaMatrix(aMatrix, xform);
+ mCanvas->concat(xform);
+
+ mCanvas->drawImage(image, 0, 0, &paint);
+
+ mCanvas->restore();
+
+ return true;
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetSkia::CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+{
+ RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
+
+ if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
+ gfxDebug() << *this << ": Failure to create source surface from data. Size: " << aSize;
+ return nullptr;
+ }
+
+ return newSurf.forget();
+}
+
+already_AddRefed<DrawTarget>
+DrawTargetSkia::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+ RefPtr<DrawTargetSkia> target = new DrawTargetSkia();
+#ifdef USE_SKIA_GPU
+ if (UsingSkiaGPU()) {
+ // Try to create a GPU draw target first if we're currently using the GPU.
+ // Mark the DT as cached so that shadow DTs, extracted subrects, and similar can be reused.
+ if (target->InitWithGrContext(mGrContext.get(), aSize, aFormat, true)) {
+ return target.forget();
+ }
+ // Otherwise, just fall back to a software draw target.
+ }
+#endif
+
+#ifdef DEBUG
+ if (!IsBackedByPixels(mCanvas.get())) {
+ // If our canvas is backed by vector storage such as PDF then we want to
+ // create a new DrawTarget with similar storage to avoid losing fidelity
+ // (fidelity will be lost if the returned DT is Snapshot()'ed and drawn
+ // back onto us since a raster will be drawn instead of vector commands).
+ NS_WARNING("Not backed by pixels - we need to handle PDF backed SkCanvas");
+ }
+#endif
+
+ if (!target->Init(aSize, aFormat)) {
+ return nullptr;
+ }
+ return target.forget();
+}
+
+bool
+DrawTargetSkia::UsingSkiaGPU() const
+{
+#ifdef USE_SKIA_GPU
+ return !!mGrContext;
+#else
+ return false;
+#endif
+}
+
+#ifdef USE_SKIA_GPU
+already_AddRefed<SourceSurface>
+DrawTargetSkia::OptimizeGPUSourceSurface(SourceSurface *aSurface) const
+{
+ // Check if the underlying SkImage already has an associated GrTexture.
+ sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
+ if (!image || image->isTextureBacked()) {
+ RefPtr<SourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ // Upload the SkImage to a GrTexture otherwise.
+ sk_sp<SkImage> texture = image->makeTextureImage(mGrContext.get());
+ if (texture) {
+ // Create a new SourceSurfaceSkia whose SkImage contains the GrTexture.
+ RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia();
+ if (surface->InitFromImage(texture, aSurface->GetFormat())) {
+ return surface.forget();
+ }
+ }
+
+ // The data was too big to fit in a GrTexture.
+ if (aSurface->GetType() == SurfaceType::SKIA) {
+ // It is already a Skia source surface, so just reuse it as-is.
+ RefPtr<SourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ // Wrap it in a Skia source surface so that can do tiled uploads on-demand.
+ RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia();
+ surface->InitFromImage(image);
+ return surface.forget();
+}
+#endif
+
+already_AddRefed<SourceSurface>
+DrawTargetSkia::OptimizeSourceSurfaceForUnknownAlpha(SourceSurface *aSurface) const
+{
+#ifdef USE_SKIA_GPU
+ if (UsingSkiaGPU()) {
+ return OptimizeGPUSourceSurface(aSurface);
+ }
+#endif
+
+ if (aSurface->GetType() == SurfaceType::SKIA) {
+ RefPtr<SourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
+
+ // For plugins, GDI can sometimes just write 0 to the alpha channel
+ // even for RGBX formats. In this case, we have to manually write
+ // the alpha channel to make Skia happy with RGBX and in case GDI
+ // writes some bad data. Luckily, this only happens on plugins.
+ WriteRGBXFormat(dataSurface->GetData(), dataSurface->GetSize(),
+ dataSurface->Stride(), dataSurface->GetFormat());
+ return dataSurface.forget();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetSkia::OptimizeSourceSurface(SourceSurface *aSurface) const
+{
+#ifdef USE_SKIA_GPU
+ if (UsingSkiaGPU()) {
+ return OptimizeGPUSourceSurface(aSurface);
+ }
+#endif
+
+ if (aSurface->GetType() == SurfaceType::SKIA) {
+ RefPtr<SourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ // 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.
+ RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
+ MOZ_ASSERT(VerifyRGBXFormat(dataSurface->GetData(), dataSurface->GetSize(),
+ dataSurface->Stride(), dataSurface->GetFormat()));
+ return dataSurface.forget();
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+{
+#ifdef USE_SKIA_GPU
+ if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE && UsingSkiaGPU()) {
+ // Wrap the OpenGL texture id in a Skia texture handle.
+ GrBackendTextureDesc texDesc;
+ texDesc.fWidth = aSurface.mSize.width;
+ texDesc.fHeight = aSurface.mSize.height;
+ texDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
+ texDesc.fConfig = GfxFormatToGrConfig(aSurface.mFormat);
+
+ GrGLTextureInfo texInfo;
+ texInfo.fTarget = LOCAL_GL_TEXTURE_2D;
+ texInfo.fID = (GrGLuint)(uintptr_t)aSurface.mSurface;
+ texDesc.fTextureHandle = reinterpret_cast<GrBackendObject>(&texInfo);
+
+ sk_sp<SkImage> texture =
+ SkImage::MakeFromAdoptedTexture(mGrContext.get(), texDesc,
+ GfxFormatToSkiaAlphaType(aSurface.mFormat));
+ RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
+ if (texture && newSurf->InitFromImage(texture, aSurface.mFormat)) {
+ return newSurf.forget();
+ }
+ return nullptr;
+ }
+#endif
+
+ return nullptr;
+}
+
+void
+DrawTargetSkia::CopySurface(SourceSurface *aSurface,
+ const IntRect& aSourceRect,
+ const IntPoint &aDestination)
+{
+ MarkChanged();
+
+ sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
+ if (!image) {
+ return;
+ }
+
+ mCanvas->save();
+ mCanvas->setMatrix(SkMatrix::MakeTrans(SkIntToScalar(aDestination.x), SkIntToScalar(aDestination.y)));
+ mCanvas->clipRect(SkRect::MakeIWH(aSourceRect.width, aSourceRect.height), kReplace_SkClipOp);
+
+ SkPaint paint;
+ if (!image->isOpaque()) {
+ // Keep the xfermode as SOURCE_OVER for opaque bitmaps
+ // http://code.google.com/p/skia/issues/detail?id=628
+ paint.setBlendMode(SkBlendMode::kSrc);
+ }
+ // drawImage with A8 images ends up doing a mask operation
+ // so we need to clear before
+ if (SkImageIsMask(image)) {
+ mCanvas->clear(SK_ColorTRANSPARENT);
+ }
+ mCanvas->drawImage(image, -SkIntToScalar(aSourceRect.x), -SkIntToScalar(aSourceRect.y), &paint);
+ mCanvas->restore();
+}
+
+bool
+DrawTargetSkia::Init(const IntSize &aSize, SurfaceFormat aFormat)
+{
+ if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) {
+ return false;
+ }
+
+ // we need to have surfaces that have a stride aligned to 4 for interop with cairo
+ SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
+ size_t stride = SkAlign4(info.minRowBytes());
+ mSurface = SkSurface::MakeRaster(info, stride, nullptr);
+ if (!mSurface) {
+ return false;
+ }
+
+ mSize = aSize;
+ mFormat = aFormat;
+ mCanvas = sk_ref_sp(mSurface->getCanvas());
+
+ if (info.isOpaque()) {
+ mCanvas->clear(SK_ColorBLACK);
+ }
+ return true;
+}
+
+bool
+DrawTargetSkia::Init(SkCanvas* aCanvas)
+{
+ mCanvas = sk_ref_sp(aCanvas);
+
+ SkImageInfo imageInfo = mCanvas->imageInfo();
+
+ // If the canvas is backed by pixels we clear it to be on the safe side. If
+ // it's not (for example, for PDF output) we don't.
+ if (IsBackedByPixels(mCanvas.get())) {
+ SkColor clearColor = imageInfo.isOpaque() ? SK_ColorBLACK : SK_ColorTRANSPARENT;
+ mCanvas->clear(clearColor);
+ }
+
+ SkISize size = mCanvas->getBaseLayerSize();
+ mSize.width = size.width();
+ mSize.height = size.height();
+ mFormat = SkiaColorTypeToGfxFormat(imageInfo.colorType(),
+ imageInfo.alphaType());
+ return true;
+}
+
+#ifdef USE_SKIA_GPU
+/** Indicating a DT should be cached means that space will be reserved in Skia's cache
+ * for the render target at creation time, with any unused resources exceeding the cache
+ * limits being purged. When the DT is freed, it will then be guaranteed to be kept around
+ * for subsequent allocations until it gets incidentally purged.
+ *
+ * If it is not marked as cached, no space will be purged to make room for the render
+ * target in the cache. When the DT is freed, If there is space within the resource limits
+ * it may be added to the cache, otherwise it will be freed immediately if the cache is
+ * already full.
+ *
+ * If you want to ensure that the resources will be kept around for reuse, it is better
+ * to mark them as cached. Such resources should be short-lived to ensure they don't
+ * permanently tie up cache resource limits. Long-lived resources should generally be
+ * left as uncached.
+ *
+ * In neither case will cache resource limits affect whether the resource allocation
+ * succeeds. The amount of in-use GPU resources is allowed to exceed the size of the cache.
+ * Thus, only hard GPU out-of-memory conditions will cause resource allocation to fail.
+ */
+bool
+DrawTargetSkia::InitWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat,
+ bool aCached)
+{
+ MOZ_ASSERT(aGrContext, "null GrContext");
+
+ if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) {
+ return false;
+ }
+
+ // Create a GPU rendertarget/texture using the supplied GrContext.
+ // NewRenderTarget also implicitly clears the underlying texture on creation.
+ mSurface =
+ SkSurface::MakeRenderTarget(aGrContext,
+ SkBudgeted(aCached),
+ MakeSkiaImageInfo(aSize, aFormat));
+ if (!mSurface) {
+ return false;
+ }
+
+ mGrContext = sk_ref_sp(aGrContext);
+ mSize = aSize;
+ mFormat = aFormat;
+ mCanvas = sk_ref_sp(mSurface->getCanvas());
+ return true;
+}
+
+#endif
+
+bool
+DrawTargetSkia::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized)
+{
+ MOZ_ASSERT((aFormat != SurfaceFormat::B8G8R8X8) ||
+ aUninitialized || VerifyRGBXFormat(aData, aSize, aStride, aFormat));
+
+ mSurface = SkSurface::MakeRasterDirect(MakeSkiaImageInfo(aSize, aFormat), aData, aStride);
+ if (!mSurface) {
+ return false;
+ }
+
+ mSize = aSize;
+ mFormat = aFormat;
+ mCanvas = sk_ref_sp(mSurface->getCanvas());
+ return true;
+}
+
+void
+DrawTargetSkia::SetTransform(const Matrix& aTransform)
+{
+ SkMatrix mat;
+ GfxMatrixToSkiaMatrix(aTransform, mat);
+ mCanvas->setMatrix(mat);
+ mTransform = aTransform;
+}
+
+void*
+DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType)
+{
+#ifdef USE_SKIA_GPU
+ if (aType == NativeSurfaceType::OPENGL_TEXTURE && mSurface) {
+ GrBackendObject handle = mSurface->getTextureHandle(SkSurface::kFlushRead_BackendHandleAccess);
+ if (handle) {
+ return (void*)(uintptr_t)reinterpret_cast<GrGLTextureInfo *>(handle)->fID;
+ }
+ }
+#endif
+ return nullptr;
+}
+
+
+already_AddRefed<PathBuilder>
+DrawTargetSkia::CreatePathBuilder(FillRule aFillRule) const
+{
+ return MakeAndAddRef<PathBuilderSkia>(aFillRule);
+}
+
+void
+DrawTargetSkia::ClearRect(const Rect &aRect)
+{
+ MarkChanged();
+ mCanvas->save();
+ mCanvas->clipRect(RectToSkRect(aRect), kIntersect_SkClipOp, true);
+ SkColor clearColor = (mFormat == SurfaceFormat::B8G8R8X8) ? SK_ColorBLACK : SK_ColorTRANSPARENT;
+ mCanvas->clear(clearColor);
+ mCanvas->restore();
+}
+
+void
+DrawTargetSkia::PushClip(const Path *aPath)
+{
+ if (aPath->GetBackendType() != BackendType::SKIA) {
+ return;
+ }
+
+ const PathSkia *skiaPath = static_cast<const PathSkia*>(aPath);
+ mCanvas->save();
+ mCanvas->clipPath(skiaPath->GetPath(), kIntersect_SkClipOp, true);
+}
+
+void
+DrawTargetSkia::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
+{
+ // Build a region by unioning all the rects together.
+ SkRegion region;
+ for (uint32_t i = 0; i < aCount; i++) {
+ region.op(IntRectToSkIRect(aRects[i]), SkRegion::kUnion_Op);
+ }
+
+ // Clip with the resulting region. clipRegion does not transform
+ // this region by the current transform, unlike the other SkCanvas
+ // clip methods, so it is just passed through in device-space.
+ mCanvas->save();
+ mCanvas->clipRegion(region, kIntersect_SkClipOp);
+}
+
+void
+DrawTargetSkia::PushClipRect(const Rect& aRect)
+{
+ SkRect rect = RectToSkRect(aRect);
+
+ mCanvas->save();
+ mCanvas->clipRect(rect, kIntersect_SkClipOp, true);
+}
+
+void
+DrawTargetSkia::PopClip()
+{
+ mCanvas->restore();
+}
+
+// Image filter that just passes the source through to the result unmodified.
+class CopyLayerImageFilter : public SkImageFilter
+{
+public:
+ CopyLayerImageFilter()
+ : SkImageFilter(nullptr, 0, nullptr)
+ {}
+
+ virtual sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source,
+ const Context& ctx,
+ SkIPoint* offset) const override {
+ offset->set(0, 0);
+ return sk_ref_sp(source);
+ }
+
+ SK_TO_STRING_OVERRIDE()
+ SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(CopyLayerImageFilter)
+};
+
+sk_sp<SkFlattenable>
+CopyLayerImageFilter::CreateProc(SkReadBuffer& buffer)
+{
+ SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
+ return sk_make_sp<CopyLayerImageFilter>();
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void
+CopyLayerImageFilter::toString(SkString* str) const
+{
+ str->append("CopyLayerImageFilter: ()");
+}
+#endif
+
+void
+DrawTargetSkia::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+ const Matrix& aMaskTransform, const IntRect& aBounds,
+ bool aCopyBackground)
+{
+ PushedLayer layer(GetPermitSubpixelAA(), aOpaque, aOpacity, aMask, aMaskTransform);
+ mPushedLayers.push_back(layer);
+
+ SkPaint paint;
+
+ // If we have a mask, set the opacity to 0 so that SkCanvas::restore skips
+ // implicitly drawing the layer so that we can properly mask it in PopLayer.
+ paint.setAlpha(aMask ? 0 : ColorFloatToByte(aOpacity));
+
+ SkRect bounds = IntRectToSkRect(aBounds);
+
+ sk_sp<SkImageFilter> backdrop(aCopyBackground ? new CopyLayerImageFilter : nullptr);
+
+ SkCanvas::SaveLayerRec saveRec(aBounds.IsEmpty() ? nullptr : &bounds,
+ &paint,
+ backdrop.get(),
+ aOpaque ? SkCanvas::kIsOpaque_SaveLayerFlag : 0);
+
+ mCanvas->saveLayer(saveRec);
+
+ SetPermitSubpixelAA(aOpaque);
+
+#ifdef MOZ_WIDGET_COCOA
+ CGContextRelease(mCG);
+ mCG = nullptr;
+#endif
+}
+
+void
+DrawTargetSkia::PopLayer()
+{
+ MarkChanged();
+
+ MOZ_ASSERT(mPushedLayers.size());
+ const PushedLayer& layer = mPushedLayers.back();
+
+ if (layer.mMask) {
+ // If we have a mask, take a reference to the top layer's device so that
+ // we can mask it ourselves. This assumes we forced SkCanvas::restore to
+ // skip implicitly drawing the layer.
+ sk_sp<SkBaseDevice> layerDevice = sk_ref_sp(mCanvas->getTopDevice());
+ SkIRect layerBounds = layerDevice->getGlobalBounds();
+ sk_sp<SkImage> layerImage;
+ SkPixmap layerPixmap;
+ if (layerDevice->peekPixels(&layerPixmap)) {
+ layerImage = SkImage::MakeFromRaster(layerPixmap, nullptr, nullptr);
+#ifdef USE_SKIA_GPU
+ } else if (GrDrawContext* drawCtx = mCanvas->internal_private_accessTopLayerDrawContext()) {
+ drawCtx->prepareForExternalIO();
+ if (GrTexture* tex = drawCtx->accessRenderTarget()->asTexture()) {
+ layerImage = sk_make_sp<SkImage_Gpu>(layerBounds.width(), layerBounds.height(),
+ kNeedNewImageUniqueID,
+ layerDevice->imageInfo().alphaType(),
+ tex, nullptr, SkBudgeted::kNo);
+ }
+#endif
+ }
+
+ // Restore the background with the layer's device left alive.
+ mCanvas->restore();
+
+ SkPaint paint;
+ paint.setAlpha(ColorFloatToByte(layer.mOpacity));
+
+ SkMatrix maskMat, layerMat;
+ // Get the total transform affecting the mask, considering its pattern
+ // transform and the current canvas transform.
+ GfxMatrixToSkiaMatrix(layer.mMaskTransform, maskMat);
+ maskMat.postConcat(mCanvas->getTotalMatrix());
+ if (!maskMat.invert(&layerMat)) {
+ gfxDebug() << *this << ": PopLayer() failed to invert mask transform";
+ } else {
+ // The layer should not be affected by the current canvas transform,
+ // even though the mask is. So first we use the inverse of the transform
+ // affecting the mask, then add back on the layer's origin.
+ layerMat.preTranslate(layerBounds.x(), layerBounds.y());
+
+ if (layerImage) {
+ paint.setShader(layerImage->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &layerMat));
+ } else {
+ paint.setColor(SK_ColorTRANSPARENT);
+ }
+
+ sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(layer.mMask);
+ if (!alphaMask) {
+ gfxDebug() << *this << ": PopLayer() failed to extract alpha for mask";
+ } else {
+ mCanvas->save();
+
+ // The layer may be smaller than the canvas size, so make sure drawing is
+ // clipped to within the bounds of the layer.
+ mCanvas->resetMatrix();
+ mCanvas->clipRect(SkRect::Make(layerBounds));
+
+ mCanvas->setMatrix(maskMat);
+ mCanvas->drawImage(alphaMask, 0, 0, &paint);
+
+ mCanvas->restore();
+ }
+ }
+ } else {
+ mCanvas->restore();
+ }
+
+ SetPermitSubpixelAA(layer.mOldPermitSubpixelAA);
+
+ mPushedLayers.pop_back();
+
+#ifdef MOZ_WIDGET_COCOA
+ CGContextRelease(mCG);
+ mCG = nullptr;
+#endif
+}
+
+already_AddRefed<GradientStops>
+DrawTargetSkia::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const
+{
+ std::vector<GradientStop> stops;
+ stops.resize(aNumStops);
+ for (uint32_t i = 0; i < aNumStops; i++) {
+ stops[i] = aStops[i];
+ }
+ std::stable_sort(stops.begin(), stops.end());
+
+ return MakeAndAddRef<GradientStopsSkia>(stops, aNumStops, aExtendMode);
+}
+
+already_AddRefed<FilterNode>
+DrawTargetSkia::CreateFilter(FilterType aType)
+{
+ return FilterNodeSoftware::Create(aType);
+}
+
+void
+DrawTargetSkia::MarkChanged()
+{
+ if (mSnapshot) {
+ mSnapshot->DrawTargetWillChange();
+ mSnapshot = nullptr;
+
+ // Handle copying of any image snapshots bound to the surface.
+ if (mSurface) {
+ mSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
+ }
+ }
+}
+
+void
+DrawTargetSkia::SnapshotDestroyed()
+{
+ mSnapshot = nullptr;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/DrawTargetSkia.h b/gfx/2d/DrawTargetSkia.h
new file mode 100644
index 000000000..c2e43da89
--- /dev/null
+++ b/gfx/2d/DrawTargetSkia.h
@@ -0,0 +1,214 @@
+/* -*- 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_SOURCESURFACESKIA_H
+#define _MOZILLA_GFX_SOURCESURFACESKIA_H
+
+#include "skia/include/core/SkCanvas.h"
+#include "skia/include/core/SkSurface.h"
+
+#include "2D.h"
+#include "HelpersSkia.h"
+#include "Rect.h"
+#include "PathSkia.h"
+#include <sstream>
+#include <vector>
+
+#ifdef MOZ_WIDGET_COCOA
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+class SourceSurfaceSkia;
+
+class DrawTargetSkia : public DrawTarget
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetSkia, override)
+ DrawTargetSkia();
+ virtual ~DrawTargetSkia();
+
+ virtual DrawTargetType GetType() const override;
+ virtual BackendType GetBackendType() const override { return BackendType::SKIA; }
+ virtual already_AddRefed<SourceSurface> Snapshot() override;
+ virtual IntSize GetSize() override { return mSize; }
+ virtual bool LockBits(uint8_t** aData, IntSize* aSize,
+ int32_t* aStride, SurfaceFormat* aFormat,
+ IntPoint* aOrigin = nullptr) override;
+ virtual void ReleaseBits(uint8_t* aData) override;
+ virtual void Flush() override;
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
+ 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) override;
+ virtual void ClearRect(const Rect &aRect) 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;
+#ifdef MOZ_WIDGET_COCOA
+ CGContextRef BorrowCGContext(const DrawOptions &aOptions);
+ void ReturnCGContext(CGContextRef);
+ bool FillGlyphsWithCG(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions(),
+ const GlyphRenderingOptions *aRenderingOptions = nullptr);
+#endif
+
+ 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 MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions = DrawOptions()) override;
+ virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
+ const Matrix4x4& aMatrix) override;
+ virtual void PushClip(const Path *aPath) override;
+ virtual void PushClipRect(const Rect& aRect) override;
+ virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override;
+ virtual void PopClip() override;
+ virtual void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) override;
+ virtual void PopLayer() override;
+ virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const override;
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
+ virtual already_AddRefed<SourceSurface> OptimizeSourceSurfaceForUnknownAlpha(SourceSurface *aSurface) const override;
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
+ virtual already_AddRefed<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
+ virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
+ virtual void SetTransform(const Matrix &aTransform) override;
+ virtual void *GetNativeSurface(NativeSurfaceType aType) override;
+ virtual void DetachAllSnapshots() override { MarkChanged(); }
+
+ bool Init(const IntSize &aSize, SurfaceFormat aFormat);
+ bool Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false);
+ bool Init(SkCanvas* aCanvas);
+
+#ifdef USE_SKIA_GPU
+ bool InitWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat,
+ bool aCached);
+ virtual bool
+ InitWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat) override {
+ return InitWithGrContext(aGrContext, aSize, aFormat, false);
+ }
+
+ already_AddRefed<SourceSurface> OptimizeGPUSourceSurface(SourceSurface *aSurface) const;
+#endif
+
+ // Skia assumes that texture sizes fit in 16-bit signed integers.
+ static size_t GetMaxSurfaceSize() {
+ return 32767;
+ }
+
+ operator std::string() const {
+ std::stringstream stream;
+ stream << "DrawTargetSkia(" << this << ")";
+ return stream.str();
+ }
+
+private:
+ friend class SourceSurfaceSkia;
+ void SnapshotDestroyed();
+
+ void MarkChanged();
+
+ bool ShouldLCDRenderText(FontType aFontType, AntialiasMode aAntialiasMode);
+
+ bool UsingSkiaGPU() const;
+
+ struct PushedLayer
+ {
+ PushedLayer(bool aOldPermitSubpixelAA,
+ bool aOpaque,
+ Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform)
+ : mOldPermitSubpixelAA(aOldPermitSubpixelAA),
+ mOpaque(aOpaque),
+ mOpacity(aOpacity),
+ mMask(aMask),
+ mMaskTransform(aMaskTransform)
+ {}
+ bool mOldPermitSubpixelAA;
+ bool mOpaque;
+ Float mOpacity;
+ RefPtr<SourceSurface> mMask;
+ Matrix mMaskTransform;
+ };
+ std::vector<PushedLayer> mPushedLayers;
+
+#ifdef USE_SKIA_GPU
+ sk_sp<GrContext> mGrContext;
+#endif
+
+ IntSize mSize;
+ sk_sp<SkSurface> mSurface;
+ sk_sp<SkCanvas> mCanvas;
+ SourceSurfaceSkia* mSnapshot;
+
+#ifdef MOZ_WIDGET_COCOA
+ CGContextRef mCG;
+ CGColorSpaceRef mColorSpace;
+ uint8_t* mCanvasData;
+ IntSize mCGSize;
+#endif
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_SOURCESURFACESKIA_H
diff --git a/gfx/2d/DrawTargetTiled.cpp b/gfx/2d/DrawTargetTiled.cpp
new file mode 100644
index 000000000..fd7465408
--- /dev/null
+++ b/gfx/2d/DrawTargetTiled.cpp
@@ -0,0 +1,337 @@
+/* -*- 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 "DrawTargetTiled.h"
+#include "Logging.h"
+#include "PathHelpers.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;
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetTiled::Snapshot()
+{
+ return MakeAndAddRef<SnapshotTiled>(mTiles, mRect);
+}
+
+void
+DrawTargetTiled::DetachAllSnapshots()
+{}
+
+// 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_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);
+ }
+ }
+}
+
+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 = aPath->GetBounds(mTransform);
+ deviceRect.Inflate(MaxStrokeExtents(aStrokeOptions, 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::StrokeRect(const Rect& aRect, const Pattern& aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions& aDrawOptions)
+{
+ Rect deviceRect = mTransform.TransformBounds(aRect);
+ Margin strokeMargin = MaxStrokeExtents(aStrokeOptions, mTransform);
+ Rect outerRect = deviceRect;
+ outerRect.Inflate(strokeMargin);
+ Rect innerRect;
+ if (mTransform.IsRectilinear()) {
+ // If rects are mapped to rects, we can compute the inner rect
+ // of the stroked rect.
+ innerRect = deviceRect;
+ innerRect.Deflate(strokeMargin);
+ }
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ if (mTiles[i].mClippedOut) {
+ continue;
+ }
+ Rect tileRect(mTiles[i].mTileOrigin.x,
+ mTiles[i].mTileOrigin.y,
+ mTiles[i].mDrawTarget->GetSize().width,
+ mTiles[i].mDrawTarget->GetSize().height);
+ if (outerRect.Intersects(tileRect) && !innerRect.Contains(tileRect)) {
+ mTiles[i].mDrawTarget->StrokeRect(aRect, aPattern, aStrokeOptions, aDrawOptions);
+ }
+ }
+}
+
+void
+DrawTargetTiled::StrokeLine(const Point& aStart, const Point& aEnd, const Pattern& aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions& aDrawOptions)
+{
+ Rect lineBounds = Rect(aStart, Size()).UnionEdges(Rect(aEnd, Size()));
+ Rect deviceRect = mTransform.TransformBounds(lineBounds);
+ deviceRect.Inflate(MaxStrokeExtents(aStrokeOptions, 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->StrokeLine(aStart, aEnd, 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);
+ }
+ }
+}
+
+void
+DrawTargetTiled::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+ const Matrix& aMaskTransform, const IntRect& aBounds,
+ bool aCopyBackground)
+{
+ // XXX - not sure this is what we want or whether we want to continue drawing to a larger
+ // intermediate surface, that would require tweaking the code in here a little though.
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ IntRect bounds = aBounds;
+ bounds.MoveBy(-mTiles[i].mTileOrigin);
+ mTiles[i].mDrawTarget->PushLayer(aOpaque, aOpacity, aMask, aMaskTransform, aBounds);
+ }
+}
+
+void
+DrawTargetTiled::PopLayer()
+{
+ // XXX - not sure this is what we want or whether we want to continue drawing to a larger
+ // intermediate surface, that would require tweaking the code in here a little though.
+ for (size_t i = 0; i < mTiles.size(); i++) {
+ mTiles[i].mDrawTarget->PopLayer();
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/DrawTargetTiled.h b/gfx/2d/DrawTargetTiled.h
new file mode 100644
index 000000000..23e8318a3
--- /dev/null
+++ b/gfx/2d/DrawTargetTiled.h
@@ -0,0 +1,221 @@
+/* -*- 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 already_AddRefed<SourceSurface> Snapshot() override;
+ virtual void DetachAllSnapshots() 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("GFX: DrawSurfaceWithShadow"); }
+
+ 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 PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds = IntRect(),
+ bool aCopyBackground = false) override;
+ virtual void PopLayer() override;
+
+
+ virtual void SetTransform(const Matrix &aTransform) override;
+
+ virtual already_AddRefed<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 already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override
+ {
+ return mTiles[0].mDrawTarget->OptimizeSourceSurface(aSurface);
+ }
+
+ virtual already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override
+ {
+ return mTiles[0].mDrawTarget->CreateSourceSurfaceFromNativeSurface(aSurface);
+ }
+
+ virtual already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override
+ {
+ return mTiles[0].mDrawTarget->CreateSimilarDrawTarget(aSize, aFormat);
+ }
+
+ virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override
+ {
+ return mTiles[0].mDrawTarget->CreatePathBuilder(aFillRule);
+ }
+
+ virtual already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode = ExtendMode::CLAMP) const override
+ {
+ return mTiles[0].mDrawTarget->CreateGradientStops(aStops, aNumStops, aExtendMode);
+ }
+ virtual already_AddRefed<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 already_AddRefed<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());
+
+ if (!dt) {
+ gfxWarning() << "DrawTargetTiled::GetDataSurface failed in CreateDrawTargetForData";
+ surf->Unmap();
+ return nullptr;
+ }
+ 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;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/gfx/2d/DrawingJob.cpp b/gfx/2d/DrawingJob.cpp
new file mode 100644
index 000000000..728e330f4
--- /dev/null
+++ b/gfx/2d/DrawingJob.cpp
@@ -0,0 +1,120 @@
+/* -*- 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 "DrawingJob.h"
+#include "JobScheduler.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+DrawingJobBuilder::DrawingJobBuilder()
+{}
+
+DrawingJobBuilder::~DrawingJobBuilder()
+{
+ MOZ_ASSERT(!mDrawTarget);
+}
+
+void
+DrawingJob::Clear()
+{
+ mCommandBuffer = nullptr;
+ mCursor = 0;
+}
+
+void
+DrawingJobBuilder::BeginDrawingJob(DrawTarget* aTarget, IntPoint aOffset,
+ SyncObject* aStart)
+{
+ MOZ_ASSERT(mCommandOffsets.empty());
+ MOZ_ASSERT(aTarget);
+ mDrawTarget = aTarget;
+ mOffset = aOffset;
+ mStart = aStart;
+}
+
+DrawingJob*
+DrawingJobBuilder::EndDrawingJob(CommandBuffer* aCmdBuffer,
+ SyncObject* aCompletion,
+ WorkerThread* aPinToWorker)
+{
+ MOZ_ASSERT(mDrawTarget);
+ DrawingJob* task = new DrawingJob(mDrawTarget, mOffset, mStart, aCompletion, aPinToWorker);
+ task->mCommandBuffer = aCmdBuffer;
+ task->mCommandOffsets = Move(mCommandOffsets);
+
+ mDrawTarget = nullptr;
+ mOffset = IntPoint();
+ mStart = nullptr;
+
+ return task;
+}
+
+DrawingJob::DrawingJob(DrawTarget* aTarget, IntPoint aOffset,
+ SyncObject* aStart, SyncObject* aCompletion,
+ WorkerThread* aPinToWorker)
+: Job(aStart, aCompletion, aPinToWorker)
+, mCommandBuffer(nullptr)
+, mCursor(0)
+, mDrawTarget(aTarget)
+, mOffset(aOffset)
+{
+ mCommandOffsets.reserve(64);
+}
+
+JobStatus
+DrawingJob::Run()
+{
+ while (mCursor < mCommandOffsets.size()) {
+
+ const DrawingCommand* cmd = mCommandBuffer->GetDrawingCommand(mCommandOffsets[mCursor]);
+
+ if (!cmd) {
+ return JobStatus::Error;
+ }
+
+ cmd->ExecuteOnDT(mDrawTarget);
+
+ ++mCursor;
+ }
+
+ return JobStatus::Complete;
+}
+
+DrawingJob::~DrawingJob()
+{
+ Clear();
+}
+
+const DrawingCommand*
+CommandBuffer::GetDrawingCommand(ptrdiff_t aId)
+{
+ return static_cast<DrawingCommand*>(mStorage.GetStorage(aId));
+}
+
+CommandBuffer::~CommandBuffer()
+{
+ mStorage.ForEach([](void* item){
+ static_cast<DrawingCommand*>(item)->~DrawingCommand();
+ });
+ mStorage.Clear();
+}
+
+void
+CommandBufferBuilder::BeginCommandBuffer(size_t aBufferSize)
+{
+ MOZ_ASSERT(!mCommands);
+ mCommands = new CommandBuffer(aBufferSize);
+}
+
+already_AddRefed<CommandBuffer>
+CommandBufferBuilder::EndCommandBuffer()
+{
+ return mCommands.forget();
+}
+
+} // namespace
+} // namespace
diff --git a/gfx/2d/DrawingJob.h b/gfx/2d/DrawingJob.h
new file mode 100644
index 000000000..a384dabda
--- /dev/null
+++ b/gfx/2d/DrawingJob.h
@@ -0,0 +1,158 @@
+/* -*- 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_COMMANDBUFFER_H_
+#define MOZILLA_GFX_COMMANDBUFFER_H_
+
+#include <stdint.h>
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/gfx/JobScheduler.h"
+#include "mozilla/gfx/IterableArena.h"
+#include "mozilla/RefCounted.h"
+#include "DrawCommand.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DrawingCommand;
+class PrintCommand;
+class SignalCommand;
+class DrawingJob;
+class WaitCommand;
+
+class SyncObject;
+class MultiThreadedJobQueue;
+
+class DrawTarget;
+
+class DrawingJobBuilder;
+class CommandBufferBuilder;
+
+/// Contains a sequence of immutable drawing commands that are typically used by
+/// several DrawingJobs.
+///
+/// CommandBuffer objects are built using CommandBufferBuilder.
+class CommandBuffer : public external::AtomicRefCounted<CommandBuffer>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(CommandBuffer)
+
+ ~CommandBuffer();
+
+ const DrawingCommand* GetDrawingCommand(ptrdiff_t aId);
+
+protected:
+ explicit CommandBuffer(size_t aSize = 256)
+ : mStorage(IterableArena::GROWABLE, aSize)
+ {}
+
+ IterableArena mStorage;
+ friend class CommandBufferBuilder;
+};
+
+/// Generates CommandBuffer objects.
+///
+/// The builder is a separate object to ensure that commands are not added to a
+/// submitted CommandBuffer.
+class CommandBufferBuilder
+{
+public:
+ void BeginCommandBuffer(size_t aBufferSize = 256);
+
+ already_AddRefed<CommandBuffer> EndCommandBuffer();
+
+ /// Build the CommandBuffer, command after command.
+ /// This must be used between BeginCommandBuffer and EndCommandBuffer.
+ template<typename T, typename... Args>
+ ptrdiff_t AddCommand(Args&&... aArgs)
+ {
+ static_assert(IsBaseOf<DrawingCommand, T>::value,
+ "T must derive from DrawingCommand");
+ return mCommands->mStorage.Alloc<T>(Forward<Args>(aArgs)...);
+ }
+
+ bool HasCommands() const { return !!mCommands; }
+
+protected:
+ RefPtr<CommandBuffer> mCommands;
+};
+
+/// Stores multiple commands to be executed sequencially.
+class DrawingJob : public Job {
+public:
+ ~DrawingJob();
+
+ virtual JobStatus Run() override;
+
+protected:
+ DrawingJob(DrawTarget* aTarget,
+ IntPoint aOffset,
+ SyncObject* aStart,
+ SyncObject* aCompletion,
+ WorkerThread* aPinToWorker = nullptr);
+
+ /// Runs the tasks's destructors and resets the buffer.
+ void Clear();
+
+ std::vector<ptrdiff_t> mCommandOffsets;
+ RefPtr<CommandBuffer> mCommandBuffer;
+ uint32_t mCursor;
+
+ RefPtr<DrawTarget> mDrawTarget;
+ IntPoint mOffset;
+
+ friend class DrawingJobBuilder;
+};
+
+/// Generates DrawingJob objects.
+///
+/// The builder is a separate object to ensure that commands are not added to a
+/// submitted DrawingJob.
+class DrawingJobBuilder {
+public:
+ DrawingJobBuilder();
+
+ ~DrawingJobBuilder();
+
+ /// Allocates a DrawingJob.
+ ///
+ /// call this method before starting to add commands.
+ void BeginDrawingJob(DrawTarget* aTarget, IntPoint aOffset,
+ SyncObject* aStart = nullptr);
+
+ /// Build the DrawingJob, command after command.
+ /// This must be used between BeginDrawingJob and EndDrawingJob.
+ void AddCommand(ptrdiff_t offset)
+ {
+ mCommandOffsets.push_back(offset);
+ }
+
+ /// Finalizes and returns the drawing task.
+ ///
+ /// If aCompletion is not null, the sync object will be signaled after the
+ /// task buffer is destroyed (and after the destructor of the tasks have run).
+ /// In most cases this means after the completion of all tasks in the task buffer,
+ /// but also when the task buffer is destroyed due to an error.
+ DrawingJob* EndDrawingJob(CommandBuffer* aCmdBuffer,
+ SyncObject* aCompletion = nullptr,
+ WorkerThread* aPinToWorker = nullptr);
+
+ /// Returns true between BeginDrawingJob and EndDrawingJob, false otherwise.
+ bool HasDrawingJob() const { return !!mDrawTarget; }
+
+protected:
+ std::vector<ptrdiff_t> mCommandOffsets;
+ RefPtr<DrawTarget> mDrawTarget;
+ IntPoint mOffset;
+ RefPtr<SyncObject> mStart;
+};
+
+} // namespace
+} // namespace
+
+#endif
diff --git a/gfx/2d/ExtendInputEffectD2D1.cpp b/gfx/2d/ExtendInputEffectD2D1.cpp
new file mode 100644
index 000000000..d48de53c3
--- /dev/null
+++ b/gfx/2d/ExtendInputEffectD2D1.cpp
@@ -0,0 +1,204 @@
+/* -*- 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>
+ );
+
+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..724c6a71e
--- /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
new file mode 100644
index 000000000..5cd5d14ea
--- /dev/null
+++ b/gfx/2d/Factory.cpp
@@ -0,0 +1,985 @@
+/* -*- 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"
+
+#ifdef USE_CAIRO
+#include "DrawTargetCairo.h"
+#include "ScaledFontCairo.h"
+#include "SourceSurfaceCairo.h"
+#endif
+
+#ifdef USE_SKIA
+#include "DrawTargetSkia.h"
+#include "ScaledFontBase.h"
+#ifdef MOZ_ENABLE_FREETYPE
+#define USE_SKIA_FREETYPE
+#include "ScaledFontCairo.h"
+#endif
+#endif
+
+#if defined(WIN32)
+#include "ScaledFontWin.h"
+#include "NativeFontResourceGDI.h"
+#endif
+
+#ifdef XP_DARWIN
+#include "ScaledFontMac.h"
+#include "NativeFontResourceMac.h"
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+#include "ScaledFontFontconfig.h"
+#endif
+
+#ifdef WIN32
+#include "DrawTargetD2D1.h"
+#include "ScaledFontDWrite.h"
+#include "NativeFontResourceDWrite.h"
+#include <d3d10_1.h>
+#include "HelpersD2D.h"
+#endif
+
+#include "DrawTargetDual.h"
+#include "DrawTargetTiled.h"
+#include "DrawTargetRecording.h"
+
+#include "SourceSurfaceRawData.h"
+
+#include "DrawEventRecorder.h"
+
+#include "Logging.h"
+
+#include "mozilla/CheckedInt.h"
+
+#if defined(MOZ_LOGGING)
+GFX2D_API mozilla::LogModule*
+GetGFX2DLog()
+{
+ static mozilla::LazyLogModule sLog("gfx2d");
+ return sLog;
+}
+#endif
+
+// The following code was largely taken from xpcom/glue/SSE.cpp and
+// made a little simpler.
+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
+HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit)
+{
+ unsigned int regs[4];
+ return __get_cpuid(level, &regs[0], &regs[1], &regs[2], &regs[3]) &&
+ (regs[reg] & bit);
+}
+#endif
+
+#define HAVE_CPU_DETECTION
+#else
+
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64))
+// MSVC 2005 or later supports __cpuid by intrin.h
+#include <intrin.h>
+
+#define HAVE_CPU_DETECTION
+#elif defined(__SUNPRO_CC) && (defined(__i386) || defined(__x86_64__))
+
+// Define a function identical to MSVC function.
+#ifdef __i386
+static void
+__cpuid(int CPUInfo[4], int InfoType)
+{
+ asm (
+ "xchg %esi, %ebx\n"
+ "cpuid\n"
+ "movl %eax, (%edi)\n"
+ "movl %ebx, 4(%edi)\n"
+ "movl %ecx, 8(%edi)\n"
+ "movl %edx, 12(%edi)\n"
+ "xchg %esi, %ebx\n"
+ :
+ : "a"(InfoType), // %eax
+ "D"(CPUInfo) // %edi
+ : "%ecx", "%edx", "%esi"
+ );
+}
+#else
+static void
+__cpuid(int CPUInfo[4], int InfoType)
+{
+ asm (
+ "xchg %rsi, %rbx\n"
+ "cpuid\n"
+ "movl %eax, (%rdi)\n"
+ "movl %ebx, 4(%rdi)\n"
+ "movl %ecx, 8(%rdi)\n"
+ "movl %edx, 12(%rdi)\n"
+ "xchg %rsi, %rbx\n"
+ :
+ : "a"(InfoType), // %eax
+ "D"(CPUInfo) // %rdi
+ : "%ecx", "%edx", "%rsi"
+ );
+}
+
+#define HAVE_CPU_DETECTION
+#endif
+#endif
+
+#ifdef HAVE_CPU_DETECTION
+static inline bool
+HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit)
+{
+ // Check that the level in question is supported.
+ volatile int regs[4];
+ __cpuid((int *)regs, level & 0x80000000u);
+ if (unsigned(regs[0]) < level)
+ return false;
+ __cpuid((int *)regs, level);
+ return !!(unsigned(regs[reg]) & bit);
+}
+#endif
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+// In Gecko, this value is managed by gfx.logging.level in gfxPrefs.
+int32_t LoggingPrefs::sGfxLogLevel = LOG_DEFAULT;
+
+#ifdef WIN32
+ID3D11Device *Factory::mD3D11Device = nullptr;
+ID2D1Device *Factory::mD2D1Device = nullptr;
+IDWriteFactory *Factory::mDWriteFactory = nullptr;
+#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->mLogForwarder;
+ delete sConfig;
+ sConfig = nullptr;
+ }
+}
+
+bool
+Factory::HasSSE2()
+{
+#if defined(__SSE2__) || defined(_M_X64) || \
+ (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
+ // gcc with -msse2 (default on OSX and x86-64)
+ // cl.exe with -arch:SSE2 (default on x64 compiler)
+ return true;
+#elif defined(HAVE_CPU_DETECTION)
+ 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::CheckBufferSize(int32_t bufSize)
+{
+ return !sConfig || bufSize < sConfig->mMaxAllocSize;
+}
+
+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;
+ }
+
+#if defined(XP_MACOSX)
+ // CoreGraphics is limited to images < 32K in *height*,
+ // so clamp all surfaces on the Mac to that height
+ if (sz.height > SHRT_MAX) {
+ gfxDebug() << "Surface size too large (exceeds CoreGraphics limit)!";
+ return false;
+ }
+#endif
+
+ // assuming 4 bytes per pixel, make sure the allocation size
+ // doesn't overflow a int32_t either
+ CheckedInt<int32_t> stride = GetAlignedStride<16>(sz.width, 4);
+ if (!stride.isValid() || stride.value() == 0) {
+ gfxDebug() << "Surface size too large (stride overflows int32_t)!";
+ return false;
+ }
+
+ CheckedInt<int32_t> numBytes = stride * 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;
+}
+
+already_AddRefed<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 (CDT) " << aSize;
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> retVal;
+ switch (aBackend) {
+#ifdef WIN32
+ case BackendType::DIRECT2D1_1:
+ {
+ RefPtr<DrawTargetD2D1> newTarget;
+ newTarget = new DrawTargetD2D1();
+ if (newTarget->Init(aSize, aFormat)) {
+ retVal = newTarget;
+ }
+ break;
+ }
+#endif
+#ifdef USE_SKIA
+ case BackendType::SKIA:
+ {
+ RefPtr<DrawTargetSkia> newTarget;
+ newTarget = new DrawTargetSkia();
+ if (newTarget->Init(aSize, aFormat)) {
+ retVal = newTarget;
+ }
+ break;
+ }
+#endif
+#ifdef USE_CAIRO
+ case BackendType::CAIRO:
+ {
+ RefPtr<DrawTargetCairo> newTarget;
+ newTarget = new DrawTargetCairo();
+ if (newTarget->Init(aSize, aFormat)) {
+ retVal = newTarget;
+ }
+ break;
+ }
+#endif
+ default:
+ return nullptr;
+ }
+
+ if (mRecorder && retVal) {
+ return MakeAndAddRef<DrawTargetRecording>(mRecorder, retVal);
+ }
+
+ if (!retVal) {
+ // Failed
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to create DrawTarget, Type: " << int(aBackend) << " Size: " << aSize;
+ }
+
+ return retVal.forget();
+}
+
+already_AddRefed<DrawTarget>
+Factory::CreateRecordingDrawTarget(DrawEventRecorder *aRecorder, DrawTarget *aDT)
+{
+ return MakeAndAddRef<DrawTargetRecording>(aRecorder, aDT);
+}
+
+already_AddRefed<DrawTarget>
+Factory::CreateDrawTargetForData(BackendType aBackend,
+ unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat,
+ bool aUninitialized)
+{
+ MOZ_ASSERT(aData);
+ if (!AllowedSurfaceSize(aSize)) {
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size (DTD) " << aSize;
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> retVal;
+
+ switch (aBackend) {
+#ifdef USE_SKIA
+ case BackendType::SKIA:
+ {
+ RefPtr<DrawTargetSkia> newTarget;
+ newTarget = new DrawTargetSkia();
+ if (newTarget->Init(aData, aSize, aStride, aFormat, aUninitialized)) {
+ retVal = newTarget;
+ }
+ 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
+ default:
+ gfxCriticalNote << "Invalid draw target type specified: " << (int)aBackend;
+ return nullptr;
+ }
+
+ if (mRecorder && retVal) {
+ return MakeAndAddRef<DrawTargetRecording>(mRecorder, retVal, true);
+ }
+
+ if (!retVal) {
+ gfxCriticalNote << "Failed to create DrawTarget, Type: " << int(aBackend) << " Size: " << aSize << ", Data: " << hexa((void *)aData) << ", Stride: " << aStride;
+ }
+
+ return retVal.forget();
+}
+
+already_AddRefed<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::BACKEND_LAST:
+ return false;
+ case BackendType::CAIRO:
+ case BackendType::SKIA:
+ return true;
+ }
+
+ return false;
+}
+
+uint32_t
+Factory::GetMaxSurfaceSize(BackendType aType)
+{
+ switch (aType) {
+ case BackendType::CAIRO:
+ return DrawTargetCairo::GetMaxSurfaceSize();
+#ifdef USE_SKIA
+ case BackendType::SKIA:
+ return DrawTargetSkia::GetMaxSurfaceSize();
+#endif
+#ifdef WIN32
+ case BackendType::DIRECT2D1_1:
+ return DrawTargetD2D1::GetMaxSurfaceSize();
+#endif
+ default:
+ return 0;
+ }
+}
+
+already_AddRefed<ScaledFont>
+Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize)
+{
+ switch (aNativeFont.mType) {
+#ifdef WIN32
+ case NativeFontType::DWRITE_FONT_FACE:
+ {
+ return MakeAndAddRef<ScaledFontDWrite>(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aSize);
+ }
+#if defined(USE_CAIRO) || defined(USE_SKIA)
+ case NativeFontType::GDI_FONT_FACE:
+ {
+ return MakeAndAddRef<ScaledFontWin>(static_cast<LOGFONT*>(aNativeFont.mFont), aSize);
+ }
+#endif
+#endif
+#ifdef XP_DARWIN
+ case NativeFontType::MAC_FONT_FACE:
+ {
+ return MakeAndAddRef<ScaledFontMac>(static_cast<CGFontRef>(aNativeFont.mFont), aSize);
+ }
+#endif
+#if defined(USE_CAIRO) || defined(USE_SKIA_FREETYPE)
+ case NativeFontType::CAIRO_FONT_FACE:
+ {
+ return MakeAndAddRef<ScaledFontCairo>(static_cast<cairo_scaled_font_t*>(aNativeFont.mFont), aSize);
+ }
+#endif
+ default:
+ gfxWarning() << "Invalid native font type specified.";
+ return nullptr;
+ }
+}
+
+already_AddRefed<NativeFontResource>
+Factory::CreateNativeFontResource(uint8_t *aData, uint32_t aSize,
+ FontType aType)
+{
+ switch (aType) {
+#ifdef WIN32
+ case FontType::DWRITE:
+ {
+ return NativeFontResourceDWrite::Create(aData, aSize,
+ /* aNeedsCairo = */ false);
+ }
+#endif
+ case FontType::CAIRO:
+#ifdef USE_SKIA
+ case FontType::SKIA:
+#endif
+ {
+#ifdef WIN32
+ if (GetDWriteFactory()) {
+ return NativeFontResourceDWrite::Create(aData, aSize,
+ /* aNeedsCairo = */ true);
+ } else {
+ return NativeFontResourceGDI::Create(aData, aSize,
+ /* aNeedsCairo = */ true);
+ }
+#elif XP_DARWIN
+ return NativeFontResourceMac::Create(aData, aSize);
+#else
+ gfxWarning() << "Unable to create cairo scaled font from truetype data";
+ return nullptr;
+#endif
+ }
+ default:
+ gfxWarning() << "Unable to create requested font resource from truetype data";
+ return nullptr;
+ }
+}
+
+already_AddRefed<ScaledFont>
+Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont, Float aSize, cairo_scaled_font_t* aScaledFont)
+{
+#ifdef USE_CAIRO
+ // In theory, we could pull the NativeFont out of the cairo_scaled_font_t*,
+ // but that would require a lot of code that would be otherwise repeated in
+ // various backends.
+ // Therefore, we just reuse CreateScaledFontForNativeFont's implementation.
+ RefPtr<ScaledFont> font = CreateScaledFontForNativeFont(aNativeFont, aSize);
+ static_cast<ScaledFontBase*>(font.get())->SetCairoScaledFont(aScaledFont);
+ return font.forget();
+#else
+ return nullptr;
+#endif
+}
+
+#ifdef MOZ_WIDGET_GTK
+already_AddRefed<ScaledFont>
+Factory::CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize)
+{
+ return MakeAndAddRef<ScaledFontFontconfig>(aScaledFont, aPattern, aSize);
+}
+#endif
+
+already_AddRefed<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
+already_AddRefed<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.forget();
+ }
+
+ gfxWarning() << "Failed to create draw target for D3D11 texture.";
+
+ // Failed
+ return nullptr;
+}
+
+bool
+Factory::SetDWriteFactory(IDWriteFactory *aFactory)
+{
+ mDWriteFactory = aFactory;
+ return true;
+}
+
+bool
+Factory::SetDirect3D11Device(ID3D11Device *aDevice)
+{
+ mD3D11Device = aDevice;
+
+ if (mD2D1Device) {
+ mD2D1Device->Release();
+ mD2D1Device = nullptr;
+ }
+
+ if (!aDevice) {
+ return true;
+ }
+
+ RefPtr<ID2D1Factory1> factory = D2DFactory1();
+
+ RefPtr<IDXGIDevice> device;
+ aDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(device));
+ HRESULT hr = factory->CreateDevice(device, &mD2D1Device);
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D2D1] Failed to create gfx factory's D2D1 device, code: " << hexa(hr);
+
+ mD3D11Device = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+ID3D11Device*
+Factory::GetDirect3D11Device()
+{
+ return mD3D11Device;
+}
+
+ID2D1Device*
+Factory::GetD2D1Device()
+{
+ return mD2D1Device;
+}
+
+IDWriteFactory*
+Factory::GetDWriteFactory()
+{
+ return mDWriteFactory;
+}
+
+bool
+Factory::SupportsD2D1()
+{
+ return !!D2DFactory1();
+}
+
+already_AddRefed<GlyphRenderingOptions>
+Factory::CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams)
+{
+ return MakeAndAddRef<GlyphRenderingOptionsDWrite>(aParams);
+}
+
+uint64_t
+Factory::GetD2DVRAMUsageDrawTarget()
+{
+ return DrawTargetD2D1::mVRAMUsageDT;
+}
+
+uint64_t
+Factory::GetD2DVRAMUsageSourceSurface()
+{
+ return DrawTargetD2D1::mVRAMUsageSS;
+}
+
+void
+Factory::D2DCleanup()
+{
+ if (mD2D1Device) {
+ mD2D1Device->Release();
+ mD2D1Device = nullptr;
+ }
+ DrawTargetD2D1::CleanupD2D();
+}
+
+already_AddRefed<ScaledFont>
+Factory::CreateScaledFontForDWriteFont(IDWriteFontFace* aFontFace,
+ const gfxFontStyle* aStyle,
+ float aSize,
+ bool aUseEmbeddedBitmap,
+ bool aForceGDIMode)
+{
+ return MakeAndAddRef<ScaledFontDWrite>(aFontFace, aSize,
+ aUseEmbeddedBitmap, aForceGDIMode,
+ aStyle);
+}
+
+#endif // XP_WIN
+
+#ifdef USE_SKIA_GPU
+already_AddRefed<DrawTarget>
+Factory::CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext,
+ const IntSize &aSize,
+ SurfaceFormat aFormat)
+{
+ RefPtr<DrawTarget> newTarget = new DrawTargetSkia();
+ if (!newTarget->InitWithGrContext(aGrContext, aSize, aFormat)) {
+ return nullptr;
+ }
+ return newTarget.forget();
+}
+
+#endif // USE_SKIA_GPU
+
+#ifdef USE_SKIA
+already_AddRefed<DrawTarget>
+Factory::CreateDrawTargetWithSkCanvas(SkCanvas* aCanvas)
+{
+ RefPtr<DrawTargetSkia> newTarget = new DrawTargetSkia();
+ if (!newTarget->Init(aCanvas)) {
+ return nullptr;
+ }
+ return newTarget.forget();
+}
+#endif
+
+void
+Factory::PurgeAllCaches()
+{
+}
+
+already_AddRefed<DrawTarget>
+Factory::CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
+{
+ if (!AllowedSurfaceSize(aSize)) {
+ gfxWarning() << "Allowing surface with invalid size (Cairo) " << aSize;
+ }
+
+ RefPtr<DrawTarget> retVal;
+
+#ifdef USE_CAIRO
+ RefPtr<DrawTargetCairo> newTarget = new DrawTargetCairo();
+
+ if (newTarget->Init(aSurface, aSize, aFormat)) {
+ retVal = newTarget;
+ }
+
+ if (mRecorder && retVal) {
+ return MakeAndAddRef<DrawTargetRecording>(mRecorder, retVal, true);
+ }
+#endif
+ return retVal.forget();
+}
+
+already_AddRefed<SourceSurface>
+Factory::CreateSourceSurfaceForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat aFormat)
+{
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ gfxWarning() << "Can't create a SourceSurface without a valid size";
+ return nullptr;
+ }
+
+#ifdef USE_CAIRO
+ return MakeAndAddRef<SourceSurfaceCairo>(aSurface, aSize, aFormat);
+#else
+ return nullptr;
+#endif
+}
+
+already_AddRefed<DataSourceSurface>
+Factory::CreateWrappingDataSourceSurface(uint8_t *aData,
+ int32_t aStride,
+ const IntSize &aSize,
+ SurfaceFormat aFormat,
+ SourceSurfaceDeallocator aDeallocator /* = nullptr */,
+ void* aClosure /* = nullptr */)
+{
+ // Just check for negative/zero size instead of the full AllowedSurfaceSize() - since
+ // the data is already allocated we do not need to check for a possible overflow - it
+ // already worked.
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ return nullptr;
+ }
+ if (!aDeallocator && aClosure) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(aData);
+
+ RefPtr<SourceSurfaceRawData> newSurf = new SourceSurfaceRawData();
+ newSurf->InitWrappingData(aData, aSize, aStride, aFormat, aDeallocator, aClosure);
+
+ return newSurf.forget();
+}
+
+#ifdef XP_DARWIN
+already_AddRefed<GlyphRenderingOptions>
+Factory::CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor)
+{
+ return MakeAndAddRef<GlyphRenderingOptionsCG>(aFontSmoothingBackgroundColor);
+}
+#endif
+
+already_AddRefed<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 (DSS) " << aSize;
+ return nullptr;
+ }
+
+ // Skia doesn't support RGBX, so memset RGBX to 0xFF
+ bool clearSurface = aZero || aFormat == SurfaceFormat::B8G8R8X8;
+ uint8_t clearValue = aFormat == SurfaceFormat::B8G8R8X8 ? 0xFF : 0;
+
+ RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData();
+ if (newSurf->Init(aSize, aFormat, clearSurface, clearValue)) {
+ return newSurf.forget();
+ }
+
+ gfxWarning() << "CreateDataSourceSurface failed in init";
+ return nullptr;
+}
+
+already_AddRefed<DataSourceSurface>
+Factory::CreateDataSourceSurfaceWithStride(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ int32_t aStride,
+ bool aZero)
+{
+ if (!AllowedSurfaceSize(aSize) ||
+ aStride < aSize.width * BytesPerPixel(aFormat)) {
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed with bad stride " << aStride << ", " << aSize << ", " << aFormat;
+ return nullptr;
+ }
+
+ // Skia doesn't support RGBX, so memset RGBX to 0xFF
+ bool clearSurface = aZero || aFormat == SurfaceFormat::B8G8R8X8;
+ uint8_t clearValue = aFormat == SurfaceFormat::B8G8R8X8 ? 0xFF : 0;
+
+ RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData();
+ if (newSurf->Init(aSize, aFormat, clearSurface, clearValue, aStride)) {
+ return newSurf.forget();
+ }
+
+ gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed to initialize " << aSize << ", " << aFormat << ", " << aStride << ", " << aZero;
+ return nullptr;
+}
+
+static uint16_t
+PackRGB565(uint8_t r, uint8_t g, uint8_t b)
+{
+ uint16_t pixel = ((r << 11) & 0xf800) |
+ ((g << 5) & 0x07e0) |
+ ((b ) & 0x001f);
+
+ return pixel;
+}
+
+void
+Factory::CopyDataSourceSurface(DataSourceSurface* aSource,
+ DataSourceSurface* aDest)
+{
+ // Don't worry too much about speed.
+ MOZ_ASSERT(aSource->GetSize() == aDest->GetSize());
+ MOZ_ASSERT(aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
+ aSource->GetFormat() == SurfaceFormat::R8G8B8X8 ||
+ aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+ aSource->GetFormat() == SurfaceFormat::B8G8R8X8);
+ MOZ_ASSERT(aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
+ aDest->GetFormat() == SurfaceFormat::R8G8B8X8 ||
+ aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+ aDest->GetFormat() == SurfaceFormat::B8G8R8X8 ||
+ aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16);
+
+ const bool isSrcBGR = aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+ aSource->GetFormat() == SurfaceFormat::B8G8R8X8;
+ const bool isDestBGR = aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+ aDest->GetFormat() == SurfaceFormat::B8G8R8X8;
+ const bool needsSwap02 = isSrcBGR != isDestBGR;
+
+ const bool srcHasAlpha = aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
+ aSource->GetFormat() == SurfaceFormat::B8G8R8A8;
+ const bool destHasAlpha = aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
+ aDest->GetFormat() == SurfaceFormat::B8G8R8A8;
+ const bool needsAlphaMask = !srcHasAlpha && destHasAlpha;
+
+ const bool needsConvertTo16Bits = aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16;
+
+ DataSourceSurface::MappedSurface srcMap;
+ DataSourceSurface::MappedSurface destMap;
+ if (!aSource->Map(DataSourceSurface::MapType::READ, &srcMap) ||
+ !aDest->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
+ MOZ_ASSERT(false, "CopyDataSourceSurface: Failed to map surface.");
+ return;
+ }
+
+ MOZ_ASSERT(srcMap.mStride >= 0);
+ MOZ_ASSERT(destMap.mStride >= 0);
+
+ const size_t srcBPP = BytesPerPixel(aSource->GetFormat());
+ const size_t srcRowBytes = aSource->GetSize().width * srcBPP;
+ const size_t srcRowHole = srcMap.mStride - srcRowBytes;
+
+ const size_t destBPP = BytesPerPixel(aDest->GetFormat());
+ const size_t destRowBytes = aDest->GetSize().width * destBPP;
+ const size_t destRowHole = destMap.mStride - destRowBytes;
+
+ uint8_t* srcRow = srcMap.mData;
+ uint8_t* destRow = destMap.mData;
+ const size_t rows = aSource->GetSize().height;
+ for (size_t i = 0; i < rows; i++) {
+ const uint8_t* srcRowEnd = srcRow + srcRowBytes;
+
+ while (srcRow != srcRowEnd) {
+ uint8_t d0 = needsSwap02 ? srcRow[2] : srcRow[0];
+ uint8_t d1 = srcRow[1];
+ uint8_t d2 = needsSwap02 ? srcRow[0] : srcRow[2];
+ uint8_t d3 = needsAlphaMask ? 0xff : srcRow[3];
+
+ if (needsConvertTo16Bits) {
+ *(uint16_t*)destRow = PackRGB565(d0, d1, d2);
+ } else {
+ destRow[0] = d0;
+ destRow[1] = d1;
+ destRow[2] = d2;
+ destRow[3] = d3;
+ }
+ srcRow += srcBPP;
+ destRow += destBPP;
+ }
+
+ srcRow += srcRowHole;
+ destRow += destRowHole;
+ }
+
+ aSource->Unmap();
+ aDest->Unmap();
+}
+
+already_AddRefed<DrawEventRecorder>
+Factory::CreateEventRecorderForFile(const char *aFilename)
+{
+ return MakeAndAddRef<DrawEventRecorderFile>(aFilename);
+}
+
+void
+Factory::SetGlobalEventRecorder(DrawEventRecorder *aRecorder)
+{
+ mRecorder = aRecorder;
+}
+
+// static
+void
+CriticalLogger::OutputMessage(const std::string &aString,
+ int aLevel, bool aNoNewline)
+{
+ if (Factory::GetLogForwarder()) {
+ Factory::GetLogForwarder()->Log(aString);
+ }
+
+ BasicLogger::OutputMessage(aString, aLevel, aNoNewline);
+}
+
+void
+CriticalLogger::CrashAction(LogReason aReason)
+{
+ if (Factory::GetLogForwarder()) {
+ Factory::GetLogForwarder()->CrashAction(aReason);
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/FilterNodeD2D1.cpp b/gfx/2d/FilterNodeD2D1.cpp
new file mode 100644
index 000000000..9f4ded23c
--- /dev/null
+++ b/gfx/2d/FilterNodeD2D1.cpp
@@ -0,0 +1,1102 @@
+/* -*- 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 "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("GFX: Unknown enum value D2DAlphaMode!");
+ }
+
+ return D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED;
+}
+
+D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE D2DAffineTransformInterpolationMode(SamplingFilter aSamplingFilter)
+{
+ switch (aSamplingFilter) {
+ case SamplingFilter::GOOD:
+ return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
+ case SamplingFilter::LINEAR:
+ return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
+ case SamplingFilter::POINT:
+ return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+ default:
+ MOZ_CRASH("GFX: Unknown enum value D2DAffineTIM!");
+ }
+
+ 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("GFX: Unknown enum value D2DBlendMode!");
+ }
+
+ 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("GFX: Unknown enum value D2DMorphologyMode!");
+ 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("GFX: Unknown enum value D2DTurbulenceNoise!");
+ 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("GFX: Unknown enum value D2DFilterCompositionMode!");
+ 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("GFX: Unknown enum value D2DChannelSelector!");
+ return D2D1_CHANNEL_SELECTOR_R;
+}
+
+already_AddRefed<ID2D1Image> GetImageForSourceSurface(DrawTarget *aDT, SourceSurface *aSurface)
+{
+ if (aDT->IsTiledDrawTarget() || aDT->IsDualDrawTarget()) {
+ gfxDevCrash(LogReason::FilterNodeD2D1Target) << "Incompatible draw target type! " << (int)aDT->IsTiledDrawTarget() << " " << (int)aDT->IsDualDrawTarget();
+ return nullptr;
+ }
+ switch (aDT->GetBackendType()) {
+ case BackendType::DIRECT2D1_1:
+ return static_cast<DrawTargetD2D1*>(aDT)->GetImageForSurface(aSurface, ExtendMode::CLAMP);
+ default:
+ gfxDevCrash(LogReason::FilterNodeD2D1Backend) << "Unknown draw target type! " << (int)aDT->GetBackendType();
+ 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(SamplingFilter(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 bool
+HasUnboundedOutputRegion(FilterType aType)
+{
+ if (IsTransferFilterType(aType)) {
+ return true;
+ }
+
+ switch (aType) {
+ case FilterType::COLOR_MATRIX:
+ case FilterType::POINT_DIFFUSE:
+ case FilterType::SPOT_DIFFUSE:
+ case FilterType::DISTANT_DIFFUSE:
+ case FilterType::POINT_SPECULAR:
+ case FilterType::SPOT_SPECULAR:
+ case FilterType::DISTANT_SPECULAR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* static */
+already_AddRefed<FilterNode>
+FilterNodeD2D1::Create(ID2D1DeviceContext *aDC, FilterType aType)
+{
+ if (aType == FilterType::CONVOLVE_MATRIX) {
+ return MakeAndAddRef<FilterNodeConvolveD2D1>(aDC);
+ }
+
+ RefPtr<ID2D1Effect> effect;
+ HRESULT hr;
+
+ hr = aDC->CreateEffect(GetCLDIDForFilterType(aType), getter_AddRefs(effect));
+
+ if (FAILED(hr) || !effect) {
+ gfxCriticalErrorOnce() << "Failed to create effect for FilterType: " << hexa(hr);
+ return nullptr;
+ }
+
+ if (aType == FilterType::ARITHMETIC_COMBINE) {
+ effect->SetValue(D2D1_ARITHMETICCOMPOSITE_PROP_CLAMP_OUTPUT, TRUE);
+ }
+
+ RefPtr<FilterNodeD2D1> filter = new FilterNodeD2D1(effect, aType);
+
+ if (HasUnboundedOutputRegion(aType)) {
+ // 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, getter_AddRefs(mEffect));
+
+ if (FAILED(hr) || !mEffect) {
+ gfxWarning() << "Failed to create ConvolveMatrix filter!";
+ return;
+ }
+
+ mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_BORDER_MODE, D2D1_BORDER_MODE_SOFT);
+
+ hr = aDC->CreateEffect(CLSID_ExtendInputEffect, getter_AddRefs(mExtendInputEffect));
+
+ if (FAILED(hr) || !mExtendInputEffect) {
+ gfxWarning() << "Failed to create ConvolveMatrix filter!";
+ return;
+ }
+
+ hr = aDC->CreateEffect(CLSID_D2D1Border, getter_AddRefs(mBorderEffect));
+
+ if (FAILED(hr) || !mBorderEffect) {
+ 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, getter_AddRefs(mExtendInputEffect));
+
+ if (FAILED(hr) || !mExtendInputEffect) {
+ 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, getter_AddRefs(mPrePremultiplyEffect));
+
+ if (FAILED(hr) || !mPrePremultiplyEffect) {
+ gfxWarning() << "Failed to create ComponentTransfer filter!";
+ return;
+ }
+
+ hr = aDC->CreateEffect(CLSID_D2D1UnPremultiply, getter_AddRefs(mPostUnpremultiplyEffect));
+
+ if (FAILED(hr) || !mPostUnpremultiplyEffect) {
+ 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..8c4c6df30
--- /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 already_AddRefed<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, override)
+ FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC);
+
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override;
+
+ virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
+ virtual void SetAttribute(uint32_t aIndex, const IntSize &aValue) override;
+ virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue) override;
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue) override;
+
+ 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, override)
+ 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, override)
+ 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..3abdb7a02
--- /dev/null
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -0,0 +1,3689 @@
+/* -*- 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 <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 129 ((1 << sCacheIndexPrecisionBits) + 1) values for bases between
+ * 0 and 1 and a fixed exponent.
+ **/
+class PowCache
+{
+public:
+ PowCache()
+ : mNumPowTablePreSquares(-1)
+ {
+ }
+
+ void CacheForExponent(Float aExponent)
+ {
+ // Since we are in the world where we only care about
+ // input and results in [0,1], there is no point in
+ // dealing with non-positive exponents.
+ if (aExponent <= 0) {
+ mNumPowTablePreSquares = -1;
+ return;
+ }
+ int numPreSquares = 0;
+ while (numPreSquares < 5 && aExponent > (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, aExponent) * (1 << sOutputIntPrecisionBits);
+ MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)), "mPowCache integer type too small");
+
+ mPowTable[i] = cachedInt;
+ }
+ }
+
+ // Only call Pow() if HasPowerTable() would return true, to avoid complicating
+ // this code and having it just return (1 << sOutputIntPrecisionBits))
+ uint16_t Pow(uint16_t aBase)
+ {
+ MOZ_ASSERT(HasPowerTable());
+ // Results should be similar to what the following code would produce:
+ // Float x = Float(aBase) / (1 << sInputIntPrecisionBits);
+ // return uint16_t(pow(x, aExponent) * (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;
+
+ inline bool HasPowerTable() const
+ {
+ return mNumPowTablePreSquares >= 0;
+ }
+
+private:
+ static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1;
+
+ 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);
+}
+
+already_AddRefed<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");
+
+ DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
+ if(MOZ2D_WARN_IF(!surfMap.IsMapped())) {
+ return;
+ }
+ uint8_t* sourcePixelData = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aPixelPos);
+ uint8_t* data = DataAtOffset(aSurface, surfMap.GetMappedSurface(), 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 * surfMap.GetStride(), 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");
+
+ DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
+ if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
+ return;
+ }
+
+ uint8_t* sampleData = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
+ uint8_t* data = DataAtOffset(aSurface, surfMap.GetMappedSurface(), 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 += surfMap.GetStride();
+ }
+ } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
+ for (int32_t y = 0; y < aFillRect.height; y++) {
+ PodCopy(data, sampleData, aFillRect.width);
+ data += surfMap.GetStride();
+ }
+ }
+}
+
+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");
+
+ DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
+ if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
+ return;
+ }
+
+ uint8_t* sampleData = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
+ uint8_t* data = DataAtOffset(aSurface, surfMap.GetMappedSurface(), 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 += surfMap.GetStride();
+ sampleData += surfMap.GetStride();
+ }
+ } 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 += surfMap.GetStride();
+ sampleData += surfMap.GetStride();
+ }
+ }
+}
+
+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 already_AddRefed<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 */ already_AddRefed<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);
+ }
+}
+
+already_AddRefed<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)
+{
+ if (mRequestedRect.Contains(aRect)) {
+ // Bail out now. Otherwise pathological filters can spend time exponential
+ // in the number of primitives, e.g. if each primitive takes the
+ // previous primitive as its two inputs.
+ return;
+ }
+ 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()) {
+ gfxDevCrash(LogReason::FilterInputError) << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
+ return;
+ }
+ 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;
+}
+
+already_AddRefed<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()) {
+ gfxDevCrash(LogReason::FilterInputData) << "Invalid data " << inputIndex << " vs. " << NumberOfSetInputs();
+ 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, 1) ||
+ 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()) {
+ gfxDevCrash(LogReason::FilterInputRect) << "Invalid rect " << inputIndex << " vs. " << NumberOfSetInputs();
+ 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) {
+ gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex;
+ 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;
+}
+
+already_AddRefed<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());
+
+ // This needs to stay in scope until the draw target has been flushed.
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
+ if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForData(BackendType::CAIRO,
+ targetMap.GetData(),
+ target->GetSize(),
+ targetMap.GetStride(),
+ target->GetFormat());
+
+ if (!dt) {
+ gfxWarning() << "FilterNodeBlendSoftware::Render failed in CreateDrawTargetForData";
+ return nullptr;
+ }
+
+ 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()
+ : mSamplingFilter(SamplingFilter::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);
+ mSamplingFilter = static_cast<SamplingFilter>(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);
+}
+
+already_AddRefed<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) {
+ gfxWarning() << "FilterNodeTransformSoftware::Render failed in CreateDrawTargetForData";
+ return nullptr;
+ }
+
+ Rect r(0, 0, srcRect.width, srcRect.height);
+ dt->SetTransform(transform);
+ dt->DrawSurface(input, r, r, DrawSurfaceOptions(mSamplingFilter));
+
+ 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 already_AddRefed<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;
+ }
+
+ DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !tmpMap.IsMapped())) {
+ return nullptr;
+ }
+ uint8_t* sourceData = DataAtOffset(aInput, sourceMap.GetMappedSurface(),
+ destRect.TopLeft() - srcRect.TopLeft());
+ uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(),
+ destRect.TopLeft() - tmpRect.TopLeft());
+
+ FilterProcessing::ApplyMorphologyHorizontal(
+ sourceData, sourceMap.GetStride(), tmpData, tmpMap.GetStride(), 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;
+ }
+
+ DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap destMap(dest, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!tmpMap.IsMapped() || !destMap.IsMapped())) {
+ return nullptr;
+ }
+ int32_t tmpStride = tmpMap.GetStride();
+ uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(), destRect.TopLeft() - tmpRect.TopLeft());
+
+ int32_t destStride = destMap.GetStride();
+ uint8_t* destData = destMap.GetData();
+
+ FilterProcessing::ApplyMorphologyVertical(
+ tmpData, tmpStride, destData, destStride, destRect, ry, aOperator);
+ }
+
+ return dest.forget();
+}
+
+already_AddRefed<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 already_AddRefed<DataSourceSurface>
+Premultiply(DataSourceSurface* aSurface)
+{
+ if (aSurface->GetFormat() == SurfaceFormat::A8) {
+ RefPtr<DataSourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ IntSize size = aSurface->GetSize();
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
+ return nullptr;
+ }
+
+ uint8_t* inputData = inputMap.GetData();
+ int32_t inputStride = inputMap.GetStride();
+ uint8_t* targetData = targetMap.GetData();
+ int32_t targetStride = targetMap.GetStride();
+
+ FilterProcessing::DoPremultiplicationCalculation(
+ size, targetData, targetStride, inputData, inputStride);
+
+ return target.forget();
+}
+
+static already_AddRefed<DataSourceSurface>
+Unpremultiply(DataSourceSurface* aSurface)
+{
+ if (aSurface->GetFormat() == SurfaceFormat::A8) {
+ RefPtr<DataSourceSurface> surface(aSurface);
+ return surface.forget();
+ }
+
+ IntSize size = aSurface->GetSize();
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+ if (MOZ2D_WARN_IF(!target)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
+ return nullptr;
+ }
+
+ uint8_t* inputData = inputMap.GetData();
+ int32_t inputStride = inputMap.GetStride();
+ uint8_t* targetData = targetMap.GetData();
+ int32_t targetStride = targetMap.GetStride();
+
+ FilterProcessing::DoUnpremultiplicationCalculation(
+ size, targetData, targetStride, inputData, inputStride);
+
+ return target.forget();
+}
+
+already_AddRefed<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;
+}
+
+already_AddRefed<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;
+ }
+
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
+ return nullptr;
+ }
+
+ uint8_t* targetData = targetMap.GetData();
+ int32_t stride = targetMap.GetStride();
+
+ 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;
+ }
+ PodZero(&targetData[aRect.width * 4], stride - aRect.width * 4);
+ 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;
+ }
+ PodZero(&targetData[aRect.width], stride - aRect.width);
+ targetData += stride;
+ }
+ } else {
+ gfxDevCrash(LogReason::FilterInputFormat) << "Bad format in flood render " << (int)format;
+ return nullptr;
+ }
+
+ return target.forget();
+}
+
+// Override GetOutput to get around caching. Rendering simple floods is
+// comparatively fast.
+already_AddRefed<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;
+ }
+};
+
+} // namespace
+
+already_AddRefed<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("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
+ }
+ 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();
+
+ DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap targetMap(aTarget, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
+ return;
+ }
+
+ uint8_t* sourceData = sourceMap.GetData();
+ int32_t sourceStride = sourceMap.GetStride();
+ uint8_t* targetData = targetMap.GetData();
+ int32_t targetStride = targetMap.GetStride();
+
+ MOZ_ASSERT(sourceStride <= targetStride, "target smaller than source");
+
+ 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]];
+ }
+ }
+
+ // Zero padding to keep valgrind happy.
+ PodZero(&targetData[y * targetStride + size.width * BytesPerPixel],
+ targetStride - size.width * BytesPerPixel);
+ }
+}
+
+bool
+IsAllZero(uint8_t aLookupTable[256])
+{
+ for (int32_t i = 0; i < 256; i++) {
+ if (aLookupTable[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+already_AddRefed<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("GFX: FilterNodeTableTransferSoftware::SetAttribute");
+ }
+ 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("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
+ }
+ 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("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
+ }
+ 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("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
+ }
+ 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("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
+ }
+ 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("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
+ }
+ 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];
+ }
+}
+
+already_AddRefed<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("GFX: TranslateDoubleToShifts");
+ }
+ if (aDouble < 1) {
+ while (1 << (aShiftR + 1) < 1 / aDouble) {
+ aShiftR++;
+ }
+ } else {
+ while (1 << (aShiftL + 1) < aDouble) {
+ aShiftL++;
+ }
+ }
+}
+
+template<typename CoordType>
+already_AddRefed<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, true);
+ }
+
+ 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();
+
+ DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
+ return nullptr;
+ }
+
+ uint8_t* sourceData = DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
+ int32_t sourceStride = sourceMap.GetStride();
+ uint8_t* targetData = targetMap.GetData();
+ int32_t targetStride = targetMap.GetStride();
+
+ // 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("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+already_AddRefed<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();
+
+ DataSourceSurface::ScopedMap inputMap(input, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap mapMap(map, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!(inputMap.IsMapped() && mapMap.IsMapped() && targetMap.IsMapped()))) {
+ return nullptr;
+ }
+
+ uint8_t* sourceData = DataAtOffset(input, inputMap.GetMappedSurface(), offset);
+ int32_t sourceStride = inputMap.GetStride();
+ uint8_t* mapData = mapMap.GetData();
+ int32_t mapStride = mapMap.GetStride();
+ uint8_t* targetData = targetMap.GetData();
+ int32_t targetStride = targetMap.GetStride();
+
+ 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);
+ }
+
+ // Keep valgrind happy.
+ PodZero(&targetData[y * targetStride + 4 * aRect.width], targetStride - 4 * aRect.width);
+ }
+
+ 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("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
+ break;
+ }
+ Invalidate();
+}
+
+void
+FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const IntRect &aRect)
+{
+ switch (aIndex) {
+ case ATT_TURBULENCE_RECT:
+ mRenderRect = aRect;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
+ 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("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
+ break;
+ }
+ Invalidate();
+}
+
+already_AddRefed<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();
+}
+
+already_AddRefed<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();
+}
+
+already_AddRefed<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;
+ }
+}
+
+already_AddRefed<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());
+
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
+ if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
+ return nullptr;
+ }
+ AlphaBoxBlur blur(r, targetMap.GetStride(), sigmaXY.width, sigmaXY.height);
+ blur.Blur(targetMap.GetData());
+ } else {
+ RefPtr<DataSourceSurface> channel0, channel1, channel2, channel3;
+ FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2, channel3);
+ if (MOZ2D_WARN_IF(!(channel0 && channel1 && channel2 && channel3))) {
+ return nullptr;
+ }
+ {
+ DataSourceSurface::ScopedMap channel0Map(channel0, DataSourceSurface::READ_WRITE);
+ DataSourceSurface::ScopedMap channel1Map(channel1, DataSourceSurface::READ_WRITE);
+ DataSourceSurface::ScopedMap channel2Map(channel2, DataSourceSurface::READ_WRITE);
+ DataSourceSurface::ScopedMap channel3Map(channel3, DataSourceSurface::READ_WRITE);
+ if (MOZ2D_WARN_IF(!(channel0Map.IsMapped() && channel1Map.IsMapped() &&
+ channel2Map.IsMapped() && channel3Map.IsMapped()))) {
+ return nullptr;
+ }
+
+ AlphaBoxBlur blur(r, channel0Map.GetStride(), sigmaXY.width, sigmaXY.height);
+ blur.Blur(channel0Map.GetData());
+ blur.Blur(channel1Map.GetData());
+ blur.Blur(channel2Map.GetData());
+ blur.Blur(channel3Map.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("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
+ }
+ 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("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
+ }
+ Invalidate();
+}
+
+void
+FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
+ uint32_t aBlurDirection)
+{
+ switch (aIndex) {
+ case ATT_DIRECTIONAL_BLUR_DIRECTION:
+ mBlurDirection = (BlurDirection)aBlurDirection;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
+ }
+ 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();
+}
+
+already_AddRefed<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;
+ }
+}
+
+already_AddRefed<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;
+ }
+}
+
+already_AddRefed<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("GFX: FilterNodeLightingSoftware::SetAttribute point");
+}
+
+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 = std::fpclassify(aValue) == FP_SUBNORMAL ? 0.0 : aValue;
+ break;
+ default:
+ MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
+ }
+ 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("GFX: FilterNodeLightingSoftware::SetAttribute size");
+ }
+ 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 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];
+ };
+
+ Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight);
+ if (!mPowCache.HasPowerTable()) {
+ dot *= (dot >= mLimitingConeCos);
+ color = aLightColor;
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] *= dot;
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] *= dot;
+ colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] *= dot;
+ } else {
+ color = aLightColor;
+ 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>
+already_AddRefed<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>
+already_AddRefed<DataSourceSurface>
+FilterNodeLightingSoftware<LightType, LightingType>::DoRender(const IntRect& aRect,
+ CoordType aKernelUnitLengthX,
+ CoordType aKernelUnitLengthY)
+{
+ MOZ_ASSERT(aKernelUnitLengthX > 0, "aKernelUnitLengthX can be a negative or zero value");
+ MOZ_ASSERT(aKernelUnitLengthY > 0, "aKernelUnitLengthY can be a negative or zero value");
+
+ 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_NONE);
+
+ 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();
+
+
+ DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() && targetMap.IsMapped()))) {
+ return nullptr;
+ }
+
+ uint8_t* sourceData = DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
+ int32_t sourceStride = sourceMap.GetStride();
+ uint8_t* targetData = targetMap.GetData();
+ int32_t targetStride = targetMap.GetStride();
+
+ 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);
+ }
+
+ // Zero padding to keep valgrind happy.
+ PodZero(&targetData[y * targetStride + 4 * size.width], targetStride - 4 * size.width);
+ }
+
+ 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)
+ , mSpecularConstantInt(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));
+ // The exponent for specular is in [1,128] range, so we don't need to check and
+ // optimize for the "default power table" scenario here.
+ MOZ_ASSERT(mPowCache.HasPowerTable());
+ 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..7ad27ec0f
--- /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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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.
+ */
+ already_AddRefed<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 already_AddRefed<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;
+ SamplingFilter mSamplingFilter;
+};
+
+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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<DataSourceSurface> GetOutput(const IntRect &aRect) override;
+ virtual already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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>
+ already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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>
+ already_AddRefed<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
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_FILTERNODESOFTWARE_H_
diff --git a/gfx/2d/FilterProcessing.cpp b/gfx/2d/FilterProcessing.cpp
new file mode 100644
index 000000000..da734c01a
--- /dev/null
+++ b/gfx/2d/FilterProcessing.cpp
@@ -0,0 +1,262 @@
+/* -*- 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 {
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ExtractAlpha(DataSourceSurface* aSource)
+{
+ IntSize size = aSource->GetSize();
+ RefPtr<DataSourceSurface> alpha = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
+ if (MOZ2D_WARN_IF(!alpha)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap sourceMap(aSource, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap alphaMap(alpha, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !alphaMap.IsMapped())) {
+ return nullptr;
+ }
+
+ uint8_t* sourceData = sourceMap.GetData();
+ int32_t sourceStride = sourceMap.GetStride();
+ uint8_t* alphaData = alphaMap.GetData();
+ int32_t alphaStride = alphaMap.GetStride();
+
+ 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();
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ConvertToB8G8R8A8(SourceSurface* aSurface)
+{
+ if (Factory::HasSSE2()) {
+#ifdef USE_SSE2
+ return ConvertToB8G8R8A8_SSE2(aSurface);
+#endif
+ }
+ return ConvertToB8G8R8A8_Scalar(aSurface);
+}
+
+already_AddRefed<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);
+ }
+}
+
+already_AddRefed<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;
+ }
+
+ DataSourceSurface::ScopedMap sourceMap(aSource, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap channel0Map(aChannel0, DataSourceSurface::WRITE);
+ DataSourceSurface::ScopedMap channel1Map(aChannel1, DataSourceSurface::WRITE);
+ DataSourceSurface::ScopedMap channel2Map(aChannel2, DataSourceSurface::WRITE);
+ DataSourceSurface::ScopedMap channel3Map(aChannel3, DataSourceSurface::WRITE);
+ if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() &&
+ channel0Map.IsMapped() && channel1Map.IsMapped() &&
+ channel2Map.IsMapped() && channel3Map.IsMapped()))) {
+ return;
+ }
+ uint8_t* sourceData = sourceMap.GetData();
+ int32_t sourceStride = sourceMap.GetStride();
+ uint8_t* channel0Data = channel0Map.GetData();
+ uint8_t* channel1Data = channel1Map.GetData();
+ uint8_t* channel2Data = channel2Map.GetData();
+ uint8_t* channel3Data = channel3Map.GetData();
+ int32_t channelStride = channel0Map.GetStride();
+
+ 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);
+ }
+}
+
+already_AddRefed<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;
+ }
+ DataSourceSurface::ScopedMap resultMap(result, DataSourceSurface::WRITE);
+ DataSourceSurface::ScopedMap channel0Map(aChannel0, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap channel1Map(aChannel1, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap channel2Map(aChannel2, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap channel3Map(aChannel3, DataSourceSurface::READ);
+ if (MOZ2D_WARN_IF(!(resultMap.IsMapped() &&
+ channel0Map.IsMapped() && channel1Map.IsMapped() &&
+ channel2Map.IsMapped() && channel3Map.IsMapped()))) {
+ return nullptr;
+ }
+ int32_t resultStride = resultMap.GetStride();
+ uint8_t* resultData = resultMap.GetData();
+ int32_t channelStride = channel0Map.GetStride();
+ uint8_t* channel0Data = channel0Map.GetData();
+ uint8_t* channel1Data = channel1Map.GetData();
+ uint8_t* channel2Data = channel2Map.GetData();
+ uint8_t* channel3Data = channel3Map.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);
+ }
+}
+
+already_AddRefed<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);
+}
+
+already_AddRefed<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..802d791a0
--- /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 already_AddRefed<DataSourceSurface> ExtractAlpha(DataSourceSurface* aSource);
+ static already_AddRefed<DataSourceSurface> ConvertToB8G8R8A8(SourceSurface* aSurface);
+ static already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<DataSourceSurface>
+ RenderTurbulence(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
+ int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect);
+ static already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<DataSourceSurface> ConvertToB8G8R8A8_SSE2(SourceSurface* aSurface);
+ static already_AddRefed<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 already_AddRefed<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 already_AddRefed<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 already_AddRefed<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..3d21a4751
--- /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 already_AddRefed<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.forget();
+}
+
+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 already_AddRefed<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.forget();
+}
+
+template<typename i32x4_t, typename i16x8_t, typename u8x16_t>
+static already_AddRefed<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 already_AddRefed<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.forget();
+}
+
+// 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("GFX: Incomplete switch");
+ }
+}
+
+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 already_AddRefed<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 already_AddRefed<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.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/FilterProcessingSSE2.cpp b/gfx/2d/FilterProcessingSSE2.cpp
new file mode 100644
index 000000000..6f14fc3ef
--- /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);
+}
+
+already_AddRefed<DataSourceSurface>
+FilterProcessing::ConvertToB8G8R8A8_SSE2(SourceSurface* aSurface)
+{
+ return ConvertToB8G8R8A8_SIMD<__m128i>(aSurface);
+}
+
+already_AddRefed<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);
+}
+
+already_AddRefed<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);
+}
+
+already_AddRefed<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);
+}
+
+already_AddRefed<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 gfx
+} // namespace mozilla
diff --git a/gfx/2d/FilterProcessingScalar.cpp b/gfx/2d/FilterProcessingScalar.cpp
new file mode 100644
index 000000000..9e88c563e
--- /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];
+ }
+ }
+}
+
+already_AddRefed<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);
+ }
+}
+
+already_AddRefed<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;
+ }
+ }
+}
+
+already_AddRefed<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);
+}
+
+already_AddRefed<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 gfx
+} // namespace mozilla
diff --git a/gfx/2d/Filters.h b/gfx/2d/Filters.h
new file mode 100644
index 000000000..12eadcebe
--- /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("GFX: FilterNode"); }
+ virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) { MOZ_CRASH("GFX: FilterNode"); }
+
+ virtual void SetAttribute(uint32_t aIndex, bool) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, uint32_t) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, Float) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Size &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const IntSize &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const IntPoint &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Rect &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const IntRect &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Point &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Matrix &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Point3D &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Color &) { MOZ_CRASH("GFX: FilterNode"); }
+ virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) { MOZ_CRASH("GFX: FilterNode"); }
+
+protected:
+ friend class Factory;
+
+ FilterNode() {}
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/gfx/2d/GenericRefCounted.h b/gfx/2d/GenericRefCounted.h
new file mode 100644
index 000000000..bee792b4d
--- /dev/null
+++ b/gfx/2d/GenericRefCounted.h
@@ -0,0 +1,132 @@
+/* -*- 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"
+#include "mozilla/RefCounted.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
new file mode 100644
index 000000000..de6e448de
--- /dev/null
+++ b/gfx/2d/GradientStopsD2D.h
@@ -0,0 +1,40 @@
+/* -*- 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_GRADIENTSTOPSD2D_H_
+#define MOZILLA_GFX_GRADIENTSTOPSD2D_H_
+
+#include "2D.h"
+
+#include <d2d1.h>
+
+namespace mozilla {
+namespace gfx {
+
+class GradientStopsD2D : public GradientStops
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsD2D)
+ GradientStopsD2D(ID2D1GradientStopCollection *aStopCollection, ID3D11Device *aDevice)
+ : mStopCollection(aStopCollection)
+ , mDevice(aDevice)
+ {}
+
+ 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;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_GRADIENTSTOPSD2D_H_ */
diff --git a/gfx/2d/Helpers.h b/gfx/2d/Helpers.h
new file mode 100644
index 000000000..11c7eec5d
--- /dev/null
+++ 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
new file mode 100644
index 000000000..bbcce274e
--- /dev/null
+++ b/gfx/2d/HelpersCairo.h
@@ -0,0 +1,352 @@
+/* -*- 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_HELPERSCAIRO_H_
+#define MOZILLA_GFX_HELPERSCAIRO_H_
+
+#include "2D.h"
+#include "cairo.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+static inline cairo_operator_t
+GfxOpToCairoOp(CompositionOp op)
+{
+ switch (op)
+ {
+ case CompositionOp::OP_OVER:
+ return CAIRO_OPERATOR_OVER;
+ case CompositionOp::OP_ADD:
+ return CAIRO_OPERATOR_ADD;
+ case CompositionOp::OP_ATOP:
+ return CAIRO_OPERATOR_ATOP;
+ case CompositionOp::OP_OUT:
+ return CAIRO_OPERATOR_OUT;
+ case CompositionOp::OP_IN:
+ return CAIRO_OPERATOR_IN;
+ case CompositionOp::OP_SOURCE:
+ return CAIRO_OPERATOR_SOURCE;
+ case CompositionOp::OP_DEST_IN:
+ return CAIRO_OPERATOR_DEST_IN;
+ case CompositionOp::OP_DEST_OUT:
+ return CAIRO_OPERATOR_DEST_OUT;
+ case CompositionOp::OP_DEST_OVER:
+ return CAIRO_OPERATOR_DEST_OVER;
+ case CompositionOp::OP_DEST_ATOP:
+ return CAIRO_OPERATOR_DEST_ATOP;
+ case CompositionOp::OP_XOR:
+ return CAIRO_OPERATOR_XOR;
+ case CompositionOp::OP_MULTIPLY:
+ return CAIRO_OPERATOR_MULTIPLY;
+ case CompositionOp::OP_SCREEN:
+ return CAIRO_OPERATOR_SCREEN;
+ case CompositionOp::OP_OVERLAY:
+ return CAIRO_OPERATOR_OVERLAY;
+ case CompositionOp::OP_DARKEN:
+ return CAIRO_OPERATOR_DARKEN;
+ case CompositionOp::OP_LIGHTEN:
+ return CAIRO_OPERATOR_LIGHTEN;
+ case CompositionOp::OP_COLOR_DODGE:
+ return CAIRO_OPERATOR_COLOR_DODGE;
+ case CompositionOp::OP_COLOR_BURN:
+ return CAIRO_OPERATOR_COLOR_BURN;
+ case CompositionOp::OP_HARD_LIGHT:
+ return CAIRO_OPERATOR_HARD_LIGHT;
+ case CompositionOp::OP_SOFT_LIGHT:
+ return CAIRO_OPERATOR_SOFT_LIGHT;
+ case CompositionOp::OP_DIFFERENCE:
+ return CAIRO_OPERATOR_DIFFERENCE;
+ case CompositionOp::OP_EXCLUSION:
+ return CAIRO_OPERATOR_EXCLUSION;
+ case CompositionOp::OP_HUE:
+ return CAIRO_OPERATOR_HSL_HUE;
+ case CompositionOp::OP_SATURATION:
+ return CAIRO_OPERATOR_HSL_SATURATION;
+ case CompositionOp::OP_COLOR:
+ return CAIRO_OPERATOR_HSL_COLOR;
+ case CompositionOp::OP_LUMINOSITY:
+ return CAIRO_OPERATOR_HSL_LUMINOSITY;
+ 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;
+ default:
+ return CAIRO_ANTIALIAS_DEFAULT;
+ }
+}
+
+static inline AntialiasMode
+CairoAntialiasToGfxAntialias(cairo_antialias_t aAntialias)
+{
+ switch(aAntialias) {
+ case CAIRO_ANTIALIAS_NONE:
+ return AntialiasMode::NONE;
+ case CAIRO_ANTIALIAS_GRAY:
+ return AntialiasMode::GRAY;
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ return AntialiasMode::SUBPIXEL;
+ default:
+ return AntialiasMode::DEFAULT;
+ }
+}
+
+static inline cairo_filter_t
+GfxSamplingFilterToCairoFilter(SamplingFilter filter)
+{
+ switch (filter)
+ {
+ case SamplingFilter::GOOD:
+ return CAIRO_FILTER_GOOD;
+ case SamplingFilter::LINEAR:
+ return CAIRO_FILTER_BILINEAR;
+ case SamplingFilter::POINT:
+ return CAIRO_FILTER_NEAREST;
+ default:
+ MOZ_CRASH("GFX: bad Cairo filter");
+ }
+
+ return CAIRO_FILTER_BILINEAR;
+}
+
+static inline cairo_extend_t
+GfxExtendToCairoExtend(ExtendMode extend)
+{
+ switch (extend)
+ {
+ case ExtendMode::CLAMP:
+ return CAIRO_EXTEND_PAD;
+ // Cairo doesn't support tiling in only 1 direction,
+ // So we have to fallback and tile in both.
+ case ExtendMode::REPEAT_X:
+ case ExtendMode::REPEAT_Y:
+ case ExtendMode::REPEAT:
+ return CAIRO_EXTEND_REPEAT;
+ case ExtendMode::REFLECT:
+ return CAIRO_EXTEND_REFLECT;
+ }
+
+ return CAIRO_EXTEND_PAD;
+}
+
+static inline cairo_format_t
+GfxFormatToCairoFormat(SurfaceFormat format)
+{
+ switch (format)
+ {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ return CAIRO_FORMAT_ARGB32;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ return CAIRO_FORMAT_RGB24;
+ case SurfaceFormat::A8:
+ return CAIRO_FORMAT_A8;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return CAIRO_FORMAT_RGB16_565;
+ default:
+ gfxCriticalError() << "Unknown image format " << (int)format;
+ return CAIRO_FORMAT_ARGB32;
+ }
+}
+
+static inline cairo_content_t
+GfxFormatToCairoContent(SurfaceFormat format)
+{
+ switch (format)
+ {
+ case SurfaceFormat::A8R8G8B8_UINT32:
+ return CAIRO_CONTENT_COLOR_ALPHA;
+ case SurfaceFormat::X8R8G8B8_UINT32:
+ case SurfaceFormat::R5G6B5_UINT16: //fall through
+ return CAIRO_CONTENT_COLOR;
+ case SurfaceFormat::A8:
+ return CAIRO_CONTENT_ALPHA;
+ default:
+ gfxCriticalError() << "Unknown image content format " << (int)format;
+ return CAIRO_CONTENT_COLOR_ALPHA;
+ }
+}
+
+static inline cairo_line_join_t
+GfxLineJoinToCairoLineJoin(JoinStyle style)
+{
+ switch (style)
+ {
+ case JoinStyle::BEVEL:
+ return CAIRO_LINE_JOIN_BEVEL;
+ case JoinStyle::ROUND:
+ return CAIRO_LINE_JOIN_ROUND;
+ case JoinStyle::MITER:
+ return CAIRO_LINE_JOIN_MITER;
+ case JoinStyle::MITER_OR_BEVEL:
+ return CAIRO_LINE_JOIN_MITER;
+ }
+
+ return CAIRO_LINE_JOIN_MITER;
+}
+
+static inline cairo_line_cap_t
+GfxLineCapToCairoLineCap(CapStyle style)
+{
+ switch (style)
+ {
+ case CapStyle::BUTT:
+ return CAIRO_LINE_CAP_BUTT;
+ case CapStyle::ROUND:
+ return CAIRO_LINE_CAP_ROUND;
+ case CapStyle::SQUARE:
+ return CAIRO_LINE_CAP_SQUARE;
+ }
+
+ return CAIRO_LINE_CAP_BUTT;
+}
+
+static inline SurfaceFormat
+CairoContentToGfxFormat(cairo_content_t content)
+{
+ switch (content)
+ {
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ return SurfaceFormat::A8R8G8B8_UINT32;
+ case CAIRO_CONTENT_COLOR:
+ // BEWARE! format may be 565
+ return SurfaceFormat::X8R8G8B8_UINT32;
+ case CAIRO_CONTENT_ALPHA:
+ return SurfaceFormat::A8;
+ }
+
+ return SurfaceFormat::B8G8R8A8;
+}
+
+static inline SurfaceFormat
+CairoFormatToGfxFormat(cairo_format_t format)
+{
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ return SurfaceFormat::A8R8G8B8_UINT32;
+ case CAIRO_FORMAT_RGB24:
+ return SurfaceFormat::X8R8G8B8_UINT32;
+ case CAIRO_FORMAT_A8:
+ return SurfaceFormat::A8;
+ case CAIRO_FORMAT_RGB16_565:
+ return SurfaceFormat::R5G6B5_UINT16;
+ default:
+ gfxCriticalError() << "Unknown cairo format " << format;
+ return SurfaceFormat::UNKNOWN;
+ }
+}
+
+static inline FontHinting
+CairoHintingToGfxHinting(cairo_hint_style_t aHintStyle)
+{
+ switch (aHintStyle) {
+ case CAIRO_HINT_STYLE_NONE:
+ return FontHinting::NONE;
+ case CAIRO_HINT_STYLE_SLIGHT:
+ return FontHinting::LIGHT;
+ case CAIRO_HINT_STYLE_MEDIUM:
+ return FontHinting::NORMAL;
+ case CAIRO_HINT_STYLE_FULL:
+ return FontHinting::FULL;
+ default:
+ return FontHinting::NORMAL;
+ }
+}
+
+SurfaceFormat GfxFormatForCairoSurface(cairo_surface_t* surface);
+
+static inline void
+GfxMatrixToCairoMatrix(const Matrix& mat, cairo_matrix_t& retval)
+{
+ cairo_matrix_init(&retval, mat._11, mat._12, mat._21, mat._22, mat._31, mat._32);
+}
+
+static inline void
+SetCairoStrokeOptions(cairo_t* aCtx, const StrokeOptions& aStrokeOptions)
+{
+ cairo_set_line_width(aCtx, aStrokeOptions.mLineWidth);
+
+ cairo_set_miter_limit(aCtx, aStrokeOptions.mMiterLimit);
+
+ if (aStrokeOptions.mDashPattern) {
+ // Convert array of floats to array of doubles
+ std::vector<double> dashes(aStrokeOptions.mDashLength);
+ bool nonZero = false;
+ for (size_t i = 0; i < aStrokeOptions.mDashLength; ++i) {
+ if (aStrokeOptions.mDashPattern[i] != 0) {
+ nonZero = true;
+ }
+ dashes[i] = aStrokeOptions.mDashPattern[i];
+ }
+ // Avoid all-zero patterns that would trigger the CAIRO_STATUS_INVALID_DASH context error state.
+ if (nonZero) {
+ cairo_set_dash(aCtx, &dashes[0], aStrokeOptions.mDashLength,
+ aStrokeOptions.mDashOffset);
+ }
+ }
+
+ cairo_set_line_join(aCtx, GfxLineJoinToCairoLineJoin(aStrokeOptions.mLineJoin));
+
+ cairo_set_line_cap(aCtx, GfxLineCapToCairoLineCap(aStrokeOptions.mLineCap));
+}
+
+static inline cairo_fill_rule_t
+GfxFillRuleToCairoFillRule(FillRule rule)
+{
+ switch (rule)
+ {
+ case FillRule::FILL_WINDING:
+ return CAIRO_FILL_RULE_WINDING;
+ case FillRule::FILL_EVEN_ODD:
+ return CAIRO_FILL_RULE_EVEN_ODD;
+ }
+
+ return CAIRO_FILL_RULE_WINDING;
+}
+
+// RAII class for temporarily changing the cairo matrix transform. It will use
+// the given matrix transform while it is in scope. When it goes out of scope
+// it will put the cairo context back the way it was.
+
+class CairoTempMatrix
+{
+public:
+ CairoTempMatrix(cairo_t* aCtx, const Matrix& aMatrix)
+ : mCtx(aCtx)
+ {
+ cairo_get_matrix(aCtx, &mSaveMatrix);
+ cairo_matrix_t matrix;
+ GfxMatrixToCairoMatrix(aMatrix, matrix);
+ cairo_set_matrix(aCtx, &matrix);
+ }
+
+ ~CairoTempMatrix()
+ {
+ cairo_set_matrix(mCtx, &mSaveMatrix);
+ }
+
+private:
+ cairo_t* mCtx;
+ cairo_matrix_t mSaveMatrix;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_HELPERSCAIRO_H_ */
diff --git a/gfx/2d/HelpersD2D.h b/gfx/2d/HelpersD2D.h
new file mode 100644
index 000000000..48aec2cb5
--- /dev/null
+++ b/gfx/2d/HelpersD2D.h
@@ -0,0 +1,967 @@
+/* -*- 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_HELPERSD2D_H_
+#define MOZILLA_GFX_HELPERSD2D_H_
+
+#include <d2d1_1.h>
+
+#include <vector>
+
+#include <dwrite.h>
+#include <versionhelpers.h>
+#include "2D.h"
+#include "Logging.h"
+#include "Tools.h"
+#include "ImageScaling.h"
+
+#include "ScaledFontDWrite.h"
+
+#undef min
+#undef max
+
+namespace mozilla {
+namespace gfx {
+
+ID2D1Factory1* D2DFactory1();
+static ID2D1Factory* D2DFactory() { return D2DFactory1(); }
+
+static inline D2D1_POINT_2F D2DPoint(const Point &aPoint)
+{
+ return D2D1::Point2F(aPoint.x, aPoint.y);
+}
+
+static inline D2D1_SIZE_U D2DIntSize(const IntSize &aSize)
+{
+ return D2D1::SizeU(aSize.width, aSize.height);
+}
+
+template <typename T>
+static inline D2D1_RECT_F D2DRect(const T &aRect)
+{
+ return D2D1::RectF(aRect.x, aRect.y, aRect.XMost(), aRect.YMost());
+}
+
+static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode, Axis aAxis)
+{
+ D2D1_EXTEND_MODE extend;
+ switch (aExtendMode) {
+ case ExtendMode::REPEAT:
+ extend = D2D1_EXTEND_MODE_WRAP;
+ break;
+ case ExtendMode::REPEAT_X:
+ {
+ extend = aAxis == Axis::X_AXIS
+ ? D2D1_EXTEND_MODE_WRAP
+ : D2D1_EXTEND_MODE_CLAMP;
+ break;
+ }
+ case ExtendMode::REPEAT_Y:
+ {
+ extend = aAxis == Axis::Y_AXIS
+ ? D2D1_EXTEND_MODE_WRAP
+ : D2D1_EXTEND_MODE_CLAMP;
+ break;
+ }
+ case ExtendMode::REFLECT:
+ extend = D2D1_EXTEND_MODE_MIRROR;
+ break;
+ default:
+ extend = D2D1_EXTEND_MODE_CLAMP;
+ }
+
+ return extend;
+}
+
+static inline D2D1_BITMAP_INTERPOLATION_MODE D2DFilter(const SamplingFilter aSamplingFilter)
+{
+ switch (aSamplingFilter) {
+ case SamplingFilter::POINT:
+ return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+ default:
+ return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
+ }
+}
+
+static inline D2D1_INTERPOLATION_MODE D2DInterpolationMode(const SamplingFilter aSamplingFilter)
+{
+ switch (aSamplingFilter) {
+ case SamplingFilter::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 AntialiasMode::NONE:
+ return D2D1_ANTIALIAS_MODE_ALIASED;
+ default:
+ return D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
+ }
+}
+
+static inline D2D1_MATRIX_3X2_F D2DMatrix(const Matrix &aTransform)
+{
+ return D2D1::Matrix3x2F(aTransform._11, aTransform._12,
+ aTransform._21, aTransform._22,
+ aTransform._31, aTransform._32);
+}
+
+static inline D2D1_COLOR_F D2DColor(const Color &aColor)
+{
+ return D2D1::ColorF(aColor.r, aColor.g, aColor.b, aColor.a);
+}
+
+static inline IntSize ToIntSize(const D2D1_SIZE_U &aSize)
+{
+ return IntSize(aSize.width, aSize.height);
+}
+
+static inline SurfaceFormat ToPixelFormat(const D2D1_PIXEL_FORMAT &aFormat)
+{
+ switch(aFormat.format) {
+ case DXGI_FORMAT_A8_UNORM:
+ case DXGI_FORMAT_R8_UNORM:
+ return SurfaceFormat::A8;
+ case DXGI_FORMAT_B8G8R8A8_UNORM:
+ if (aFormat.alphaMode == D2D1_ALPHA_MODE_IGNORE) {
+ return SurfaceFormat::B8G8R8X8;
+ } else {
+ return SurfaceFormat::B8G8R8A8;
+ }
+ default:
+ return SurfaceFormat::B8G8R8A8;
+ }
+}
+
+static inline Rect ToRect(const D2D1_RECT_F &aRect)
+{
+ return Rect(aRect.left, aRect.top, aRect.right - aRect.left, aRect.bottom - aRect.top);
+}
+
+static inline Matrix ToMatrix(const D2D1_MATRIX_3X2_F &aTransform)
+{
+ return Matrix(aTransform._11, aTransform._12,
+ aTransform._21, aTransform._22,
+ 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 SurfaceFormat::B8G8R8A8:
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
+ case SurfaceFormat::B8G8R8X8:
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
+ case SurfaceFormat::A8:
+ return DXGI_FORMAT_A8_UNORM;
+ default:
+ return DXGI_FORMAT_UNKNOWN;
+ }
+}
+
+static inline D2D1_ALPHA_MODE D2DAlphaModeForFormat(SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8X8:
+ return D2D1_ALPHA_MODE_IGNORE;
+ default:
+ return D2D1_ALPHA_MODE_PREMULTIPLIED;
+ }
+}
+
+static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat 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 D2DSupportsPrimitiveBlendMode(CompositionOp aOp)
+{
+ switch (aOp) {
+ case CompositionOp::OP_OVER:
+// case CompositionOp::OP_SOURCE:
+ return true;
+// case CompositionOp::OP_DARKEN:
+ case CompositionOp::OP_ADD:
+ return IsWindows8Point1OrGreater();
+ default:
+ return false;
+ }
+}
+
+static inline D2D1_PRIMITIVE_BLEND D2DPrimitiveBlendMode(CompositionOp aOp)
+{
+ switch (aOp) {
+ case CompositionOp::OP_OVER:
+ return D2D1_PRIMITIVE_BLEND_SOURCE_OVER;
+ // D2D1_PRIMITIVE_BLEND_COPY should leave pixels out of the source's
+ // bounds unchanged, but doesn't- breaking unbounded ops.
+ // D2D1_PRIMITIVE_BLEND_MIN doesn't quite work like darken either, as it
+ // accounts for the source alpha.
+ //
+ // case CompositionOp::OP_SOURCE:
+ // return D2D1_PRIMITIVE_BLEND_COPY;
+ // case CompositionOp::OP_DARKEN:
+ // return D2D1_PRIMITIVE_BLEND_MIN;
+ case CompositionOp::OP_ADD:
+ return D2D1_PRIMITIVE_BLEND_ADD;
+ default:
+ return D2D1_PRIMITIVE_BLEND_SOURCE_OVER;
+ }
+}
+
+static inline bool IsPatternSupportedByD2D(const Pattern &aPattern)
+{
+ if (aPattern.GetType() != PatternType::RADIAL_GRADIENT) {
+ return true;
+ }
+
+ const RadialGradientPattern *pat =
+ static_cast<const RadialGradientPattern*>(&aPattern);
+
+ if (pat->mRadius1 != 0) {
+ return false;
+ }
+
+ Point diff = pat->mCenter2 - pat->mCenter1;
+
+ if (sqrt(diff.x * diff.x + diff.y * diff.y) >= pat->mRadius2) {
+ // Inner point lies outside the circle.
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * This structure is used to pass rectangles to our shader constant. We can use
+ * this for passing rectangular areas to SetVertexShaderConstant. In the format
+ * of a 4 component float(x,y,width,height). Our vertex shader can then use
+ * this to construct rectangular positions from the 0,0-1,1 quad that we source
+ * it with.
+ */
+struct ShaderConstantRectD3D10
+{
+ float mX, mY, mWidth, mHeight;
+ ShaderConstantRectD3D10(float aX, float aY, float aWidth, float aHeight)
+ : mX(aX), mY(aY), mWidth(aWidth), mHeight(aHeight)
+ { }
+
+ // For easy passing to SetVertexShaderConstantF.
+ operator float* () { return &mX; }
+};
+
+static inline DWRITE_MATRIX
+DWriteMatrixFromMatrix(Matrix &aMatrix)
+{
+ DWRITE_MATRIX mat;
+ mat.m11 = aMatrix._11;
+ mat.m12 = aMatrix._12;
+ mat.m21 = aMatrix._21;
+ mat.m22 = aMatrix._22;
+ mat.dx = aMatrix._31;
+ mat.dy = aMatrix._32;
+ return mat;
+}
+
+class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN
+{
+ static const unsigned kNumAutoGlyphs = 256;
+
+public:
+ AutoDWriteGlyphRun() {
+ glyphCount = 0;
+ }
+
+ ~AutoDWriteGlyphRun() {
+ if (glyphCount > kNumAutoGlyphs) {
+ delete[] glyphIndices;
+ delete[] glyphAdvances;
+ delete[] glyphOffsets;
+ }
+ }
+
+ void allocate(unsigned aNumGlyphs) {
+ glyphCount = aNumGlyphs;
+ if (aNumGlyphs <= kNumAutoGlyphs) {
+ glyphIndices = &mAutoIndices[0];
+ glyphAdvances = &mAutoAdvances[0];
+ glyphOffsets = &mAutoOffsets[0];
+ } else {
+ glyphIndices = new UINT16[aNumGlyphs];
+ glyphAdvances = new FLOAT[aNumGlyphs];
+ glyphOffsets = new DWRITE_GLYPH_OFFSET[aNumGlyphs];
+ }
+ }
+
+private:
+ DWRITE_GLYPH_OFFSET mAutoOffsets[kNumAutoGlyphs];
+ FLOAT mAutoAdvances[kNumAutoGlyphs];
+ UINT16 mAutoIndices[kNumAutoGlyphs];
+};
+
+static inline void
+DWriteGlyphRunFromGlyphs(const GlyphBuffer &aGlyphs, ScaledFontDWrite *aFont, AutoDWriteGlyphRun *run)
+{
+ run->allocate(aGlyphs.mNumGlyphs);
+
+ FLOAT *advances = const_cast<FLOAT*>(run->glyphAdvances);
+ UINT16 *indices = const_cast<UINT16*>(run->glyphIndices);
+ DWRITE_GLYPH_OFFSET *offsets = const_cast<DWRITE_GLYPH_OFFSET*>(run->glyphOffsets);
+
+ memset(advances, 0, sizeof(FLOAT) * aGlyphs.mNumGlyphs);
+ for (unsigned int i = 0; i < aGlyphs.mNumGlyphs; i++) {
+ indices[i] = aGlyphs.mGlyphs[i].mIndex;
+ offsets[i].advanceOffset = aGlyphs.mGlyphs[i].mPosition.x;
+ offsets[i].ascenderOffset = -aGlyphs.mGlyphs[i].mPosition.y;
+ }
+
+ run->bidiLevel = 0;
+ run->fontFace = aFont->mFontFace;
+ run->fontEmSize = aFont->GetSize();
+ run->glyphCount = aGlyphs.mNumGlyphs;
+ run->isSideways = FALSE;
+}
+
+static inline already_AddRefed<ID2D1Geometry>
+ConvertRectToGeometry(const D2D1_RECT_F& aRect)
+{
+ RefPtr<ID2D1RectangleGeometry> rectGeom;
+ D2DFactory()->CreateRectangleGeometry(&aRect, getter_AddRefs(rectGeom));
+ return rectGeom.forget();
+}
+
+static inline already_AddRefed<ID2D1Geometry>
+GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform)
+{
+ RefPtr<ID2D1PathGeometry> tmpGeometry;
+ D2DFactory()->CreatePathGeometry(getter_AddRefs(tmpGeometry));
+ RefPtr<ID2D1GeometrySink> currentSink;
+ tmpGeometry->Open(getter_AddRefs(currentSink));
+ aGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+ aTransform, currentSink);
+ currentSink->Close();
+ return tmpGeometry.forget();
+}
+
+static inline already_AddRefed<ID2D1Geometry>
+IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB)
+{
+ RefPtr<ID2D1PathGeometry> pathGeom;
+ D2DFactory()->CreatePathGeometry(getter_AddRefs(pathGeom));
+ RefPtr<ID2D1GeometrySink> sink;
+ pathGeom->Open(getter_AddRefs(sink));
+ aGeometryA->CombineWithGeometry(aGeometryB, D2D1_COMBINE_MODE_INTERSECT, nullptr, sink);
+ sink->Close();
+
+ return pathGeom.forget();
+}
+
+static inline already_AddRefed<ID2D1StrokeStyle>
+CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions)
+{
+ RefPtr<ID2D1StrokeStyle> style;
+
+ D2D1_CAP_STYLE capStyle;
+ D2D1_LINE_JOIN joinStyle;
+
+ switch (aStrokeOptions.mLineCap) {
+ case CapStyle::BUTT:
+ capStyle = D2D1_CAP_STYLE_FLAT;
+ break;
+ case CapStyle::ROUND:
+ capStyle = D2D1_CAP_STYLE_ROUND;
+ break;
+ case CapStyle::SQUARE:
+ capStyle = D2D1_CAP_STYLE_SQUARE;
+ break;
+ }
+
+ switch (aStrokeOptions.mLineJoin) {
+ case JoinStyle::MITER:
+ joinStyle = D2D1_LINE_JOIN_MITER;
+ break;
+ case JoinStyle::MITER_OR_BEVEL:
+ joinStyle = D2D1_LINE_JOIN_MITER_OR_BEVEL;
+ break;
+ case JoinStyle::ROUND:
+ joinStyle = D2D1_LINE_JOIN_ROUND;
+ break;
+ case JoinStyle::BEVEL:
+ joinStyle = D2D1_LINE_JOIN_BEVEL;
+ break;
+ }
+
+
+ HRESULT hr;
+ // 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.
+ // So fix the multiplication in advance.
+ Float lineWidth = aStrokeOptions.mLineWidth;
+ FloatVector dash(aStrokeOptions.mDashPattern,
+ aStrokeOptions.mDashPattern + aStrokeOptions.mDashLength);
+ for (FloatVector::iterator it = dash.begin(); it != dash.end(); ++it) {
+ *it /= lineWidth;
+ }
+
+ hr = D2DFactory()->CreateStrokeStyle(
+ D2D1::StrokeStyleProperties(capStyle, capStyle,
+ capStyle, joinStyle,
+ aStrokeOptions.mMiterLimit,
+ D2D1_DASH_STYLE_CUSTOM,
+ aStrokeOptions.mDashOffset / lineWidth),
+ &dash[0], // data() is not C++98, although it's in recent gcc
+ // and VC10's STL
+ dash.size(),
+ getter_AddRefs(style));
+ } else {
+ hr = D2DFactory()->CreateStrokeStyle(
+ D2D1::StrokeStyleProperties(capStyle, capStyle,
+ capStyle, joinStyle,
+ aStrokeOptions.mMiterLimit),
+ nullptr, 0, getter_AddRefs(style));
+ }
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create Direct2D stroke 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 inline already_AddRefed<ID2D1Bitmap>
+CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestinationTransform,
+ const IntSize &aDestinationSize, ExtendMode aExtendMode,
+ Matrix &aSourceTransform, ID2D1RenderTarget *aRT,
+ const IntRect* aSourceRect = nullptr)
+{
+ RefPtr<ID2D1Bitmap> bitmap;
+
+ // This is where things get complicated. The source surface was
+ // created for a surface that was too large to fit in a texture.
+ // We'll need to figure out if we can work with a partial upload
+ // or downsample in software.
+
+ Matrix transform = aDestinationTransform;
+ Matrix invTransform = transform = aSourceTransform * transform;
+ if (!invTransform.Invert()) {
+ // Singular transform, nothing to be drawn.
+ return nullptr;
+ }
+
+ Rect rect(0, 0, Float(aDestinationSize.width), Float(aDestinationSize.height));
+
+ // Calculate the rectangle of the source mapped to our surface.
+ rect = invTransform.TransformBounds(rect);
+ rect.RoundOut();
+
+ 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
+ //
+ // region we will paint from
+ // uploadRect
+ // .---------------. .---------------. resulting uploadRect
+ // | |rect | |
+ // | .---------. .----. .----. .---------------.
+ // | | | ----> | | | | ----> | |
+ // | '---------' '----' '----' '---------------'
+ // '---------------' '---------------'
+ //
+ //
+
+ if (uploadRect.Contains(rect)) {
+ // Extend mode is irrelevant, the displayed rect is completely contained
+ // by the source bitmap.
+ uploadRect = rect;
+ } else if (aExtendMode == ExtendMode::CLAMP && uploadRect.Intersects(rect)) {
+ // Calculate the rectangle on the source bitmap that touches our
+ // surface, and upload that, for ExtendMode::CLAMP we can actually guarantee
+ // correct behaviour in this case.
+ uploadRect = uploadRect.Intersect(rect);
+
+ // We now proceed to check if we can limit at least one dimension of the
+ // upload rect safely without looking at extend mode.
+ } else if (rect.x >= 0 && rect.XMost() < size.width) {
+ uploadRect.x = rect.x;
+ uploadRect.width = rect.width;
+ } else if (rect.y >= 0 && rect.YMost() < size.height) {
+ uploadRect.y = rect.y;
+ uploadRect.height = rect.height;
+ }
+
+ if (uploadRect.IsEmpty()) {
+ // Nothing to be drawn.
+ return nullptr;
+ }
+
+ if (uploadRect.width <= aRT->GetMaximumBitmapSize() &&
+ uploadRect.height <= aRT->GetMaximumBitmapSize()) {
+ {
+ // Scope to auto-Unmap() |mapping|.
+ DataSourceSurface::ScopedMap mapping(aSurface, DataSourceSurface::READ);
+ if (MOZ2D_WARN_IF(!mapping.IsMapped())) {
+ return nullptr;
+ }
+
+ // A partial upload will suffice.
+ aRT->CreateBitmap(D2D1::SizeU(uint32_t(uploadRect.width), uint32_t(uploadRect.height)),
+ mapping.GetData() + int(uploadRect.x) * 4 + int(uploadRect.y) * mapping.GetStride(),
+ mapping.GetStride(),
+ D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
+ getter_AddRefs(bitmap));
+ }
+
+ aSourceTransform.PreTranslate(uploadRect.x, uploadRect.y);
+
+ return bitmap.forget();
+ } else {
+ int Bpp = BytesPerPixel(aSurface->GetFormat());
+
+ if (Bpp != 4) {
+ // This shouldn't actually happen in practice!
+ MOZ_ASSERT(false);
+ return nullptr;
+ }
+
+ {
+ // Scope to auto-Unmap() |mapping|.
+ DataSourceSurface::ScopedMap mapping(aSurface, DataSourceSurface::READ);
+ if (MOZ2D_WARN_IF(!mapping.IsMapped())) {
+ return nullptr;
+ }
+ ImageHalfScaler scaler(mapping.GetData(), mapping.GetStride(), size);
+
+ // Calculate the maximum width/height of the image post transform.
+ Point topRight = transform.TransformPoint(Point(Float(size.width), 0));
+ Point topLeft = transform.TransformPoint(Point(0, 0));
+ Point bottomRight = transform.TransformPoint(Point(Float(size.width), Float(size.height)));
+ Point bottomLeft = transform.TransformPoint(Point(0, Float(size.height)));
+
+ IntSize scaleSize;
+
+ scaleSize.width = int32_t(std::max(Distance(topRight, topLeft),
+ Distance(bottomRight, bottomLeft)));
+ scaleSize.height = int32_t(std::max(Distance(topRight, bottomRight),
+ Distance(topLeft, bottomLeft)));
+
+ if (unsigned(scaleSize.width) > aRT->GetMaximumBitmapSize()) {
+ // Ok, in this case we'd really want a downscale of a part of the bitmap,
+ // perhaps we can do this later but for simplicity let's do something
+ // different here and assume it's good enough, this should be rare!
+ scaleSize.width = 4095;
+ }
+ if (unsigned(scaleSize.height) > aRT->GetMaximumBitmapSize()) {
+ scaleSize.height = 4095;
+ }
+
+ 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())),
+ getter_AddRefs(bitmap));
+
+ aSourceTransform.PreScale(Float(size.width) / newSize.width,
+ Float(size.height) / newSize.height);
+ }
+ return bitmap.forget();
+ }
+}
+
+static inline void AddRectToSink(ID2D1GeometrySink* aSink, const D2D1_RECT_F& aRect)
+{
+ aSink->BeginFigure(D2D1::Point2F(aRect.left, aRect.top), D2D1_FIGURE_BEGIN_FILLED);
+ aSink->AddLine(D2D1::Point2F(aRect.right, aRect.top));
+ aSink->AddLine(D2D1::Point2F(aRect.right, aRect.bottom));
+ aSink->AddLine(D2D1::Point2F(aRect.left, aRect.bottom));
+ aSink->EndFigure(D2D1_FIGURE_END_CLOSED);
+}
+
+class DCCommandSink : public ID2D1CommandSink
+{
+public:
+ DCCommandSink(ID2D1DeviceContext* aCtx) : mCtx(aCtx)
+ {
+ }
+
+ 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_ID2D1CommandSink) {
+ *aPtr = static_cast<ID2D1CommandSink*>(this);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return 1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return 1;
+ }
+
+ STDMETHODIMP BeginDraw()
+ {
+ // We don't want to do anything here!
+ return S_OK;
+ }
+ STDMETHODIMP EndDraw()
+ {
+ // We don't want to do anything here!
+ return S_OK;
+ }
+
+ STDMETHODIMP SetAntialiasMode(
+ D2D1_ANTIALIAS_MODE antialiasMode
+ )
+ {
+ mCtx->SetAntialiasMode(antialiasMode);
+ return S_OK;
+ }
+
+ STDMETHODIMP SetTags(D2D1_TAG tag1, D2D1_TAG tag2)
+ {
+ mCtx->SetTags(tag1, tag2);
+ return S_OK;
+ }
+
+ STDMETHODIMP SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode)
+ {
+ mCtx->SetTextAntialiasMode(textAntialiasMode);
+ return S_OK;
+ }
+
+ STDMETHODIMP SetTextRenderingParams(_In_opt_ IDWriteRenderingParams *textRenderingParams)
+ {
+ mCtx->SetTextRenderingParams(textRenderingParams);
+ return S_OK;
+ }
+
+ STDMETHODIMP SetTransform(_In_ CONST D2D1_MATRIX_3X2_F *transform)
+ {
+ mCtx->SetTransform(transform);
+ return S_OK;
+ }
+
+ STDMETHODIMP SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND primitiveBlend)
+ {
+ mCtx->SetPrimitiveBlend(primitiveBlend);
+ return S_OK;
+ }
+
+ STDMETHODIMP SetUnitMode(D2D1_UNIT_MODE unitMode)
+ {
+ mCtx->SetUnitMode(unitMode);
+ return S_OK;
+ }
+
+ STDMETHODIMP Clear(_In_opt_ CONST D2D1_COLOR_F *color)
+ {
+ mCtx->Clear(color);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawGlyphRun(
+ D2D1_POINT_2F baselineOrigin,
+ _In_ CONST DWRITE_GLYPH_RUN *glyphRun,
+ _In_opt_ CONST DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription,
+ _In_ ID2D1Brush *foregroundBrush,
+ DWRITE_MEASURING_MODE measuringMode
+ )
+ {
+ mCtx->DrawGlyphRun(baselineOrigin, glyphRun, glyphRunDescription,
+ foregroundBrush, measuringMode);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawLine(
+ D2D1_POINT_2F point0,
+ D2D1_POINT_2F point1,
+ _In_ ID2D1Brush *brush,
+ FLOAT strokeWidth,
+ _In_opt_ ID2D1StrokeStyle *strokeStyle
+ )
+ {
+ mCtx->DrawLine(point0, point1, brush, strokeWidth, strokeStyle);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawGeometry(
+ _In_ ID2D1Geometry *geometry,
+ _In_ ID2D1Brush *brush,
+ FLOAT strokeWidth,
+ _In_opt_ ID2D1StrokeStyle *strokeStyle
+ )
+ {
+ mCtx->DrawGeometry(geometry, brush, strokeWidth, strokeStyle);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawRectangle(
+ _In_ CONST D2D1_RECT_F *rect,
+ _In_ ID2D1Brush *brush,
+ FLOAT strokeWidth,
+ _In_opt_ ID2D1StrokeStyle *strokeStyle
+ )
+ {
+ mCtx->DrawRectangle(rect, brush, strokeWidth, strokeStyle);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawBitmap(
+ _In_ ID2D1Bitmap *bitmap,
+ _In_opt_ CONST D2D1_RECT_F *destinationRectangle,
+ FLOAT opacity,
+ D2D1_INTERPOLATION_MODE interpolationMode,
+ _In_opt_ CONST D2D1_RECT_F *sourceRectangle,
+ _In_opt_ CONST D2D1_MATRIX_4X4_F *perspectiveTransform
+ )
+ {
+ mCtx->DrawBitmap(bitmap, destinationRectangle, opacity,
+ interpolationMode, sourceRectangle,
+ perspectiveTransform);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawImage(
+ _In_ ID2D1Image *image,
+ _In_opt_ CONST D2D1_POINT_2F *targetOffset,
+ _In_opt_ CONST D2D1_RECT_F *imageRectangle,
+ D2D1_INTERPOLATION_MODE interpolationMode,
+ D2D1_COMPOSITE_MODE compositeMode
+ )
+ {
+ mCtx->DrawImage(image, targetOffset, imageRectangle,
+ interpolationMode, compositeMode);
+ return S_OK;
+ }
+
+ STDMETHODIMP DrawGdiMetafile(
+ _In_ ID2D1GdiMetafile *gdiMetafile,
+ _In_opt_ CONST D2D1_POINT_2F *targetOffset
+ )
+ {
+ mCtx->DrawGdiMetafile(gdiMetafile, targetOffset);
+ return S_OK;
+ }
+
+ STDMETHODIMP FillMesh(
+ _In_ ID2D1Mesh *mesh,
+ _In_ ID2D1Brush *brush
+ )
+ {
+ mCtx->FillMesh(mesh, brush);
+ return S_OK;
+ }
+
+ STDMETHODIMP FillOpacityMask(
+ _In_ ID2D1Bitmap *opacityMask,
+ _In_ ID2D1Brush *brush,
+ _In_opt_ CONST D2D1_RECT_F *destinationRectangle,
+ _In_opt_ CONST D2D1_RECT_F *sourceRectangle
+ )
+ {
+ mCtx->FillOpacityMask(opacityMask, brush, destinationRectangle,
+ sourceRectangle);
+ return S_OK;
+ }
+
+ STDMETHODIMP FillGeometry(
+ _In_ ID2D1Geometry *geometry,
+ _In_ ID2D1Brush *brush,
+ _In_opt_ ID2D1Brush *opacityBrush
+ )
+ {
+ mCtx->FillGeometry(geometry, brush, opacityBrush);
+ return S_OK;
+ }
+
+ STDMETHODIMP FillRectangle(
+ _In_ CONST D2D1_RECT_F *rect,
+ _In_ ID2D1Brush *brush
+ )
+ {
+ mCtx->FillRectangle(rect, brush);
+ return S_OK;
+ }
+
+ STDMETHODIMP PushAxisAlignedClip(
+ _In_ CONST D2D1_RECT_F *clipRect,
+ D2D1_ANTIALIAS_MODE antialiasMode
+ )
+ {
+ mCtx->PushAxisAlignedClip(clipRect, antialiasMode);
+ return S_OK;
+ }
+
+ STDMETHODIMP PushLayer(
+ _In_ CONST D2D1_LAYER_PARAMETERS1 *layerParameters1,
+ _In_opt_ ID2D1Layer *layer
+ )
+ {
+ mCtx->PushLayer(layerParameters1, layer);
+ return S_OK;
+ }
+
+ STDMETHODIMP PopAxisAlignedClip()
+ {
+ mCtx->PopAxisAlignedClip();
+ return S_OK;
+ }
+
+ STDMETHODIMP PopLayer()
+ {
+ mCtx->PopLayer();
+ return S_OK;
+ }
+
+ ID2D1DeviceContext* mCtx;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_HELPERSD2D_H_ */
diff --git a/gfx/2d/HelpersSkia.h b/gfx/2d/HelpersSkia.h
new file mode 100644
index 000000000..95c67ad05
--- /dev/null
+++ b/gfx/2d/HelpersSkia.h
@@ -0,0 +1,396 @@
+/* -*- 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_HELPERSSKIA_H_
+#define MOZILLA_GFX_HELPERSSKIA_H_
+
+#include "2D.h"
+#include "skia/include/core/SkCanvas.h"
+#include "skia/include/effects/SkDashPathEffect.h"
+#include "skia/include/core/SkShader.h"
+#ifdef USE_SKIA_GPU
+#include "skia/include/gpu/GrTypes.h"
+#endif
+#include "mozilla/Assertions.h"
+#include <vector>
+#include "nsDebug.h"
+
+namespace mozilla {
+namespace gfx {
+
+static inline SkColorType
+GfxFormatToSkiaColorType(SurfaceFormat format)
+{
+ switch (format)
+ {
+ case SurfaceFormat::B8G8R8A8:
+ return kBGRA_8888_SkColorType;
+ case SurfaceFormat::B8G8R8X8:
+ // We probably need to do something here.
+ return kBGRA_8888_SkColorType;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return kRGB_565_SkColorType;
+ case SurfaceFormat::A8:
+ return kAlpha_8_SkColorType;
+ default:
+ return kRGBA_8888_SkColorType;
+ }
+}
+
+static inline SurfaceFormat
+SkiaColorTypeToGfxFormat(SkColorType aColorType, SkAlphaType aAlphaType = kPremul_SkAlphaType)
+{
+ switch (aColorType)
+ {
+ case kBGRA_8888_SkColorType:
+ return aAlphaType == kOpaque_SkAlphaType ?
+ SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
+ case kRGB_565_SkColorType:
+ return SurfaceFormat::R5G6B5_UINT16;
+ case kAlpha_8_SkColorType:
+ return SurfaceFormat::A8;
+ default:
+ return SurfaceFormat::B8G8R8A8;
+ }
+}
+
+static inline SkAlphaType
+GfxFormatToSkiaAlphaType(SurfaceFormat format)
+{
+ switch (format)
+ {
+ case SurfaceFormat::B8G8R8X8:
+ case SurfaceFormat::R5G6B5_UINT16:
+ return kOpaque_SkAlphaType;
+ default:
+ return kPremul_SkAlphaType;
+ }
+}
+
+static inline SkImageInfo
+MakeSkiaImageInfo(const IntSize& aSize, SurfaceFormat aFormat)
+{
+ return SkImageInfo::Make(aSize.width, aSize.height,
+ GfxFormatToSkiaColorType(aFormat),
+ GfxFormatToSkiaAlphaType(aFormat));
+}
+
+#ifdef USE_SKIA_GPU
+static inline GrPixelConfig
+GfxFormatToGrConfig(SurfaceFormat format)
+{
+ switch (format)
+ {
+ case SurfaceFormat::B8G8R8A8:
+ return kBGRA_8888_GrPixelConfig;
+ case SurfaceFormat::B8G8R8X8:
+ // We probably need to do something here.
+ return kBGRA_8888_GrPixelConfig;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return kRGB_565_GrPixelConfig;
+ case SurfaceFormat::A8:
+ return kAlpha_8_GrPixelConfig;
+ default:
+ return kRGBA_8888_GrPixelConfig;
+ }
+
+}
+#endif
+static inline void
+GfxMatrixToSkiaMatrix(const Matrix& mat, SkMatrix& retval)
+{
+ retval.setAll(SkFloatToScalar(mat._11), SkFloatToScalar(mat._21), SkFloatToScalar(mat._31),
+ SkFloatToScalar(mat._12), SkFloatToScalar(mat._22), SkFloatToScalar(mat._32),
+ 0, 0, SK_Scalar1);
+}
+
+static inline void
+GfxMatrixToSkiaMatrix(const Matrix4x4& aMatrix, SkMatrix& aResult)
+{
+ aResult.setAll(SkFloatToScalar(aMatrix._11), SkFloatToScalar(aMatrix._21), SkFloatToScalar(aMatrix._41),
+ SkFloatToScalar(aMatrix._12), SkFloatToScalar(aMatrix._22), SkFloatToScalar(aMatrix._42),
+ SkFloatToScalar(aMatrix._14), SkFloatToScalar(aMatrix._24), SkFloatToScalar(aMatrix._44));
+}
+
+static inline SkPaint::Cap
+CapStyleToSkiaCap(CapStyle aCap)
+{
+ switch (aCap)
+ {
+ case CapStyle::BUTT:
+ return SkPaint::kButt_Cap;
+ case CapStyle::ROUND:
+ return SkPaint::kRound_Cap;
+ case CapStyle::SQUARE:
+ return SkPaint::kSquare_Cap;
+ }
+ return SkPaint::kDefault_Cap;
+}
+
+static inline SkPaint::Join
+JoinStyleToSkiaJoin(JoinStyle aJoin)
+{
+ switch (aJoin)
+ {
+ case JoinStyle::BEVEL:
+ return SkPaint::kBevel_Join;
+ case JoinStyle::ROUND:
+ return SkPaint::kRound_Join;
+ case JoinStyle::MITER:
+ case JoinStyle::MITER_OR_BEVEL:
+ return SkPaint::kMiter_Join;
+ }
+ return SkPaint::kDefault_Join;
+}
+
+static inline bool
+StrokeOptionsToPaint(SkPaint& aPaint, const StrokeOptions &aOptions)
+{
+ // Skia renders 0 width strokes with a width of 1 (and in black),
+ // so we should just skip the draw call entirely.
+ // Skia does not handle non-finite line widths.
+ if (!aOptions.mLineWidth || !IsFinite(aOptions.mLineWidth)) {
+ return false;
+ }
+ aPaint.setStrokeWidth(SkFloatToScalar(aOptions.mLineWidth));
+ aPaint.setStrokeMiter(SkFloatToScalar(aOptions.mMiterLimit));
+ aPaint.setStrokeCap(CapStyleToSkiaCap(aOptions.mLineCap));
+ aPaint.setStrokeJoin(JoinStyleToSkiaJoin(aOptions.mLineJoin));
+
+ if (aOptions.mDashLength > 0) {
+ // Skia only supports dash arrays that are multiples of 2.
+ uint32_t dashCount;
+
+ if (aOptions.mDashLength % 2 == 0) {
+ dashCount = aOptions.mDashLength;
+ } else {
+ dashCount = aOptions.mDashLength * 2;
+ }
+
+ std::vector<SkScalar> pattern;
+ pattern.resize(dashCount);
+
+ for (uint32_t i = 0; i < dashCount; i++) {
+ pattern[i] = SkFloatToScalar(aOptions.mDashPattern[i % aOptions.mDashLength]);
+ }
+
+ sk_sp<SkPathEffect> dash = SkDashPathEffect::Make(&pattern.front(),
+ dashCount,
+ SkFloatToScalar(aOptions.mDashOffset));
+ aPaint.setPathEffect(dash);
+ }
+
+ aPaint.setStyle(SkPaint::kStroke_Style);
+ return true;
+}
+
+static inline SkBlendMode
+GfxOpToSkiaOp(CompositionOp op)
+{
+ switch (op)
+ {
+ case CompositionOp::OP_OVER:
+ return SkBlendMode::kSrcOver;
+ case CompositionOp::OP_ADD:
+ return SkBlendMode::kPlus;
+ case CompositionOp::OP_ATOP:
+ return SkBlendMode::kSrcATop;
+ case CompositionOp::OP_OUT:
+ return SkBlendMode::kSrcOut;
+ case CompositionOp::OP_IN:
+ return SkBlendMode::kSrcIn;
+ case CompositionOp::OP_SOURCE:
+ return SkBlendMode::kSrc;
+ case CompositionOp::OP_DEST_IN:
+ return SkBlendMode::kDstIn;
+ case CompositionOp::OP_DEST_OUT:
+ return SkBlendMode::kDstOut;
+ case CompositionOp::OP_DEST_OVER:
+ return SkBlendMode::kDstOver;
+ case CompositionOp::OP_DEST_ATOP:
+ return SkBlendMode::kDstATop;
+ case CompositionOp::OP_XOR:
+ return SkBlendMode::kXor;
+ case CompositionOp::OP_MULTIPLY:
+ return SkBlendMode::kMultiply;
+ case CompositionOp::OP_SCREEN:
+ return SkBlendMode::kScreen;
+ case CompositionOp::OP_OVERLAY:
+ return SkBlendMode::kOverlay;
+ case CompositionOp::OP_DARKEN:
+ return SkBlendMode::kDarken;
+ case CompositionOp::OP_LIGHTEN:
+ return SkBlendMode::kLighten;
+ case CompositionOp::OP_COLOR_DODGE:
+ return SkBlendMode::kColorDodge;
+ case CompositionOp::OP_COLOR_BURN:
+ return SkBlendMode::kColorBurn;
+ case CompositionOp::OP_HARD_LIGHT:
+ return SkBlendMode::kHardLight;
+ case CompositionOp::OP_SOFT_LIGHT:
+ return SkBlendMode::kSoftLight;
+ case CompositionOp::OP_DIFFERENCE:
+ return SkBlendMode::kDifference;
+ case CompositionOp::OP_EXCLUSION:
+ return SkBlendMode::kExclusion;
+ case CompositionOp::OP_HUE:
+ return SkBlendMode::kHue;
+ case CompositionOp::OP_SATURATION:
+ return SkBlendMode::kSaturation;
+ case CompositionOp::OP_COLOR:
+ return SkBlendMode::kColor;
+ case CompositionOp::OP_LUMINOSITY:
+ return SkBlendMode::kLuminosity;
+ default:
+ return SkBlendMode::kSrcOver;
+ }
+}
+
+/* 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 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 SkPoint
+PointToSkPoint(const Point &aPoint)
+{
+ return SkPoint::Make(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
+}
+
+static inline SkRect
+RectToSkRect(const Rect& aRect)
+{
+ return SkRect::MakeXYWH(SkFloatToScalar(aRect.x), SkFloatToScalar(aRect.y),
+ SkFloatToScalar(aRect.width), SkFloatToScalar(aRect.height));
+}
+
+static inline SkRect
+IntRectToSkRect(const IntRect& aRect)
+{
+ return SkRect::MakeXYWH(SkIntToScalar(aRect.x), SkIntToScalar(aRect.y),
+ SkIntToScalar(aRect.width), SkIntToScalar(aRect.height));
+}
+
+static inline SkIRect
+RectToSkIRect(const Rect& aRect)
+{
+ return SkIRect::MakeXYWH(int32_t(aRect.x), int32_t(aRect.y),
+ int32_t(aRect.width), int32_t(aRect.height));
+}
+
+static inline SkIRect
+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, Axis aAxis)
+{
+ switch (aMode)
+ {
+ case ExtendMode::CLAMP:
+ return SkShader::kClamp_TileMode;
+ case ExtendMode::REPEAT:
+ return SkShader::kRepeat_TileMode;
+ case ExtendMode::REFLECT:
+ return SkShader::kMirror_TileMode;
+ case ExtendMode::REPEAT_X:
+ {
+ return aAxis == Axis::X_AXIS
+ ? SkShader::kRepeat_TileMode
+ : SkShader::kClamp_TileMode;
+ }
+ case ExtendMode::REPEAT_Y:
+ {
+ return aAxis == Axis::Y_AXIS
+ ? SkShader::kRepeat_TileMode
+ : SkShader::kClamp_TileMode;
+ }
+ }
+ return SkShader::kClamp_TileMode;
+}
+
+static inline SkPaint::Hinting
+GfxHintingToSkiaHinting(FontHinting aHinting)
+{
+ switch (aHinting) {
+ case FontHinting::NONE:
+ return SkPaint::kNo_Hinting;
+ case FontHinting::LIGHT:
+ return SkPaint::kSlight_Hinting;
+ case FontHinting::NORMAL:
+ return SkPaint::kNormal_Hinting;
+ case FontHinting::FULL:
+ return SkPaint::kFull_Hinting;
+ }
+ return SkPaint::kNormal_Hinting;
+}
+
+static inline FillRule GetFillRule(SkPath::FillType aFillType)
+{
+ switch (aFillType)
+ {
+ case SkPath::kWinding_FillType:
+ return FillRule::FILL_WINDING;
+ case SkPath::kEvenOdd_FillType:
+ return FillRule::FILL_EVEN_ODD;
+ case SkPath::kInverseWinding_FillType:
+ case SkPath::kInverseEvenOdd_FillType:
+ default:
+ NS_WARNING("Unsupported fill type\n");
+ break;
+ }
+
+ return FillRule::FILL_EVEN_ODD;
+}
+
+/**
+ * Returns true if the canvas is backed by pixels. Returns false if the canvas
+ * wraps an SkPDFDocument, for example.
+ *
+ * Note: It is not clear whether the test used to implement this function may
+ * result in it returning false in some circumstances even when the canvas
+ * _is_ pixel backed. In other words maybe it is possible for such a canvas to
+ * have kUnknown_SkPixelGeometry?
+ */
+static inline bool IsBackedByPixels(const SkCanvas* aCanvas)
+{
+ SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+ if (!aCanvas->getProps(&props) ||
+ props.pixelGeometry() == kUnknown_SkPixelGeometry) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_HELPERSSKIA_H_ */
diff --git a/gfx/2d/HelpersWinFonts.h b/gfx/2d/HelpersWinFonts.h
new file mode 100644
index 000000000..cd84070d4
--- /dev/null
+++ b/gfx/2d/HelpersWinFonts.h
@@ -0,0 +1,61 @@
+/* -*- 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/. */
+
+namespace mozilla {
+namespace gfx {
+
+// Cleartype can be dynamically enabled/disabled, so we have to check it
+// everytime we want to render some text.
+static BYTE
+GetSystemTextQuality()
+{
+ BOOL font_smoothing;
+ UINT smoothing_type;
+
+ if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) {
+ return DEFAULT_QUALITY;
+ }
+
+ if (font_smoothing) {
+ if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE,
+ 0, &smoothing_type, 0)) {
+ return DEFAULT_QUALITY;
+ }
+
+ if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) {
+ return CLEARTYPE_QUALITY;
+ }
+
+ return ANTIALIASED_QUALITY;
+ }
+
+ return DEFAULT_QUALITY;
+}
+
+static AntialiasMode
+GetSystemDefaultAAMode()
+{
+ AntialiasMode defaultMode = AntialiasMode::SUBPIXEL;
+ if (gfxPrefs::DisableAllTextAA()) {
+ return AntialiasMode::NONE;
+ }
+
+ switch (GetSystemTextQuality()) {
+ case CLEARTYPE_QUALITY:
+ defaultMode = AntialiasMode::SUBPIXEL;
+ break;
+ case ANTIALIASED_QUALITY:
+ defaultMode = AntialiasMode::GRAY;
+ break;
+ case DEFAULT_QUALITY:
+ defaultMode = AntialiasMode::NONE;
+ break;
+ }
+
+ return defaultMode;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/ImageScaling.cpp b/gfx/2d/ImageScaling.cpp
new file mode 100644
index 000000000..190b7a7b9
--- /dev/null
+++ b/gfx/2d/ImageScaling.cpp
@@ -0,0 +1,251 @@
+/* -*- 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 "ImageScaling.h"
+#include "2D.h"
+#include "DataSurfaceHelpers.h"
+
+#include <math.h>
+#include <algorithm>
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+inline uint32_t Avg2x2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
+{
+ // Prepare half-adder work
+ uint32_t sum = a ^ b ^ c;
+ uint32_t carry = (a & b) | (a & c) | (b & c);
+
+ // Before shifting, mask lower order bits of each byte to avoid underflow.
+ uint32_t mask = 0xfefefefe;
+
+ // Add d to sum and divide by 2.
+ sum = (((sum ^ d) & mask) >> 1) + (sum & d);
+
+ // Sum is now shifted into place relative to carry, add them together.
+ return (((sum ^ carry) & mask) >> 1) + (sum & carry);
+}
+
+inline uint32_t Avg2(uint32_t a, uint32_t b)
+{
+ // Prepare half-adder work
+ uint32_t sum = a ^ b;
+ uint32_t carry = (a & b);
+
+ // Before shifting, mask lower order bits of each byte to avoid underflow.
+ uint32_t mask = 0xfefefefe;
+
+ // Add d to sum and divide by 2.
+ return ((sum & mask) >> 1) + carry;
+}
+
+void
+ImageHalfScaler::ScaleForSize(const IntSize &aSize)
+{
+ uint32_t horizontalDownscales = 0;
+ uint32_t verticalDownscales = 0;
+
+ IntSize scaleSize = mOrigSize;
+ while ((scaleSize.height / 2) > aSize.height) {
+ verticalDownscales++;
+ scaleSize.height /= 2;
+ }
+
+ while ((scaleSize.width / 2) > aSize.width) {
+ horizontalDownscales++;
+ scaleSize.width /= 2;
+ }
+
+ if (scaleSize == mOrigSize) {
+ return;
+ }
+
+ delete [] mDataStorage;
+
+ IntSize internalSurfSize;
+ internalSurfSize.width = max(scaleSize.width, mOrigSize.width / 2);
+ internalSurfSize.height = max(scaleSize.height, mOrigSize.height / 2);
+
+ size_t bufLen = 0;
+ mStride = GetAlignedStride<16>(internalSurfSize.width, 4);
+ if (mStride > 0) {
+ // Allocate 15 bytes extra to make sure we can get 16 byte alignment. We
+ // should add tools for this, see bug 751696.
+ 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!
+ mData = (uint8_t*)(uintptr_t(mDataStorage) +
+ (16 - (uintptr_t(mDataStorage) % 16)));
+ } else {
+ mData = mDataStorage;
+ }
+
+ mSize = scaleSize;
+
+ /* The surface we sample from might not be even sized, if it's not we will
+ * ignore the last row/column. This means we lose some data but it keeps the
+ * code very simple. There's also no perfect answer that provides a better
+ * solution.
+ */
+ IntSize currentSampledSize = mOrigSize;
+ uint32_t currentSampledStride = mOrigStride;
+ uint8_t *currentSampledData = mOrigData;
+
+ while (verticalDownscales && horizontalDownscales) {
+ if (currentSampledSize.width % 2) {
+ currentSampledSize.width -= 1;
+ }
+ if (currentSampledSize.height % 2) {
+ currentSampledSize.height -= 1;
+ }
+
+ HalfImage2D(currentSampledData, currentSampledStride, currentSampledSize,
+ mData, mStride);
+
+ verticalDownscales--;
+ horizontalDownscales--;
+ currentSampledSize.width /= 2;
+ currentSampledSize.height /= 2;
+ currentSampledData = mData;
+ currentSampledStride = mStride;
+ }
+
+ while (verticalDownscales) {
+ if (currentSampledSize.height % 2) {
+ currentSampledSize.height -= 1;
+ }
+
+ HalfImageVertical(currentSampledData, currentSampledStride, currentSampledSize,
+ mData, mStride);
+
+ verticalDownscales--;
+ currentSampledSize.height /= 2;
+ currentSampledData = mData;
+ currentSampledStride = mStride;
+ }
+
+
+ while (horizontalDownscales) {
+ if (currentSampledSize.width % 2) {
+ currentSampledSize.width -= 1;
+ }
+
+ HalfImageHorizontal(currentSampledData, currentSampledStride, currentSampledSize,
+ mData, mStride);
+
+ horizontalDownscales--;
+ currentSampledSize.width /= 2;
+ currentSampledData = mData;
+ currentSampledStride = mStride;
+ }
+}
+
+void
+ImageHalfScaler::HalfImage2D(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+#ifdef USE_SSE2
+ if (Factory::HasSSE2()) {
+ HalfImage2D_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
+ } else
+#endif
+ {
+ HalfImage2D_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
+ }
+}
+
+void
+ImageHalfScaler::HalfImageVertical(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+#ifdef USE_SSE2
+ if (Factory::HasSSE2()) {
+ HalfImageVertical_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
+ } else
+#endif
+ {
+ HalfImageVertical_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
+ }
+}
+
+void
+ImageHalfScaler::HalfImageHorizontal(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+#ifdef USE_SSE2
+ if (Factory::HasSSE2()) {
+ HalfImageHorizontal_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
+ } else
+#endif
+ {
+ HalfImageHorizontal_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
+ }
+}
+
+void
+ImageHalfScaler::HalfImage2D_C(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+ for (int y = 0; y < aSourceSize.height; y += 2) {
+ uint32_t *storage = (uint32_t*)(aDest + (y / 2) * aDestStride);
+ for (int x = 0; x < aSourceSize.width; x += 2) {
+ uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
+ uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
+
+ *storage++ = Avg2x2(*(uint32_t*)upperRow, *((uint32_t*)upperRow + 1),
+ *(uint32_t*)lowerRow, *((uint32_t*)lowerRow + 1));
+ }
+ }
+}
+
+void
+ImageHalfScaler::HalfImageVertical_C(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+ for (int y = 0; y < aSourceSize.height; y += 2) {
+ uint32_t *storage = (uint32_t*)(aDest + (y / 2) * aDestStride);
+ for (int x = 0; x < aSourceSize.width; x++) {
+ uint32_t *upperRow = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
+ uint32_t *lowerRow = (uint32_t*)(aSource + ((y + 1) * aSourceStride + x * 4));
+
+ *storage++ = Avg2(*upperRow, *lowerRow);
+ }
+ }
+}
+
+void
+ImageHalfScaler::HalfImageHorizontal_C(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+ for (int y = 0; y < aSourceSize.height; y++) {
+ uint32_t *storage = (uint32_t*)(aDest + y * aDestStride);
+ for (int x = 0; x < aSourceSize.width; x+= 2) {
+ uint32_t *pixels = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
+
+ *storage++ = Avg2(*pixels, *(pixels + 1));
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/ImageScaling.h b/gfx/2d/ImageScaling.h
new file mode 100644
index 000000000..56173b644
--- /dev/null
+++ b/gfx/2d/ImageScaling.h
@@ -0,0 +1,76 @@
+/* -*- 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_IMAGESCALING_H
+#define _MOZILLA_GFX_IMAGESCALING_H
+
+#include "Types.h"
+
+#include <vector>
+#include "Point.h"
+
+namespace mozilla {
+namespace gfx {
+
+class ImageHalfScaler
+{
+public:
+ ImageHalfScaler(uint8_t *aData, int32_t aStride, const IntSize &aSize)
+ : mOrigData(aData), mOrigStride(aStride), mOrigSize(aSize)
+ , mDataStorage(nullptr)
+ {
+ }
+
+ ~ImageHalfScaler()
+ {
+ delete [] mDataStorage;
+ }
+
+ void ScaleForSize(const IntSize &aSize);
+
+ uint8_t *GetScaledData() const { return mData; }
+ IntSize GetSize() const { return mSize; }
+ uint32_t GetStride() const { return mStride; }
+
+private:
+ void HalfImage2D(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+ void HalfImageVertical(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+ void HalfImageHorizontal(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+
+ // This is our SSE2 scaling function. Our destination must always be 16-byte
+ // aligned and use a 16-byte aligned stride.
+ void HalfImage2D_SSE2(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+ void HalfImageVertical_SSE2(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+ void HalfImageHorizontal_SSE2(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+
+ void HalfImage2D_C(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+ void HalfImageVertical_C(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+ void HalfImageHorizontal_C(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
+ uint8_t *aDest, uint32_t aDestStride);
+
+ uint8_t *mOrigData;
+ int32_t mOrigStride;
+ IntSize mOrigSize;
+
+ uint8_t *mDataStorage;
+ // Guaranteed 16-byte aligned
+ uint8_t *mData;
+ IntSize mSize;
+ // Guaranteed 16-byte aligned
+ uint32_t mStride;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/gfx/2d/ImageScalingSSE2.cpp b/gfx/2d/ImageScalingSSE2.cpp
new file mode 100644
index 000000000..1f4d4778e
--- /dev/null
+++ b/gfx/2d/ImageScalingSSE2.cpp
@@ -0,0 +1,330 @@
+/* -*- 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 "ImageScaling.h"
+#include "mozilla/Attributes.h"
+
+#include "SSEHelpers.h"
+
+/* The functions below use the following system for averaging 4 pixels:
+ *
+ * The first observation is that a half-adder is implemented as follows:
+ * R = S + 2C or in the case of a and b (a ^ b) + ((a & b) << 1);
+ *
+ * This can be trivially extended to three pixels by observaring that when
+ * doing (a ^ b ^ c) as the sum, the carry is simply the bitwise-or of the
+ * carries of the individual numbers, since the sum of 3 bits can only ever
+ * have a carry of one.
+ *
+ * We then observe that the average is then ((carry << 1) + sum) >> 1, or,
+ * assuming eliminating overflows and underflows, carry + (sum >> 1).
+ *
+ * We now average our existing sum with the fourth number, so we get:
+ * sum2 = (sum + d) >> 1 or (sum >> 1) + (d >> 1).
+ *
+ * We now observe that our sum has been moved into place relative to the
+ * carry, so we can now average with the carry to get the final 4 input
+ * average: avg = (sum2 + carry) >> 1;
+ *
+ * Or to reverse the proof:
+ * avg = ((sum >> 1) + carry + d >> 1) >> 1
+ * avg = ((a + b + c) >> 1 + d >> 1) >> 1
+ * avg = ((a + b + c + d) >> 2)
+ *
+ * An additional fact used in the SSE versions is the concept that we can
+ * trivially convert a rounded average to a truncated average:
+ *
+ * We have:
+ * f(a, b) = (a + b + 1) >> 1
+ *
+ * And want:
+ * g(a, b) = (a + b) >> 1
+ *
+ * Observe:
+ * ~f(~a, ~b) == ~((~a + ~b + 1) >> 1)
+ * == ~((-a - 1 + -b - 1 + 1) >> 1)
+ * == ~((-a - 1 + -b) >> 1)
+ * == ~((-(a + b) - 1) >> 1)
+ * == ~((~(a + b)) >> 1)
+ * == (a + b) >> 1
+ * == g(a, b)
+ */
+
+MOZ_ALWAYS_INLINE __m128i _mm_not_si128(__m128i arg)
+{
+ __m128i minusone = _mm_set1_epi32(0xffffffff);
+ return _mm_xor_si128(arg, minusone);
+}
+
+/* We have to pass pointers here, MSVC does not allow passing more than 3
+ * __m128i arguments on the stack. And it does not allow 16-byte aligned
+ * stack variables. This inlines properly on MSVC 2010. It does -not- inline
+ * with just the inline directive.
+ */
+MOZ_ALWAYS_INLINE __m128i avg_sse2_8x2(__m128i *a, __m128i *b, __m128i *c, __m128i *d)
+{
+#define shuf1 _MM_SHUFFLE(2, 0, 2, 0)
+#define shuf2 _MM_SHUFFLE(3, 1, 3, 1)
+
+// This cannot be an inline function as the __Imm argument to _mm_shuffle_ps
+// needs to be a compile time constant.
+#define shuffle_si128(arga, argb, imm) \
+ _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps((arga)), _mm_castsi128_ps((argb)), (imm)));
+
+ __m128i t = shuffle_si128(*a, *b, shuf1);
+ *b = shuffle_si128(*a, *b, shuf2);
+ *a = t;
+ t = shuffle_si128(*c, *d, shuf1);
+ *d = shuffle_si128(*c, *d, shuf2);
+ *c = t;
+
+#undef shuf1
+#undef shuf2
+#undef shuffle_si128
+
+ __m128i sum = _mm_xor_si128(*a, _mm_xor_si128(*b, *c));
+
+ __m128i carry = _mm_or_si128(_mm_and_si128(*a, *b), _mm_or_si128(_mm_and_si128(*a, *c), _mm_and_si128(*b, *c)));
+
+ sum = _mm_avg_epu8(_mm_not_si128(sum), _mm_not_si128(*d));
+
+ return _mm_not_si128(_mm_avg_epu8(sum, _mm_not_si128(carry)));
+}
+
+MOZ_ALWAYS_INLINE __m128i avg_sse2_4x2_4x1(__m128i a, __m128i b)
+{
+ return _mm_not_si128(_mm_avg_epu8(_mm_not_si128(a), _mm_not_si128(b)));
+}
+
+MOZ_ALWAYS_INLINE __m128i avg_sse2_8x1_4x1(__m128i a, __m128i b)
+{
+ __m128i t = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(3, 1, 3, 1)));
+ b = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(2, 0, 2, 0)));
+ a = t;
+
+ return _mm_not_si128(_mm_avg_epu8(_mm_not_si128(a), _mm_not_si128(b)));
+}
+
+MOZ_ALWAYS_INLINE uint32_t Avg2x2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
+{
+ uint32_t sum = a ^ b ^ c;
+ uint32_t carry = (a & b) | (a & c) | (b & c);
+
+ uint32_t mask = 0xfefefefe;
+
+ // Not having a byte based average instruction means we should mask to avoid
+ // underflow.
+ sum = (((sum ^ d) & mask) >> 1) + (sum & d);
+
+ return (((sum ^ carry) & mask) >> 1) + (sum & carry);
+}
+
+// Simple 2 pixel average version of the function above.
+MOZ_ALWAYS_INLINE uint32_t Avg2(uint32_t a, uint32_t b)
+{
+ uint32_t sum = a ^ b;
+ uint32_t carry = (a & b);
+
+ uint32_t mask = 0xfefefefe;
+
+ return ((sum & mask) >> 1) + carry;
+}
+
+namespace mozilla {
+namespace gfx {
+
+void
+ImageHalfScaler::HalfImage2D_SSE2(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+ const int Bpp = 4;
+
+ for (int y = 0; y < aSourceSize.height; y += 2) {
+ __m128i *storage = (__m128i*)(aDest + (y / 2) * aDestStride);
+ int x = 0;
+ // Run a loop depending on alignment.
+ if (!(uintptr_t(aSource + (y * aSourceStride)) % 16) &&
+ !(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
+ for (; x < (aSourceSize.width - 7); x += 8) {
+ __m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
+ __m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
+
+ __m128i a = _mm_load_si128(upperRow);
+ __m128i b = _mm_load_si128(upperRow + 1);
+ __m128i c = _mm_load_si128(lowerRow);
+ __m128i d = _mm_load_si128(lowerRow + 1);
+
+ *storage++ = avg_sse2_8x2(&a, &b, &c, &d);
+ }
+ } else if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
+ for (; x < (aSourceSize.width - 7); x += 8) {
+ __m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
+ __m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
+
+ __m128i a = _mm_load_si128(upperRow);
+ __m128i b = _mm_load_si128(upperRow + 1);
+ __m128i c = loadUnaligned128(lowerRow);
+ __m128i d = loadUnaligned128(lowerRow + 1);
+
+ *storage++ = avg_sse2_8x2(&a, &b, &c, &d);
+ }
+ } else if (!(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
+ for (; x < (aSourceSize.width - 7); x += 8) {
+ __m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
+ __m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
+
+ __m128i a = loadUnaligned128((__m128i*)upperRow);
+ __m128i b = loadUnaligned128((__m128i*)upperRow + 1);
+ __m128i c = _mm_load_si128((__m128i*)lowerRow);
+ __m128i d = _mm_load_si128((__m128i*)lowerRow + 1);
+
+ *storage++ = avg_sse2_8x2(&a, &b, &c, &d);
+ }
+ } else {
+ for (; x < (aSourceSize.width - 7); x += 8) {
+ __m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
+ __m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
+
+ __m128i a = loadUnaligned128(upperRow);
+ __m128i b = loadUnaligned128(upperRow + 1);
+ __m128i c = loadUnaligned128(lowerRow);
+ __m128i d = loadUnaligned128(lowerRow + 1);
+
+ *storage++ = avg_sse2_8x2(&a, &b, &c, &d);
+ }
+ }
+
+ uint32_t *unalignedStorage = (uint32_t*)storage;
+ // Take care of the final pixels, we know there's an even number of pixels
+ // in the source rectangle. We use a 2x2 'simd' implementation for this.
+ //
+ // Potentially we only have to do this in the last row since overflowing
+ // 8 pixels in an earlier row would appear to be harmless as it doesn't
+ // touch invalid memory. Even when reading and writing to the same surface.
+ // in practice we only do this when doing an additional downscale pass, and
+ // in this situation we have unused stride to write into harmlessly.
+ // I do not believe the additional code complexity would be worth it though.
+ for (; x < aSourceSize.width; x += 2) {
+ uint8_t *upperRow = aSource + (y * aSourceStride + x * Bpp);
+ uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * Bpp);
+
+ *unalignedStorage++ = Avg2x2(*(uint32_t*)upperRow, *((uint32_t*)upperRow + 1),
+ *(uint32_t*)lowerRow, *((uint32_t*)lowerRow + 1));
+ }
+ }
+}
+
+void
+ImageHalfScaler::HalfImageVertical_SSE2(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+ for (int y = 0; y < aSourceSize.height; y += 2) {
+ __m128i *storage = (__m128i*)(aDest + (y / 2) * aDestStride);
+ int x = 0;
+ // Run a loop depending on alignment.
+ if (!(uintptr_t(aSource + (y * aSourceStride)) % 16) &&
+ !(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);
+
+ __m128i a = _mm_load_si128((__m128i*)upperRow);
+ __m128i b = _mm_load_si128((__m128i*)lowerRow);
+
+ *storage++ = avg_sse2_4x2_4x1(a, b);
+ }
+ } else if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
+ // This line doesn't align well.
+ for (; x < (aSourceSize.width - 3); x += 4) {
+ uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
+ uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
+
+ __m128i a = _mm_load_si128((__m128i*)upperRow);
+ __m128i b = loadUnaligned128((__m128i*)lowerRow);
+
+ *storage++ = avg_sse2_4x2_4x1(a, b);
+ }
+ } 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);
+
+ __m128i a = loadUnaligned128((__m128i*)upperRow);
+ __m128i b = _mm_load_si128((__m128i*)lowerRow);
+
+ *storage++ = avg_sse2_4x2_4x1(a, b);
+ }
+ } else {
+ for (; x < (aSourceSize.width - 3); x += 4) {
+ uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
+ uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
+
+ __m128i a = loadUnaligned128((__m128i*)upperRow);
+ __m128i b = loadUnaligned128((__m128i*)lowerRow);
+
+ *storage++ = avg_sse2_4x2_4x1(a, b);
+ }
+ }
+
+ uint32_t *unalignedStorage = (uint32_t*)storage;
+ // Take care of the final pixels, we know there's an even number of pixels
+ // in the source rectangle.
+ //
+ // Similar overflow considerations are valid as in the previous function.
+ for (; x < aSourceSize.width; x++) {
+ uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
+ uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
+
+ *unalignedStorage++ = Avg2(*(uint32_t*)upperRow, *(uint32_t*)lowerRow);
+ }
+ }
+}
+
+void
+ImageHalfScaler::HalfImageHorizontal_SSE2(uint8_t *aSource, int32_t aSourceStride,
+ const IntSize &aSourceSize, uint8_t *aDest,
+ uint32_t aDestStride)
+{
+ for (int y = 0; y < aSourceSize.height; y++) {
+ __m128i *storage = (__m128i*)(aDest + (y * aDestStride));
+ int x = 0;
+ // Run a loop depending on alignment.
+ if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
+ for (; x < (aSourceSize.width - 7); x += 8) {
+ __m128i* pixels = (__m128i*)(aSource + (y * aSourceStride + x * 4));
+
+ __m128i a = _mm_load_si128(pixels);
+ __m128i b = _mm_load_si128(pixels + 1);
+
+ *storage++ = avg_sse2_8x1_4x1(a, b);
+ }
+ } else {
+ for (; x < (aSourceSize.width - 7); x += 8) {
+ __m128i* pixels = (__m128i*)(aSource + (y * aSourceStride + x * 4));
+
+ __m128i a = loadUnaligned128(pixels);
+ __m128i b = loadUnaligned128(pixels + 1);
+
+ *storage++ = avg_sse2_8x1_4x1(a, b);
+ }
+ }
+
+ uint32_t *unalignedStorage = (uint32_t*)storage;
+ // Take care of the final pixels, we know there's an even number of pixels
+ // in the source rectangle.
+ //
+ // Similar overflow considerations are valid as in the previous function.
+ for (; x < aSourceSize.width; x += 2) {
+ uint32_t *pixels = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
+
+ *unalignedStorage++ = Avg2(*pixels, *(pixels + 1));
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/IterableArena.h b/gfx/2d/IterableArena.h
new file mode 100644
index 000000000..a444c9a38
--- /dev/null
+++ b/gfx/2d/IterableArena.h
@@ -0,0 +1,193 @@
+/* -*- 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_ITERABLEARENA_H_
+#define MOZILLA_GFX_ITERABLEARENA_H_
+
+#include "mozilla/Move.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/gfx/Logging.h"
+
+#include <string.h>
+#include <vector>
+#include <stdint.h>
+#include <stdio.h>
+
+namespace mozilla {
+namespace gfx {
+
+/// A simple pool allocator for plain data structures.
+///
+/// Beware that the pool will not attempt to run the destructors. It is the
+/// responsibility of the user of this class to either use objects with no
+/// destructor or to manually call the allocated objects destructors.
+/// If the pool is growable, its allocated objects must be safely moveable in
+/// in memory (through memcpy).
+class IterableArena {
+protected:
+ struct Header
+ {
+ size_t mBlocSize;
+ };
+public:
+ enum ArenaType {
+ FIXED_SIZE,
+ GROWABLE
+ };
+
+ IterableArena(ArenaType aType, size_t aStorageSize)
+ : mSize(aStorageSize)
+ , mCursor(0)
+ , mIsGrowable(aType == GROWABLE)
+ {
+ if (mSize == 0) {
+ mSize = 128;
+ }
+
+ mStorage = (uint8_t*)malloc(mSize);
+ if (mStorage == nullptr) {
+ gfxCriticalError() << "Not enough Memory allocate a memory pool of size " << aStorageSize;
+ MOZ_CRASH("GFX: Out of memory IterableArena");
+ }
+ }
+
+ ~IterableArena()
+ {
+ free(mStorage);
+ }
+
+ /// Constructs a new item in the pool and returns a positive offset in case of
+ /// success.
+ ///
+ /// The offset never changes even if the storage is reallocated, so users
+ /// of this class should prefer storing offsets rather than direct pointers
+ /// to the allocated objects.
+ /// Alloc can cause the storage to be reallocated if the pool was initialized
+ /// with IterableArena::GROWABLE.
+ /// If for any reason the pool fails to allocate enough space for the new item
+ /// Alloc returns a negative offset and the object's constructor is not called.
+ template<typename T, typename... Args>
+ ptrdiff_t
+ Alloc(Args&&... aArgs)
+ {
+ void* storage = nullptr;
+ auto offset = AllocRaw(sizeof(T), &storage);
+ if (offset < 0) {
+ return offset;
+ }
+ new (storage) T(Forward<Args>(aArgs)...);
+ return offset;
+ }
+
+ ptrdiff_t AllocRaw(size_t aSize, void** aOutPtr = nullptr)
+ {
+ const size_t blocSize = AlignedSize(sizeof(Header) + aSize);
+
+ if (AlignedSize(mCursor + blocSize) > mSize) {
+ if (!mIsGrowable) {
+ return -1;
+ }
+
+ size_t newSize = mSize * 2;
+ while (AlignedSize(mCursor + blocSize) > newSize) {
+ newSize *= 2;
+ }
+
+ uint8_t* newStorage = (uint8_t*)realloc(mStorage, newSize);
+ if (!newStorage) {
+ gfxCriticalError() << "Not enough Memory to grow the memory pool, size: " << newSize;
+ return -1;
+ }
+
+ mStorage = newStorage;
+ mSize = newSize;
+ }
+ ptrdiff_t offset = mCursor;
+ GetHeader(offset)->mBlocSize = blocSize;
+ mCursor += blocSize;
+ if (aOutPtr) {
+ *aOutPtr = GetStorage(offset);
+ }
+ return offset;
+ }
+
+ /// Get access to an allocated item at a given offset (only use offsets returned
+ /// by Alloc or AllocRaw).
+ ///
+ /// If the pool is growable, the returned pointer is only valid temporarily. The
+ /// underlying storage can be reallocated in Alloc or AllocRaw, so do not keep
+ /// these pointers around and store the offset instead.
+ void* GetStorage(ptrdiff_t offset = 0)
+ {
+ MOZ_ASSERT(offset >= 0);
+ MOZ_ASSERT(offset < mCursor);
+ return offset >= 0 ? mStorage + offset + sizeof(Header) : nullptr;
+ }
+
+ /// Clears the storage without running any destructor and without deallocating it.
+ void Clear()
+ {
+ mCursor = 0;
+ }
+
+ /// Iterate over the elements allocated in this pool.
+ ///
+ /// Takes a lambda or function object accepting a void* as parameter.
+ template<typename Func>
+ void ForEach(Func cb)
+ {
+ Iterator it;
+ while (void* ptr = it.Next(this)) {
+ cb(ptr);
+ }
+ }
+
+ /// A simple iterator over an arena.
+ class Iterator {
+ public:
+ Iterator()
+ : mCursor(0)
+ {}
+
+ void* Next(IterableArena* aArena)
+ {
+ if (mCursor >= aArena->mCursor) {
+ return nullptr;
+ }
+ void* result = aArena->GetStorage(mCursor);
+ const size_t blocSize = aArena->GetHeader(mCursor)->mBlocSize;
+ MOZ_ASSERT(blocSize != 0);
+ mCursor += blocSize;
+ return result;
+ }
+
+ private:
+ ptrdiff_t mCursor;
+ };
+
+protected:
+ Header* GetHeader(ptrdiff_t offset)
+ {
+ return (Header*) (mStorage + offset);
+ }
+
+ size_t AlignedSize(size_t aSize) const
+ {
+ const size_t alignment = sizeof(uintptr_t);
+ return aSize + (alignment - (aSize % alignment)) % alignment;
+ }
+
+ uint8_t* mStorage;
+ uint32_t mSize;
+ ptrdiff_t mCursor;
+ bool mIsGrowable;
+
+ friend class Iterator;
+};
+
+} // namespace
+} // namespace
+
+#endif
diff --git a/gfx/2d/JobScheduler.cpp b/gfx/2d/JobScheduler.cpp
new file mode 100644
index 000000000..2c687cde0
--- /dev/null
+++ b/gfx/2d/JobScheduler.cpp
@@ -0,0 +1,288 @@
+/* -*- 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 "JobScheduler.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+JobScheduler* JobScheduler::sSingleton = nullptr;
+
+bool JobScheduler::Init(uint32_t aNumThreads, uint32_t aNumQueues)
+{
+ MOZ_ASSERT(!sSingleton);
+ MOZ_ASSERT(aNumThreads >= aNumQueues);
+
+ sSingleton = new JobScheduler();
+ sSingleton->mNextQueue = 0;
+
+ for (uint32_t i = 0; i < aNumQueues; ++i) {
+ sSingleton->mDrawingQueues.push_back(new MultiThreadedJobQueue());
+ }
+
+ for (uint32_t i = 0; i < aNumThreads; ++i) {
+ sSingleton->mWorkerThreads.push_back(WorkerThread::Create(sSingleton->mDrawingQueues[i%aNumQueues]));
+ }
+ return true;
+}
+
+void JobScheduler::ShutDown()
+{
+ MOZ_ASSERT(IsEnabled());
+ if (!IsEnabled()) {
+ return;
+ }
+
+ for (auto queue : sSingleton->mDrawingQueues) {
+ queue->ShutDown();
+ delete queue;
+ }
+
+ for (WorkerThread* thread : sSingleton->mWorkerThreads) {
+ // this will block until the thread is joined.
+ delete thread;
+ }
+
+ sSingleton->mWorkerThreads.clear();
+ delete sSingleton;
+ sSingleton = nullptr;
+}
+
+JobStatus
+JobScheduler::ProcessJob(Job* aJob)
+{
+ MOZ_ASSERT(aJob);
+ auto status = aJob->Run();
+ if (status == JobStatus::Error || status == JobStatus::Complete) {
+ delete aJob;
+ }
+ return status;
+}
+
+void
+JobScheduler::SubmitJob(Job* aJob)
+{
+ MOZ_ASSERT(aJob);
+ RefPtr<SyncObject> start = aJob->GetStartSync();
+ if (start && start->Register(aJob)) {
+ // The Job buffer starts with a non-signaled sync object, it
+ // is now registered in the list of task buffers waiting on the
+ // sync object, so we should not place it in the queue.
+ return;
+ }
+
+ GetQueueForJob(aJob)->SubmitJob(aJob);
+}
+
+void
+JobScheduler::Join(SyncObject* aCompletion)
+{
+ RefPtr<EventObject> waitForCompletion = new EventObject();
+ JobScheduler::SubmitJob(new SetEventJob(waitForCompletion, aCompletion));
+ waitForCompletion->Wait();
+}
+
+MultiThreadedJobQueue*
+JobScheduler::GetQueueForJob(Job* aJob)
+{
+ return aJob->IsPinnedToAThread() ? aJob->GetWorkerThread()->GetJobQueue()
+ : GetDrawingQueue();
+}
+
+Job::Job(SyncObject* aStart, SyncObject* aCompletion, WorkerThread* aThread)
+: mNextWaitingJob(nullptr)
+, mStartSync(aStart)
+, mCompletionSync(aCompletion)
+, mPinToThread(aThread)
+{
+ if (mStartSync) {
+ mStartSync->AddSubsequent(this);
+ }
+ if (mCompletionSync) {
+ mCompletionSync->AddPrerequisite(this);
+ }
+}
+
+Job::~Job()
+{
+ if (mCompletionSync) {
+ //printf(" -- Job %p dtor completion %p\n", this, mCompletionSync);
+ mCompletionSync->Signal();
+ mCompletionSync = nullptr;
+ }
+}
+
+JobStatus
+SetEventJob::Run()
+{
+ mEvent->Set();
+ return JobStatus::Complete;
+}
+
+SetEventJob::SetEventJob(EventObject* aEvent,
+ SyncObject* aStart, SyncObject* aCompletion,
+ WorkerThread* aWorker)
+: Job(aStart, aCompletion, aWorker)
+, mEvent(aEvent)
+{}
+
+SetEventJob::~SetEventJob()
+{}
+
+SyncObject::SyncObject(uint32_t aNumPrerequisites)
+: mSignals(aNumPrerequisites)
+, mFirstWaitingJob(nullptr)
+#ifdef DEBUG
+, mNumPrerequisites(aNumPrerequisites)
+, mAddedPrerequisites(0)
+#endif
+{}
+
+SyncObject::~SyncObject()
+{
+ MOZ_ASSERT(mFirstWaitingJob == nullptr);
+}
+
+bool
+SyncObject::Register(Job* aJob)
+{
+ MOZ_ASSERT(aJob);
+
+ // For now, ensure that when we schedule the first subsequent, we have already
+ // created all of the prerequisites. This is an arbitrary restriction because
+ // we specify the number of prerequisites in the constructor, but in the typical
+ // scenario, if the assertion FreezePrerequisite blows up here it probably means
+ // we got the initial nmber of prerequisites wrong. We can decide to remove
+ // this restriction if needed.
+ FreezePrerequisites();
+
+ int32_t signals = mSignals;
+
+ if (signals > 0) {
+ AddWaitingJob(aJob);
+ // Since Register and Signal can be called concurrently, it can happen that
+ // reading mSignals in Register happens before decrementing mSignals in Signal,
+ // but SubmitWaitingJobs happens before AddWaitingJob. This ordering means
+ // the SyncObject ends up in the signaled state with a task sitting in the
+ // waiting list. To prevent that we check mSignals a second time and submit
+ // again if signals reached zero in the mean time.
+ // We do this instead of holding a mutex around mSignals+mJobs to reduce
+ // lock contention.
+ int32_t signals2 = mSignals;
+ if (signals2 == 0) {
+ SubmitWaitingJobs();
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void
+SyncObject::Signal()
+{
+ int32_t signals = --mSignals;
+ MOZ_ASSERT(signals >= 0);
+
+ if (signals == 0) {
+ SubmitWaitingJobs();
+ }
+}
+
+void
+SyncObject::AddWaitingJob(Job* aJob)
+{
+ // Push (using atomics) the task into the list of waiting tasks.
+ for (;;) {
+ Job* first = mFirstWaitingJob;
+ aJob->mNextWaitingJob = first;
+ if (mFirstWaitingJob.compareExchange(first, aJob)) {
+ break;
+ }
+ }
+}
+
+void SyncObject::SubmitWaitingJobs()
+{
+ // Scheduling the tasks can cause code that modifies <this>'s reference
+ // count to run concurrently, and cause the caller of this function to
+ // be owned by another thread. We need to make sure the reference count
+ // does not reach 0 on another thread before the end of this method, so
+ // hold a strong ref to prevent that!
+ RefPtr<SyncObject> kungFuDeathGrip(this);
+
+ // First atomically swap mFirstWaitingJob and waitingJobs...
+ Job* waitingJobs = nullptr;
+ for (;;) {
+ waitingJobs = mFirstWaitingJob;
+ if (mFirstWaitingJob.compareExchange(waitingJobs, nullptr)) {
+ break;
+ }
+ }
+
+ // ... and submit all of the waiting tasks in waitingJob now that they belong
+ // to this thread.
+ while (waitingJobs) {
+ Job* next = waitingJobs->mNextWaitingJob;
+ waitingJobs->mNextWaitingJob = nullptr;
+ JobScheduler::GetQueueForJob(waitingJobs)->SubmitJob(waitingJobs);
+ waitingJobs = next;
+ }
+}
+
+bool
+SyncObject::IsSignaled()
+{
+ return mSignals == 0;
+}
+
+void
+SyncObject::FreezePrerequisites()
+{
+ MOZ_ASSERT(mAddedPrerequisites == mNumPrerequisites);
+}
+
+void
+SyncObject::AddPrerequisite(Job* aJob)
+{
+ MOZ_ASSERT(++mAddedPrerequisites <= mNumPrerequisites);
+}
+
+void
+SyncObject::AddSubsequent(Job* aJob)
+{
+}
+
+WorkerThread::WorkerThread(MultiThreadedJobQueue* aJobQueue)
+: mQueue(aJobQueue)
+{
+ aJobQueue->RegisterThread();
+}
+
+void
+WorkerThread::Run()
+{
+ SetName("gfx worker");
+
+ for (;;) {
+ Job* commands = nullptr;
+ if (!mQueue->WaitForJob(commands)) {
+ mQueue->UnregisterThread();
+ return;
+ }
+
+ JobStatus status = JobScheduler::ProcessJob(commands);
+
+ if (status == JobStatus::Error) {
+ // Don't try to handle errors for now, but that's open to discussions.
+ // I expect errors to be mostly OOM issues.
+ gfxDevCrash(LogReason::JobStatusError) << "Invalid job status " << (int)status;
+ }
+ }
+}
+
+} //namespace
+} //namespace
diff --git a/gfx/2d/JobScheduler.h b/gfx/2d/JobScheduler.h
new file mode 100644
index 000000000..483842904
--- /dev/null
+++ b/gfx/2d/JobScheduler.h
@@ -0,0 +1,257 @@
+/* -*- 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_TASKSCHEDULER_H_
+#define MOZILLA_GFX_TASKSCHEDULER_H_
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/RefCounted.h"
+
+#ifdef WIN32
+#include "mozilla/gfx/JobScheduler_win32.h"
+#else
+#include "mozilla/gfx/JobScheduler_posix.h"
+#endif
+
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class MultiThreadedJobQueue;
+class SyncObject;
+class WorkerThread;
+
+class JobScheduler {
+public:
+ /// Return one of the queues that the drawing worker threads pull from, chosen
+ /// pseudo-randomly.
+ static MultiThreadedJobQueue* GetDrawingQueue()
+ {
+ return sSingleton->mDrawingQueues[
+ sSingleton->mNextQueue++ % sSingleton->mDrawingQueues.size()
+ ];
+ }
+
+ /// Return one of the queues that the drawing worker threads pull from with a
+ /// hash to choose the queue.
+ ///
+ /// Calling this function several times with the same hash will yield the same queue.
+ static MultiThreadedJobQueue* GetDrawingQueue(uint32_t aHash)
+ {
+ return sSingleton->mDrawingQueues[
+ aHash % sSingleton->mDrawingQueues.size()
+ ];
+ }
+
+ /// Return the task queue associated to the worker the task is pinned to if
+ /// the task is pinned to a worker, or a random queue.
+ static MultiThreadedJobQueue* GetQueueForJob(Job* aJob);
+
+ /// Initialize the task scheduler with aNumThreads worker threads for drawing
+ /// and aNumQueues task queues.
+ ///
+ /// The number of threads must be superior or equal to the number of queues
+ /// (since for now a worker thread only pulls from one queue).
+ static bool Init(uint32_t aNumThreads, uint32_t aNumQueues);
+
+ /// Shut the scheduler down.
+ ///
+ /// This will block until worker threads are joined and deleted.
+ static void ShutDown();
+
+ /// Returns true if there is a successfully initialized JobScheduler singleton.
+ static bool IsEnabled() { return !!sSingleton; }
+
+ /// Submit a task buffer to its associated queue.
+ ///
+ /// The caller looses ownership of the task buffer.
+ static void SubmitJob(Job* aJobs);
+
+ /// Convenience function to block the current thread until a given SyncObject
+ /// is in the signaled state.
+ ///
+ /// The current thread will first try to steal jobs before blocking.
+ static void Join(SyncObject* aCompletionSync);
+
+ /// Process commands until the command buffer needs to block on a sync object,
+ /// completes, yields, or encounters an error.
+ ///
+ /// Can be used on any thread. Worker threads basically loop over this, but the
+ /// main thread can also dequeue pending task buffers and process them alongside
+ /// the worker threads if it is about to block until completion anyway.
+ ///
+ /// The caller looses ownership of the task buffer.
+ static JobStatus ProcessJob(Job* aJobs);
+
+protected:
+ static JobScheduler* sSingleton;
+
+ // queues of Job that are ready to be processed
+ std::vector<MultiThreadedJobQueue*> mDrawingQueues;
+ std::vector<WorkerThread*> mWorkerThreads;
+ Atomic<uint32_t> mNextQueue;
+};
+
+/// Jobs are not reference-counted because they don't have shared ownership.
+/// The ownership of tasks can change when they are passed to certain methods
+/// of JobScheduler and SyncObject. See the docuumentaion of these classes.
+class Job {
+public:
+ Job(SyncObject* aStart, SyncObject* aCompletion, WorkerThread* aThread = nullptr);
+
+ virtual ~Job();
+
+ virtual JobStatus Run() = 0;
+
+ /// For use in JobScheduler::SubmitJob. Don't use it anywhere else.
+ //already_AddRefed<SyncObject> GetAndResetStartSync();
+ SyncObject* GetStartSync() { return mStartSync; }
+
+ bool IsPinnedToAThread() const { return !!mPinToThread; }
+
+ WorkerThread* GetWorkerThread() { return mPinToThread; }
+
+protected:
+ // An intrusive linked list of tasks waiting for a sync object to enter the
+ // signaled state. When the task is not waiting for a sync object, mNextWaitingJob
+ // should be null. This is only accessed from the thread that owns the task.
+ Job* mNextWaitingJob;
+
+ RefPtr<SyncObject> mStartSync;
+ RefPtr<SyncObject> mCompletionSync;
+ WorkerThread* mPinToThread;
+
+ friend class SyncObject;
+};
+
+class EventObject;
+
+/// This task will set an EventObject.
+///
+/// Typically used as the final task, so that the main thread can block on the
+/// corresponfing EventObject until all of the tasks are processed.
+class SetEventJob : public Job
+{
+public:
+ explicit SetEventJob(EventObject* aEvent,
+ SyncObject* aStart, SyncObject* aCompletion = nullptr,
+ WorkerThread* aPinToWorker = nullptr);
+
+ ~SetEventJob();
+
+ JobStatus Run() override;
+
+ EventObject* GetEvent() { return mEvent; }
+
+protected:
+ RefPtr<EventObject> mEvent;
+};
+
+/// A synchronization object that can be used to express dependencies and ordering between
+/// tasks.
+///
+/// Jobs can register to SyncObjects in order to asynchronously wait for a signal.
+/// In practice, Job objects usually start with a sync object (startSyc) and end
+/// with another one (completionSync).
+/// a Job never gets processed before its startSync is in the signaled state, and
+/// signals its completionSync as soon as it finishes. This is how dependencies
+/// between tasks is expressed.
+class SyncObject final : public external::AtomicRefCounted<SyncObject> {
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(SyncObject)
+
+ /// Create a synchronization object.
+ ///
+ /// aNumPrerequisites represents the number of times the object must be signaled
+ /// before actually entering the signaled state (in other words, it means the
+ /// number of dependencies of this sync object).
+ ///
+ /// Explicitly specifying the number of prerequisites when creating sync objects
+ /// makes it easy to start scheduling some of the prerequisite tasks while
+ /// creating the others, which is how we typically use the task scheduler.
+ /// Automatically determining the number of prerequisites using Job's constructor
+ /// brings the risk that the sync object enters the signaled state while we
+ /// are still adding prerequisites which is hard to fix without using muteces.
+ explicit SyncObject(uint32_t aNumPrerequisites = 1);
+
+ ~SyncObject();
+
+ /// Attempt to register a task.
+ ///
+ /// If the sync object is already in the signaled state, the buffer is *not*
+ /// registered and the sync object does not take ownership of the task.
+ /// If the object is not yet in the signaled state, it takes ownership of
+ /// the task and places it in a list of pending tasks.
+ /// Pending tasks will not be processed by the worker thread.
+ /// When the SyncObject reaches the signaled state, it places the pending
+ /// tasks back in the available buffer queue, so that they can be
+ /// scheduled again.
+ ///
+ /// Returns true if the SyncOject is not already in the signaled state.
+ /// This means that if this method returns true, the SyncObject has taken
+ /// ownership of the Job.
+ bool Register(Job* aJob);
+
+ /// Signal the SyncObject.
+ ///
+ /// This decrements an internal counter. The sync object reaches the signaled
+ /// state when the counter gets to zero.
+ void Signal();
+
+ /// Returns true if mSignals is equal to zero. In other words, returns true
+ /// if all prerequisite tasks have already signaled the sync object.
+ bool IsSignaled();
+
+ /// Asserts that the number of added prerequisites is equal to the number
+ /// specified in the constructor (does nothin in release builds).
+ void FreezePrerequisites();
+
+private:
+ // Called by Job's constructor
+ void AddSubsequent(Job* aJob);
+ void AddPrerequisite(Job* aJob);
+
+ void AddWaitingJob(Job* aJob);
+
+ void SubmitWaitingJobs();
+
+ Atomic<int32_t> mSignals;
+ Atomic<Job*> mFirstWaitingJob;
+
+#ifdef DEBUG
+ uint32_t mNumPrerequisites;
+ Atomic<uint32_t> mAddedPrerequisites;
+#endif
+
+ friend class Job;
+ friend class JobScheduler;
+};
+
+/// Base class for worker threads.
+class WorkerThread
+{
+public:
+ static WorkerThread* Create(MultiThreadedJobQueue* aJobQueue);
+
+ virtual ~WorkerThread() {}
+
+ void Run();
+
+ MultiThreadedJobQueue* GetJobQueue() { return mQueue; }
+
+protected:
+ explicit WorkerThread(MultiThreadedJobQueue* aJobQueue);
+
+ virtual void SetName(const char* aName) {}
+
+ MultiThreadedJobQueue* mQueue;
+};
+
+} // namespace
+} // namespace
+
+#endif
diff --git a/gfx/2d/JobScheduler_posix.cpp b/gfx/2d/JobScheduler_posix.cpp
new file mode 100644
index 000000000..e41388f21
--- /dev/null
+++ b/gfx/2d/JobScheduler_posix.cpp
@@ -0,0 +1,194 @@
+/* -*- 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 "JobScheduler.h"
+#include "mozilla/gfx/Logging.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+void* ThreadCallback(void* threadData);
+
+class WorkerThreadPosix : public WorkerThread {
+public:
+ explicit WorkerThreadPosix(MultiThreadedJobQueue* aJobQueue)
+ : WorkerThread(aJobQueue)
+ {
+ pthread_create(&mThread, nullptr, ThreadCallback, static_cast<WorkerThread*>(this));
+ }
+
+ ~WorkerThreadPosix()
+ {
+ pthread_join(mThread, nullptr);
+ }
+
+ virtual void SetName(const char*) override
+ {
+// XXX - temporarily disabled, see bug 1209039
+//
+// // Call this from the thread itself because of Mac.
+//#ifdef XP_MACOSX
+// pthread_setname_np(aName);
+//#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+// pthread_set_name_np(mThread, aName);
+//#elif defined(__NetBSD__)
+// pthread_setname_np(mThread, "%s", (void*)aName);
+//#else
+// pthread_setname_np(mThread, aName);
+//#endif
+ }
+
+protected:
+ pthread_t mThread;
+};
+
+void* ThreadCallback(void* threadData)
+{
+ WorkerThread* thread = static_cast<WorkerThread*>(threadData);
+ thread->Run();
+ return nullptr;
+}
+
+WorkerThread*
+WorkerThread::Create(MultiThreadedJobQueue* aJobQueue)
+{
+ return new WorkerThreadPosix(aJobQueue);
+}
+
+MultiThreadedJobQueue::MultiThreadedJobQueue()
+: mThreadsCount(0)
+, mShuttingDown(false)
+{}
+
+MultiThreadedJobQueue::~MultiThreadedJobQueue()
+{
+ MOZ_ASSERT(mJobs.empty());
+}
+
+bool
+MultiThreadedJobQueue::WaitForJob(Job*& aOutJob)
+{
+ return PopJob(aOutJob, BLOCKING);
+}
+
+bool
+MultiThreadedJobQueue::PopJob(Job*& aOutJobs, AccessType aAccess)
+{
+ for (;;) {
+ CriticalSectionAutoEnter lock(&mMutex);
+
+ while (aAccess == BLOCKING && !mShuttingDown && mJobs.empty()) {
+ mAvailableCondvar.Wait(&mMutex);
+ }
+
+ if (mShuttingDown) {
+ return false;
+ }
+
+ if (mJobs.empty()) {
+ if (aAccess == NON_BLOCKING) {
+ return false;
+ }
+ continue;
+ }
+
+ Job* task = mJobs.front();
+ MOZ_ASSERT(task);
+
+ mJobs.pop_front();
+
+ aOutJobs = task;
+ return true;
+ }
+}
+
+void
+MultiThreadedJobQueue::SubmitJob(Job* aJobs)
+{
+ MOZ_ASSERT(aJobs);
+ CriticalSectionAutoEnter lock(&mMutex);
+ mJobs.push_back(aJobs);
+ mAvailableCondvar.Broadcast();
+}
+
+size_t
+MultiThreadedJobQueue::NumJobs()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ return mJobs.size();
+}
+
+bool
+MultiThreadedJobQueue::IsEmpty()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ return mJobs.empty();
+}
+
+void
+MultiThreadedJobQueue::ShutDown()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ mShuttingDown = true;
+ while (mThreadsCount) {
+ mAvailableCondvar.Broadcast();
+ mShutdownCondvar.Wait(&mMutex);
+ }
+}
+
+void
+MultiThreadedJobQueue::RegisterThread()
+{
+ mThreadsCount += 1;
+}
+
+void
+MultiThreadedJobQueue::UnregisterThread()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ mThreadsCount -= 1;
+ if (mThreadsCount == 0) {
+ mShutdownCondvar.Broadcast();
+ }
+}
+
+EventObject::EventObject()
+: mIsSet(false)
+{}
+
+EventObject::~EventObject()
+{}
+
+bool
+EventObject::Peak()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ return mIsSet;
+}
+
+void
+EventObject::Set()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ if (!mIsSet) {
+ mIsSet = true;
+ mCond.Broadcast();
+ }
+}
+
+void
+EventObject::Wait()
+{
+ CriticalSectionAutoEnter lock(&mMutex);
+ if (mIsSet) {
+ return;
+ }
+ mCond.Wait(&mMutex);
+}
+
+} // namespce
+} // namespce
diff --git a/gfx/2d/JobScheduler_posix.h b/gfx/2d/JobScheduler_posix.h
new file mode 100644
index 000000000..cc1bef84e
--- /dev/null
+++ b/gfx/2d/JobScheduler_posix.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 WIN32
+#ifndef MOZILLA_GFX_TASKSCHEDULER_POSIX_H_
+#define MOZILLA_GFX_TASKSCHEDULER_POSIX_H_
+
+#include <string>
+#include <vector>
+#include <list>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/CriticalSection.h"
+#include "mozilla/RefCounted.h"
+
+namespace mozilla {
+namespace gfx {
+
+class Job;
+class PosixCondVar;
+class WorkerThread;
+
+// posix platforms only!
+class PosixCondVar {
+public:
+ PosixCondVar() {
+ DebugOnly<int> err = pthread_cond_init(&mCond, nullptr);
+ MOZ_ASSERT(!err);
+ }
+
+ ~PosixCondVar() {
+ DebugOnly<int> err = pthread_cond_destroy(&mCond);
+ MOZ_ASSERT(!err);
+ }
+
+ void Wait(CriticalSection* aMutex) {
+ DebugOnly<int> err = pthread_cond_wait(&mCond, &aMutex->mMutex);
+ MOZ_ASSERT(!err);
+ }
+
+ void Broadcast() {
+ DebugOnly<int> err = pthread_cond_broadcast(&mCond);
+ MOZ_ASSERT(!err);
+ }
+
+protected:
+ pthread_cond_t mCond;
+};
+
+
+/// A simple and naive multithreaded task queue
+///
+/// The public interface of this class must remain identical to its equivalent
+/// in JobScheduler_win32.h
+class MultiThreadedJobQueue {
+public:
+ enum AccessType {
+ BLOCKING,
+ NON_BLOCKING
+ };
+
+ // Producer thread
+ MultiThreadedJobQueue();
+
+ // Producer thread
+ ~MultiThreadedJobQueue();
+
+ // Worker threads
+ bool WaitForJob(Job*& aOutJob);
+
+ // Any thread
+ bool PopJob(Job*& aOutJob, AccessType aAccess);
+
+ // Any threads
+ void SubmitJob(Job* aJob);
+
+ // Producer thread
+ void ShutDown();
+
+ // Any thread
+ size_t NumJobs();
+
+ // Any thread
+ bool IsEmpty();
+
+ // Producer thread
+ void RegisterThread();
+
+ // Worker threads
+ void UnregisterThread();
+
+protected:
+
+ std::list<Job*> mJobs;
+ CriticalSection mMutex;
+ PosixCondVar mAvailableCondvar;
+ PosixCondVar mShutdownCondvar;
+ int32_t mThreadsCount;
+ bool mShuttingDown;
+
+ friend class WorkerThread;
+};
+
+/// An object that a thread can synchronously wait on.
+/// Usually set by a SetEventJob.
+class EventObject : public external::AtomicRefCounted<EventObject>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(EventObject)
+
+ EventObject();
+
+ ~EventObject();
+
+ /// Synchronously wait until the event is set.
+ void Wait();
+
+ /// Return true if the event is set, without blocking.
+ bool Peak();
+
+ /// Set the event.
+ void Set();
+
+protected:
+ CriticalSection mMutex;
+ PosixCondVar mCond;
+ bool mIsSet;
+};
+
+} // namespace
+} // namespace
+
+#include "JobScheduler.h"
+
+#endif
+#endif
diff --git a/gfx/2d/JobScheduler_win32.cpp b/gfx/2d/JobScheduler_win32.cpp
new file mode 100644
index 000000000..989965adc
--- /dev/null
+++ b/gfx/2d/JobScheduler_win32.cpp
@@ -0,0 +1,148 @@
+/* -*- 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 "JobScheduler.h"
+#include "mozilla/gfx/Logging.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+DWORD __stdcall ThreadCallback(void* threadData);
+
+class WorkerThreadWin32 : public WorkerThread {
+public:
+ explicit WorkerThreadWin32(MultiThreadedJobQueue* aJobQueue)
+ : WorkerThread(aJobQueue)
+ {
+ mThread = ::CreateThread(nullptr, 0, ThreadCallback, static_cast<WorkerThread*>(this), 0, nullptr);
+ }
+
+ ~WorkerThreadWin32()
+ {
+ ::WaitForSingleObject(mThread, INFINITE);
+ ::CloseHandle(mThread);
+ }
+
+protected:
+ HANDLE mThread;
+};
+
+DWORD __stdcall ThreadCallback(void* threadData)
+{
+ WorkerThread* thread = static_cast<WorkerThread*>(threadData);
+ thread->Run();
+ return 0;
+}
+
+WorkerThread*
+WorkerThread::Create(MultiThreadedJobQueue* aJobQueue)
+{
+ return new WorkerThreadWin32(aJobQueue);
+}
+
+bool
+MultiThreadedJobQueue::PopJob(Job*& aOutJob, AccessType aAccess)
+{
+ for (;;) {
+ while (aAccess == BLOCKING && mJobs.empty()) {
+ {
+ CriticalSectionAutoEnter lock(&mSection);
+ if (mShuttingDown) {
+ return false;
+ }
+ }
+
+ HANDLE handles[] = { mAvailableEvent, mShutdownEvent };
+ ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ }
+
+ CriticalSectionAutoEnter lock(&mSection);
+
+ if (mShuttingDown) {
+ return false;
+ }
+
+ if (mJobs.empty()) {
+ if (aAccess == NON_BLOCKING) {
+ return false;
+ }
+ continue;
+ }
+
+ Job* task = mJobs.front();
+ MOZ_ASSERT(task);
+
+ mJobs.pop_front();
+
+ if (mJobs.empty()) {
+ ::ResetEvent(mAvailableEvent);
+ }
+
+ aOutJob = task;
+ return true;
+ }
+}
+
+void
+MultiThreadedJobQueue::SubmitJob(Job* aJob)
+{
+ MOZ_ASSERT(aJob);
+ CriticalSectionAutoEnter lock(&mSection);
+ mJobs.push_back(aJob);
+ ::SetEvent(mAvailableEvent);
+}
+
+void
+MultiThreadedJobQueue::ShutDown()
+{
+ {
+ CriticalSectionAutoEnter lock(&mSection);
+ mShuttingDown = true;
+ }
+ while (mThreadsCount) {
+ ::SetEvent(mAvailableEvent);
+ ::WaitForSingleObject(mShutdownEvent, INFINITE);
+ }
+}
+
+size_t
+MultiThreadedJobQueue::NumJobs()
+{
+ CriticalSectionAutoEnter lock(&mSection);
+ return mJobs.size();
+}
+
+bool
+MultiThreadedJobQueue::IsEmpty()
+{
+ CriticalSectionAutoEnter lock(&mSection);
+ return mJobs.empty();
+}
+
+void
+MultiThreadedJobQueue::RegisterThread()
+{
+ mThreadsCount += 1;
+}
+
+void
+MultiThreadedJobQueue::UnregisterThread()
+{
+ mSection.Enter();
+ mThreadsCount -= 1;
+ bool finishShutdown = mThreadsCount == 0;
+ mSection.Leave();
+
+ if (finishShutdown) {
+ // Can't touch mSection or any other member from now on because this object
+ // may get deleted on the main thread after mShutdownEvent is set.
+ ::SetEvent(mShutdownEvent);
+ }
+}
+
+} // namespace
+} // namespace
diff --git a/gfx/2d/JobScheduler_win32.h b/gfx/2d/JobScheduler_win32.h
new file mode 100644
index 000000000..73ccbd4ed
--- /dev/null
+++ b/gfx/2d/JobScheduler_win32.h
@@ -0,0 +1,99 @@
+/* -*- 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/. */
+
+#ifdef WIN32
+#ifndef MOZILLA_GFX_TASKSCHEDULER_WIN32_H_
+#define MOZILLA_GFX_TASKSCHEDULER_WIN32_H_
+
+#include <windows.h>
+#include <list>
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/CriticalSection.h"
+#include "mozilla/RefCounted.h"
+
+namespace mozilla {
+namespace gfx {
+
+class WorkerThread;
+class Job;
+
+// The public interface of this class must remain identical to its equivalent
+// in JobScheduler_posix.h
+class MultiThreadedJobQueue {
+public:
+ enum AccessType {
+ BLOCKING,
+ NON_BLOCKING
+ };
+
+ MultiThreadedJobQueue()
+ : mThreadsCount(0)
+ , mShuttingDown(false)
+ {
+ mAvailableEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ mShutdownEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ }
+
+ ~MultiThreadedJobQueue()
+ {
+ ::CloseHandle(mAvailableEvent);
+ ::CloseHandle(mShutdownEvent);
+ }
+
+ bool WaitForJob(Job*& aOutJob) { return PopJob(aOutJob, BLOCKING); }
+
+ bool PopJob(Job*& aOutJob, AccessType aAccess);
+
+ void SubmitJob(Job* aJob);
+
+ void ShutDown();
+
+ size_t NumJobs();
+
+ bool IsEmpty();
+
+ void RegisterThread();
+
+ void UnregisterThread();
+
+protected:
+ std::list<Job*> mJobs;
+ CriticalSection mSection;
+ HANDLE mAvailableEvent;
+ HANDLE mShutdownEvent;
+ int32_t mThreadsCount;
+ bool mShuttingDown;
+
+ friend class WorkerThread;
+};
+
+
+// The public interface of this class must remain identical to its equivalent
+// in JobScheduler_posix.h
+class EventObject : public external::AtomicRefCounted<EventObject>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(EventObject)
+
+ EventObject() { mEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr); }
+
+ ~EventObject() { ::CloseHandle(mEvent); }
+
+ void Wait() { ::WaitForSingleObject(mEvent, INFINITE); }
+
+ bool Peak() { return ::WaitForSingleObject(mEvent, 0) == WAIT_OBJECT_0; }
+
+ void Set() { ::SetEvent(mEvent); }
+protected:
+ // TODO: it's expensive to create events so we should try to reuse them
+ HANDLE mEvent;
+};
+
+} // namespace
+} // namespace
+
+#endif
+#endif
diff --git a/gfx/2d/Logging.h b/gfx/2d/Logging.h
new file mode 100644
index 000000000..e6be2a7e8
--- /dev/null
+++ b/gfx/2d/Logging.h
@@ -0,0 +1,706 @@
+/* -*- 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_LOGGING_H_
+#define MOZILLA_GFX_LOGGING_H_
+
+#include <string>
+#include <sstream>
+#include <stdio.h>
+#include <vector>
+
+#ifdef MOZ_LOGGING
+#include "mozilla/Logging.h"
+#endif
+#include "mozilla/Tuple.h"
+
+#if defined(MOZ_WIDGET_ANDROID)
+#include "nsDebug.h"
+#endif
+#include "Point.h"
+#include "BaseRect.h"
+#include "Matrix.h"
+#include "LoggingConstants.h"
+
+#if defined(MOZ_LOGGING)
+extern GFX2D_API mozilla::LogModule* GetGFX2DLog();
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+#if defined(MOZ_LOGGING)
+inline mozilla::LogLevel PRLogLevelForLevel(int aLevel) {
+ switch (aLevel) {
+ case LOG_CRITICAL:
+ return LogLevel::Error;
+ case LOG_WARNING:
+ return LogLevel::Warning;
+ case LOG_DEBUG:
+ return LogLevel::Debug;
+ case LOG_DEBUG_PRLOG:
+ return LogLevel::Debug;
+ case LOG_EVERYTHING:
+ return LogLevel::Error;
+ }
+ return LogLevel::Debug;
+}
+#endif
+
+class LoggingPrefs
+{
+public:
+ // 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 or 4 add debug logging.
+ // In addition to setting the value to 4, you will need to set the
+ // environment variable MOZ_LOG to gfx:4. See mozilla/Logging.h for details.
+ static int32_t sGfxLogLevel;
+};
+
+/// 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.
+///
+/// On platforms that support MOZ_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 the "gfx2d" logging module. However, in the case
+/// where such module would disable the output, in all but gfxDebug cases,
+/// we will still send a printf.
+
+// The range is due to the values set in Histograms.json
+enum class LogReason : int {
+ MustBeMoreThanThis = -1,
+ // Start. Do not insert, always add at end. If you remove items,
+ // make sure the other items retain their values.
+ D3D11InvalidCallDeviceRemoved = 0,
+ D3D11InvalidCall,
+ D3DLockTimeout,
+ D3D10FinalizeFrame,
+ D3D11FinalizeFrame,
+ D3D10SyncLock,
+ D3D11SyncLock,
+ D2D1NoWriteMap,
+ JobStatusError,
+ FilterInputError,
+ FilterInputData, // 10
+ FilterInputRect,
+ FilterInputSet,
+ FilterInputFormat,
+ FilterNodeD2D1Target,
+ FilterNodeD2D1Backend,
+ SourceSurfaceIncompatible,
+ GlyphAllocFailedCairo,
+ GlyphAllocFailedCG,
+ InvalidRect,
+ CannotDraw3D, // 20
+ IncompatibleBasicTexturedEffect,
+ InvalidFont,
+ PAllocTextureBackendMismatch,
+ GetFontFileDataFailed,
+ MessageChannelCloseFailure,
+ MessageChannelInvalidHandle,
+ TextureAliveAfterShutdown,
+ InvalidContext,
+ InvalidCommandList,
+ AsyncTransactionTimeout, // 30
+ TextureCreation,
+ InvalidCacheSurface,
+ AlphaWithBasicClient,
+ UnbalancedClipStack,
+ ProcessingError,
+ NativeFontResourceNotFound,
+ // End
+ MustBeLessThanThis = 101,
+};
+
+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 (LoggingPrefs::sGfxLogLevel >= aLevel) {
+#if defined(MOZ_WIDGET_ANDROID)
+ return true;
+#else
+#if defined(MOZ_LOGGING)
+ if (MOZ_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
+ return true;
+ } else
+#endif
+ if ((LoggingPrefs::sGfxLogLevel >= LOG_DEBUG_PRLOG) ||
+ (aLevel < LOG_DEBUG)) {
+ return true;
+ }
+#endif
+ }
+ return false;
+ }
+
+ // Only for really critical errors.
+ static void CrashAction(LogReason aReason) {}
+
+ 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 (LoggingPrefs::sGfxLogLevel >= aLevel) {
+#if defined(MOZ_WIDGET_ANDROID)
+ printf_stderr("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
+#else
+#if defined(MOZ_LOGGING)
+ if (MOZ_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
+ PR_LogPrint("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
+ } else
+#endif
+ if ((LoggingPrefs::sGfxLogLevel >= LOG_DEBUG_PRLOG) ||
+ (aLevel < LOG_DEBUG)) {
+ printf("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
+ }
+#endif
+ }
+ }
+};
+
+struct CriticalLogger {
+ static void OutputMessage(const std::string &aString, int aLevel, bool aNoNewline);
+ static void CrashAction(LogReason aReason);
+};
+
+// 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. The double is expected to be the "TimeDuration",
+// time in seconds since the process creation.
+typedef mozilla::Tuple<int32_t,std::string,double> LoggingRecordEntry;
+
+// Implement this interface and init the Factory with an instance to
+// forward critical logs.
+typedef std::vector<LoggingRecordEntry> LoggingRecord;
+class LogForwarder {
+public:
+ virtual ~LogForwarder() {}
+ virtual void Log(const std::string &aString) = 0;
+ virtual void CrashAction(LogReason aReason) = 0;
+ virtual bool UpdateStringsVector(const std::string& aString) = 0;
+
+ // Provide a copy of the logs to the caller.
+ virtual LoggingRecord LoggingRecordCopy() = 0;
+};
+
+class NoLog
+{
+public:
+ NoLog() {}
+ ~NoLog() {}
+
+ // No-op
+ MOZ_IMPLICIT NoLog(const NoLog&) {}
+
+ template<typename T>
+ NoLog &operator <<(const T &aLogText) { return *this; }
+};
+
+enum class LogOptions : int {
+ NoNewline = 0x01,
+ AutoPrefix = 0x02,
+ AssertOnCall = 0x04,
+ CrashAction = 0x08,
+};
+
+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:
+ // 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));
+ }
+
+ // 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),
+ LogReason aReason = LogReason::MustBeMoreThanThis)
+ : mOptions(0)
+ , mLogIt(false)
+ {
+ Init(aOptions, BasicLogger::ShouldOutputMessage(L), aReason);
+ }
+
+ ~Log() {
+ Flush();
+ }
+
+ void Flush() {
+ if (MOZ_LIKELY(!LogIt())) return;
+
+ std::string str = mMessage.str();
+ if (!str.empty()) {
+ WriteLog(str);
+ }
+ mMessage.str("");
+ }
+
+ 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 << std::showbase << std::hex
+ << aHex.mVal
+ << std::noshowbase << 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_UINT16:
+ mMessage << "SurfaceFormat::R5G6B5_UINT16";
+ 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); }
+ inline bool ValidReason() const { return (int)mReason > (int)LogReason::MustBeMoreThanThis && (int)mReason < (int)LogReason::MustBeLessThanThis; }
+
+ // We do not want this version to do any work, and stringstream can't be
+ // copied anyway. It does come in handy for the "Once" macro defined below.
+ MOZ_IMPLICIT Log(const Log& log) { Init(log.mOptions, false, log.mReason); }
+
+private:
+ // Initialization common to two constructors
+ void Init(int aOptions, bool aLogIt, LogReason aReason) {
+ mOptions = aOptions;
+ mReason = aReason;
+ mLogIt = aLogIt;
+ if (mLogIt) {
+ if (AutoPrefix()) {
+ if (mOptions & int(LogOptions::AssertOnCall)) {
+ mMessage << "[GFX" << L;
+ } else {
+ mMessage << "[GFX" << L << "-";
+ }
+ }
+ if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) {
+ mMessage << " " << (int)mReason;
+ }
+ if (AutoPrefix()) {
+ mMessage << "]: ";
+ }
+ }
+ }
+
+ void WriteLog(const std::string &aString) {
+ if (MOZ_UNLIKELY(LogIt())) {
+ Logger::OutputMessage(aString, L, NoNewline());
+ // Assert if required. We don't have a three parameter MOZ_ASSERT
+ // so use the underlying functions instead (see bug 1281702):
+#ifdef DEBUG
+ if (mOptions & int(LogOptions::AssertOnCall)) {
+ MOZ_ReportAssertionFailure(aString.c_str(), __FILE__, __LINE__);
+ MOZ_CRASH("GFX: An assert from the graphics logger");
+ }
+#endif
+ if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) {
+ Logger::CrashAction(mReason);
+ }
+ }
+ }
+
+ std::stringstream mMessage;
+ int mOptions;
+ LogReason mReason;
+ bool mLogIt;
+};
+
+typedef Log<LOG_DEBUG> DebugLog;
+typedef Log<LOG_WARNING> WarningLog;
+typedef Log<LOG_CRITICAL, CriticalLogger> CriticalLog;
+
+// Macro to glue names to get us less chance of name clashing.
+#if defined GFX_LOGGING_GLUE1 || defined GFX_LOGGING_GLUE
+#error "Clash of the macro GFX_LOGGING_GLUE1 or GFX_LOGGING_GLUE"
+#endif
+#define GFX_LOGGING_GLUE1(x, y) x##y
+#define GFX_LOGGING_GLUE(x, y) GFX_LOGGING_GLUE1(x, y)
+
+// This log goes into crash reports, use with care.
+#define gfxCriticalError mozilla::gfx::CriticalLog
+#define gfxCriticalErrorOnce static gfxCriticalError GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxCriticalError
+
+// This is a shortcut for errors we want logged in crash reports/about support
+// but we do not want asserting. These are available in all builds, so it is
+// not worth trying to do magic to avoid matching the syntax of gfxCriticalError.
+// So, this one is used as
+// gfxCriticalNote << "Something to report and not assert";
+// while the critical error is
+// gfxCriticalError() << "Something to report and assert";
+#define gfxCriticalNote gfxCriticalError(gfxCriticalError::DefaultOptions(false))
+#define gfxCriticalNoteOnce static gfxCriticalError GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxCriticalNote
+
+// The "once" versions will only trigger the first time through. You can do this:
+// gfxCriticalErrorOnce() << "This message only shows up once;
+// instead of the usual:
+// static bool firstTime = true;
+// if (firstTime) {
+// firstTime = false;
+// gfxCriticalError() << "This message only shows up once;
+// }
+#if defined(DEBUG)
+#define gfxDebug mozilla::gfx::DebugLog
+#define gfxDebugOnce static gfxDebug GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxDebug
+#else
+#define gfxDebug if (1) ; else mozilla::gfx::NoLog
+#define gfxDebugOnce if (1) ; else mozilla::gfx::NoLog
+#endif
+
+// Have gfxWarning available (behind a runtime preference)
+#define gfxWarning mozilla::gfx::WarningLog
+#define gfxWarningOnce static gfxWarning GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxWarning
+
+// In the debug build, this is equivalent to the default gfxCriticalError.
+// In the non-debug build, on nightly and dev edition, it will MOZ_CRASH.
+// On beta and release versions, it will telemetry count, but proceed.
+//
+// You should create a (new) enum in the LogReason and use it for the reason
+// parameter to ensure uniqueness.
+#define gfxDevCrash(reason) gfxCriticalError(int(gfx::LogOptions::AutoPrefix) | int(gfx::LogOptions::AssertOnCall) | int(gfx::LogOptions::CrashAction), (reason))
+
+// 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 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() {
+ MOZ_ASSERT(mDepth > 0);
+ --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(const TreeAutoIndent& aTreeAutoIndent) :
+ TreeAutoIndent(aTreeAutoIndent.mTreeLog) {
+ mTreeLog.IncreaseIndent();
+ }
+
+ TreeAutoIndent& operator=(const TreeAutoIndent& aTreeAutoIndent) = delete;
+
+ ~TreeAutoIndent() {
+ mTreeLog.DecreaseIndent();
+ }
+private:
+ TreeLog& mTreeLog;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_LOGGING_H_ */
diff --git a/gfx/2d/LoggingConstants.h b/gfx/2d/LoggingConstants.h
new file mode 100644
index 000000000..08eb2014d
--- /dev/null
+++ b/gfx/2d/LoggingConstants.h
@@ -0,0 +1,30 @@
+/* -*- 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_LOGGING_CONSTANTS_H_
+#define MOZILLA_GFX_LOGGING_CONSTANTS_H_
+
+namespace mozilla {
+namespace gfx {
+
+// Attempting to be consistent with prlog values, but that isn't critical
+// (and note that 5 has a special meaning - see the description
+// with LoggingPrefs::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
+
+#if defined(DEBUG)
+const int LOG_DEFAULT = LOG_EVERYTHING;
+#else
+const int LOG_DEFAULT = LOG_CRITICAL;
+#endif
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_LOGGING_CONSTANTS_H_ */
diff --git a/gfx/2d/MMIHelpers.h b/gfx/2d/MMIHelpers.h
new file mode 100644
index 000000000..2a9b16a9d
--- /dev/null
+++ b/gfx/2d/MMIHelpers.h
@@ -0,0 +1,196 @@
+/*
+ ============================================================================
+ Name : MMIHelpers.h
+ Author : Heiher <r@hev.cc>
+ Version : 0.0.1
+ Copyright : Copyright (c) 2015 everyone.
+ Description : The helpers for x86 SSE to Loongson MMI.
+ ============================================================================
+ */
+
+#ifndef __MMI_HELPERS_H__
+#define __MMI_HELPERS_H__
+
+#define __mm_packxxxx(_f, _D, _d, _s, _t) \
+ #_f" %["#_t"], %["#_d"h], %["#_s"h] \n\t" \
+ #_f" %["#_D"l], %["#_d"l], %["#_s"l] \n\t" \
+ "punpckhwd %["#_D"h], %["#_D"l], %["#_t"] \n\t" \
+ "punpcklwd %["#_D"l], %["#_D"l], %["#_t"] \n\t"
+
+#define _mm_or(_D, _d, _s) \
+ "or %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "or %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+#define _mm_xor(_D, _d, _s) \
+ "xor %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "xor %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+#define _mm_and(_D, _d, _s) \
+ "and %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "and %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: pandn */
+#define _mm_pandn(_D, _d, _s) \
+ "pandn %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "pandn %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: pshuflw */
+#define _mm_pshuflh(_D, _d, _s) \
+ "mov.d %["#_D"h], %["#_d"h] \n\t" \
+ "pshufh %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: psllw (bits) */
+#define _mm_psllh(_D, _d, _s) \
+ "psllh %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "psllh %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: pslld (bits) */
+#define _mm_psllw(_D, _d, _s) \
+ "psllw %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "psllw %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: psllq (bits) */
+#define _mm_pslld(_D, _d, _s) \
+ "dsll %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "dsll %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: pslldq (bytes) */
+#define _mm_psllq(_D, _d, _s, _s64, _tf) \
+ "subu %["#_tf"], %["#_s64"], %["#_s"] \n\t" \
+ "dsrl %["#_tf"], %["#_d"l], %["#_tf"] \n\t" \
+ "dsll %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "dsll %["#_D"l], %["#_d"l], %["#_s"] \n\t" \
+ "or %["#_D"h], %["#_D"h], %["#_tf"] \n\t"
+
+/* SSE: psrlw (bits) */
+#define _mm_psrlh(_D, _d, _s) \
+ "psrlh %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "psrlh %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: psrld (bits) */
+#define _mm_psrlw(_D, _d, _s) \
+ "psrlw %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "psrlw %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: psrlq (bits) */
+#define _mm_psrld(_D, _d, _s) \
+ "dsrl %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "dsrl %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: psrldq (bytes) */
+#define _mm_psrlq(_D, _d, _s, _s64, _tf) \
+ "subu %["#_tf"], %["#_s64"], %["#_s"] \n\t" \
+ "dsll %["#_tf"], %["#_d"h], %["#_tf"] \n\t" \
+ "dsrl %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "dsrl %["#_D"l], %["#_d"l], %["#_s"] \n\t" \
+ "or %["#_D"l], %["#_D"l], %["#_tf"] \n\t"
+
+/* SSE: psrad */
+#define _mm_psraw(_D, _d, _s) \
+ "psraw %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
+ "psraw %["#_D"l], %["#_d"l], %["#_s"] \n\t"
+
+/* SSE: paddb */
+#define _mm_paddb(_D, _d, _s) \
+ "paddb %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "paddb %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: paddw */
+#define _mm_paddh(_D, _d, _s) \
+ "paddh %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "paddh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: paddd */
+#define _mm_paddw(_D, _d, _s) \
+ "paddw %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "paddw %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: paddq */
+#define _mm_paddd(_D, _d, _s) \
+ "dadd %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "dadd %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: psubw */
+#define _mm_psubh(_D, _d, _s) \
+ "psubh %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "psubh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: psubd */
+#define _mm_psubw(_D, _d, _s) \
+ "psubw %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "psubw %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: pmaxub */
+#define _mm_pmaxub(_D, _d, _s) \
+ "pmaxub %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "pmaxub %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: pmullw */
+#define _mm_pmullh(_D, _d, _s) \
+ "pmullh %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "pmullh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: pmulhw */
+#define _mm_pmulhh(_D, _d, _s) \
+ "pmulhh %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "pmulhh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: pmuludq */
+#define _mm_pmuluw(_D, _d, _s) \
+ "pmuluw %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
+ "pmuluw %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: packsswb */
+#define _mm_packsshb(_D, _d, _s, _t) \
+ __mm_packxxxx(packsshb, _D, _d, _s, _t)
+
+/* SSE: packssdw */
+#define _mm_packsswh(_D, _d, _s, _t) \
+ __mm_packxxxx(packsswh, _D, _d, _s, _t)
+
+/* SSE: packuswb */
+#define _mm_packushb(_D, _d, _s, _t) \
+ __mm_packxxxx(packushb, _D, _d, _s, _t)
+
+/* SSE: punpcklbw */
+#define _mm_punpcklbh(_D, _d, _s) \
+ "punpckhbh %["#_D"h], %["#_d"l], %["#_s"l] \n\t" \
+ "punpcklbh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: punpcklwd */
+#define _mm_punpcklhw(_D, _d, _s) \
+ "punpckhhw %["#_D"h], %["#_d"l], %["#_s"l] \n\t" \
+ "punpcklhw %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: punpckldq */
+#define _mm_punpcklwd(_D, _d, _s) \
+ "punpckhwd %["#_D"h], %["#_d"l], %["#_s"l] \n\t" \
+ "punpcklwd %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
+
+/* SSE: punpcklqdq */
+#define _mm_punpckldq(_D, _d, _s) \
+ "mov.d %["#_D"h], %["#_s"l] \n\t" \
+ "mov.d %["#_D"l], %["#_d"l] \n\t"
+
+/* SSE: punpckhbw */
+#define _mm_punpckhbh(_D, _d, _s) \
+ "punpcklbh %["#_D"l], %["#_d"h], %["#_s"h] \n\t" \
+ "punpckhbh %["#_D"h], %["#_d"h], %["#_s"h] \n\t"
+
+/* SSE: punpckhwd */
+#define _mm_punpckhhw(_D, _d, _s) \
+ "punpcklhw %["#_D"l], %["#_d"h], %["#_s"h] \n\t" \
+ "punpckhhw %["#_D"h], %["#_d"h], %["#_s"h] \n\t"
+
+/* SSE: punpckhdq */
+#define _mm_punpckhwd(_D, _d, _s) \
+ "punpcklwd %["#_D"l], %["#_d"h], %["#_s"h] \n\t" \
+ "punpckhwd %["#_D"h], %["#_d"h], %["#_s"h] \n\t"
+
+/* SSE: punpckhqdq */
+#define _mm_punpckhdq(_D, _d, _s) \
+ "mov.d %["#_D"l], %["#_d"h] \n\t" \
+ "mov.d %["#_D"h], %["#_s"h] \n\t"
+
+#endif /* __MMI_HELPERS_H__ */
+
diff --git a/gfx/2d/MacIOSurface.cpp b/gfx/2d/MacIOSurface.cpp
new file mode 100644
index 000000000..759de8575
--- /dev/null
+++ b/gfx/2d/MacIOSurface.cpp
@@ -0,0 +1,615 @@
+/* -*- 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"
+#include "GLConsts.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;
+IOSurfaceSizePlaneTFunc MacIOSurfaceLib::sWidth;
+IOSurfaceSizePlaneTFunc MacIOSurfaceLib::sHeight;
+IOSurfaceSizeTFunc MacIOSurfaceLib::sPlaneCount;
+IOSurfaceSizePlaneTFunc 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;
+IOSurfacePixelFormatFunc MacIOSurfaceLib::sPixelFormat;
+
+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, size_t plane) {
+ return sWidth(aIOSurfacePtr, plane);
+}
+
+size_t MacIOSurfaceLib::IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr, size_t plane) {
+ return sHeight(aIOSurfacePtr, plane);
+}
+
+size_t MacIOSurfaceLib::IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr, size_t plane) {
+ return sBytesPerRow(aIOSurfacePtr, plane);
+}
+
+size_t MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(CFStringRef property) {
+ return sGetPropertyMaximum(property);
+}
+
+OSType MacIOSurfaceLib::IOSurfaceGetPixelFormat(IOSurfacePtr aIOSurfacePtr) {
+ return sPixelFormat(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);
+}
+
+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, "IOSurfaceGetWidthOfPlane");
+ sHeight = GET_IOSYM(sHeight, "IOSurfaceGetHeightOfPlane");
+ sBytesPerRow = GET_IOSYM(sBytesPerRow, "IOSurfaceGetBytesPerRowOfPlane");
+ 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");
+ sPixelFormat = GET_IOSYM(sPixelFormat, "IOSurfaceGetPixelFormat");
+
+ 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);
+}
+
+already_AddRefed<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();
+}
+
+already_AddRefed<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 plane) {
+ size_t intScaleFactor = ceil(mContentsScaleFactor);
+ return GetDevicePixelWidth(plane) / intScaleFactor;
+}
+
+size_t MacIOSurface::GetHeight(size_t plane) {
+ size_t intScaleFactor = ceil(mContentsScaleFactor);
+ return GetDevicePixelHeight(plane) / 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(size_t plane) {
+ return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr, plane);
+}
+
+size_t MacIOSurface::GetDevicePixelHeight(size_t plane) {
+ return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr, plane);
+}
+
+size_t MacIOSurface::GetBytesPerRow(size_t plane) {
+ return MacIOSurfaceLib::IOSurfaceGetBytesPerRow(mIOSurfacePtr, plane);
+}
+
+OSType MacIOSurface::GetPixelFormat() {
+ return MacIOSurfaceLib::IOSurfaceGetPixelFormat(mIOSurfacePtr);
+}
+
+void MacIOSurface::IncrementUseCount() {
+ MacIOSurfaceLib::IOSurfaceIncrementUseCount(mIOSurfacePtr);
+}
+
+void MacIOSurface::DecrementUseCount() {
+ MacIOSurfaceLib::IOSurfaceDecrementUseCount(mIOSurfacePtr);
+}
+
+#define READ_ONLY 0x1
+void MacIOSurface::Lock(bool aReadOnly) {
+ MacIOSurfaceLib::IOSurfaceLock(mIOSurfacePtr, aReadOnly ? READ_ONLY : 0, nullptr);
+}
+
+void MacIOSurface::Unlock(bool aReadOnly) {
+ MacIOSurfaceLib::IOSurfaceUnlock(mIOSurfacePtr, aReadOnly ? READ_ONLY : 0, nullptr);
+}
+
+using mozilla::gfx::SourceSurface;
+using mozilla::gfx::IntSize;
+using mozilla::gfx::SurfaceFormat;
+
+void
+MacIOSurfaceBufferDeallocator(void* aClosure)
+{
+ MOZ_ASSERT(aClosure);
+
+ delete [] static_cast<unsigned char*>(aClosure);
+}
+
+already_AddRefed<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 =
+ new unsigned char[bytesPerRow * ioHeight / sizeof(unsigned char)];
+ 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<mozilla::gfx::DataSourceSurface> surf =
+ mozilla::gfx::Factory::CreateWrappingDataSourceSurface(dataCpy,
+ bytesPerRow,
+ IntSize(ioWidth, ioHeight),
+ format,
+ &MacIOSurfaceBufferDeallocator,
+ static_cast<void*>(dataCpy));
+
+ return surf.forget();
+}
+
+SurfaceFormat
+MacIOSurface::GetFormat()
+{
+ OSType pixelFormat = GetPixelFormat();
+ if (pixelFormat == '420v') {
+ return SurfaceFormat::NV12;
+ } else if (pixelFormat == '2vuy') {
+ return SurfaceFormat::YUV422;
+ } else {
+ return HasAlpha() ? SurfaceFormat::R8G8B8A8 : SurfaceFormat::R8G8B8X8;
+ }
+}
+
+SurfaceFormat
+MacIOSurface::GetReadFormat()
+{
+ OSType pixelFormat = GetPixelFormat();
+ if (pixelFormat == '420v') {
+ return SurfaceFormat::NV12;
+ } else if (pixelFormat == '2vuy') {
+ return SurfaceFormat::R8G8B8X8;
+ } else {
+ return HasAlpha() ? SurfaceFormat::R8G8B8A8 : SurfaceFormat::R8G8B8X8;
+ }
+}
+
+CGLError
+MacIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctx, size_t plane)
+{
+ MOZ_ASSERT(plane >= 0);
+ OSType pixelFormat = GetPixelFormat();
+
+ GLenum internalFormat;
+ GLenum format;
+ GLenum type;
+ if (pixelFormat == '420v') {
+ MOZ_ASSERT(GetPlaneCount() == 2);
+ MOZ_ASSERT(plane < 2);
+
+ if (plane == 0) {
+ internalFormat = format = GL_LUMINANCE;
+ } else {
+ internalFormat = format = GL_LUMINANCE_ALPHA;
+ }
+ type = GL_UNSIGNED_BYTE;
+ } else if (pixelFormat == '2vuy') {
+ MOZ_ASSERT(plane == 0);
+
+ internalFormat = GL_RGB;
+ format = LOCAL_GL_YCBCR_422_APPLE;
+ type = GL_UNSIGNED_SHORT_8_8_APPLE;
+ } else {
+ MOZ_ASSERT(plane == 0);
+
+ internalFormat = HasAlpha() ? GL_RGBA : GL_RGB;
+ format = GL_BGRA;
+ type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ }
+ CGLError temp = MacIOSurfaceLib::CGLTexImageIOSurface2D(ctx,
+ GL_TEXTURE_RECTANGLE_ARB,
+ internalFormat,
+ GetDevicePixelWidth(plane),
+ GetDevicePixelHeight(plane),
+ format,
+ type,
+ mIOSurfacePtr, plane);
+ return temp;
+}
+
+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);
+}
+
+already_AddRefed<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
new file mode 100644
index 000000000..0d4f79a15
--- /dev/null
+++ b/gfx/2d/MacIOSurface.h
@@ -0,0 +1,222 @@
+/* -*- 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/. */
+
+#ifndef MacIOSurface_h__
+#define MacIOSurface_h__
+#ifdef XP_DARWIN
+#include <QuartzCore/QuartzCore.h>
+#include <CoreVideo/CoreVideo.h>
+#include <dlfcn.h>
+
+struct _CGLContextObject;
+
+typedef _CGLContextObject* CGLContextObj;
+typedef struct CGContext* CGContextRef;
+typedef struct CGImage* CGImageRef;
+typedef uint32_t IOSurfaceID;
+
+#ifdef XP_IOS
+typedef kern_return_t IOReturn;
+typedef int CGLError;
+#endif
+
+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 (*IOSurfaceSizePlaneTFunc)(IOSurfacePtr io_surface, size_t plane);
+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);
+
+typedef OSType (*IOSurfacePixelFormatFunc)(IOSurfacePtr io_surface);
+
+#ifdef XP_MACOSX
+#import <OpenGL/OpenGL.h>
+#else
+#import <OpenGLES/ES2/gl.h>
+#endif
+
+#include "2D.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/RefCounted.h"
+
+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 final : public mozilla::external::AtomicRefCounted<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 already_AddRefed<MacIOSurface> CreateIOSurface(int aWidth, int aHeight,
+ double aContentsScaleFactor = 1.0,
+ bool aHasAlpha = true);
+ static void ReleaseIOSurface(MacIOSurface *aIOSurface);
+ static already_AddRefed<MacIOSurface> LookupSurface(IOSurfaceID aSurfaceID,
+ double aContentsScaleFactor = 1.0,
+ bool aHasAlpha = true);
+
+ explicit MacIOSurface(const void *aIOSurfacePtr,
+ double aContentsScaleFactor = 1.0,
+ bool aHasAlpha = true);
+ ~MacIOSurface();
+ IOSurfaceID GetIOSurfaceID();
+ void *GetBaseAddress();
+ void *GetBaseAddressOfPlane(size_t planeIndex);
+ size_t GetPlaneCount();
+ OSType GetPixelFormat();
+ // 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
+ // device pixel. Use GetDevicePixel**() to get device pixels.
+ size_t GetWidth(size_t plane = 0);
+ size_t GetHeight(size_t plane = 0);
+ double GetContentsScaleFactor() { return mContentsScaleFactor; }
+ size_t GetDevicePixelWidth(size_t plane = 0);
+ size_t GetDevicePixelHeight(size_t plane = 0);
+ size_t GetBytesPerRow(size_t plane = 0);
+ void Lock(bool aReadOnly = true);
+ void Unlock(bool aReadOnly = true);
+ void IncrementUseCount();
+ void DecrementUseCount();
+ bool HasAlpha() { return mHasAlpha; }
+ mozilla::gfx::SurfaceFormat GetFormat();
+ mozilla::gfx::SurfaceFormat GetReadFormat();
+
+ // 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(CGLContextObj ctxt, size_t plane = 0);
+ already_AddRefed<SourceSurface> GetAsSurface();
+ CGContextRef CreateIOSurfaceContext();
+
+ // FIXME This doesn't really belong here
+ static CGImageRef CreateImageFromIOSurfaceContext(CGContextRef aContext);
+ static already_AddRefed<MacIOSurface> IOSurfaceContextGetSurface(CGContextRef aContext,
+ 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:
+ MacIOSurfaceLib() = delete;
+ 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 IOSurfaceSizePlaneTFunc sWidth;
+ static IOSurfaceSizePlaneTFunc sHeight;
+ static IOSurfaceSizePlaneTFunc sBytesPerRow;
+ static IOSurfaceGetPropertyMaximumFunc sGetPropertyMaximum;
+ static CGLTexImageIOSurface2DFunc sTexImage;
+ static IOSurfaceContextCreateFunc sIOSurfaceContextCreate;
+ static IOSurfaceContextCreateImageFunc sIOSurfaceContextCreateImage;
+ static IOSurfaceContextGetSurfaceFunc sIOSurfaceContextGetSurface;
+ static CVPixelBufferGetIOSurfaceFunc sCVPixelBufferGetIOSurface;
+ static IOSurfacePixelFormatFunc sPixelFormat;
+ 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, size_t plane);
+ static size_t IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr, size_t plane);
+ static size_t IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr, size_t plane);
+ 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 OSType IOSurfaceGetPixelFormat(IOSurfacePtr aIOSurfacePtr);
+ static unsigned int (*sCGContextGetTypePtr) (CGContextRef);
+ static void LoadLibrary();
+ static void CloseLibrary();
+
+ // Static deconstructor
+ static class LibraryUnloader {
+ public:
+ ~LibraryUnloader() {
+ CloseLibrary();
+ }
+ } sLibraryUnloader;
+};
+
+#endif
+#endif
diff --git a/gfx/2d/Matrix.cpp b/gfx/2d/Matrix.cpp
new file mode 100644
index 000000000..b3fce00ba
--- /dev/null
+++ b/gfx/2d/Matrix.cpp
@@ -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/. */
+
+#include "Matrix.h"
+#include "Quaternion.h"
+#include "Tools.h"
+#include <algorithm>
+#include <ostream>
+#include <math.h>
+#include <float.h> // for FLT_EPSILON
+
+#include "mozilla/FloatingPoint.h" // for UnspecifiedNaN
+
+using namespace std;
+
+
+namespace mozilla {
+namespace gfx {
+
+/* Force small values to zero. We do this to avoid having sin(360deg)
+ * evaluate to a tiny but nonzero value.
+ */
+double
+FlushToZero(double aVal)
+{
+ // XXX Is double precision really necessary here
+ if (-FLT_EPSILON < aVal && aVal < FLT_EPSILON) {
+ return 0.0f;
+ } else {
+ return aVal;
+ }
+}
+
+/* Computes tan(aTheta). For values of aTheta such that tan(aTheta) is
+ * undefined or very large, SafeTangent returns a manageably large value
+ * of the correct sign.
+ */
+double
+SafeTangent(double aTheta)
+{
+ // XXX Is double precision really necessary here
+ const double kEpsilon = 0.0001;
+
+ /* tan(theta) = sin(theta)/cos(theta); problems arise when
+ * cos(theta) is too close to zero. Limit cos(theta) to the
+ * range [-1, -epsilon] U [epsilon, 1].
+ */
+
+ double sinTheta = sin(aTheta);
+ double cosTheta = cos(aTheta);
+
+ if (cosTheta >= 0 && cosTheta < kEpsilon) {
+ cosTheta = kEpsilon;
+ } else if (cosTheta < 0 && cosTheta >= -kEpsilon) {
+ cosTheta = -kEpsilon;
+ }
+ return FlushToZero(sinTheta / cosTheta);
+}
+
+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)
+{
+ Matrix newMatrix;
+
+ Float s = sinf(aAngle);
+ Float c = cosf(aAngle);
+
+ newMatrix._11 = c;
+ newMatrix._12 = s;
+ newMatrix._21 = -s;
+ newMatrix._22 = c;
+
+ return newMatrix;
+}
+
+Rect
+Matrix::TransformBounds(const Rect &aRect) const
+{
+ int i;
+ Point quad[4];
+ Float min_x, max_x;
+ Float min_y, max_y;
+
+ quad[0] = TransformPoint(aRect.TopLeft());
+ quad[1] = TransformPoint(aRect.TopRight());
+ quad[2] = TransformPoint(aRect.BottomLeft());
+ quad[3] = TransformPoint(aRect.BottomRight());
+
+ min_x = max_x = quad[0].x;
+ min_y = max_y = quad[0].y;
+
+ for (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);
+}
+
+Matrix&
+Matrix::NudgeToIntegers()
+{
+ NudgeToInteger(&_11);
+ NudgeToInteger(&_12);
+ NudgeToInteger(&_21);
+ NudgeToInteger(&_22);
+ NudgeToInteger(&_31);
+ NudgeToInteger(&_32);
+ return *this;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/Matrix.h b/gfx/2d/Matrix.h
new file mode 100644
index 000000000..22a01ca10
--- /dev/null
+++ b/gfx/2d/Matrix.h
@@ -0,0 +1,1676 @@
+/* -*- 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_MATRIX_H_
+#define MOZILLA_GFX_MATRIX_H_
+
+#include "Types.h"
+#include "Triangle.h"
+#include "Rect.h"
+#include "Point.h"
+#include "Quaternion.h"
+#include <iosfwd>
+#include <math.h>
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/FloatingPoint.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:
+ Matrix()
+ : _11(1.0f), _12(0)
+ , _21(0), _22(1.0f)
+ , _31(0), _32(0)
+ {}
+ Matrix(Float a11, Float a12, Float a21, Float a22, Float a31, Float a32)
+ : _11(a11), _12(a12)
+ , _21(a21), _22(a22)
+ , _31(a31), _32(a32)
+ {}
+ union {
+ struct {
+ Float _11, _12;
+ Float _21, _22;
+ Float _31, _32;
+ };
+ Float components[6];
+ };
+
+ MOZ_ALWAYS_INLINE Matrix Copy() const
+ {
+ return Matrix(*this);
+ }
+
+ friend std::ostream& operator<<(std::ostream& aStream, const Matrix& aMatrix);
+
+ Point TransformPoint(const Point &aPoint) const
+ {
+ Point retPoint;
+
+ retPoint.x = aPoint.x * _11 + aPoint.y * _21 + _31;
+ retPoint.y = aPoint.x * _12 + aPoint.y * _22 + _32;
+
+ return retPoint;
+ }
+
+ Size TransformSize(const Size &aSize) const
+ {
+ Size retSize;
+
+ retSize.width = aSize.width * _11 + aSize.height * _21;
+ retSize.height = aSize.width * _12 + aSize.height * _22;
+
+ return retSize;
+ }
+
+ GFX2D_API Rect TransformBounds(const Rect& rect) const;
+
+ 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;
+ _21 *= aY;
+ _22 *= aY;
+
+ return *this;
+ }
+
+ /**
+ * Similar to PostTranslate, but applies a scale instead of a translation.
+ */
+ Matrix &PostScale(Float aScaleX, Float aScaleY)
+ {
+ _11 *= aScaleX;
+ _12 *= aScaleY;
+ _21 *= aScaleX;
+ _22 *= aScaleY;
+ _31 *= aScaleX;
+ _32 *= aScaleY;
+
+ return *this;
+ }
+
+ GFX2D_API static Matrix Rotation(Float aAngle);
+
+ /**
+ * Similar to PreTranslate, but applies a rotation instead of a translation.
+ */
+ Matrix &PreRotate(Float aAngle)
+ {
+ return *this = Matrix::Rotation(aAngle) * *this;
+ }
+
+ bool Invert()
+ {
+ // Compute co-factors.
+ Float A = _22;
+ Float B = -_21;
+ Float C = _21 * _32 - _22 * _31;
+ Float D = -_12;
+ Float E = _11;
+ Float F = _31 * _12 - _11 * _32;
+
+ Float det = Determinant();
+
+ if (!det) {
+ return false;
+ }
+
+ Float inv_det = 1 / det;
+
+ _11 = inv_det * A;
+ _12 = inv_det * D;
+ _21 = inv_det * B;
+ _22 = inv_det * E;
+ _31 = inv_det * C;
+ _32 = inv_det * F;
+
+ 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;
+ }
+
+ Matrix operator*(const Matrix &aMatrix) const
+ {
+ Matrix resultMatrix;
+
+ resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21;
+ resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22;
+ resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21;
+ resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22;
+ resultMatrix._31 = this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + aMatrix._31;
+ resultMatrix._32 = this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + aMatrix._32;
+
+ 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!
+ */
+ bool operator==(const Matrix& other) const
+ {
+ return FuzzyEqual(_11, other._11) && FuzzyEqual(_12, other._12) &&
+ FuzzyEqual(_21, other._21) && FuzzyEqual(_22, other._22) &&
+ FuzzyEqual(_31, other._31) && FuzzyEqual(_32, other._32);
+ }
+
+ bool operator!=(const Matrix& other) const
+ {
+ return !(*this == other);
+ }
+
+ bool ExactlyEquals(const Matrix& o) const
+ {
+ return _11 == o._11 && _12 == o._12 &&
+ _21 == o._21 && _22 == o._22 &&
+ _31 == o._31 && _32 == o._32;
+ }
+
+ /* Verifies that the matrix contains no Infs or NaNs. */
+ bool IsFinite() const
+ {
+ return mozilla::IsFinite(_11) && mozilla::IsFinite(_12) &&
+ mozilla::IsFinite(_21) && mozilla::IsFinite(_22) &&
+ mozilla::IsFinite(_31) && mozilla::IsFinite(_32);
+ }
+
+ /* Returns true if the matrix is a rectilinear transformation (i.e.
+ * grid-aligned rectangles are transformed to grid-aligned rectangles)
+ */
+ bool IsRectilinear() const {
+ if (FuzzyEqual(_12, 0) && FuzzyEqual(_21, 0)) {
+ return true;
+ } else if (FuzzyEqual(_22, 0) && FuzzyEqual(_11, 0)) {
+ return true;
+ }
+
+ 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 + Float(0.5))) ||
+ !FuzzyEqual(_32, floor(_32 + Float(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
+ {
+ return _11 == 1.0f && _12 == 0.0f &&
+ _21 == 0.0f && _22 == 1.0f &&
+ _31 == 0.0f && _32 == 0.0f;
+ }
+
+ /* Returns true if the matrix is singular.
+ */
+ bool IsSingular() const
+ {
+ Float det = Determinant();
+ return !mozilla::IsFinite(det) || det == 0;
+ }
+
+ GFX2D_API Matrix &NudgeToIntegers();
+
+ 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
+ * rotation.
+ */
+ bool HasNonAxisAlignedTransform() const {
+ return !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
+ }
+
+ /**
+ * Returns true if the matrix has negative scaling (i.e. flip).
+ */
+ bool HasNegativeScaling() const {
+ return (_11 < 0.0) || (_22 < 0.0);
+ }
+};
+
+// Helper functions used by Matrix4x4Typed defined in Matrix.cpp
+double
+SafeTangent(double aTheta);
+double
+FlushToZero(double aVal);
+
+template<class Units, class F>
+Point4DTyped<Units, F>
+ComputePerspectivePlaneIntercept(const Point4DTyped<Units, F>& aFirst,
+ const Point4DTyped<Units, F>& aSecond)
+{
+ // This function will always return a point with a w value of 0.
+ // The X, Y, and Z components will point towards an infinite vanishing
+ // point.
+
+ // We want to interpolate aFirst and aSecond to find the point intersecting
+ // with the w=0 plane.
+
+ // Since we know what we want the w component to be, we can rearrange the
+ // interpolation equation and solve for t.
+ float t = -aFirst.w / (aSecond.w - aFirst.w);
+
+ // Use t to find the remainder of the components
+ return aFirst + (aSecond - aFirst) * t;
+}
+
+
+template <typename SourceUnits, typename TargetUnits>
+class Matrix4x4Typed
+{
+public:
+ typedef PointTyped<SourceUnits> SourcePoint;
+ typedef PointTyped<TargetUnits> TargetPoint;
+ typedef Point3DTyped<SourceUnits> SourcePoint3D;
+ typedef Point3DTyped<TargetUnits> TargetPoint3D;
+ typedef Point4DTyped<SourceUnits> SourcePoint4D;
+ typedef Point4DTyped<TargetUnits> TargetPoint4D;
+ typedef RectTyped<SourceUnits> SourceRect;
+ typedef RectTyped<TargetUnits> TargetRect;
+
+ Matrix4x4Typed()
+ : _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)
+ {}
+
+ Matrix4x4Typed(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)
+ {}
+
+ explicit Matrix4x4Typed(const Float aArray[16])
+ {
+ memcpy(components, aArray, sizeof(components));
+ }
+
+ Matrix4x4Typed(const Matrix4x4Typed& aOther)
+ {
+ memcpy(this, &aOther, sizeof(*this));
+ }
+
+ union {
+ struct {
+ Float _11, _12, _13, _14;
+ Float _21, _22, _23, _24;
+ Float _31, _32, _33, _34;
+ Float _41, _42, _43, _44;
+ };
+ Float components[16];
+ };
+
+ friend std::ostream& operator<<(std::ostream& aStream, const Matrix4x4Typed& aMatrix)
+ {
+ const Float *f = &aMatrix._11;
+ aStream << "[ " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ;" << std::endl; f += 4;
+ aStream << " " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ;" << std::endl; f += 4;
+ aStream << " " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ;" << std::endl; f += 4;
+ aStream << " " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ]" << std::endl;
+ return aStream;
+ }
+
+ 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.
+ */
+ bool Is2D() const
+ {
+ if (_13 != 0.0f || _14 != 0.0f ||
+ _23 != 0.0f || _24 != 0.0f ||
+ _31 != 0.0f || _32 != 0.0f || _33 != 1.0f || _34 != 0.0f ||
+ _43 != 0.0f || _44 != 1.0f) {
+ return false;
+ }
+ 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");
+
+ return Matrix(_11, _12, _21, _22, _41, _42);
+ }
+
+ 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;
+ }
+
+ Matrix4x4Typed& ProjectTo2D() {
+ _31 = 0.0f;
+ _32 = 0.0f;
+ _13 = 0.0f;
+ _23 = 0.0f;
+ _33 = 1.0f;
+ _43 = 0.0f;
+ _34 = 0.0f;
+ // Some matrices, such as those derived from perspective transforms,
+ // can modify _44 from 1, while leaving the rest of the fourth column
+ // (_14, _24) at 0. In this case, after resetting the third row and
+ // third column above, the value of _44 functions only to scale the
+ // coordinate transform divide by W. The matrix can be converted to
+ // a true 2D matrix by normalizing out the scaling effect of _44 on
+ // the remaining components ahead of time.
+ if (_14 == 0.0f && _24 == 0.0f &&
+ _44 != 1.0f && _44 != 0.0f) {
+ Float scale = 1.0f / _44;
+ _11 *= scale;
+ _12 *= scale;
+ _21 *= scale;
+ _22 *= scale;
+ _41 *= scale;
+ _42 *= scale;
+ _44 = 1.0f;
+ }
+ return *this;
+ }
+
+ template<class F>
+ Point4DTyped<TargetUnits, F>
+ ProjectPoint(const PointTyped<SourceUnits, F>& 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:
+ F z = -(aPoint.x * _13 + aPoint.y * _23 + _43) / _33;
+
+ // Compute the transformed point
+ return this->TransformPoint(Point4DTyped<SourceUnits, F>(aPoint.x, aPoint.y, z, 1));
+ }
+
+ template<class F>
+ RectTyped<TargetUnits, F>
+ ProjectRectBounds(const RectTyped<SourceUnits, F>& aRect, const RectTyped<TargetUnits, F>& aClip) const
+ {
+ // This function must never return std::numeric_limits<Float>::max() or any
+ // other arbitrary large value in place of inifinity. This often occurs when
+ // aRect is an inversed projection matrix or when aRect is transformed to be
+ // partly behind and in front of the camera (w=0 plane in homogenous
+ // coordinates) - See Bug 1035611
+
+ // Some call-sites will call RoundGfxRectToAppRect which clips both the
+ // extents and dimensions of the rect to be bounded by nscoord_MAX.
+ // If we return a Rect that, when converted to nscoords, has a width or height
+ // greater than nscoord_MAX, RoundGfxRectToAppRect will clip the overflow
+ // off both the min and max end of the rect after clipping the extents of the
+ // rect, resulting in a translation of the rect towards the infinite end.
+
+ // The bounds returned by ProjectRectBounds are expected to be clipped only on
+ // the edges beyond the bounds of the coordinate system; otherwise, the
+ // clipped bounding box would be smaller than the correct one and result
+ // bugs such as incorrect culling (eg. Bug 1073056)
+
+ // To address this without requiring all code to work in homogenous
+ // coordinates or interpret infinite values correctly, a specialized
+ // clipping function is integrated into ProjectRectBounds.
+
+ // Callers should pass an aClip value that represents the extents to clip
+ // the result to, in the same coordinate system as aRect.
+ Point4DTyped<TargetUnits, F> points[4];
+
+ points[0] = ProjectPoint(aRect.TopLeft());
+ points[1] = ProjectPoint(aRect.TopRight());
+ points[2] = ProjectPoint(aRect.BottomRight());
+ points[3] = ProjectPoint(aRect.BottomLeft());
+
+ F min_x = std::numeric_limits<F>::max();
+ F min_y = std::numeric_limits<F>::max();
+ F max_x = -std::numeric_limits<F>::max();
+ F max_y = -std::numeric_limits<F>::max();
+
+ for (int i=0; i<4; i++) {
+ // Only use points that exist above the w=0 plane
+ if (points[i].HasPositiveWCoord()) {
+ PointTyped<TargetUnits, F> point2d = aClip.ClampPoint(points[i].As2DPoint());
+ min_x = std::min<F>(point2d.x, min_x);
+ max_x = std::max<F>(point2d.x, max_x);
+ min_y = std::min<F>(point2d.y, min_y);
+ max_y = std::max<F>(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
+ // to find the point of intersection with the w=0 plane and use that
+ // instead.
+ Point4DTyped<TargetUnits, F> intercept =
+ ComputePerspectivePlaneIntercept(points[i], points[next]);
+ // Since intercept.w will always be 0 here, we interpret x,y,z as a
+ // direction towards an infinite vanishing point.
+ if (intercept.x < 0.0f) {
+ min_x = aClip.x;
+ } else if (intercept.x > 0.0f) {
+ max_x = aClip.XMost();
+ }
+ if (intercept.y < 0.0f) {
+ min_y = aClip.y;
+ } else if (intercept.y > 0.0f) {
+ max_y = aClip.YMost();
+ }
+ }
+ }
+
+ if (max_x < min_x || max_y < min_y) {
+ return RectTyped<TargetUnits, F>(0, 0, 0, 0);
+ }
+
+ return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x, max_y - min_y);
+ }
+
+ /**
+ * TransformAndClipBounds transforms aRect as a bounding box, while clipping
+ * the transformed bounds to the extents of aClip.
+ */
+ template<class F>
+ RectTyped<TargetUnits, F> TransformAndClipBounds(const RectTyped<SourceUnits, F>& aRect,
+ const RectTyped<TargetUnits, F>& aClip) const
+ {
+ PointTyped<UnknownUnits, F> verts[kTransformAndClipRectMaxVerts];
+ size_t vertCount = TransformAndClipRect(aRect, aClip, verts);
+
+ F min_x = std::numeric_limits<F>::max();
+ F min_y = std::numeric_limits<F>::max();
+ F max_x = -std::numeric_limits<F>::max();
+ F max_y = -std::numeric_limits<F>::max();
+ for (size_t i=0; i < vertCount; i++) {
+ min_x = std::min(min_x, verts[i].x);
+ max_x = std::max(max_x, verts[i].x);
+ min_y = std::min(min_y, verts[i].y);
+ max_y = std::max(max_y, verts[i].y);
+ }
+
+ if (max_x < min_x || max_y < min_y) {
+ return RectTyped<TargetUnits, F>(0, 0, 0, 0);
+ }
+
+ return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x, max_y - min_y);
+ }
+
+ template<class F>
+ RectTyped<TargetUnits, F> TransformAndClipBounds(const TriangleTyped<SourceUnits, F>& aTriangle,
+ const RectTyped<TargetUnits, F>& aClip) const
+ {
+ return TransformAndClipBounds(aTriangle.BoundingBox(), aClip);
+ }
+
+ /**
+ * TransformAndClipRect projects a rectangle and clips against view frustum
+ * clipping planes in homogenous space so that its projected vertices are
+ * constrained within the 2d rectangle passed in aClip.
+ * The resulting vertices are populated in aVerts. aVerts must be
+ * pre-allocated to hold at least kTransformAndClipRectMaxVerts Points.
+ * The vertex count is returned by TransformAndClipRect. It is possible to
+ * emit fewer that 3 vertices, indicating that aRect will not be visible
+ * within aClip.
+ */
+ template<class F>
+ size_t TransformAndClipRect(const RectTyped<SourceUnits, F>& aRect,
+ const RectTyped<TargetUnits, F>& aClip,
+ PointTyped<TargetUnits, F>* aVerts) const
+ {
+ // Initialize a double-buffered array of points in homogenous space with
+ // the input rectangle, aRect.
+ Point4DTyped<UnknownUnits, F> points[2][kTransformAndClipRectMaxVerts];
+ Point4DTyped<UnknownUnits, F>* dstPoint = points[0];
+
+ *dstPoint++ = TransformPoint(Point4DTyped<UnknownUnits, F>(aRect.x, aRect.y, 0, 1));
+ *dstPoint++ = TransformPoint(Point4DTyped<UnknownUnits, F>(aRect.XMost(), aRect.y, 0, 1));
+ *dstPoint++ = TransformPoint(Point4DTyped<UnknownUnits, F>(aRect.XMost(), aRect.YMost(), 0, 1));
+ *dstPoint++ = TransformPoint(Point4DTyped<UnknownUnits, F>(aRect.x, aRect.YMost(), 0, 1));
+
+ // View frustum clipping planes are described as normals originating from
+ // the 0,0,0,0 origin.
+ Point4DTyped<UnknownUnits, F> planeNormals[4];
+ planeNormals[0] = Point4DTyped<UnknownUnits, F>(1.0, 0.0, 0.0, -aClip.x);
+ planeNormals[1] = Point4DTyped<UnknownUnits, F>(-1.0, 0.0, 0.0, aClip.XMost());
+ planeNormals[2] = Point4DTyped<UnknownUnits, F>(0.0, 1.0, 0.0, -aClip.y);
+ planeNormals[3] = Point4DTyped<UnknownUnits, F>(0.0, -1.0, 0.0, aClip.YMost());
+
+ // Iterate through each clipping plane and clip the polygon.
+ // In each pass, we double buffer, alternating between points[0] and
+ // points[1].
+ for (int plane=0; plane < 4; plane++) {
+ planeNormals[plane].Normalize();
+ Point4DTyped<UnknownUnits, F>* srcPoint = points[plane & 1];
+ Point4DTyped<UnknownUnits, F>* srcPointEnd = dstPoint;
+
+ dstPoint = points[~plane & 1];
+ Point4DTyped<UnknownUnits, F>* dstPointStart = dstPoint;
+
+ Point4DTyped<UnknownUnits, F>* prevPoint = srcPointEnd - 1;
+ F prevDot = planeNormals[plane].DotProduct(*prevPoint);
+ while (srcPoint < srcPointEnd && ((dstPoint - dstPointStart) < kTransformAndClipRectMaxVerts)) {
+ F nextDot = planeNormals[plane].DotProduct(*srcPoint);
+
+ if ((nextDot >= 0.0) != (prevDot >= 0.0)) {
+ // An intersection with the clipping plane has been detected.
+ // Interpolate to find the intersecting point and emit it.
+ F t = -prevDot / (nextDot - prevDot);
+ *dstPoint++ = *srcPoint * t + *prevPoint * (1.0 - t);
+ }
+
+ if (nextDot >= 0.0) {
+ // Emit any source points that are on the positive side of the
+ // clipping plane.
+ *dstPoint++ = *srcPoint;
+ }
+
+ prevPoint = srcPoint++;
+ prevDot = nextDot;
+ }
+
+ if (dstPoint == dstPointStart) {
+ break;
+ }
+ }
+
+ size_t dstPointCount = 0;
+ size_t srcPointCount = dstPoint - points[0];
+ for (Point4DTyped<UnknownUnits, F>* srcPoint = points[0]; srcPoint < points[0] + srcPointCount; srcPoint++) {
+
+ PointTyped<TargetUnits, F> p;
+ if (srcPoint->w == 0.0) {
+ // If a point lies on the intersection of the clipping planes at
+ // (0,0,0,0), we must avoid a division by zero w component.
+ p = PointTyped<TargetUnits, F>(0.0, 0.0);
+ } else {
+ p = srcPoint->As2DPoint();
+ }
+ // Emit only unique points
+ if (dstPointCount == 0 || p != aVerts[dstPointCount - 1]) {
+ aVerts[dstPointCount++] = p;
+ }
+ }
+
+ return dstPointCount;
+ }
+
+ static const int kTransformAndClipRectMaxVerts = 32;
+
+ static Matrix4x4Typed From2D(const Matrix &aMatrix) {
+ Matrix4x4Typed 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();
+ }
+
+ TargetPoint4D TransposeTransform4D(const SourcePoint4D& 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 TargetPoint4D(x, y, z, w);
+ }
+
+ template<class F>
+ Point4DTyped<TargetUnits, F> TransformPoint(const Point4DTyped<SourceUnits, F>& aPoint) const
+ {
+ Point4DTyped<TargetUnits, F> retPoint;
+
+ retPoint.x = aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + aPoint.w * _41;
+ retPoint.y = aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + aPoint.w * _42;
+ retPoint.z = aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + aPoint.w * _43;
+ retPoint.w = aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + aPoint.w * _44;
+
+ return retPoint;
+ }
+
+ template<class F>
+ Point3DTyped<TargetUnits, F> TransformPoint(const Point3DTyped<SourceUnits, F>& aPoint) const
+ {
+ Point3DTyped<TargetUnits, F> result;
+ result.x = aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + _41;
+ result.y = aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + _42;
+ result.z = aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + _43;
+
+ result /= (aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + _44);
+
+ return result;
+ }
+
+ template<class F>
+ PointTyped<TargetUnits, F> TransformPoint(const PointTyped<SourceUnits, F> &aPoint) const
+ {
+ Point4DTyped<SourceUnits, F> temp(aPoint.x, aPoint.y, 0, 1);
+ return TransformPoint(temp).As2DPoint();
+ }
+
+ template<class F>
+ GFX2D_API RectTyped<TargetUnits, F> TransformBounds(const RectTyped<SourceUnits, F>& aRect) const
+ {
+ Point4DTyped<TargetUnits, F> verts[4];
+ verts[0] = TransformPoint(Point4DTyped<SourceUnits, F>(aRect.x, aRect.y, 0.0, 1.0));
+ verts[1] = TransformPoint(Point4DTyped<SourceUnits, F>(aRect.XMost(), aRect.y, 0.0, 1.0));
+ verts[2] = TransformPoint(Point4DTyped<SourceUnits, F>(aRect.XMost(), aRect.YMost(), 0.0, 1.0));
+ verts[3] = TransformPoint(Point4DTyped<SourceUnits, F>(aRect.x, aRect.YMost(), 0.0, 1.0));
+
+ PointTyped<TargetUnits, F> quad[4];
+ F min_x, max_x;
+ F min_y, max_y;
+
+ quad[0] = TransformPoint(aRect.TopLeft());
+ quad[1] = TransformPoint(aRect.TopRight());
+ quad[2] = TransformPoint(aRect.BottomLeft());
+ quad[3] = TransformPoint(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 RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x, max_y - min_y);
+ }
+
+ static Matrix4x4Typed Translation(Float aX, Float aY, Float aZ)
+ {
+ return Matrix4x4Typed(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 Matrix4x4Typed Translation(const TargetPoint3D& aP)
+ {
+ return Translation(aP.x, aP.y, aP.z);
+ }
+
+ static Matrix4x4Typed Translation(const TargetPoint& aP)
+ {
+ return Translation(aP.x, aP.y, 0);
+ }
+
+ /**
+ * 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.)
+ */
+ Matrix4x4Typed &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;
+ }
+
+ Matrix4x4Typed &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.
+ */
+ Matrix4x4Typed &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;
+ }
+
+ Matrix4x4Typed &PostTranslate(const TargetPoint3D& aPoint) {
+ return PostTranslate(aPoint.x, aPoint.y, aPoint.z);
+ }
+
+ Matrix4x4Typed &PostTranslate(const TargetPoint& aPoint) {
+ return PostTranslate(aPoint.x, aPoint.y, 0);
+ }
+
+ static Matrix4x4Typed Scaling(Float aScaleX, Float aScaleY, float aScaleZ)
+ {
+ return Matrix4x4Typed(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.
+ */
+ Matrix4x4Typed &PreScale(Float aX, Float aY, Float aZ)
+ {
+ _11 *= aX;
+ _12 *= aX;
+ _13 *= aX;
+ _14 *= aX;
+ _21 *= aY;
+ _22 *= aY;
+ _23 *= aY;
+ _24 *= aY;
+ _31 *= aZ;
+ _32 *= aZ;
+ _33 *= aZ;
+ _34 *= aZ;
+
+ return *this;
+ }
+
+ /**
+ * Similar to PostTranslate, but applies a scale instead of a translation.
+ */
+ Matrix4x4Typed &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;
+ }
+
+ Matrix4x4Typed &ChangeBasis(const Point3D& aOrigin)
+ {
+ return ChangeBasis(aOrigin.x, aOrigin.y, aOrigin.z);
+ }
+
+ Matrix4x4Typed &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;
+ }
+
+ Matrix4x4Typed& 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 Matrix4x4Typed& 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 Matrix4x4Typed& o) const
+ {
+ return !((*this) == o);
+ }
+
+ template <typename NewTargetUnits>
+ Matrix4x4Typed<SourceUnits, NewTargetUnits> operator*(const Matrix4x4Typed<TargetUnits, NewTargetUnits> &aMatrix) const
+ {
+ Matrix4x4Typed<SourceUnits, NewTargetUnits> 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;
+ }
+
+ Matrix4x4Typed& operator*=(const Matrix4x4Typed<TargetUnits, TargetUnits> &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;
+ }
+
+ // Invert() is not unit-correct. Prefer Inverse() where possible.
+ bool Invert()
+ {
+ Float det = Determinant();
+ if (!det) {
+ return false;
+ }
+
+ Matrix4x4Typed<SourceUnits, TargetUnits> 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;
+ }
+
+ Matrix4x4Typed<TargetUnits, SourceUnits> Inverse() const
+ {
+ typedef Matrix4x4Typed<TargetUnits, SourceUnits> InvertedMatrix;
+ InvertedMatrix clone = InvertedMatrix::FromUnknownMatrix(ToUnknownMatrix());
+ 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 Matrix4x4Typed& 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 FuzzyEqualsMultiplicative(const Matrix4x4Typed& o) const
+ {
+ return ::mozilla::FuzzyEqualsMultiplicative(_11, o._11) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_12, o._12) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_13, o._13) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_14, o._14) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_21, o._21) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_22, o._22) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_23, o._23) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_24, o._24) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_31, o._31) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_32, o._32) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_33, o._33) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_34, o._34) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_41, o._41) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_42, o._42) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_43, o._43) &&
+ ::mozilla::FuzzyEqualsMultiplicative(_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;
+ }
+
+ Matrix4x4Typed &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;
+ }
+
+ // Sets this matrix to a rotation matrix given by aQuat.
+ // This quaternion *MUST* be normalized!
+ // Implemented in Quaternion.cpp
+ void SetRotationFromQuaternion(const Quaternion& q)
+ {
+ const Float x2 = q.x + q.x, y2 = q.y + q.y, z2 = q.z + q.z;
+ const Float xx = q.x * x2, xy = q.x * y2, xz = q.x * z2;
+ const Float yy = q.y * y2, yz = q.y * z2, zz = q.z * z2;
+ const Float wx = q.w * x2, wy = q.w * y2, wz = q.w * z2;
+
+ _11 = 1.0f - (yy + zz);
+ _21 = xy + wz;
+ _31 = xz - wy;
+ _41 = 0.0f;
+
+ _12 = xy - wz;
+ _22 = 1.0f - (xx + zz);
+ _32 = yz + wx;
+ _42 = 0.0f;
+
+ _13 = xz + wy;
+ _23 = yz - wx;
+ _33 = 1.0f - (xx + yy);
+ _43 = 0.0f;
+
+ _14 = _42 = _43 = 0.0f;
+ _44 = 1.0f;
+ }
+
+ // Set all the members of the matrix to NaN
+ void 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>();
+ }
+
+ void SkewXY(double aXSkew, double aYSkew)
+ {
+ // XXX Is double precision really necessary here
+ float tanX = SafeTangent(aXSkew);
+ float tanY = SafeTangent(aYSkew);
+ float temp;
+
+ temp = _11;
+ _11 += tanY * _21;
+ _21 += tanX * temp;
+
+ temp = _12;
+ _12 += tanY * _22;
+ _22 += tanX * temp;
+
+ temp = _13;
+ _13 += tanY * _23;
+ _23 += tanX * temp;
+
+ temp = _14;
+ _14 += tanY * _24;
+ _24 += tanX * temp;
+ }
+
+ void RotateX(double aTheta)
+ {
+ // XXX Is double precision really necessary here
+ double cosTheta = FlushToZero(cos(aTheta));
+ double sinTheta = FlushToZero(sin(aTheta));
+
+ float temp;
+
+ temp = _21;
+ _21 = cosTheta * _21 + sinTheta * _31;
+ _31 = -sinTheta * temp + cosTheta * _31;
+
+ temp = _22;
+ _22 = cosTheta * _22 + sinTheta * _32;
+ _32 = -sinTheta * temp + cosTheta * _32;
+
+ temp = _23;
+ _23 = cosTheta * _23 + sinTheta * _33;
+ _33 = -sinTheta * temp + cosTheta * _33;
+
+ temp = _24;
+ _24 = cosTheta * _24 + sinTheta * _34;
+ _34 = -sinTheta * temp + cosTheta * _34;
+ }
+
+ void RotateY(double aTheta)
+ {
+ // XXX Is double precision really necessary here
+ double cosTheta = FlushToZero(cos(aTheta));
+ double sinTheta = FlushToZero(sin(aTheta));
+
+ float temp;
+
+ temp = _11;
+ _11 = cosTheta * _11 + -sinTheta * _31;
+ _31 = sinTheta * temp + cosTheta * _31;
+
+ temp = _12;
+ _12 = cosTheta * _12 + -sinTheta * _32;
+ _32 = sinTheta * temp + cosTheta * _32;
+
+ temp = _13;
+ _13 = cosTheta * _13 + -sinTheta * _33;
+ _33 = sinTheta * temp + cosTheta * _33;
+
+ temp = _14;
+ _14 = cosTheta * _14 + -sinTheta * _34;
+ _34 = sinTheta * temp + cosTheta * _34;
+ }
+
+ void RotateZ(double aTheta)
+ {
+ // XXX Is double precision really necessary here
+ double cosTheta = FlushToZero(cos(aTheta));
+ double sinTheta = FlushToZero(sin(aTheta));
+
+ float temp;
+
+ temp = _11;
+ _11 = cosTheta * _11 + sinTheta * _21;
+ _21 = -sinTheta * temp + cosTheta * _21;
+
+ temp = _12;
+ _12 = cosTheta * _12 + sinTheta * _22;
+ _22 = -sinTheta * temp + cosTheta * _22;
+
+ temp = _13;
+ _13 = cosTheta * _13 + sinTheta * _23;
+ _23 = -sinTheta * temp + cosTheta * _23;
+
+ temp = _14;
+ _14 = cosTheta * _14 + sinTheta * _24;
+ _24 = -sinTheta * temp + cosTheta * _24;
+ }
+
+ // Sets this matrix to a rotation matrix about a
+ // vector [x,y,z] by angle theta. The vector is normalized
+ // to a unit vector.
+ // https://www.w3.org/TR/css3-3d-transforms/#Rotate3dDefined
+ void SetRotateAxisAngle(double aX, double aY, double aZ, double aTheta)
+ {
+ Point3D vector(aX, aY, aZ);
+ if (!vector.Length()) {
+ return;
+ }
+ vector.Normalize();
+
+ double x = vector.x;
+ double y = vector.y;
+ double z = vector.z;
+
+ double cosTheta = FlushToZero(cos(aTheta));
+ double sinTheta = FlushToZero(sin(aTheta));
+
+ // sin(aTheta / 2) * cos(aTheta / 2)
+ double sc = sinTheta / 2;
+ // pow(sin(aTheta / 2), 2)
+ double sq = (1 - cosTheta) / 2;
+
+ _11 = 1 - 2 * (y * y + z * z) * sq;
+ _12 = 2 * (x * y * sq + z * sc);
+ _13 = 2 * (x * z * sq - y * sc);
+ _14 = 0.0f;
+ _21 = 2 * (x * y * sq - z * sc);
+ _22 = 1 - 2 * (x * x + z * z) * sq;
+ _23 = 2 * (y * z * sq + x * sc);
+ _24 = 0.0f;
+ _31 = 2 * (x * z * sq + y * sc);
+ _32 = 2 * (y * z * sq - x * sc);
+ _33 = 1 - 2 * (x * x + y * y) * sq;
+ _34 = 0.0f;
+ _41 = 0.0f;
+ _42 = 0.0f;
+ _43 = 0.0f;
+ _44 = 1.0f;
+ }
+
+ void Perspective(float aDepth)
+ {
+ MOZ_ASSERT(aDepth > 0.0f, "Perspective must be positive!");
+ _31 += -1.0/aDepth * _41;
+ _32 += -1.0/aDepth * _42;
+ _33 += -1.0/aDepth * _43;
+ _34 += -1.0/aDepth * _44;
+ }
+
+ Point3D GetNormalVector() const
+ {
+ // Define a plane in transformed space as the transformations
+ // of 3 points on the z=0 screen plane.
+ Point3D a = TransformPoint(Point3D(0, 0, 0));
+ Point3D b = TransformPoint(Point3D(0, 1, 0));
+ Point3D c = TransformPoint(Point3D(1, 0, 0));
+
+ // Convert to two vectors on the surface of the plane.
+ Point3D ab = b - a;
+ Point3D ac = c - a;
+
+ return ac.CrossProduct(ab);
+ }
+
+ /**
+ * Returns true if the matrix has any transform other
+ * than a straight translation.
+ */
+ bool HasNonTranslation() const {
+ return !gfx::FuzzyEqual(_11, 1.0) || !gfx::FuzzyEqual(_22, 1.0) ||
+ !gfx::FuzzyEqual(_12, 0.0) || !gfx::FuzzyEqual(_21, 0.0) ||
+ !gfx::FuzzyEqual(_13, 0.0) || !gfx::FuzzyEqual(_23, 0.0) ||
+ !gfx::FuzzyEqual(_31, 0.0) || !gfx::FuzzyEqual(_32, 0.0) ||
+ !gfx::FuzzyEqual(_33, 1.0);
+ }
+
+ /**
+ * Returns true if the matrix is anything other than a straight
+ * translation by integers.
+ */
+ bool HasNonIntegerTranslation() const {
+ return HasNonTranslation() ||
+ !gfx::FuzzyEqual(_41, floor(_41 + 0.5)) ||
+ !gfx::FuzzyEqual(_42, floor(_42 + 0.5)) ||
+ !gfx::FuzzyEqual(_43, floor(_43 + 0.5));
+ }
+
+ /**
+ * Return true if the matrix is with perspective (w).
+ */
+ bool HasPerspectiveComponent() const {
+ return _14 != 0 || _24 != 0 || _34 != 0 || _44 != 1;
+ }
+
+ /**
+ * Convert between typed and untyped matrices.
+ */
+ Matrix4x4 ToUnknownMatrix() const {
+ return Matrix4x4{_11, _12, _13, _14,
+ _21, _22, _23, _24,
+ _31, _32, _33, _34,
+ _41, _42, _43, _44};
+ }
+ static Matrix4x4Typed FromUnknownMatrix(const Matrix4x4& aUnknown) {
+ return Matrix4x4Typed{aUnknown._11, aUnknown._12, aUnknown._13, aUnknown._14,
+ aUnknown._21, aUnknown._22, aUnknown._23, aUnknown._24,
+ aUnknown._31, aUnknown._32, aUnknown._33, aUnknown._34,
+ aUnknown._41, aUnknown._42, aUnknown._43, aUnknown._44};
+ }
+};
+
+typedef Matrix4x4Typed<UnknownUnits, UnknownUnits> Matrix4x4;
+
+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;
+ }
+
+ union {
+ struct {
+ 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;
+ };
+ Float components[20];
+ };
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_MATRIX_H_ */
diff --git a/gfx/2d/MatrixFwd.h b/gfx/2d/MatrixFwd.h
new file mode 100644
index 000000000..ec62368b3
--- /dev/null
+++ b/gfx/2d/MatrixFwd.h
@@ -0,0 +1,26 @@
+/* -*- 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_MATRIX_FWD_H_
+#define MOZILLA_GFX_MATRIX_FWD_H_
+
+
+// Forward declare enough things to define the typedef |Matrix4x4|.
+
+namespace mozilla {
+namespace gfx {
+
+struct UnknownUnits;
+
+template<class SourceUnits, class TargetUnits>
+class Matrix4x4Typed;
+
+typedef Matrix4x4Typed<UnknownUnits, UnknownUnits> Matrix4x4;
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/gfx/2d/NativeFontResourceDWrite.cpp b/gfx/2d/NativeFontResourceDWrite.cpp
new file mode 100644
index 000000000..e4d12ad87
--- /dev/null
+++ b/gfx/2d/NativeFontResourceDWrite.cpp
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "NativeFontResourceDWrite.h"
+
+#include <unordered_map>
+
+#include "DrawTargetD2D1.h"
+#include "Logging.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace gfx {
+
+static Atomic<uint64_t> sNextFontFileKey;
+static std::unordered_map<uint64_t, IDWriteFontFileStream*> sFontFileStreams;
+
+class DWriteFontFileLoader : public IDWriteFontFileLoader
+{
+public:
+ DWriteFontFileLoader()
+ {
+ }
+
+ // IUnknown interface
+ IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
+ {
+ if (iid == __uuidof(IDWriteFontFileLoader)) {
+ *ppObject = static_cast<IDWriteFontFileLoader*>(this);
+ return S_OK;
+ } else if (iid == __uuidof(IUnknown)) {
+ *ppObject = static_cast<IUnknown*>(this);
+ return S_OK;
+ } else {
+ return E_NOINTERFACE;
+ }
+ }
+
+ IFACEMETHOD_(ULONG, AddRef)()
+ {
+ return 1;
+ }
+
+ IFACEMETHOD_(ULONG, Release)()
+ {
+ return 1;
+ }
+
+ // IDWriteFontFileLoader methods
+ /**
+ * Important! Note the key here has to be a uint64_t that will have been
+ * generated by incrementing sNextFontFileKey.
+ */
+ virtual HRESULT STDMETHODCALLTYPE
+ CreateStreamFromKey(void const* fontFileReferenceKey,
+ UINT32 fontFileReferenceKeySize,
+ OUT IDWriteFontFileStream** fontFileStream);
+
+ /**
+ * Gets the singleton loader instance. Note that when using this font
+ * loader, the key must be a uint64_t that has been generated by incrementing
+ * sNextFontFileKey.
+ * Also note that this is _not_ threadsafe.
+ */
+ static IDWriteFontFileLoader* Instance()
+ {
+ if (!mInstance) {
+ mInstance = new DWriteFontFileLoader();
+ DrawTargetD2D1::GetDWriteFactory()->
+ RegisterFontFileLoader(mInstance);
+ }
+ return mInstance;
+ }
+
+private:
+ static IDWriteFontFileLoader* mInstance;
+};
+
+class DWriteFontFileStream : public IDWriteFontFileStream
+{
+public:
+ /**
+ * Used by the FontFileLoader to create a new font stream,
+ * this font stream is created from data in memory. The memory
+ * passed may be released after object creation, it will be
+ * copied internally.
+ *
+ * @param aData Font data
+ */
+ DWriteFontFileStream(uint8_t *aData, uint32_t aSize, uint64_t aFontFileKey);
+ ~DWriteFontFileStream();
+
+ // IUnknown interface
+ IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
+ {
+ if (iid == __uuidof(IDWriteFontFileStream)) {
+ *ppObject = static_cast<IDWriteFontFileStream*>(this);
+ return S_OK;
+ } else if (iid == __uuidof(IUnknown)) {
+ *ppObject = static_cast<IUnknown*>(this);
+ return S_OK;
+ } else {
+ return E_NOINTERFACE;
+ }
+ }
+
+ IFACEMETHOD_(ULONG, AddRef)()
+ {
+ ++mRefCnt;
+ return mRefCnt;
+ }
+
+ IFACEMETHOD_(ULONG, Release)()
+ {
+ --mRefCnt;
+ if (mRefCnt == 0) {
+ delete this;
+ return 0;
+ }
+ return mRefCnt;
+ }
+
+ // IDWriteFontFileStream methods
+ virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart,
+ UINT64 fileOffset,
+ UINT64 fragmentSize,
+ OUT void** fragmentContext);
+
+ virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext);
+
+ virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize);
+
+ virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime);
+
+private:
+ std::vector<uint8_t> mData;
+ uint32_t mRefCnt;
+ uint64_t mFontFileKey;
+};
+
+IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr;
+
+HRESULT STDMETHODCALLTYPE
+DWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey,
+ UINT32 fontFileReferenceKeySize,
+ IDWriteFontFileStream **fontFileStream)
+{
+ if (!fontFileReferenceKey || !fontFileStream) {
+ return E_POINTER;
+ }
+
+ uint64_t fontFileKey = *static_cast<const uint64_t*>(fontFileReferenceKey);
+ auto found = sFontFileStreams.find(fontFileKey);
+ if (found == sFontFileStreams.end()) {
+ *fontFileStream = nullptr;
+ return E_FAIL;
+ }
+
+ found->second->AddRef();
+ *fontFileStream = found->second;
+ return S_OK;
+}
+
+DWriteFontFileStream::DWriteFontFileStream(uint8_t *aData, uint32_t aSize,
+ uint64_t aFontFileKey)
+ : mRefCnt(0)
+ , mFontFileKey(aFontFileKey)
+{
+ mData.resize(aSize);
+ memcpy(&mData.front(), aData, aSize);
+}
+
+DWriteFontFileStream::~DWriteFontFileStream()
+{
+ sFontFileStreams.erase(mFontFileKey);
+}
+
+HRESULT STDMETHODCALLTYPE
+DWriteFontFileStream::GetFileSize(UINT64 *fileSize)
+{
+ *fileSize = mData.size();
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+DWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DWriteFontFileStream::ReadFileFragment(const void **fragmentStart,
+ UINT64 fileOffset,
+ UINT64 fragmentSize,
+ void **fragmentContext)
+{
+ // We are required to do bounds checking.
+ if (fileOffset + fragmentSize > mData.size()) {
+ return E_FAIL;
+ }
+
+ // truncate the 64 bit fileOffset to size_t sized index into mData
+ size_t index = static_cast<size_t>(fileOffset);
+
+ // We should be alive for the duration of this.
+ *fragmentStart = &mData[index];
+ *fragmentContext = nullptr;
+ return S_OK;
+}
+
+void STDMETHODCALLTYPE
+DWriteFontFileStream::ReleaseFileFragment(void *fragmentContext)
+{
+}
+
+/* static */
+already_AddRefed<NativeFontResourceDWrite>
+NativeFontResourceDWrite::Create(uint8_t *aFontData, uint32_t aDataLength,
+ bool aNeedsCairo)
+{
+ IDWriteFactory *factory = DrawTargetD2D1::GetDWriteFactory();
+ if (!factory) {
+ gfxWarning() << "Failed to get DWrite Factory.";
+ return nullptr;
+ }
+
+ uint64_t fontFileKey = sNextFontFileKey++;
+ RefPtr<IDWriteFontFileStream> ffsRef =
+ new DWriteFontFileStream(aFontData, aDataLength, fontFileKey);
+ sFontFileStreams[fontFileKey] = ffsRef;
+
+ RefPtr<IDWriteFontFile> fontFile;
+ HRESULT hr =
+ factory->CreateCustomFontFileReference(&fontFileKey, sizeof(fontFileKey),
+ DWriteFontFileLoader::Instance(),
+ getter_AddRefs(fontFile));
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to load font file from data!";
+ return nullptr;
+ }
+
+ BOOL isSupported;
+ DWRITE_FONT_FILE_TYPE fileType;
+ DWRITE_FONT_FACE_TYPE faceType;
+ UINT32 numberOfFaces;
+ hr = fontFile->Analyze(&isSupported, &fileType, &faceType, &numberOfFaces);
+ if (FAILED(hr) || !isSupported) {
+ gfxWarning() << "Font file is not supported.";
+ return nullptr;
+ }
+
+ RefPtr<NativeFontResourceDWrite> fontResource =
+ new NativeFontResourceDWrite(factory, fontFile.forget(), faceType,
+ numberOfFaces, aNeedsCairo);
+ return fontResource.forget();
+}
+
+already_AddRefed<ScaledFont>
+NativeFontResourceDWrite::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
+ const uint8_t* aInstanceData, uint32_t aInstanceDataLength)
+{
+ if (aIndex >= mNumberOfFaces) {
+ gfxWarning() << "Font face index is too high for font resource.";
+ return nullptr;
+ }
+
+ IDWriteFontFile *fontFile = mFontFile;
+ RefPtr<IDWriteFontFace> fontFace;
+ if (FAILED(mFactory->CreateFontFace(mFaceType, 1, &fontFile, aIndex,
+ DWRITE_FONT_SIMULATIONS_NONE, getter_AddRefs(fontFace)))) {
+ gfxWarning() << "Failed to create font face from font file data.";
+ return nullptr;
+ }
+
+ RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(fontFace, aGlyphSize);
+ if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
+ gfxWarning() << "Unable to create cairo scaled font DWrite font.";
+ return nullptr;
+ }
+
+ return scaledFont.forget();
+}
+
+} // gfx
+} // mozilla
diff --git a/gfx/2d/NativeFontResourceDWrite.h b/gfx/2d/NativeFontResourceDWrite.h
new file mode 100644
index 000000000..fc11c7b0d
--- /dev/null
+++ b/gfx/2d/NativeFontResourceDWrite.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_NativeFontResourceDWrite_h
+#define mozilla_gfx_NativeFontResourceDWrite_h
+
+#include <dwrite.h>
+
+#include "2D.h"
+#include "mozilla/AlreadyAddRefed.h"
+
+namespace mozilla {
+namespace gfx {
+
+class NativeFontResourceDWrite final : public NativeFontResource
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResourceDWrite)
+ /**
+ * Creates a NativeFontResourceDWrite if data is valid. Note aFontData will be
+ * copied if required and so can be released after calling.
+ *
+ * @param aFontData the SFNT data.
+ * @param aDataLength length of data.
+ * @param aNeedsCairo whether the ScaledFont created needs a cairo scaled font
+ * @return Referenced NativeFontResourceDWrite or nullptr if invalid.
+ */
+ static already_AddRefed<NativeFontResourceDWrite>
+ Create(uint8_t *aFontData, uint32_t aDataLength, bool aNeedsCairo);
+
+ already_AddRefed<ScaledFont>
+ CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
+ const uint8_t* aInstanceData, uint32_t aInstanceDataLength) final;
+
+private:
+ NativeFontResourceDWrite(IDWriteFactory *aFactory,
+ already_AddRefed<IDWriteFontFile> aFontFile,
+ DWRITE_FONT_FACE_TYPE aFaceType,
+ uint32_t aNumberOfFaces, bool aNeedsCairo)
+ : mFactory(aFactory), mFontFile(aFontFile), mFaceType(aFaceType)
+ , mNumberOfFaces(aNumberOfFaces), mNeedsCairo(aNeedsCairo)
+ {}
+
+ IDWriteFactory *mFactory;
+ RefPtr<IDWriteFontFile> mFontFile;
+ DWRITE_FONT_FACE_TYPE mFaceType;
+ uint32_t mNumberOfFaces;
+ bool mNeedsCairo;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_NativeFontResourceDWrite_h
diff --git a/gfx/2d/NativeFontResourceGDI.cpp b/gfx/2d/NativeFontResourceGDI.cpp
new file mode 100644
index 000000000..f51e75179
--- /dev/null
+++ b/gfx/2d/NativeFontResourceGDI.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "NativeFontResourceGDI.h"
+
+#include "Logging.h"
+#include "mozilla/RefPtr.h"
+#include "ScaledFontWin.h"
+
+namespace mozilla {
+namespace gfx {
+
+/* static */
+already_AddRefed<NativeFontResourceGDI>
+NativeFontResourceGDI::Create(uint8_t *aFontData, uint32_t aDataLength,
+ bool aNeedsCairo)
+{
+ DWORD numberOfFontsAdded;
+ HANDLE fontResourceHandle = ::AddFontMemResourceEx(aFontData, aDataLength,
+ 0, &numberOfFontsAdded);
+ if (!fontResourceHandle) {
+ gfxWarning() << "Failed to add memory font resource.";
+ return nullptr;
+ }
+
+ RefPtr<NativeFontResourceGDI> fontResouce =
+ new NativeFontResourceGDI(fontResourceHandle, aNeedsCairo);
+
+ return fontResouce.forget();
+}
+
+NativeFontResourceGDI::~NativeFontResourceGDI()
+{
+ ::RemoveFontMemResourceEx(mFontResourceHandle);
+}
+
+already_AddRefed<ScaledFont>
+NativeFontResourceGDI::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
+ const uint8_t* aInstanceData, uint32_t aInstanceDataLength)
+{
+ if (aInstanceDataLength < sizeof(LOGFONT)) {
+ gfxWarning() << "GDI scaled font instance data is truncated.";
+ return nullptr;
+ }
+
+ const LOGFONT* logFont = reinterpret_cast<const LOGFONT*>(aInstanceData);
+
+ // Constructor for ScaledFontWin dereferences and copies the LOGFONT, so we
+ // are safe to pass this reference.
+ RefPtr<ScaledFontBase> scaledFont = new ScaledFontWin(logFont, aGlyphSize);
+ if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
+ gfxWarning() << "Unable to create cairo scaled font GDI font.";
+ return nullptr;
+ }
+
+ return scaledFont.forget();
+}
+
+} // gfx
+} // mozilla
diff --git a/gfx/2d/NativeFontResourceGDI.h b/gfx/2d/NativeFontResourceGDI.h
new file mode 100644
index 000000000..8a68b13e5
--- /dev/null
+++ b/gfx/2d/NativeFontResourceGDI.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_NativeFontResourceGDI_h
+#define mozilla_gfx_NativeFontResourceGDI_h
+
+#include <windows.h>
+
+#include "2D.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Vector.h"
+
+namespace mozilla {
+namespace gfx {
+
+class NativeFontResourceGDI final : public NativeFontResource
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResourceGDI)
+ /**
+ * Creates a NativeFontResourceGDI if data is valid. Note aFontData will be
+ * copied if required and so can be released after calling.
+ *
+ * @param aFontData the SFNT data.
+ * @param aDataLength length of data.
+ * @param aNeedsCairo whether the ScaledFont created need a cairo scaled font
+ * @return Referenced NativeFontResourceGDI or nullptr if invalid.
+ */
+ static already_AddRefed<NativeFontResourceGDI>
+ Create(uint8_t *aFontData, uint32_t aDataLength, bool aNeedsCairo);
+
+ ~NativeFontResourceGDI();
+
+ already_AddRefed<ScaledFont>
+ CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
+ const uint8_t* aInstanceData, uint32_t aInstanceDataLength) final;
+
+private:
+ NativeFontResourceGDI(HANDLE aFontResourceHandle,
+ bool aNeedsCairo)
+ : mFontResourceHandle(aFontResourceHandle)
+ , mNeedsCairo(aNeedsCairo)
+ {}
+
+ HANDLE mFontResourceHandle;
+ bool mNeedsCairo;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_NativeFontResourceGDI_h
diff --git a/gfx/2d/NativeFontResourceMac.cpp b/gfx/2d/NativeFontResourceMac.cpp
new file mode 100644
index 000000000..aaf6db181
--- /dev/null
+++ b/gfx/2d/NativeFontResourceMac.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "NativeFontResourceMac.h"
+#include "Types.h"
+
+#include "mozilla/RefPtr.h"
+
+#ifdef MOZ_WIDGET_UIKIT
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+/* static */
+already_AddRefed<NativeFontResourceMac>
+NativeFontResourceMac::Create(uint8_t *aFontData, uint32_t aDataLength)
+{
+ // copy font data
+ CFDataRef data = CFDataCreate(kCFAllocatorDefault, aFontData, aDataLength);
+ if (!data) {
+ return nullptr;
+ }
+
+ // create a provider
+ CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
+
+ // release our reference to the CFData, provider keeps it alive
+ CFRelease(data);
+
+ // create the font object
+ CGFontRef fontRef = CGFontCreateWithDataProvider(provider);
+
+ // release our reference, font will keep it alive as long as needed
+ CGDataProviderRelease(provider);
+
+ if (!fontRef) {
+ return nullptr;
+ }
+
+ // passes ownership of fontRef to the NativeFontResourceMac instance
+ RefPtr<NativeFontResourceMac> fontResource =
+ new NativeFontResourceMac(fontRef);
+
+ return fontResource.forget();
+}
+
+already_AddRefed<ScaledFont>
+NativeFontResourceMac::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
+ const uint8_t* aInstanceData, uint32_t aInstanceDataLength)
+{
+ RefPtr<ScaledFontBase> scaledFont = new ScaledFontMac(mFontRef, aGlyphSize);
+
+ if (!scaledFont->PopulateCairoScaledFont()) {
+ gfxWarning() << "Unable to create cairo scaled Mac font.";
+ return nullptr;
+ }
+
+ return scaledFont.forget();
+}
+
+} // gfx
+} // mozilla
diff --git a/gfx/2d/NativeFontResourceMac.h b/gfx/2d/NativeFontResourceMac.h
new file mode 100644
index 000000000..47ca92e68
--- /dev/null
+++ b/gfx/2d/NativeFontResourceMac.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_NativeFontResourceMac_h
+#define mozilla_gfx_NativeFontResourceMac_h
+
+#include "2D.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "ScaledFontMac.h"
+
+namespace mozilla {
+namespace gfx {
+
+class NativeFontResourceMac final : public NativeFontResource
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResourceMac)
+
+ static already_AddRefed<NativeFontResourceMac>
+ Create(uint8_t *aFontData, uint32_t aDataLength);
+
+ already_AddRefed<ScaledFont>
+ CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
+ const uint8_t* aInstanceData, uint32_t aInstanceDataLength) final;
+
+ ~NativeFontResourceMac()
+ {
+ CFRelease(mFontRef);
+ }
+
+private:
+ explicit NativeFontResourceMac(CGFontRef aFontRef) : mFontRef(aFontRef) {}
+
+ CGFontRef mFontRef;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_NativeFontResourceMac_h
diff --git a/gfx/2d/NumericTools.h b/gfx/2d/NumericTools.h
new file mode 100644
index 000000000..03aa7a8e2
--- /dev/null
+++ b/gfx/2d/NumericTools.h
@@ -0,0 +1,43 @@
+/* -*- 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_NUMERICTOOLS_H_
+#define MOZILLA_GFX_NUMERICTOOLS_H_
+
+namespace mozilla {
+
+// XXX - Move these into mfbt/MathAlgorithms.h?
+
+// Returns the largest multiple of aMultiplied that's <= x.
+// Same as int32_t(floor(double(x) / aMultiplier)) * aMultiplier,
+// but faster.
+inline int32_t
+RoundDownToMultiple(int32_t x, int32_t aMultiplier)
+{
+ // We don't use float division + floor because that's hard for the compiler
+ // to optimize.
+ int mod = x % aMultiplier;
+ if (x > 0) {
+ return x - mod;
+ }
+ return mod ? x - aMultiplier - mod : x;
+}
+
+// Returns the smallest multiple of aMultiplied that's >= x.
+// Same as int32_t(ceil(double(x) / aMultiplier)) * aMultiplier,
+// but faster.
+inline int32_t
+RoundUpToMultiple(int32_t x, int32_t aMultiplier)
+{
+ int mod = x % aMultiplier;
+ if (x > 0) {
+ return mod ? x + aMultiplier - mod : x;
+ }
+ return x - mod;
+}
+
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_NUMERICTOOLS_H_ */
diff --git a/gfx/2d/Path.cpp b/gfx/2d/Path.cpp
new file mode 100644
index 000000000..f863e12ec
--- /dev/null
+++ b/gfx/2d/Path.cpp
@@ -0,0 +1,550 @@
+/* -*- 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 double CubicRoot(double aValue) {
+ if (aValue < 0.0) {
+ return -CubicRoot(-aValue);
+ }
+ else {
+ return pow(aValue, 1.0 / 3.0);
+ }
+}
+
+struct PointD : public BasePoint<double, PointD> {
+ typedef BasePoint<double, PointD> Super;
+
+ PointD() : Super() {}
+ PointD(double aX, double aY) : Super(aX, aY) {}
+ MOZ_IMPLICIT PointD(const Point& aPoint) : Super(aPoint.x, aPoint.y) {}
+
+ Point ToPoint() const {
+ return Point(static_cast<Float>(x), static_cast<Float>(y));
+ }
+};
+
+struct BezierControlPoints
+{
+ BezierControlPoints() {}
+ BezierControlPoints(const PointD &aCP1, const PointD &aCP2,
+ const PointD &aCP3, const PointD &aCP4)
+ : mCP1(aCP1), mCP2(aCP2), mCP3(aCP3), mCP4(aCP4)
+ {
+ }
+
+ PointD mCP1, mCP2, mCP3, mCP4;
+};
+
+void
+FlattenBezier(const BezierControlPoints &aPoints,
+ PathSink *aSink, double 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,
+ double t)
+{
+ MOZ_ASSERT(aSecondSegmentControlPoints);
+
+ *aSecondSegmentControlPoints = aControlPoints;
+
+ PointD cp1a = aControlPoints.mCP1 + (aControlPoints.mCP2 - aControlPoints.mCP1) * t;
+ PointD cp2a = aControlPoints.mCP2 + (aControlPoints.mCP3 - aControlPoints.mCP2) * t;
+ PointD cp1aa = cp1a + (cp2a - cp1a) * t;
+ PointD cp3a = aControlPoints.mCP3 + (aControlPoints.mCP4 - aControlPoints.mCP3) * t;
+ PointD cp2aa = cp2a + (cp3a - cp2a) * t;
+ PointD 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,
+ double 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;
+
+ double t = 0;
+ while (t < 1.0) {
+ PointD cp21 = currentCP.mCP2 - currentCP.mCP1;
+ PointD cp31 = currentCP.mCP3 - currentCP.mCP1;
+
+ /* To remove divisions and check for divide-by-zero, this is optimized from:
+ * Float s3 = (cp31.x * cp21.y - cp31.y * cp21.x) / hypotf(cp21.x, cp21.y);
+ * t = 2 * Float(sqrt(aTolerance / (3. * std::abs(s3))));
+ */
+ double cp21x31 = cp31.x * cp21.y - cp31.y * cp21.x;
+ double h = hypot(cp21.x, cp21.y);
+ if (cp21x31 * h == 0) {
+ break;
+ }
+
+ double s3inv = h / cp21x31;
+ t = 2 * sqrt(aTolerance * std::abs(s3inv) / 3.);
+ if (t >= 1.0) {
+ break;
+ }
+
+ SplitBezier(currentCP, nullptr, &currentCP, t);
+
+ aSink->LineTo(currentCP.mCP1.ToPoint());
+ }
+
+ aSink->LineTo(currentCP.mCP4.ToPoint());
+}
+
+static inline void
+FindInflectionApproximationRange(BezierControlPoints aControlPoints,
+ double *aMin, double *aMax, double aT,
+ double aTolerance)
+{
+ SplitBezier(aControlPoints, nullptr, &aControlPoints, aT);
+
+ PointD cp21 = aControlPoints.mCP2 - aControlPoints.mCP1;
+ PointD cp41 = aControlPoints.mCP4 - aControlPoints.mCP1;
+
+ if (cp21.x == 0. && cp21.y == 0.) {
+ // 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;
+ }
+
+ double s3 = (cp41.x * cp21.y - cp41.y * cp21.x) / hypot(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.0;
+ *aMax = 2.0;
+ return;
+ }
+
+ double 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,
+ double *aT1, double *aT2, uint32_t *aCount)
+{
+ // Find inflection points.
+ // See www.faculty.idc.ac.il/arik/quality/appendixa.html for an explanation
+ // of this approach.
+ PointD A = aControlPoints.mCP2 - aControlPoints.mCP1;
+ PointD B = aControlPoints.mCP3 - (aControlPoints.mCP2 * 2) + aControlPoints.mCP1;
+ PointD C = aControlPoints.mCP4 - (aControlPoints.mCP3 * 3) + (aControlPoints.mCP2 * 3) - aControlPoints.mCP1;
+
+ double a = B.x * C.y - B.y * C.x;
+ double b = A.x * C.y - A.y * C.x;
+ double c = A.x * B.y - 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 {
+ double 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
+ */
+ double q = sqrt(discriminant);
+ if (b < 0) {
+ q = b - q;
+ } else {
+ q = b + q;
+ }
+ q *= -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, double aTolerance)
+{
+ double t1;
+ double 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.0 || t1 >= 1.0) && (count == 1 || (t2 < 0.0 || t2 >= 1.0))) ) {
+ FlattenBezierCurveSegment(aControlPoints, aSink, aTolerance);
+ return;
+ }
+
+ double 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.ToPoint());
+ 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.ToPoint());
+
+ 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.ToPoint());
+ 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.ToPoint());
+ } 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.
+ double 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.ToPoint());
+ FlattenBezierCurveSegment(nextCPs, aSink, aTolerance);
+ } else {
+ // Our approximation range extends beyond the end of the curve.
+ aSink->LineTo(aControlPoints.mCP4.ToPoint());
+ return;
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/PathAnalysis.h b/gfx/2d/PathAnalysis.h
new file mode 100644
index 000000000..5a38ed161
--- /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;
+};
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/PathCG.cpp b/gfx/2d/PathCG.cpp
new file mode 100644
index 000000000..4d70b2607
--- /dev/null
+++ b/gfx/2d/PathCG.cpp
@@ -0,0 +1,435 @@
+/* -*- 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 "PathCG.h"
+#include <math.h>
+#include "Logging.h"
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+static inline Rect
+CGRectToRect(CGRect rect)
+{
+ return Rect(rect.origin.x,
+ rect.origin.y,
+ rect.size.width,
+ 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 CapStyle::BUTT:
+ CGContextSetLineCap(cg, kCGLineCapButt);
+ break;
+ case CapStyle::ROUND:
+ CGContextSetLineCap(cg, kCGLineCapRound);
+ break;
+ case CapStyle::SQUARE:
+ CGContextSetLineCap(cg, kCGLineCapSquare);
+ break;
+ }
+
+ switch (aStrokeOptions.mLineJoin)
+ {
+ case JoinStyle::BEVEL:
+ CGContextSetLineJoin(cg, kCGLineJoinBevel);
+ break;
+ case JoinStyle::ROUND:
+ CGContextSetLineJoin(cg, kCGLineJoinRound);
+ break;
+ case JoinStyle::MITER:
+ case JoinStyle::MITER_OR_BEVEL:
+ CGContextSetLineJoin(cg, kCGLineJoinMiter);
+ break;
+ }
+
+ CGContextSetLineWidth(cg, aStrokeOptions.mLineWidth);
+ CGContextSetMiterLimit(cg, aStrokeOptions.mMiterLimit);
+
+ // XXX: rename mDashLength to dashLength
+ if (aStrokeOptions.mDashLength > 0) {
+ // we use a regular array instead of a std::vector here because we don't want to leak the <vector> include
+ CGFloat *dashes = new CGFloat[aStrokeOptions.mDashLength];
+ for (size_t i=0; i<aStrokeOptions.mDashLength; i++) {
+ dashes[i] = aStrokeOptions.mDashPattern[i];
+ }
+ CGContextSetLineDash(cg, aStrokeOptions.mDashOffset, dashes, aStrokeOptions.mDashLength);
+ delete[] dashes;
+ }
+}
+
+static inline CGAffineTransform
+GfxMatrixToCGAffineTransform(const Matrix &m)
+{
+ CGAffineTransform t;
+ t.a = m._11;
+ t.b = m._12;
+ t.c = m._21;
+ t.d = m._22;
+ t.tx = m._31;
+ t.ty = m._32;
+ return t;
+}
+
+PathBuilderCG::~PathBuilderCG()
+{
+ CGPathRelease(mCGPath);
+}
+
+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
+ CGPathAddLineToPoint(mCGPath, nullptr, aPoint.x, aPoint.y);
+}
+
+void
+PathBuilderCG::BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3)
+{
+ if (!aCP1.IsFinite() || !aCP2.IsFinite() || !aCP3.IsFinite()) {
+ return;
+ }
+
+ if (CGPathIsEmpty(mCGPath))
+ MoveTo(aCP1);
+ CGPathAddCurveToPoint(mCGPath, nullptr,
+ aCP1.x, aCP1.y,
+ aCP2.x, aCP2.y,
+ aCP3.x, aCP3.y);
+
+}
+
+void
+PathBuilderCG::QuadraticBezierTo(const Point &aCP1,
+ 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);
+}
+
+void
+PathBuilderCG::Close()
+{
+ if (!CGPathIsEmpty(mCGPath))
+ CGPathCloseSubpath(mCGPath);
+}
+
+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
+{
+ Point ret;
+ if (!CGPathIsEmpty(mCGPath)) {
+ CGPoint pt = CGPathGetCurrentPoint(mCGPath);
+ ret.MoveTo(pt.x, pt.y);
+ }
+ return ret;
+}
+
+void
+PathBuilderCG::EnsureActive(const Point &aPoint)
+{
+}
+
+already_AddRefed<Path>
+PathBuilderCG::Finish()
+{
+ return MakeAndAddRef<PathCG>(mCGPath, mFillRule);
+}
+
+already_AddRefed<PathBuilder>
+PathCG::CopyToBuilder(FillRule aFillRule) const
+{
+ CGMutablePathRef path = CGPathCreateMutableCopy(mPath);
+ return MakeAndAddRef<PathBuilderCG>(path, aFillRule);
+}
+
+
+
+already_AddRefed<PathBuilder>
+PathCG::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
+{
+ // 10.7 adds CGPathCreateMutableCopyByTransformingPath it might be faster than doing
+ // this by hand
+
+ struct TransformApplier {
+ CGMutablePathRef path;
+ CGAffineTransform transform;
+ static void
+ TranformCGPathApplierFunc(void *vinfo, const CGPathElement *element)
+ {
+ TransformApplier *info = reinterpret_cast<TransformApplier*>(vinfo);
+ switch (element->type) {
+ case kCGPathElementMoveToPoint:
+ {
+ CGPoint pt = element->points[0];
+ CGPathMoveToPoint(info->path, &info->transform, pt.x, pt.y);
+ break;
+ }
+ case kCGPathElementAddLineToPoint:
+ {
+ CGPoint pt = element->points[0];
+ CGPathAddLineToPoint(info->path, &info->transform, pt.x, pt.y);
+ break;
+ }
+ case kCGPathElementAddQuadCurveToPoint:
+ {
+ CGPoint cpt = element->points[0];
+ CGPoint pt = element->points[1];
+ CGPathAddQuadCurveToPoint(info->path, &info->transform, cpt.x, cpt.y, pt.x, pt.y);
+ break;
+ }
+ case kCGPathElementAddCurveToPoint:
+ {
+ CGPoint cpt1 = element->points[0];
+ CGPoint cpt2 = element->points[1];
+ CGPoint pt = element->points[2];
+ CGPathAddCurveToPoint(info->path, &info->transform, cpt1.x, cpt1.y, cpt2.x, cpt2.y, pt.x, pt.y);
+ break;
+ }
+ case kCGPathElementCloseSubpath:
+ {
+ CGPathCloseSubpath(info->path);
+ break;
+ }
+ }
+ }
+ };
+
+ TransformApplier ta;
+ ta.path = CGPathCreateMutable();
+ ta.transform = GfxMatrixToCGAffineTransform(aTransform);
+
+ CGPathApply(mPath, &ta, TransformApplier::TranformCGPathApplierFunc);
+ return MakeAndAddRef<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
+{
+ Matrix inverse = aTransform;
+ inverse.Invert();
+ Point transformedPoint = inverse.TransformPoint(aPoint);
+ // We could probably drop the input transform and just transform the point at the caller?
+ CGPoint point = {transformedPoint.x, transformedPoint.y};
+
+ // 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 == FillRule::FILL_EVEN_ODD);
+}
+
+static size_t
+PutBytesNull(void *info, const void *buffer, size_t count)
+{
+ return count;
+}
+
+/* The idea of a scratch context comes from WebKit */
+static CGContextRef
+CreateScratchContext()
+{
+ CGDataConsumerCallbacks callbacks = {PutBytesNull, nullptr};
+ CGDataConsumerRef consumer = CGDataConsumerCreate(nullptr, &callbacks);
+ CGContextRef cg = CGPDFContextCreate(consumer, nullptr, nullptr);
+ CGDataConsumerRelease(consumer);
+ return cg;
+}
+
+static CGContextRef
+ScratchContext()
+{
+ static CGContextRef cg = CreateScratchContext();
+ return cg;
+}
+
+bool
+PathCG::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const
+{
+ Matrix inverse = aTransform;
+ inverse.Invert();
+ Point transformedPoint = inverse.TransformPoint(aPoint);
+ // We could probably drop the input transform and just transform the point at the caller?
+ CGPoint point = {transformedPoint.x, transformedPoint.y};
+
+ CGContextRef cg = ScratchContext();
+
+ CGContextSaveGState(cg);
+
+ CGContextBeginPath(cg);
+ CGContextAddPath(cg, mPath);
+
+ SetStrokeOptions(cg, aStrokeOptions);
+
+ CGContextReplacePathWithStrokedPath(cg);
+ CGContextRestoreGState(cg);
+
+ CGPathRef sPath = CGContextCopyPath(cg);
+ bool inStroke = CGPathContainsPoint(sPath, nullptr, point, false);
+ CGPathRelease(sPath);
+
+ return inStroke;
+}
+
+//XXX: what should these functions return for an empty path?
+// currently they return CGRectNull {inf,inf, 0, 0}
+Rect
+PathCG::GetBounds(const Matrix &aTransform) const
+{
+ //XXX: are these bounds tight enough
+ Rect bounds = CGRectToRect(CGPathGetBoundingBox(mPath));
+
+ //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);
+}
+
+Rect
+PathCG::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform) const
+{
+ // 10.7 has CGPathCreateCopyByStrokingPath which we could use
+ // instead of this scratch context business
+ CGContextRef cg = ScratchContext();
+
+ CGContextSaveGState(cg);
+
+ CGContextBeginPath(cg);
+ CGContextAddPath(cg, mPath);
+
+ SetStrokeOptions(cg, aStrokeOptions);
+
+ CGContextReplacePathWithStrokedPath(cg);
+ Rect bounds = CGRectToRect(CGContextGetPathBoundingBox(cg));
+
+ CGContextRestoreGState(cg);
+
+ if (!bounds.IsFinite()) {
+ return Rect();
+ }
+
+ return aTransform.TransformBounds(bounds);
+}
+
+
+} // namespace gfx
+
+} // namespace mozilla
diff --git a/gfx/2d/PathCG.h b/gfx/2d/PathCG.h
new file mode 100644
index 000000000..db609cb57
--- /dev/null
+++ b/gfx/2d/PathCG.h
@@ -0,0 +1,114 @@
+/* -*- 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_PATHCG_H_
+#define MOZILLA_GFX_PATHCG_H_
+
+#ifdef MOZ_WIDGET_COCOA
+#include <ApplicationServices/ApplicationServices.h>
+#else
+#include <CoreGraphics/CoreGraphics.h>
+#endif
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+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)
+ {
+ mCGPath = aPath;
+ }
+
+ explicit PathBuilderCG(FillRule aFillRule)
+ : mFillRule(aFillRule)
+ {
+ mCGPath = CGPathCreateMutable();
+ }
+
+ virtual ~PathBuilderCG();
+
+ 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;
+
+ virtual already_AddRefed<Path> Finish();
+
+ virtual BackendType GetBackendType() const { return BackendType::SKIA; }
+
+private:
+ friend class PathCG;
+ friend class ScaledFontMac;
+
+ void EnsureActive(const Point &aPoint);
+
+ CGMutablePathRef mCGPath;
+ Point mCurrentPoint;
+ Point mBeginPoint;
+ FillRule mFillRule;
+};
+
+class PathCG : public Path
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathCG)
+ PathCG(CGMutablePathRef aPath, FillRule aFillRule)
+ : mPath(aPath)
+ , mFillRule(aFillRule)
+ {
+ CGPathRetain(mPath);
+ }
+ virtual ~PathCG() { CGPathRelease(mPath); }
+
+ // Paths will always return BackendType::COREGRAPHICS, but note that they
+ // are compatible with BackendType::COREGRAPHICS_ACCELERATED backend.
+ virtual BackendType GetBackendType() const { return BackendType::SKIA; }
+
+ virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const;
+ virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
+ FillRule aFillRule) const;
+
+ virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
+ virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const;
+ virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
+ 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; }
+
+private:
+ friend class DrawTargetCG;
+
+ CGMutablePathRef mPath;
+ Point mEndPoint;
+ FillRule mFillRule;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/gfx/2d/PathCairo.cpp b/gfx/2d/PathCairo.cpp
new file mode 100644
index 000000000..45bc201cb
--- /dev/null
+++ b/gfx/2d/PathCairo.cpp
@@ -0,0 +1,331 @@
+/* -*- 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 "PathCairo.h"
+#include <math.h>
+#include "DrawTargetCairo.h"
+#include "Logging.h"
+#include "PathHelpers.h"
+#include "HelpersCairo.h"
+
+namespace mozilla {
+namespace gfx {
+
+PathBuilderCairo::PathBuilderCairo(FillRule aFillRule)
+ : mFillRule(aFillRule)
+{
+}
+
+void
+PathBuilderCairo::MoveTo(const Point &aPoint)
+{
+ 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)
+{
+ 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
+PathBuilderCairo::BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3)
+{
+ 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)
+{
+ // 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;
+
+ 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()
+{
+ 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, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
+}
+
+Point
+PathBuilderCairo::CurrentPoint() const
+{
+ return mCurrentPoint;
+}
+
+already_AddRefed<Path>
+PathBuilderCairo::Finish()
+{
+ return MakeAndAddRef<PathCairo>(mFillRule, mPathData, mCurrentPoint);
+}
+
+PathCairo::PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint)
+ : mFillRule(aFillRule)
+ , mContainingContext(nullptr)
+ , mCurrentPoint(aCurrentPoint)
+{
+ mPathData.swap(aPathData);
+}
+
+PathCairo::PathCairo(cairo_t *aContext)
+ : mFillRule(FillRule::FILL_WINDING)
+ , mContainingContext(nullptr)
+{
+ 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()
+{
+ if (mContainingContext) {
+ cairo_destroy(mContainingContext);
+ }
+}
+
+already_AddRefed<PathBuilder>
+PathCairo::CopyToBuilder(FillRule aFillRule) const
+{
+ RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
+
+ builder->mPathData = mPathData;
+ builder->mCurrentPoint = mCurrentPoint;
+
+ return builder.forget();
+}
+
+already_AddRefed<PathBuilder>
+PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
+{
+ RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
+
+ AppendPathToBuilder(builder, &aTransform);
+ builder->mCurrentPoint = aTransform.TransformPoint(mCurrentPoint);
+
+ return builder.forget();
+}
+
+bool
+PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
+{
+ Matrix inverse = aTransform;
+ inverse.Invert();
+ Point transformed = inverse.TransformPoint(aPoint);
+
+ EnsureContainingContext(aTransform);
+
+ return cairo_in_fill(mContainingContext, transformed.x, transformed.y);
+}
+
+bool
+PathCairo::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const
+{
+ Matrix inverse = aTransform;
+ inverse.Invert();
+ Point transformed = inverse.TransformPoint(aPoint);
+
+ EnsureContainingContext(aTransform);
+
+ SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
+
+ return cairo_in_stroke(mContainingContext, transformed.x, transformed.y);
+}
+
+Rect
+PathCairo::GetBounds(const Matrix &aTransform) const
+{
+ EnsureContainingContext(aTransform);
+
+ double 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);
+}
+
+Rect
+PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform) const
+{
+ EnsureContainingContext(aTransform);
+
+ double x1, y1, x2, y2;
+
+ SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
+
+ cairo_stroke_extents(mContainingContext, &x1, &y1, &x2, &y2);
+ Rect bounds((Float)x1, (Float)y1, (Float)(x2 - x1), (Float)(y2 - y1));
+ return aTransform.TransformBounds(bounds);
+}
+
+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 Matrix &aTransform) const
+{
+ if (mContainingContext) {
+ if (mContainingTransform.ExactlyEquals(aTransform)) {
+ return;
+ }
+ } else {
+ mContainingContext = cairo_create(DrawTargetCairo::GetDummySurface());
+ }
+
+ mContainingTransform = aTransform;
+
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(mContainingTransform, mat);
+ cairo_set_matrix(mContainingContext, &mat);
+
+ SetPathOnContext(mContainingContext);
+}
+
+void
+PathCairo::SetPathOnContext(cairo_t *aContext) const
+{
+ // 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->TransformPoint(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]);
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/PathCairo.h b/gfx/2d/PathCairo.h
new file mode 100644
index 000000000..5addfb220
--- /dev/null
+++ b/gfx/2d/PathCairo.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_PATH_CAIRO_H_
+#define MOZILLA_GFX_PATH_CAIRO_H_
+
+#include "2D.h"
+#include "cairo.h"
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class PathCairo;
+
+class PathBuilderCairo : public PathBuilder
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderCairo)
+ explicit PathBuilderCairo(FillRule aFillRule);
+
+ 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;
+ virtual already_AddRefed<Path> Finish();
+
+ virtual BackendType GetBackendType() const { return BackendType::CAIRO; }
+
+private: // data
+ friend class PathCairo;
+
+ 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:
+ 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 BackendType::CAIRO; }
+
+ virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const;
+ virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
+ FillRule aFillRule) const;
+
+ virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
+
+ virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const;
+
+ virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
+
+ virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform = Matrix()) const;
+
+ virtual void StreamToSink(PathSink *aSink) const;
+
+ virtual FillRule GetFillRule() const { return mFillRule; }
+
+ void SetPathOnContext(cairo_t *aContext) const;
+
+ void AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform = nullptr) const;
+private:
+ void EnsureContainingContext(const Matrix &aTransform) const;
+
+ FillRule mFillRule;
+ std::vector<cairo_path_data_t> mPathData;
+ mutable cairo_t *mContainingContext;
+ mutable Matrix mContainingTransform;
+ Point mCurrentPoint;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PATH_CAIRO_H_ */
diff --git a/gfx/2d/PathD2D.cpp b/gfx/2d/PathD2D.cpp
new file mode 100644
index 000000000..b10e456e1
--- /dev/null
+++ b/gfx/2d/PathD2D.cpp
@@ -0,0 +1,523 @@
+/* -*- 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 "PathD2D.h"
+#include "HelpersD2D.h"
+#include <math.h>
+#include "DrawTargetD2D1.h"
+#include "Logging.h"
+
+namespace mozilla {
+namespace gfx {
+
+// This class exists as a wrapper for ID2D1SimplifiedGeometry sink, it allows
+// a geometry to be duplicated into a geometry sink, while removing the final
+// figure end and thus allowing a figure that was implicitly closed to be
+// continued.
+class OpeningGeometrySink : public ID2D1SimplifiedGeometrySink
+{
+public:
+ OpeningGeometrySink(ID2D1SimplifiedGeometrySink *aSink)
+ : mSink(aSink)
+ , mNeedsFigureEnded(false)
+ {
+ }
+
+ 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, the copier will decide.
+ STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE aMode)
+ { EnsureFigureEnded(); return; }
+ STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F aPoint, D2D1_FIGURE_BEGIN aBegin)
+ { EnsureFigureEnded(); return mSink->BeginFigure(aPoint, aBegin); }
+ STDMETHOD_(void, AddLines)(const D2D1_POINT_2F *aLines, UINT aCount)
+ { EnsureFigureEnded(); return mSink->AddLines(aLines, aCount); }
+ STDMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *aSegments, UINT aCount)
+ { EnsureFigureEnded(); return mSink->AddBeziers(aSegments, aCount); }
+ STDMETHOD(Close)()
+ { /* Should never be called! */ return S_OK; }
+ STDMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT aFlags)
+ { return mSink->SetSegmentFlags(aFlags); }
+
+ // This function is special - it's the reason this class exists.
+ // It needs to intercept the very last endfigure. So that a user can
+ // continue writing to this sink as if they never stopped.
+ STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END aEnd)
+ {
+ if (aEnd == D2D1_FIGURE_END_CLOSED) {
+ return mSink->EndFigure(aEnd);
+ } else {
+ mNeedsFigureEnded = true;
+ }
+ }
+private:
+ void EnsureFigureEnded()
+ {
+ if (mNeedsFigureEnded) {
+ mSink->EndFigure(D2D1_FIGURE_END_OPEN);
+ mNeedsFigureEnded = false;
+ }
+ }
+
+ ID2D1SimplifiedGeometrySink *mSink;
+ bool mNeedsFigureEnded;
+};
+
+class MOZ_STACK_CLASS AutoRestoreFP
+{
+public:
+ AutoRestoreFP()
+ {
+ // save the current floating point control word
+ _controlfp_s(&savedFPSetting, 0, 0);
+ UINT unused;
+ // set the floating point control word to its default value
+ _controlfp_s(&unused, _CW_DEFAULT, MCW_PC);
+ }
+ ~AutoRestoreFP()
+ {
+ UINT unused;
+ // restore the saved floating point control word
+ _controlfp_s(&unused, savedFPSetting, MCW_PC);
+ }
+private:
+ UINT savedFPSetting;
+};
+
+// Note that overrides of ID2D1SimplifiedGeometrySink methods in this class may
+// get called from D2D with nonstandard floating point settings (see comments in
+// bug 1134549) - use AutoRestoreFP to reset the floating point control word to
+// what we expect
+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)
+ {
+ AutoRestoreFP resetFloatingPoint;
+ mSink->MoveTo(ToPoint(aPoint));
+ }
+ STDMETHOD_(void, AddLines)(const D2D1_POINT_2F *aLines, UINT aCount)
+ {
+ AutoRestoreFP resetFloatingPoint;
+ for (UINT i = 0; i < aCount; i++) { mSink->LineTo(ToPoint(aLines[i])); }
+ }
+ STDMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *aSegments, UINT aCount)
+ {
+ AutoRestoreFP resetFloatingPoint;
+ 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)
+ {
+ AutoRestoreFP resetFloatingPoint;
+ if (aEnd == D2D1_FIGURE_END_CLOSED) {
+ return mSink->Close();
+ }
+ }
+private:
+
+ PathSink *mSink;
+};
+
+PathBuilderD2D::~PathBuilderD2D()
+{
+}
+
+void
+PathBuilderD2D::MoveTo(const Point &aPoint)
+{
+ if (mFigureActive) {
+ mSink->EndFigure(D2D1_FIGURE_END_OPEN);
+ mFigureActive = false;
+ }
+ EnsureActive(aPoint);
+ mCurrentPoint = aPoint;
+}
+
+void
+PathBuilderD2D::LineTo(const Point &aPoint)
+{
+ EnsureActive(aPoint);
+ mSink->AddLine(D2DPoint(aPoint));
+
+ mCurrentPoint = aPoint;
+}
+
+void
+PathBuilderD2D::BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3)
+ {
+ EnsureActive(aCP1);
+ mSink->AddBezier(D2D1::BezierSegment(D2DPoint(aCP1),
+ D2DPoint(aCP2),
+ D2DPoint(aCP3)));
+
+ mCurrentPoint = aCP3;
+}
+
+void
+PathBuilderD2D::QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2)
+{
+ EnsureActive(aCP1);
+ mSink->AddQuadraticBezier(D2D1::QuadraticBezierSegment(D2DPoint(aCP1),
+ D2DPoint(aCP2)));
+
+ mCurrentPoint = aCP2;
+}
+
+void
+PathBuilderD2D::Close()
+{
+ if (mFigureActive) {
+ mSink->EndFigure(D2D1_FIGURE_END_CLOSED);
+
+ mFigureActive = false;
+
+ EnsureActive(mBeginPoint);
+ }
+}
+
+void
+PathBuilderD2D::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
+ Float aEndAngle, bool aAntiClockwise)
+{
+ MOZ_ASSERT(aRadius >= 0);
+
+ if (aAntiClockwise && aStartAngle < aEndAngle) {
+ // D2D does things a little differently, and draws the arc by specifying an
+ // beginning and an end point. This means the circle will be the wrong way
+ // around if the start angle is smaller than the end angle. It might seem
+ // tempting to invert aAntiClockwise but that would change the sweeping
+ // direction of the arc so instead we exchange start/begin.
+ Float oldStart = aStartAngle;
+ aStartAngle = aEndAngle;
+ aEndAngle = oldStart;
+ }
+
+ // XXX - Workaround for now, D2D does not appear to do the desired thing when
+ // the angle sweeps a complete circle.
+ bool fullCircle = false;
+ if (aEndAngle - aStartAngle >= 2 * M_PI) {
+ fullCircle = true;
+ aEndAngle = Float(aStartAngle + M_PI * 1.9999);
+ } else if (aStartAngle - aEndAngle >= 2 * M_PI) {
+ fullCircle = true;
+ aStartAngle = Float(aEndAngle + M_PI * 1.9999);
+ }
+
+ Point startPoint;
+ startPoint.x = aOrigin.x + aRadius * cos(aStartAngle);
+ startPoint.y = aOrigin.y + aRadius * sin(aStartAngle);
+
+ if (!mFigureActive) {
+ EnsureActive(startPoint);
+ } else {
+ mSink->AddLine(D2DPoint(startPoint));
+ }
+
+ Point endPoint;
+ endPoint.x = aOrigin.x + aRadius * cosf(aEndAngle);
+ endPoint.y = aOrigin.y + aRadius * sinf(aEndAngle);
+
+ D2D1_ARC_SIZE arcSize = D2D1_ARC_SIZE_SMALL;
+ D2D1_SWEEP_DIRECTION direction =
+ aAntiClockwise ? D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE :
+ D2D1_SWEEP_DIRECTION_CLOCKWISE;
+
+ // if startPoint and endPoint of our circle are too close there are D2D issues
+ // with drawing the circle as a single arc
+ const Float kEpsilon = 1e-5f;
+ if (!fullCircle ||
+ (std::abs(startPoint.x - endPoint.x) +
+ std::abs(startPoint.y - endPoint.y) > kEpsilon)) {
+
+ if (aAntiClockwise) {
+ if (aStartAngle - aEndAngle > M_PI) {
+ arcSize = D2D1_ARC_SIZE_LARGE;
+ }
+ } else {
+ if (aEndAngle - aStartAngle > M_PI) {
+ arcSize = D2D1_ARC_SIZE_LARGE;
+ }
+ }
+
+ mSink->AddArc(D2D1::ArcSegment(D2DPoint(endPoint),
+ D2D1::SizeF(aRadius, aRadius),
+ 0.0f,
+ direction,
+ arcSize));
+ }
+ else {
+ // our first workaround attempt didn't work, so instead draw the circle as
+ // two half-circles
+ Float midAngle = aEndAngle > aStartAngle ?
+ Float(aStartAngle + M_PI) : Float(aEndAngle + M_PI);
+ Point midPoint;
+ midPoint.x = aOrigin.x + aRadius * cosf(midAngle);
+ midPoint.y = aOrigin.y + aRadius * sinf(midAngle);
+
+ mSink->AddArc(D2D1::ArcSegment(D2DPoint(midPoint),
+ D2D1::SizeF(aRadius, aRadius),
+ 0.0f,
+ direction,
+ arcSize));
+
+ // if the adjusted endPoint computed above is used here and endPoint !=
+ // startPoint then this half of the circle won't render...
+ mSink->AddArc(D2D1::ArcSegment(D2DPoint(startPoint),
+ D2D1::SizeF(aRadius, aRadius),
+ 0.0f,
+ direction,
+ arcSize));
+ }
+
+ mCurrentPoint = endPoint;
+}
+
+Point
+PathBuilderD2D::CurrentPoint() const
+{
+ return mCurrentPoint;
+}
+
+void
+PathBuilderD2D::EnsureActive(const Point &aPoint)
+{
+ if (!mFigureActive) {
+ mSink->BeginFigure(D2DPoint(aPoint), D2D1_FIGURE_BEGIN_FILLED);
+ mBeginPoint = aPoint;
+ mFigureActive = true;
+ }
+}
+
+already_AddRefed<Path>
+PathBuilderD2D::Finish()
+{
+ if (mFigureActive) {
+ mSink->EndFigure(D2D1_FIGURE_END_OPEN);
+ }
+
+ HRESULT hr = mSink->Close();
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to close PathSink. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ return MakeAndAddRef<PathD2D>(mGeometry, mFigureActive, mCurrentPoint, mFillRule, mBackendType);
+}
+
+already_AddRefed<PathBuilder>
+PathD2D::CopyToBuilder(FillRule aFillRule) const
+{
+ return TransformedCopyToBuilder(Matrix(), aFillRule);
+}
+
+already_AddRefed<PathBuilder>
+PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
+{
+ RefPtr<ID2D1PathGeometry> path;
+ HRESULT hr = DrawTargetD2D1::factory()->CreatePathGeometry(getter_AddRefs(path));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create PathGeometry. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ RefPtr<ID2D1GeometrySink> sink;
+ hr = path->Open(getter_AddRefs(sink));
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to open Geometry for writing. Code: " << hexa(hr);
+ return nullptr;
+ }
+
+ if (aFillRule == FillRule::FILL_WINDING) {
+ sink->SetFillMode(D2D1_FILL_MODE_WINDING);
+ }
+
+ if (mEndedActive) {
+ OpeningGeometrySink wrapSink(sink);
+ hr = mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+ D2DMatrix(aTransform),
+ &wrapSink);
+ } else {
+ hr = mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+ D2DMatrix(aTransform),
+ sink);
+ }
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to simplify PathGeometry to tranformed copy. Code: " << hexa(hr) << " Active: " << mEndedActive;
+ return nullptr;
+ }
+
+ RefPtr<PathBuilderD2D> pathBuilder = new PathBuilderD2D(sink, path, aFillRule, mBackendType);
+
+ pathBuilder->mCurrentPoint = aTransform.TransformPoint(mEndPoint);
+
+ if (mEndedActive) {
+ pathBuilder->mFigureActive = true;
+ }
+
+ 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
+{
+ BOOL result;
+
+ HRESULT hr = mGeometry->FillContainsPoint(D2DPoint(aPoint), D2DMatrix(aTransform), 0.001f, &result);
+
+ if (FAILED(hr)) {
+ // Log
+ return false;
+ }
+
+ return !!result;
+}
+
+bool
+PathD2D::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const
+{
+ BOOL result;
+
+ RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
+ HRESULT hr = mGeometry->StrokeContainsPoint(D2DPoint(aPoint),
+ aStrokeOptions.mLineWidth,
+ strokeStyle,
+ D2DMatrix(aTransform),
+ &result);
+
+ if (FAILED(hr)) {
+ // Log
+ return false;
+ }
+
+ return !!result;
+}
+
+Rect
+PathD2D::GetBounds(const Matrix &aTransform) const
+{
+ D2D1_RECT_F d2dBounds;
+
+ HRESULT hr = mGeometry->GetBounds(D2DMatrix(aTransform), &d2dBounds);
+
+ Rect bounds = ToRect(d2dBounds);
+ if (FAILED(hr) || !bounds.IsFinite()) {
+ gfxWarning() << "Failed to get stroked bounds for path. Code: " << hexa(hr);
+ return Rect();
+ }
+
+ return bounds;
+}
+
+Rect
+PathD2D::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform) const
+{
+ D2D1_RECT_F d2dBounds;
+
+ RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
+ HRESULT hr =
+ mGeometry->GetWidenedBounds(aStrokeOptions.mLineWidth, strokeStyle,
+ D2DMatrix(aTransform), &d2dBounds);
+
+ Rect bounds = ToRect(d2dBounds);
+ if (FAILED(hr) || !bounds.IsFinite()) {
+ gfxWarning() << "Failed to get stroked bounds for path. Code: " << hexa(hr);
+ return Rect();
+ }
+
+ return bounds;
+}
+
+}
+}
diff --git a/gfx/2d/PathD2D.h b/gfx/2d/PathD2D.h
new file mode 100644
index 000000000..0fb550b4a
--- /dev/null
+++ b/gfx/2d/PathD2D.h
@@ -0,0 +1,117 @@
+/* -*- 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_PATHD2D_H_
+#define MOZILLA_GFX_PATHD2D_H_
+
+#include <d2d1.h>
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+class PathD2D;
+
+class PathBuilderD2D : public PathBuilder
+{
+public:
+ 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();
+
+ 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;
+
+ virtual already_AddRefed<Path> Finish();
+
+ virtual BackendType GetBackendType() const { return mBackendType; }
+
+ ID2D1GeometrySink *GetSink() { return mSink; }
+
+ bool IsFigureActive() const { return mFigureActive; }
+
+private:
+ friend class PathD2D;
+
+ void EnsureActive(const Point &aPoint);
+
+ RefPtr<ID2D1GeometrySink> mSink;
+ RefPtr<ID2D1PathGeometry> mGeometry;
+
+ bool mFigureActive;
+ 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, BackendType aBackendType)
+ : mGeometry(aGeometry)
+ , mEndedActive(aEndedActive)
+ , mEndPoint(aEndPoint)
+ , mFillRule(aFillRule)
+ , mBackendType(aBackendType)
+ {}
+
+ virtual BackendType GetBackendType() const { return mBackendType; }
+
+ virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const;
+ virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
+ FillRule aFillRule) const;
+
+ virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
+
+ virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const;
+
+ virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
+
+ 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;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_PATHD2D_H_ */
diff --git a/gfx/2d/PathHelpers.cpp b/gfx/2d/PathHelpers.cpp
new file mode 100644
index 000000000..49c344b42
--- /dev/null
+++ b/gfx/2d/PathHelpers.cpp
@@ -0,0 +1,277 @@
+/* -*- 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,
+ Float aLineWidth)
+{
+ 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();
+
+ aP1 = p1;
+ aP2 = p2;
+
+ bool lineWidthIsOdd = (int(aLineWidth) % 2) == 1;
+ if (lineWidthIsOdd) {
+ if (aP1.x == aP2.x) {
+ // snap vertical line, adding 0.5 to align it to be mid-pixel:
+ aP1 += Point(0.5, 0);
+ aP2 += Point(0.5, 0);
+ } else {
+ // snap horizontal line, adding 0.5 to align it to be mid-pixel:
+ aP1 += Point(0, 0.5);
+ aP2 += 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,
+ aStrokeOptions.mLineWidth);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+ p1 = aRect.BottomLeft();
+ p2 = aRect.BottomRight();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
+ aStrokeOptions.mLineWidth);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+ p1 = aRect.TopLeft();
+ p2 = aRect.TopRight();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
+ aStrokeOptions.mLineWidth);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+ p1 = aRect.TopRight();
+ p2 = aRect.BottomRight();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
+ aStrokeOptions.mLineWidth);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+}
+
+// The logic for this comes from _cairo_stroke_style_max_distance_from_path
+Margin
+MaxStrokeExtents(const StrokeOptions& aStrokeOptions,
+ 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);
+ return Margin(dy, dx, dy, dx);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/PathHelpers.h b/gfx/2d/PathHelpers.h
new file mode 100644
index 000000000..553a886b9
--- /dev/null
+++ b/gfx/2d/PathHelpers.h
@@ -0,0 +1,424 @@
+/* -*- 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_PATHHELPERS_H_
+#define MOZILLA_GFX_PATHHELPERS_H_
+
+#include "2D.h"
+#include "UserData.h"
+
+#include <cmath>
+
+namespace mozilla {
+namespace gfx {
+
+// Kappa constant for 90-degree angle
+const Float kKappaFactor = 0.55191497064665766025f;
+
+// 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.
+inline Float ComputeKappaFactor(Float aAngle)
+{
+ return (4.0f / 3.0f) * tanf(aAngle / 4.0f);
+}
+
+/**
+ * Draws a partial arc <= 90 degrees given exact start and end points.
+ * Assumes that it is continuing from an already specified start point.
+ */
+template <typename T>
+inline void PartialArcToBezier(T* aSink,
+ const Point& aStartOffset, const Point& aEndOffset,
+ const Matrix& aTransform,
+ Float aKappaFactor = kKappaFactor)
+{
+ Point cp1 =
+ aStartOffset + Point(-aStartOffset.y, aStartOffset.x) * aKappaFactor;
+
+ Point cp2 =
+ aEndOffset + Point(aEndOffset.y, -aEndOffset.x) * aKappaFactor;
+
+ aSink->BezierTo(aTransform.TransformPoint(cp1),
+ aTransform.TransformPoint(cp2),
+ aTransform.TransformPoint(aEndOffset));
+}
+
+/**
+ * Draws an acute arc (<= 90 degrees) given exact start and end points.
+ * Specialized version avoiding kappa calculation.
+ */
+template <typename T>
+inline void AcuteArcToBezier(T* aSink,
+ const Point& aOrigin, const Size& aRadius,
+ const Point& aStartPoint, const Point& aEndPoint,
+ Float aKappaFactor = kKappaFactor)
+{
+ aSink->LineTo(aStartPoint);
+ if (!aRadius.IsEmpty()) {
+ Float kappaX = aKappaFactor * aRadius.width / aRadius.height;
+ Float kappaY = aKappaFactor * aRadius.height / aRadius.width;
+ Point startOffset = aStartPoint - aOrigin;
+ Point endOffset = aEndPoint - aOrigin;
+ aSink->BezierTo(aStartPoint + Point(-startOffset.y * kappaX, startOffset.x * kappaY),
+ aEndPoint + Point(endOffset.y * kappaX, -endOffset.x * kappaY),
+ aEndPoint);
+ } else if (aEndPoint != aStartPoint) {
+ aSink->LineTo(aEndPoint);
+ }
+}
+
+/**
+ * Draws an acute arc (<= 90 degrees) given exact start and end points.
+ */
+template <typename T>
+inline void AcuteArcToBezier(T* aSink,
+ const Point& aOrigin, const Size& aRadius,
+ const Point& aStartPoint, const Point& aEndPoint,
+ Float aStartAngle, Float aEndAngle)
+{
+ AcuteArcToBezier(aSink, aOrigin, aRadius, aStartPoint, aEndPoint,
+ ComputeKappaFactor(aEndAngle - aStartAngle));
+}
+
+template <typename T>
+void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius,
+ float aStartAngle, float aEndAngle, bool aAntiClockwise,
+ float aRotation = 0.0f)
+{
+ Float sweepDirection = aAntiClockwise ? -1.0f : 1.0f;
+
+ // Calculate the total arc we're going to sweep.
+ Float arcSweepLeft = (aEndAngle - aStartAngle) * sweepDirection;
+
+ // Clockwise we always sweep from the smaller to the larger angle, ccw
+ // it's vice versa.
+ if (arcSweepLeft < 0) {
+ // Rerverse sweep is modulo'd into range rather than clamped.
+ arcSweepLeft = Float(2.0f * M_PI) + fmodf(arcSweepLeft, Float(2.0f * M_PI));
+ // Recalculate the start angle to land closer to end angle.
+ aStartAngle = aEndAngle - arcSweepLeft * sweepDirection;
+ } else if (arcSweepLeft > Float(2.0f * M_PI)) {
+ // Sweeping more than 2 * pi is a full circle.
+ arcSweepLeft = Float(2.0f * M_PI);
+ }
+
+ Float currentStartAngle = aStartAngle;
+ Point currentStartOffset(cosf(aStartAngle), sinf(aStartAngle));
+ Matrix transform = Matrix::Scaling(aRadius.width, aRadius.height);
+ if (aRotation != 0.0f) {
+ transform *= Matrix::Rotation(aRotation);
+ }
+ transform.PostTranslate(aOrigin);
+ aSink->LineTo(transform.TransformPoint(currentStartOffset));
+
+ while (arcSweepLeft > 0) {
+ Float currentEndAngle =
+ currentStartAngle + std::min(arcSweepLeft, Float(M_PI / 2.0f)) * sweepDirection;
+ Point currentEndOffset(cosf(currentEndAngle), sinf(currentEndAngle));
+
+ PartialArcToBezier(aSink, currentStartOffset, currentEndOffset, transform,
+ ComputeKappaFactor(currentEndAngle - currentStartAngle));
+
+ // We guarantee here the current point is the start point of the next
+ // curve segment.
+ arcSweepLeft -= Float(M_PI / 2.0f);
+ currentStartAngle = currentEndAngle;
+ currentStartOffset = currentEndOffset;
+ }
+}
+
+/* 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)
+{
+ Matrix transform(aRadius.width, 0, 0, aRadius.height, aOrigin.x, aOrigin.y);
+ Point currentStartOffset(1, 0);
+
+ aSink->LineTo(transform.TransformPoint(currentStartOffset));
+
+ for (int i = 0; i < 4; i++) {
+ // cos(x+pi/2) == -sin(x)
+ // sin(x+pi/2) == cos(x)
+ Point currentEndOffset(-currentStartOffset.y, currentStartOffset.x);
+
+ PartialArcToBezier(aSink, currentStartOffset, currentEndOffset, transform);
+
+ // We guarantee here the current point is the start point of the next
+ // curve segment.
+ currentStartOffset = currentEndOffset;
+ }
+}
+
+/**
+ * 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 already_AddRefed<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];
+ }
+
+ bool operator==(const RectCornerRadii& aOther) const {
+ for (size_t i = 0; i < RectCorner::Count; i++) {
+ if (radii[i] != aOther.radii[i]) return false;
+ }
+ return true;
+ }
+
+ 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 already_AddRefed<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 already_AddRefed<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,
+ Float aLineWidth);
+
+/**
+ * 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);
+
+/**
+ * Return the margin, in device space, by which a stroke can extend beyond the
+ * rendered shape.
+ * @param aStrokeOptions The stroke options that the stroke is drawn with.
+ * @param aTransform The user space to device space transform.
+ * @return The stroke margin.
+ */
+GFX2D_API Margin MaxStrokeExtents(const StrokeOptions& aStrokeOptions,
+ const Matrix& aTransform);
+
+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).
+ *
+ * Empty snaps are those which result in a rectangle of 0 area. If they are
+ * disallowed, an axis is left unsnapped if the rounding process results in a
+ * length of 0.
+ */
+inline bool UserToDevicePixelSnapped(Rect& aRect, const DrawTarget& aDrawTarget,
+ bool aAllowScaleOr90DegreeRotate = false,
+ bool aAllowEmptySnaps = true)
+{
+ 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.TransformPoint(aRect.TopLeft());
+ Point p2 = mat.TransformPoint(aRect.TopRight());
+ Point p3 = mat.TransformPoint(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)) {
+ Point p1r = p1;
+ Point p3r = p3;
+ p1r.Round();
+ p3r.Round();
+ if (aAllowEmptySnaps || p1r.x != p3r.x) {
+ p1.x = p1r.x;
+ p3.x = p3r.x;
+ }
+ if (aAllowEmptySnaps || p1r.y != p3r.y) {
+ p1.y = p1r.y;
+ p3.y = p3r.y;
+ }
+
+ 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 bool MaybeSnapToDevicePixels(Rect& aRect, const DrawTarget& aDrawTarget,
+ bool aAllowScaleOr90DegreeRotate = false,
+ bool aAllowEmptySnaps = true)
+{
+ if (UserToDevicePixelSnapped(aRect, aDrawTarget,
+ aAllowScaleOr90DegreeRotate, aAllowEmptySnaps)) {
+ // 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);
+ return true;
+ }
+ return false;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PATHHELPERS_H_ */
diff --git a/gfx/2d/PathRecording.cpp b/gfx/2d/PathRecording.cpp
new file mode 100644
index 000000000..5884a0de9
--- /dev/null
+++ b/gfx/2d/PathRecording.cpp
@@ -0,0 +1,120 @@
+/* -*- 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 "PathRecording.h"
+#include "DrawEventRecorder.h"
+
+namespace mozilla {
+namespace gfx {
+
+using namespace std;
+
+void
+PathBuilderRecording::MoveTo(const Point &aPoint)
+{
+ PathOp op;
+ op.mType = PathOp::OP_MOVETO;
+ op.mP1 = aPoint;
+ mPathOps.push_back(op);
+ mPathBuilder->MoveTo(aPoint);
+}
+
+void
+PathBuilderRecording::LineTo(const Point &aPoint)
+{
+ PathOp op;
+ op.mType = PathOp::OP_LINETO;
+ op.mP1 = aPoint;
+ mPathOps.push_back(op);
+ mPathBuilder->LineTo(aPoint);
+}
+
+void
+PathBuilderRecording::BezierTo(const Point &aCP1, const Point &aCP2, const Point &aCP3)
+{
+ PathOp op;
+ op.mType = PathOp::OP_BEZIERTO;
+ op.mP1 = aCP1;
+ op.mP2 = aCP2;
+ op.mP3 = aCP3;
+ mPathOps.push_back(op);
+ mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
+}
+
+void
+PathBuilderRecording::QuadraticBezierTo(const Point &aCP1, const Point &aCP2)
+{
+ PathOp op;
+ op.mType = PathOp::OP_QUADRATICBEZIERTO;
+ op.mP1 = aCP1;
+ op.mP2 = aCP2;
+ mPathOps.push_back(op);
+ mPathBuilder->QuadraticBezierTo(aCP1, aCP2);
+}
+
+void
+PathBuilderRecording::Close()
+{
+ PathOp op;
+ op.mType = PathOp::OP_CLOSE;
+ mPathOps.push_back(op);
+ mPathBuilder->Close();
+}
+
+Point
+PathBuilderRecording::CurrentPoint() const
+{
+ return mPathBuilder->CurrentPoint();
+}
+
+already_AddRefed<Path>
+PathBuilderRecording::Finish()
+{
+ RefPtr<Path> path = mPathBuilder->Finish();
+ return MakeAndAddRef<PathRecording>(path, mPathOps, mFillRule);
+}
+
+PathRecording::~PathRecording()
+{
+ for (size_t i = 0; i < mStoredRecorders.size(); i++) {
+ mStoredRecorders[i]->RemoveStoredObject(this);
+ mStoredRecorders[i]->RecordEvent(RecordedPathDestruction(this));
+ }
+}
+
+already_AddRefed<PathBuilder>
+PathRecording::CopyToBuilder(FillRule aFillRule) const
+{
+ RefPtr<PathBuilder> pathBuilder = mPath->CopyToBuilder(aFillRule);
+ RefPtr<PathBuilderRecording> recording = new PathBuilderRecording(pathBuilder, aFillRule);
+ recording->mPathOps = mPathOps;
+ return recording.forget();
+}
+
+already_AddRefed<PathBuilder>
+PathRecording::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
+{
+ RefPtr<PathBuilder> pathBuilder = mPath->TransformedCopyToBuilder(aTransform, aFillRule);
+ RefPtr<PathBuilderRecording> recording = new PathBuilderRecording(pathBuilder, aFillRule);
+ typedef std::vector<PathOp> pathOpVec;
+ for (pathOpVec::const_iterator iter = mPathOps.begin(); iter != mPathOps.end(); iter++) {
+ PathOp newPathOp;
+ newPathOp.mType = iter->mType;
+ if (sPointCount[newPathOp.mType] >= 1) {
+ newPathOp.mP1 = aTransform.TransformPoint(iter->mP1);
+ }
+ if (sPointCount[newPathOp.mType] >= 2) {
+ newPathOp.mP2 = aTransform.TransformPoint(iter->mP2);
+ }
+ if (sPointCount[newPathOp.mType] >= 3) {
+ newPathOp.mP3 = aTransform.TransformPoint(iter->mP3);
+ }
+ recording->mPathOps.push_back(newPathOp);
+ }
+ return recording.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/PathRecording.h b/gfx/2d/PathRecording.h
new file mode 100644
index 000000000..7faaa716f
--- /dev/null
+++ b/gfx/2d/PathRecording.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_PATHRECORDING_H_
+#define MOZILLA_GFX_PATHRECORDING_H_
+
+#include "2D.h"
+#include <vector>
+#include <ostream>
+
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+struct PathOp
+{
+ enum OpType {
+ OP_MOVETO = 0,
+ OP_LINETO,
+ OP_BEZIERTO,
+ OP_QUADRATICBEZIERTO,
+ OP_CLOSE
+ };
+
+ OpType mType;
+ Point mP1;
+ Point mP2;
+ Point mP3;
+};
+
+const int32_t sPointCount[] = { 1, 1, 3, 2, 0, 0 };
+
+class PathRecording;
+class DrawEventRecorderPrivate;
+
+class PathBuilderRecording : public PathBuilder
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderRecording)
+ PathBuilderRecording(PathBuilder *aBuilder, FillRule aFillRule)
+ : mPathBuilder(aBuilder), mFillRule(aFillRule)
+ {
+ }
+
+ /* 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);
+ /* Add a linesegment to the current figure */
+ virtual void LineTo(const Point &aPoint);
+ /* Add a cubic bezier curve to the current figure */
+ virtual void BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3);
+ /* Add a quadratic bezier curve to the current figure */
+ virtual void QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2);
+ /* 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();
+
+ /* Add an arc to the current figure */
+ virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise) {
+ ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle,
+ aAntiClockwise);
+ }
+
+ /* Point the current subpath is at - or where the next subpath will start
+ * if there is no active subpath.
+ */
+ virtual Point CurrentPoint() const;
+
+ virtual already_AddRefed<Path> Finish();
+
+ virtual BackendType GetBackendType() const { return BackendType::RECORDING; }
+
+private:
+ friend class PathRecording;
+
+ RefPtr<PathBuilder> mPathBuilder;
+ FillRule mFillRule;
+ std::vector<PathOp> mPathOps;
+};
+
+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)
+ {
+ }
+
+ ~PathRecording();
+
+ virtual BackendType GetBackendType() const { return BackendType::RECORDING; }
+ virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const;
+ virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
+ FillRule aFillRule) const;
+ virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
+ { return mPath->ContainsPoint(aPoint, aTransform); }
+ virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const
+ { return mPath->StrokeContainsPoint(aStrokeOptions, aPoint, aTransform); }
+
+ virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const
+ { return mPath->GetBounds(aTransform); }
+
+ 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;
+ static void ReadPathToBuilder(std::istream &aStream, PathBuilder *aBuilder);
+
+private:
+ friend class DrawTargetRecording;
+ friend class RecordedPathCreation;
+
+ RefPtr<Path> mPath;
+ std::vector<PathOp> mPathOps;
+ FillRule mFillRule;
+
+ // Event recorders that have this path in their event stream.
+ std::vector<RefPtr<DrawEventRecorderPrivate>> mStoredRecorders;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PATHRECORDING_H_ */
diff --git a/gfx/2d/PathSkia.cpp b/gfx/2d/PathSkia.cpp
new file mode 100644
index 000000000..44329bb76
--- /dev/null
+++ b/gfx/2d/PathSkia.cpp
@@ -0,0 +1,237 @@
+/* -*- 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 "PathSkia.h"
+#include <math.h>
+#include "DrawTargetSkia.h"
+#include "Logging.h"
+#include "HelpersSkia.h"
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+PathBuilderSkia::PathBuilderSkia(const Matrix& aTransform, const SkPath& aPath, FillRule aFillRule)
+ : mPath(aPath)
+{
+ SkMatrix matrix;
+ GfxMatrixToSkiaMatrix(aTransform, matrix);
+ mPath.transform(matrix);
+ SetFillRule(aFillRule);
+}
+
+PathBuilderSkia::PathBuilderSkia(FillRule aFillRule)
+{
+ SetFillRule(aFillRule);
+}
+
+void
+PathBuilderSkia::SetFillRule(FillRule aFillRule)
+{
+ mFillRule = aFillRule;
+ if (mFillRule == FillRule::FILL_WINDING) {
+ mPath.setFillType(SkPath::kWinding_FillType);
+ } else {
+ mPath.setFillType(SkPath::kEvenOdd_FillType);
+ }
+}
+
+void
+PathBuilderSkia::MoveTo(const Point &aPoint)
+{
+ mPath.moveTo(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
+}
+
+void
+PathBuilderSkia::LineTo(const Point &aPoint)
+{
+ if (!mPath.countPoints()) {
+ MoveTo(aPoint);
+ } else {
+ mPath.lineTo(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
+ }
+}
+
+void
+PathBuilderSkia::BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3)
+{
+ if (!mPath.countPoints()) {
+ MoveTo(aCP1);
+ }
+ mPath.cubicTo(SkFloatToScalar(aCP1.x), SkFloatToScalar(aCP1.y),
+ SkFloatToScalar(aCP2.x), SkFloatToScalar(aCP2.y),
+ SkFloatToScalar(aCP3.x), SkFloatToScalar(aCP3.y));
+}
+
+void
+PathBuilderSkia::QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2)
+{
+ if (!mPath.countPoints()) {
+ MoveTo(aCP1);
+ }
+ mPath.quadTo(SkFloatToScalar(aCP1.x), SkFloatToScalar(aCP1.y),
+ SkFloatToScalar(aCP2.x), SkFloatToScalar(aCP2.y));
+}
+
+void
+PathBuilderSkia::Close()
+{
+ mPath.close();
+}
+
+void
+PathBuilderSkia::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise)
+{
+ ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
+}
+
+Point
+PathBuilderSkia::CurrentPoint() const
+{
+ int pointCount = mPath.countPoints();
+ if (!pointCount) {
+ return Point(0, 0);
+ }
+ SkPoint point = mPath.getPoint(pointCount - 1);
+ return Point(SkScalarToFloat(point.fX), SkScalarToFloat(point.fY));
+}
+
+already_AddRefed<Path>
+PathBuilderSkia::Finish()
+{
+ return MakeAndAddRef<PathSkia>(mPath, mFillRule);
+}
+
+void
+PathBuilderSkia::AppendPath(const SkPath &aPath)
+{
+ mPath.addPath(aPath);
+}
+
+already_AddRefed<PathBuilder>
+PathSkia::CopyToBuilder(FillRule aFillRule) const
+{
+ return TransformedCopyToBuilder(Matrix(), aFillRule);
+}
+
+already_AddRefed<PathBuilder>
+PathSkia::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
+{
+ return MakeAndAddRef<PathBuilderSkia>(aTransform, mPath, aFillRule);
+}
+
+static bool
+SkPathContainsPoint(const SkPath& aPath, const Point& aPoint, const Matrix& aTransform)
+{
+ Matrix inverse = aTransform;
+ if (!inverse.Invert()) {
+ return false;
+ }
+
+ SkPoint point = PointToSkPoint(inverse.TransformPoint(aPoint));
+ return aPath.contains(point.fX, point.fY);
+}
+
+bool
+PathSkia::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
+{
+ if (!mPath.isFinite()) {
+ return false;
+ }
+
+ return SkPathContainsPoint(mPath, aPoint, aTransform);
+}
+
+bool
+PathSkia::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const
+{
+ if (!mPath.isFinite()) {
+ return false;
+ }
+
+ SkPaint paint;
+ if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
+ return false;
+ }
+
+ SkPath strokePath;
+ paint.getFillPath(mPath, &strokePath);
+
+ return SkPathContainsPoint(strokePath, aPoint, aTransform);
+}
+
+Rect
+PathSkia::GetBounds(const Matrix &aTransform) const
+{
+ if (!mPath.isFinite()) {
+ return Rect();
+ }
+
+ Rect bounds = SkRectToRect(mPath.getBounds());
+ return aTransform.TransformBounds(bounds);
+}
+
+Rect
+PathSkia::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
+ const Matrix &aTransform) const
+{
+ if (!mPath.isFinite()) {
+ return Rect();
+ }
+
+ SkPaint paint;
+ if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
+ return Rect();
+ }
+
+ SkPath result;
+ paint.getFillPath(mPath, &result);
+
+ Rect bounds = SkRectToRect(result.getBounds());
+ 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!
+ }
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/PathSkia.h b/gfx/2d/PathSkia.h
new file mode 100644
index 000000000..aec06bb2e
--- /dev/null
+++ b/gfx/2d/PathSkia.h
@@ -0,0 +1,92 @@
+/* -*- 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_PATH_SKIA_H_
+#define MOZILLA_GFX_PATH_SKIA_H_
+
+#include "2D.h"
+#include "skia/include/core/SkPath.h"
+
+namespace mozilla {
+namespace gfx {
+
+class PathSkia;
+
+class PathBuilderSkia : public PathBuilder
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderSkia)
+ PathBuilderSkia(const Matrix& aTransform, const SkPath& aPath, FillRule aFillRule);
+ explicit PathBuilderSkia(FillRule aFillRule);
+
+ 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;
+ virtual already_AddRefed<Path> Finish();
+
+ void AppendPath(const SkPath &aPath);
+
+ virtual BackendType GetBackendType() const { return BackendType::SKIA; }
+
+private:
+
+ void SetFillRule(FillRule aFillRule);
+
+ SkPath mPath;
+ FillRule mFillRule;
+};
+
+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 BackendType::SKIA; }
+
+ virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const;
+ virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
+ FillRule aFillRule) const;
+
+ virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
+
+ virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
+ const Point &aPoint,
+ const Matrix &aTransform) const;
+
+ virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
+
+ 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; }
+
+private:
+ friend class DrawTargetSkia;
+
+ SkPath mPath;
+ FillRule mFillRule;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_PATH_SKIA_H_ */
diff --git a/gfx/2d/PatternHelpers.h b/gfx/2d/PatternHelpers.h
new file mode 100644
index 000000000..763b30a6f
--- /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(),
+ SamplingFilter aSamplingFilter = SamplingFilter::GOOD,
+ const IntRect &aSamplingRect = IntRect()) {
+ MOZ_ASSERT(!mPattern);
+ mPattern = new (mSurfacePattern.addr())
+ SurfacePattern(aSourceSurface, aExtendMode, aMatrix, aSamplingFilter, aSamplingRect);
+ return mSurfacePattern.addr();
+ }
+
+ Pattern* GetPattern() {
+ return mPattern;
+ }
+
+ const Pattern* GetPattern() const {
+ return mPattern;
+ }
+
+ operator Pattern&() {
+ if (!mPattern) {
+ MOZ_CRASH("GFX: 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
new file mode 100644
index 000000000..f66967622
--- /dev/null
+++ b/gfx/2d/Point.h
@@ -0,0 +1,343 @@
+/* -*- 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_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 {};
+
+} // namespace gfx
+
+template<> struct IsPixel<gfx::UnknownUnits> : TrueType {};
+
+namespace gfx {
+
+/// Use this for parameters of functions to allow implicit conversions to
+/// integer types but not floating point types.
+/// We use this wrapper to prevent IntSize and IntPoint's constructors to
+/// take foating point values as parameters, and not require their constructors
+/// to have implementations for each permutation of integer types.
+template<typename T>
+struct IntParam {
+ constexpr MOZ_IMPLICIT IntParam(char val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned char val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(short val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned short val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(int val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned int val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(long val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned long val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(long long val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned long long val) : value(val) {}
+ template<typename Unit>
+ constexpr MOZ_IMPLICIT IntParam(IntCoordTyped<Unit> val) : value(val) {}
+
+ // Disable the evil ones!
+ MOZ_IMPLICIT IntParam(float val) = delete;
+ MOZ_IMPLICIT IntParam(double val) = delete;
+
+ T value;
+};
+
+template<class units, class> struct PointTyped;
+template<class units, class> struct SizeTyped;
+
+template<class units>
+struct IntPointTyped :
+ public BasePoint< int32_t, IntPointTyped<units>, IntCoordTyped<units> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef IntParam<int32_t> ToInt;
+ typedef IntCoordTyped<units> Coord;
+ typedef BasePoint< int32_t, IntPointTyped<units>, IntCoordTyped<units> > Super;
+
+ constexpr IntPointTyped() : Super() {}
+ constexpr IntPointTyped(ToInt aX, ToInt aY) : Super(Coord(aX.value), Coord(aY.value)) {}
+
+ static IntPointTyped<units> Round(float aX, float aY) {
+ return IntPointTyped(int32_t(floorf(aX + 0.5)), int32_t(floorf(aY + 0.5)));
+ }
+
+ static IntPointTyped<units> Ceil(float aX, float aY) {
+ return IntPointTyped(int32_t(ceil(aX)), int32_t(ceil(aY)));
+ }
+
+ static IntPointTyped<units> Floor(float aX, float aY) {
+ return IntPointTyped(int32_t(floorf(aX)), int32_t(floorf(aY)));
+ }
+
+ static IntPointTyped<units> Truncate(float aX, float aY) {
+ return IntPointTyped(int32_t(aX), int32_t(aY));
+ }
+
+ static IntPointTyped<units> Round(const PointTyped<units, float>& aPoint);
+ static IntPointTyped<units> Ceil(const PointTyped<units, float>& aPoint);
+ static IntPointTyped<units> Floor(const PointTyped<units, float>& aPoint);
+ static IntPointTyped<units> Truncate(const PointTyped<units, float>& aPoint);
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static IntPointTyped<units> FromUnknownPoint(const IntPointTyped<UnknownUnits>& aPoint) {
+ return IntPointTyped<units>(aPoint.x, aPoint.y);
+ }
+
+ IntPointTyped<UnknownUnits> ToUnknownPoint() const {
+ return IntPointTyped<UnknownUnits>(this->x, this->y);
+ }
+};
+typedef IntPointTyped<UnknownUnits> IntPoint;
+
+template<class units, class F = Float>
+struct PointTyped :
+ public BasePoint< F, PointTyped<units, F>, CoordTyped<units, F> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef CoordTyped<units, F> Coord;
+ typedef BasePoint< F, PointTyped<units, F>, CoordTyped<units, F> > Super;
+
+ constexpr PointTyped() : Super() {}
+ constexpr PointTyped(F aX, F 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.
+ constexpr PointTyped(F aX, Coord aY) : Super(Coord(aX), aY) {}
+ constexpr PointTyped(Coord aX, F aY) : Super(aX, Coord(aY)) {}
+ constexpr PointTyped(Coord aX, Coord aY) : Super(aX.value, aY.value) {}
+ constexpr MOZ_IMPLICIT PointTyped(const IntPointTyped<units>& point) : Super(F(point.x), F(point.y)) {}
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static PointTyped<units, F> FromUnknownPoint(const PointTyped<UnknownUnits, F>& aPoint) {
+ return PointTyped<units, F>(aPoint.x, aPoint.y);
+ }
+
+ PointTyped<UnknownUnits, F> ToUnknownPoint() const {
+ return PointTyped<UnknownUnits, F>(this->x, this->y);
+ }
+};
+typedef PointTyped<UnknownUnits> Point;
+typedef PointTyped<UnknownUnits, double> PointDouble;
+
+template<class units>
+IntPointTyped<units> RoundedToInt(const PointTyped<units>& aPoint) {
+ return IntPointTyped<units>::Round(aPoint.x, aPoint.y);
+}
+
+template<class units>
+IntPointTyped<units> TruncatedToInt(const PointTyped<units>& aPoint) {
+ return IntPointTyped<units>::Truncate(aPoint.x, aPoint.y);
+}
+
+template<class units, class F = Float>
+struct Point3DTyped :
+ public BasePoint3D< F, Point3DTyped<units, F> > {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BasePoint3D< F, Point3DTyped<units, F> > Super;
+
+ Point3DTyped() : Super() {}
+ Point3DTyped(F aX, F aY, F 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, F> FromUnknownPoint(const Point3DTyped<UnknownUnits, F>& aPoint) {
+ return Point3DTyped<units, F>(aPoint.x, aPoint.y, aPoint.z);
+ }
+
+ Point3DTyped<UnknownUnits, F> ToUnknownPoint() const {
+ return Point3DTyped<UnknownUnits, F>(this->x, this->y, this->z);
+ }
+};
+typedef Point3DTyped<UnknownUnits> Point3D;
+typedef Point3DTyped<UnknownUnits, double> PointDouble3D;
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Round(const PointTyped<units, float>& aPoint)
+{
+ return IntPointTyped::Round(aPoint.x, aPoint.y);
+}
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Ceil(const PointTyped<units, float>& aPoint)
+{
+ return IntPointTyped::Ceil(aPoint.x, aPoint.y);
+}
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Floor(const PointTyped<units, float>& aPoint)
+{
+ return IntPointTyped::Floor(aPoint.x, aPoint.y);
+}
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Truncate(const PointTyped<units, float>& aPoint)
+{
+ return IntPointTyped::Truncate(aPoint.x, aPoint.y);
+}
+
+template<class units, class F = Float>
+struct Point4DTyped :
+ public BasePoint4D< F, Point4DTyped<units, F> > {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BasePoint4D< F, Point4DTyped<units, F> > Super;
+
+ Point4DTyped() : Super() {}
+ Point4DTyped(F aX, F aY, F aZ, F 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, F> FromUnknownPoint(const Point4DTyped<UnknownUnits, F>& aPoint) {
+ return Point4DTyped<units, F>(aPoint.x, aPoint.y, aPoint.z, aPoint.w);
+ }
+
+ Point4DTyped<UnknownUnits, F> ToUnknownPoint() const {
+ return Point4DTyped<UnknownUnits, F>(this->x, this->y, this->z, this->w);
+ }
+
+ PointTyped<units, F> As2DPoint() {
+ return PointTyped<units, F>(this->x / this->w, this->y / this->w);
+ }
+};
+typedef Point4DTyped<UnknownUnits> Point4D;
+typedef Point4DTyped<UnknownUnits, double> PointDouble4D;
+
+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 IntParam<int32_t> ToInt;
+ typedef BaseSize< int32_t, IntSizeTyped<units> > Super;
+
+ constexpr IntSizeTyped() : Super() {}
+ constexpr IntSizeTyped(ToInt aWidth, ToInt aHeight) : Super(aWidth.value, aHeight.value) {}
+
+ static IntSizeTyped<units> Round(float aWidth, float aHeight) {
+ return IntSizeTyped(int32_t(floorf(aWidth + 0.5)), int32_t(floorf(aHeight + 0.5)));
+ }
+
+ static IntSizeTyped<units> Truncate(float aWidth, float aHeight) {
+ return IntSizeTyped(int32_t(aWidth), int32_t(aHeight));
+ }
+
+ static IntSizeTyped<units> Ceil(float aWidth, float aHeight) {
+ return IntSizeTyped(int32_t(ceil(aWidth)), int32_t(ceil(aHeight)));
+ }
+
+ static IntSizeTyped<units> Floor(float aWidth, float aHeight) {
+ return IntSizeTyped(int32_t(floorf(aWidth)), int32_t(floorf(aHeight)));
+ }
+
+ static IntSizeTyped<units> Round(const SizeTyped<units, float>& aSize);
+ static IntSizeTyped<units> Ceil(const SizeTyped<units, float>& aSize);
+ static IntSizeTyped<units> Floor(const SizeTyped<units, float>& aSize);
+ static IntSizeTyped<units> Truncate(const SizeTyped<units, float>& aSize);
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static IntSizeTyped<units> FromUnknownSize(const IntSizeTyped<UnknownUnits>& aSize) {
+ return IntSizeTyped<units>(aSize.width, aSize.height);
+ }
+
+ IntSizeTyped<UnknownUnits> ToUnknownSize() const {
+ return IntSizeTyped<UnknownUnits>(this->width, this->height);
+ }
+};
+typedef IntSizeTyped<UnknownUnits> IntSize;
+
+template<class units, class F = Float>
+struct SizeTyped :
+ public BaseSize< F, SizeTyped<units> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseSize< F, SizeTyped<units, F> > Super;
+
+ constexpr SizeTyped() : Super() {}
+ constexpr SizeTyped(F aWidth, F aHeight) : Super(aWidth, aHeight) {}
+ explicit SizeTyped(const IntSizeTyped<units>& size) :
+ Super(F(size.width), F(size.height)) {}
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static SizeTyped<units, F> FromUnknownSize(const SizeTyped<UnknownUnits, F>& aSize) {
+ return SizeTyped<units, F>(aSize.width, aSize.height);
+ }
+
+ SizeTyped<UnknownUnits, F> ToUnknownSize() const {
+ return SizeTyped<UnknownUnits, F>(this->width, this->height);
+ }
+};
+typedef SizeTyped<UnknownUnits> Size;
+typedef SizeTyped<UnknownUnits, double> SizeDouble;
+
+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)));
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Round(const SizeTyped<units, float>& aSize) {
+ return IntSizeTyped::Round(aSize.width, aSize.height);
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Ceil(const SizeTyped<units, float>& aSize) {
+ return IntSizeTyped::Ceil(aSize.width, aSize.height);
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Floor(const SizeTyped<units, float>& aSize) {
+ return IntSizeTyped::Floor(aSize.width, aSize.height);
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Truncate(const SizeTyped<units, float>& aSize) {
+ return IntSizeTyped::Truncate(aSize.width, aSize.height);
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_POINT_H_ */
diff --git a/gfx/2d/Polygon.h b/gfx/2d/Polygon.h
new file mode 100644
index 000000000..e1738684c
--- /dev/null
+++ b/gfx/2d/Polygon.h
@@ -0,0 +1,295 @@
+/* -*- 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_POLYGON_H
+#define MOZILLA_GFX_POLYGON_H
+
+#include "Matrix.h"
+#include "mozilla/Move.h"
+#include "nsTArray.h"
+#include "Point.h"
+#include "Triangle.h"
+
+#include <initializer_list>
+
+namespace mozilla {
+namespace gfx {
+
+// Polygon3DTyped stores the points of a convex planar polygon.
+template<class Units>
+class Polygon3DTyped {
+public:
+ Polygon3DTyped() {}
+
+ explicit Polygon3DTyped(const std::initializer_list<Point3DTyped<Units>>& aPoints,
+ Point3DTyped<Units> aNormal =
+ Point3DTyped<Units>(0.0f, 0.0f, 1.0f))
+ : mNormal(aNormal), mPoints(aPoints)
+ {
+#ifdef DEBUG
+ EnsurePlanarPolygon();
+#endif
+ }
+
+ explicit Polygon3DTyped(nsTArray<Point3DTyped<Units>>&& aPoints,
+ Point3DTyped<Units> aNormal =
+ Point3DTyped<Units>(0.0f, 0.0f, 1.0f))
+ : mNormal(aNormal), mPoints(Move(aPoints))
+ {
+#ifdef DEBUG
+ EnsurePlanarPolygon();
+#endif
+ }
+
+ explicit Polygon3DTyped(const nsTArray<Point3DTyped<Units>>& aPoints,
+ Point3DTyped<Units> aNormal =
+ Point3DTyped<Units>(0.0f, 0.0f, 1.0f))
+ : mNormal(aNormal), mPoints(aPoints)
+ {
+#ifdef DEBUG
+ EnsurePlanarPolygon();
+#endif
+ }
+
+ RectTyped<Units> BoundingBox() const
+ {
+ float minX, maxX, minY, maxY;
+ minX = maxX = mPoints[0].x;
+ minY = maxY = mPoints[0].y;
+
+ for (const Point3DTyped<Units>& point : mPoints) {
+ minX = std::min(point.x, minX);
+ maxX = std::max(point.x, maxX);
+
+ minY = std::min(point.y, minY);
+ maxY = std::max(point.y, maxY);
+ }
+
+ return RectTyped<Units>(minX, minY, maxX - minX, maxY - minY);
+ }
+
+ nsTArray<float>
+ CalculateDotProducts(const Polygon3DTyped<Units>& aPlane,
+ size_t& aPos, size_t& aNeg) const
+ {
+ // Point classification might produce incorrect results due to numerical
+ // inaccuracies. Using an epsilon value makes the splitting plane "thicker".
+ const float epsilon = 0.05f;
+
+ MOZ_ASSERT(!aPlane.GetPoints().IsEmpty());
+ const Point3DTyped<Units>& planeNormal = aPlane.GetNormal();
+ const Point3DTyped<Units>& planePoint = aPlane[0];
+
+ aPos = aNeg = 0;
+ nsTArray<float> dotProducts;
+ for (const Point3DTyped<Units>& point : mPoints) {
+ float dot = (point - planePoint).DotProduct(planeNormal);
+
+ if (dot > epsilon) {
+ aPos++;
+ } else if (dot < -epsilon) {
+ aNeg++;
+ } else {
+ // The point is within the thick plane.
+ dot = 0.0f;
+ }
+
+ dotProducts.AppendElement(dot);
+ }
+
+ return dotProducts;
+ }
+
+ // Clips the polygon against the given 2D rectangle.
+ Polygon3DTyped<Units> ClipPolygon(const RectTyped<Units>& aRect) const
+ {
+ Polygon3DTyped<Units> polygon(mPoints, mNormal);
+
+ // Left edge
+ ClipPolygonWithEdge(polygon, aRect.BottomLeft(), aRect.TopLeft());
+
+ // Bottom edge
+ ClipPolygonWithEdge(polygon, aRect.BottomRight(), aRect.BottomLeft());
+
+ // Right edge
+ ClipPolygonWithEdge(polygon, aRect.TopRight(), aRect.BottomRight());
+
+ // Top edge
+ ClipPolygonWithEdge(polygon, aRect.TopLeft(), aRect.TopRight());
+
+ return polygon;
+ }
+
+ const Point3DTyped<Units>& GetNormal() const
+ {
+ return mNormal;
+ }
+
+ const nsTArray<Point3DTyped<Units>>& GetPoints() const
+ {
+ return mPoints;
+ }
+
+ const Point3DTyped<Units>& operator[](size_t aIndex) const
+ {
+ MOZ_ASSERT(mPoints.Length() > aIndex);
+ return mPoints[aIndex];
+ }
+
+ void SplitPolygon(const Polygon3DTyped<Units>& aSplittingPlane,
+ const nsTArray<float>& aDots,
+ nsTArray<Point3DTyped<Units>>& aBackPoints,
+ nsTArray<Point3DTyped<Units>>& aFrontPoints) const
+ {
+ static const auto Sign = [](const float& f) {
+ if (f > 0.0f) return 1;
+ if (f < 0.0f) return -1;
+ return 0;
+ };
+
+ const Point3DTyped<Units>& normal = aSplittingPlane.GetNormal();
+ const size_t pointCount = mPoints.Length();
+
+ for (size_t i = 0; i < pointCount; ++i) {
+ size_t j = (i + 1) % pointCount;
+
+ const Point3DTyped<Units>& a = mPoints[i];
+ const Point3DTyped<Units>& b = mPoints[j];
+ const float dotA = aDots[i];
+ const float dotB = aDots[j];
+
+ // The point is in front of or on the plane.
+ if (dotA >= 0) {
+ aFrontPoints.AppendElement(a);
+ }
+
+ // The point is behind or on the plane.
+ if (dotA <= 0) {
+ aBackPoints.AppendElement(a);
+ }
+
+ // If the sign of the dot products changes between two consecutive
+ // vertices, then the plane intersects with the polygon edge.
+ // The case where the polygon edge is within the plane is handled above.
+ if (Sign(dotA) && Sign(dotB) && Sign(dotA) != Sign(dotB)) {
+ // Calculate the line segment and plane intersection point.
+ const Point3DTyped<Units> ab = b - a;
+ const float dotAB = ab.DotProduct(normal);
+ const float t = -dotA / dotAB;
+ const Point3DTyped<Units> p = a + (ab * t);
+
+ // Add the intersection point to both polygons.
+ aBackPoints.AppendElement(p);
+ aFrontPoints.AppendElement(p);
+ }
+ }
+ }
+
+ nsTArray<TriangleTyped<Units>> ToTriangles() const
+ {
+ nsTArray<TriangleTyped<Units>> triangles;
+
+ if (mPoints.Length() < 3) {
+ return triangles;
+ }
+
+ for (size_t i = 1; i < mPoints.Length() - 1; ++i) {
+ TriangleTyped<Units> triangle(Point(mPoints[0].x, mPoints[0].y),
+ Point(mPoints[i].x, mPoints[i].y),
+ Point(mPoints[i+1].x, mPoints[i+1].y));
+ triangles.AppendElement(Move(triangle));
+ }
+
+ return triangles;
+ }
+
+ void TransformToLayerSpace(const Matrix4x4Typed<Units, Units>& aTransform)
+ {
+ TransformPoints(aTransform);
+ mNormal = Point3DTyped<Units>(0.0f, 0.0f, 1.0f);
+ }
+
+ void TransformToScreenSpace(const Matrix4x4Typed<Units, Units>& aTransform)
+ {
+ TransformPoints(aTransform);
+
+ // Normal vectors should be transformed using inverse transpose.
+ mNormal = aTransform.Inverse().Transpose().TransformPoint(mNormal);
+ }
+
+private:
+ void ClipPolygonWithEdge(Polygon3DTyped<Units>& aPolygon,
+ const PointTyped<Units>& aFirst,
+ const PointTyped<Units>& aSecond) const
+ {
+ const Point3DTyped<Units> a(aFirst.x, aFirst.y, 0.0f);
+ const Point3DTyped<Units> b(aSecond.x, aSecond.y, 0.0f);
+ const Point3DTyped<Units> normal(b.y - a.y, a.x - b.x, 0.0f);
+ Polygon3DTyped<Units> plane({a, b}, normal);
+
+ size_t pos, neg;
+ nsTArray<float> dots = aPolygon.CalculateDotProducts(plane, pos, neg);
+
+ nsTArray<Point3DTyped<Units>> backPoints, frontPoints;
+ aPolygon.SplitPolygon(plane, dots, backPoints, frontPoints);
+
+ // Only use the points that are behind the clipping plane.
+ aPolygon = Polygon3DTyped<Units>(Move(backPoints), aPolygon.GetNormal());
+ }
+
+#ifdef DEBUG
+ void EnsurePlanarPolygon() const
+ {
+ if (mPoints.Length() <= 3) {
+ // Polygons with three or less points are guaranteed to be planar.
+ return;
+ }
+
+ // This normal calculation method works only for planar polygons.
+ // The resulting normal vector will point towards the viewer when the
+ // polygon has a counter-clockwise winding order from the perspective
+ // of the viewer.
+ Point3DTyped<Units> normal;
+
+ for (size_t i = 1; i < mPoints.Length() - 1; ++i) {
+ normal +=
+ (mPoints[i] - mPoints[0]).CrossProduct(mPoints[i + 1] - mPoints[0]);
+ }
+
+ // Ensure that at least one component is greater than zero.
+ // This avoids division by zero when normalizing the vector.
+ bool hasNonZeroComponent = std::abs(normal.x) > 0.0f ||
+ std::abs(normal.y) > 0.0f ||
+ std::abs(normal.z) > 0.0f;
+ MOZ_ASSERT(hasNonZeroComponent);
+
+ normal.Normalize();
+
+ // Ensure that the polygon is planar.
+ // http://mathworld.wolfram.com/Point-PlaneDistance.html
+ const float epsilon = 0.01f;
+ for (const Point3DTyped<Units>& point : mPoints) {
+ float d = normal.DotProduct(point - mPoints[0]);
+ MOZ_ASSERT(std::abs(d) < epsilon);
+ }
+ }
+#endif
+ void TransformPoints(const Matrix4x4Typed<Units, Units>& aTransform)
+ {
+ for (Point3DTyped<Units>& point : mPoints) {
+ point = aTransform.TransformPoint(point);
+ }
+ }
+
+ Point3DTyped<Units> mNormal;
+ nsTArray<Point3DTyped<Units>> mPoints;
+};
+
+typedef Polygon3DTyped<UnknownUnits> Polygon3D;
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_POLYGON_H */
diff --git a/gfx/2d/QuartzSupport.h b/gfx/2d/QuartzSupport.h
new file mode 100644
index 000000000..f56fcb77c
--- /dev/null
+++ b/gfx/2d/QuartzSupport.h
@@ -0,0 +1,98 @@
+/* -*- 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/. */
+
+#ifndef nsCoreAnimationSupport_h__
+#define nsCoreAnimationSupport_h__
+#ifdef XP_MACOSX
+
+#import <OpenGL/OpenGL.h>
+#import <OpenGL/gl.h>
+#import "ApplicationServices/ApplicationServices.h"
+#include "gfxTypes.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "nsError.h"
+
+// Get the system color space.
+CGColorSpaceRef CreateSystemColorSpace();
+
+// Manages a CARenderer
+struct _CGLContextObject;
+
+enum AllowOfflineRendererEnum { ALLOW_OFFLINE_RENDERER, DISALLOW_OFFLINE_RENDERER };
+
+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),
+ mUnsupportedWidth(UINT32_MAX), mUnsupportedHeight(UINT32_MAX),
+ mAllowOfflineRenderer(DISALLOW_OFFLINE_RENDERER),
+ mContentsScaleFactor(1.0) {}
+ ~nsCARenderer();
+ // aWidth and aHeight are 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 device pixel. Multiply
+ // display pixels by aContentsScaleFactor to get device pixels.
+ nsresult SetupRenderer(void* aCALayer, int aWidth, int aHeight,
+ double aContentsScaleFactor,
+ AllowOfflineRendererEnum aAllowOfflineRenderer);
+ // aWidth and aHeight are in "display pixels". Multiply by
+ // aContentsScaleFactor to get device pixels.
+ nsresult Render(int aWidth, int aHeight,
+ double aContentsScaleFactor,
+ CGImageRef *aOutCAImage);
+ bool isInit() { return mCARenderer != nullptr; }
+ /*
+ * Render the CALayer to an IOSurface. If no IOSurface
+ * is attached then an internal pixel buffer will be
+ * used.
+ */
+ void AttachIOSurface(MacIOSurface *aSurface);
+ IOSurfaceID GetIOSurfaceID();
+ // aX, aY, aWidth and aHeight are in "display pixels". Multiply by
+ // surf->GetContentsScaleFactor() to get device pixels.
+ static nsresult DrawSurfaceToCGContext(CGContextRef aContext,
+ MacIOSurface *surf,
+ CGColorSpaceRef aColorSpace,
+ int aX, int aY,
+ size_t aWidth, size_t aHeight);
+
+ // Remove & Add the layer without destroying
+ // the renderer for fast back buffer swapping.
+ void DetachCALayer();
+ void AttachCALayer(void *aCALayer);
+#ifdef DEBUG
+ static void SaveToDisk(MacIOSurface *surf);
+#endif
+private:
+ // aWidth and aHeight are in "display pixels". Multiply by
+ // mContentsScaleFactor to get device pixels.
+ void SetBounds(int aWidth, int aHeight);
+ // aWidth and aHeight are in "display pixels". Multiply by
+ // mContentsScaleFactor to get device pixels.
+ void SetViewport(int aWidth, int aHeight);
+ void Destroy();
+
+ void *mCARenderer;
+ void *mWrapperCALayer;
+ GLuint mFBOTexture;
+ _CGLContextObject *mOpenGLContext;
+ CGImageRef mCGImage;
+ void *mCGData;
+ RefPtr<MacIOSurface> mIOSurface;
+ uint32_t mFBO;
+ uint32_t mIOTexture;
+ int mUnsupportedWidth;
+ int mUnsupportedHeight;
+ AllowOfflineRendererEnum mAllowOfflineRenderer;
+ double mContentsScaleFactor;
+};
+
+#endif // XP_MACOSX
+#endif // nsCoreAnimationSupport_h__
+
diff --git a/gfx/2d/QuartzSupport.mm b/gfx/2d/QuartzSupport.mm
new file mode 100644
index 000000000..464db5ece
--- /dev/null
+++ b/gfx/2d/QuartzSupport.mm
@@ -0,0 +1,625 @@
+/* -*- 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 "QuartzSupport.h"
+#include "nsDebug.h"
+#include "MacIOSurface.h"
+#include "mozilla/Sprintf.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"
+#define OPENGL_FRAMEWORK_PATH \
+ "/System/Library/Frameworks/OpenGL.framework/OpenGL"
+#define COREGRAPHICS_FRAMEWORK_PATH \
+ "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/CoreGraphics"
+
+@interface CALayer (ContentsScale)
+- (double)contentsScale;
+- (void)setContentsScale:(double)scale;
+@end
+
+
+CGColorSpaceRef CreateSystemColorSpace() {
+ CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID());
+ if (!cspace) {
+ cspace = ::CGColorSpaceCreateDeviceRGB();
+ }
+ return cspace;
+}
+
+nsCARenderer::~nsCARenderer() {
+ Destroy();
+}
+
+void cgdata_release_callback(void *aCGData, const void *data, size_t size) {
+ if (aCGData) {
+ free(aCGData);
+ }
+}
+
+void nsCARenderer::Destroy() {
+ if (mCARenderer) {
+ CARenderer* caRenderer = (CARenderer*)mCARenderer;
+ // Bug 556453:
+ // Explicitly remove the layer from the renderer
+ // otherwise it does not always happen right away.
+ caRenderer.layer = nullptr;
+ [caRenderer release];
+ }
+ if (mWrapperCALayer) {
+ CALayer* wrapperLayer = (CALayer*)mWrapperCALayer;
+ [wrapperLayer release];
+ }
+ if (mOpenGLContext) {
+ if (mFBO || mIOTexture || mFBOTexture) {
+ // Release these resources with the context that allocated them
+ CGLContextObj oldContext = ::CGLGetCurrentContext();
+ ::CGLSetCurrentContext(mOpenGLContext);
+
+ if (mFBOTexture) {
+ ::glDeleteTextures(1, &mFBOTexture);
+ }
+ if (mIOTexture) {
+ ::glDeleteTextures(1, &mIOTexture);
+ }
+ if (mFBO) {
+ ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ ::glDeleteFramebuffersEXT(1, &mFBO);
+ }
+
+ if (oldContext)
+ ::CGLSetCurrentContext(oldContext);
+ }
+ ::CGLDestroyContext((CGLContextObj)mOpenGLContext);
+ }
+ if (mCGImage) {
+ ::CGImageRelease(mCGImage);
+ }
+ // mCGData is deallocated by cgdata_release_callback
+
+ mCARenderer = nil;
+ mWrapperCALayer = nil;
+ mFBOTexture = 0;
+ mOpenGLContext = nullptr;
+ mCGImage = nullptr;
+ mIOSurface = nullptr;
+ mFBO = 0;
+ mIOTexture = 0;
+}
+
+nsresult nsCARenderer::SetupRenderer(void *aCALayer, int aWidth, int aHeight,
+ double aContentsScaleFactor,
+ AllowOfflineRendererEnum aAllowOfflineRenderer) {
+ mAllowOfflineRenderer = aAllowOfflineRenderer;
+
+ if (aWidth == 0 || aHeight == 0 || aContentsScaleFactor <= 0)
+ return NS_ERROR_FAILURE;
+
+ if (aWidth == mUnsupportedWidth &&
+ aHeight == mUnsupportedHeight) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CGLPixelFormatAttribute attributes[] = {
+ kCGLPFAAccelerated,
+ kCGLPFADepthSize, (CGLPixelFormatAttribute)24,
+ kCGLPFAAllowOfflineRenderers,
+ (CGLPixelFormatAttribute)0
+ };
+
+ if (mAllowOfflineRenderer == DISALLOW_OFFLINE_RENDERER) {
+ attributes[3] = (CGLPixelFormatAttribute)0;
+ }
+
+ GLint screen;
+ CGLPixelFormatObj format;
+ if (::CGLChoosePixelFormat(attributes, &format, &screen) != kCGLNoError) {
+ mUnsupportedWidth = aWidth;
+ mUnsupportedHeight = aHeight;
+ Destroy();
+ return NS_ERROR_FAILURE;
+ }
+
+ if (::CGLCreateContext(format, nullptr, &mOpenGLContext) != kCGLNoError) {
+ mUnsupportedWidth = aWidth;
+ mUnsupportedHeight = aHeight;
+ Destroy();
+ return NS_ERROR_FAILURE;
+ }
+ ::CGLDestroyPixelFormat(format);
+
+ CARenderer* caRenderer = [[CARenderer rendererWithCGLContext:mOpenGLContext
+ options:nil] retain];
+ if (caRenderer == nil) {
+ mUnsupportedWidth = aWidth;
+ mUnsupportedHeight = aHeight;
+ Destroy();
+ return NS_ERROR_FAILURE;
+ }
+ CALayer* wrapperCALayer = [[CALayer layer] retain];
+ if (wrapperCALayer == nil) {
+ [caRenderer release];
+ mUnsupportedWidth = aWidth;
+ mUnsupportedHeight = aHeight;
+ Destroy();
+ return NS_ERROR_FAILURE;
+ }
+
+ mCARenderer = caRenderer;
+ mWrapperCALayer = wrapperCALayer;
+ caRenderer.layer = wrapperCALayer;
+ [wrapperCALayer addSublayer:(CALayer*)aCALayer];
+ mContentsScaleFactor = aContentsScaleFactor;
+ size_t intScaleFactor = ceil(mContentsScaleFactor);
+ SetBounds(aWidth, aHeight);
+
+ // We target rendering to a CGImage if no shared IOSurface are given.
+ if (!mIOSurface) {
+ mCGData = malloc(aWidth*intScaleFactor*aHeight*4*intScaleFactor);
+ if (!mCGData) {
+ mUnsupportedWidth = aWidth;
+ mUnsupportedHeight = aHeight;
+ Destroy();
+ return NS_ERROR_FAILURE;
+ }
+ memset(mCGData, 0, aWidth*intScaleFactor*aHeight*4*intScaleFactor);
+
+ CGDataProviderRef dataProvider = nullptr;
+ dataProvider = ::CGDataProviderCreateWithData(mCGData,
+ mCGData, aHeight*intScaleFactor*aWidth*4*intScaleFactor,
+ cgdata_release_callback);
+ if (!dataProvider) {
+ cgdata_release_callback(mCGData, mCGData,
+ aHeight*intScaleFactor*aWidth*4*intScaleFactor);
+ mUnsupportedWidth = aWidth;
+ mUnsupportedHeight = aHeight;
+ Destroy();
+ return NS_ERROR_FAILURE;
+ }
+
+ CGColorSpaceRef colorSpace = CreateSystemColorSpace();
+
+ mCGImage = ::CGImageCreate(aWidth * intScaleFactor, aHeight * intScaleFactor,
+ 8, 32, aWidth * intScaleFactor * 4, colorSpace,
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
+ dataProvider, nullptr, true, kCGRenderingIntentDefault);
+
+ ::CGDataProviderRelease(dataProvider);
+ ::CGColorSpaceRelease(colorSpace);
+ if (!mCGImage) {
+ mUnsupportedWidth = aWidth;
+ mUnsupportedHeight = aHeight;
+ Destroy();
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ CGLContextObj oldContext = ::CGLGetCurrentContext();
+ ::CGLSetCurrentContext(mOpenGLContext);
+
+ if (mIOSurface) {
+ // Create the IOSurface mapped texture.
+ ::glGenTextures(1, &mIOTexture);
+ ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture);
+ ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB,
+ GL_RGBA, aWidth * intScaleFactor,
+ aHeight * intScaleFactor,
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
+ mIOSurface->mIOSurfacePtr, 0);
+ ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+ } else {
+ ::glGenTextures(1, &mFBOTexture);
+ ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFBOTexture);
+ ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+ }
+
+ // Create the fbo
+ ::glGenFramebuffersEXT(1, &mFBO);
+ ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
+ if (mIOSurface) {
+ ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_RECTANGLE_ARB, mIOTexture, 0);
+ } else {
+ ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_RECTANGLE_ARB, mFBOTexture, 0);
+ }
+
+
+ // Make sure that the Framebuffer configuration is supported on the client machine
+ GLenum fboStatus;
+ fboStatus = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ if (fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT) {
+ NS_ERROR("FBO not supported");
+ if (oldContext)
+ ::CGLSetCurrentContext(oldContext);
+ mUnsupportedWidth = aWidth;
+ mUnsupportedHeight = aHeight;
+ Destroy();
+ return NS_ERROR_FAILURE;
+ }
+
+ SetViewport(aWidth, aHeight);
+
+ GLenum result = ::glGetError();
+ if (result != GL_NO_ERROR) {
+ NS_ERROR("Unexpected OpenGL Error");
+ mUnsupportedWidth = aWidth;
+ mUnsupportedHeight = aHeight;
+ Destroy();
+ if (oldContext)
+ ::CGLSetCurrentContext(oldContext);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (oldContext)
+ ::CGLSetCurrentContext(oldContext);
+
+ return NS_OK;
+}
+
+void nsCARenderer::SetBounds(int aWidth, int aHeight) {
+ CARenderer* caRenderer = (CARenderer*)mCARenderer;
+ CALayer* wrapperLayer = (CALayer*)mWrapperCALayer;
+ NSArray* sublayers = [wrapperLayer sublayers];
+ CALayer* pluginLayer = (CALayer*) [sublayers objectAtIndex:0];
+
+ // Create a transaction and disable animations
+ // to make the position update instant.
+ [CATransaction begin];
+ NSMutableDictionary *newActions =
+ [[NSMutableDictionary alloc] initWithObjectsAndKeys:
+ [NSNull null], @"onOrderIn",
+ [NSNull null], @"onOrderOut",
+ [NSNull null], @"sublayers",
+ [NSNull null], @"contents",
+ [NSNull null], @"position",
+ [NSNull null], @"bounds",
+ nil];
+ wrapperLayer.actions = newActions;
+ [newActions release];
+
+ // If we're in HiDPI mode, mContentsScaleFactor will (presumably) be 2.0.
+ // For some reason, to make things work properly in HiDPI mode we need to
+ // make caRenderer's 'bounds' and 'layer' different sizes -- to set 'bounds'
+ // to the size of 'layer's backing store. And to avoid this possibly
+ // confusing the plugin, we need to hide it's effects from the plugin by
+ // making pluginLayer (usually the CALayer* provided by the plugin) a
+ // sublayer of our own wrapperLayer (see bug 829284).
+ size_t intScaleFactor = ceil(mContentsScaleFactor);
+ [CATransaction setValue: [NSNumber numberWithFloat:0.0f] forKey: kCATransactionAnimationDuration];
+ [CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions];
+ [wrapperLayer setBounds:CGRectMake(0, 0, aWidth, aHeight)];
+ [wrapperLayer setPosition:CGPointMake(aWidth/2.0, aHeight/2.0)];
+ [pluginLayer setBounds:CGRectMake(0, 0, aWidth, aHeight)];
+ [pluginLayer setFrame:CGRectMake(0, 0, aWidth, aHeight)];
+ caRenderer.bounds = CGRectMake(0, 0, aWidth * intScaleFactor, aHeight * intScaleFactor);
+ if (mContentsScaleFactor != 1.0) {
+ CGAffineTransform affineTransform = [wrapperLayer affineTransform];
+ affineTransform.a = mContentsScaleFactor;
+ affineTransform.d = mContentsScaleFactor;
+ affineTransform.tx = ((double)aWidth)/mContentsScaleFactor;
+ affineTransform.ty = ((double)aHeight)/mContentsScaleFactor;
+ [wrapperLayer setAffineTransform:affineTransform];
+ } else {
+ // These settings are the default values. But they might have been
+ // changed as above if we were previously running in a HiDPI mode
+ // (i.e. if we just switched from that to a non-HiDPI mode).
+ [wrapperLayer setAffineTransform:CGAffineTransformIdentity];
+ }
+ [CATransaction commit];
+}
+
+void nsCARenderer::SetViewport(int aWidth, int aHeight) {
+ size_t intScaleFactor = ceil(mContentsScaleFactor);
+ aWidth *= intScaleFactor;
+ aHeight *= intScaleFactor;
+
+ ::glViewport(0.0, 0.0, aWidth, aHeight);
+ ::glMatrixMode(GL_PROJECTION);
+ ::glLoadIdentity();
+ ::glOrtho (0.0, aWidth, 0.0, aHeight, -1, 1);
+
+ // Render upside down to speed up CGContextDrawImage
+ ::glTranslatef(0.0f, aHeight, 0.0);
+ ::glScalef(1.0, -1.0, 1.0);
+}
+
+void nsCARenderer::AttachIOSurface(MacIOSurface *aSurface) {
+ if (mIOSurface &&
+ aSurface->GetIOSurfaceID() == mIOSurface->GetIOSurfaceID()) {
+ return;
+ }
+
+ mIOSurface = aSurface;
+
+ // Update the framebuffer and viewport
+ if (mCARenderer) {
+ CARenderer* caRenderer = (CARenderer*)mCARenderer;
+ size_t intScaleFactor = ceil(mContentsScaleFactor);
+ int width = caRenderer.bounds.size.width / intScaleFactor;
+ int height = caRenderer.bounds.size.height / intScaleFactor;
+
+ CGLContextObj oldContext = ::CGLGetCurrentContext();
+ ::CGLSetCurrentContext(mOpenGLContext);
+ ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture);
+ MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB,
+ GL_RGBA, mIOSurface->GetDevicePixelWidth(),
+ mIOSurface->GetDevicePixelHeight(),
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
+ mIOSurface->mIOSurfacePtr, 0);
+ ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+
+ // Rebind the FBO to make it live
+ ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
+
+ if (static_cast<int>(mIOSurface->GetWidth()) != width ||
+ static_cast<int>(mIOSurface->GetHeight()) != height) {
+ width = mIOSurface->GetWidth();
+ height = mIOSurface->GetHeight();
+ SetBounds(width, height);
+ SetViewport(width, height);
+ }
+
+ if (oldContext) {
+ ::CGLSetCurrentContext(oldContext);
+ }
+ }
+}
+
+IOSurfaceID nsCARenderer::GetIOSurfaceID() {
+ if (!mIOSurface) {
+ return 0;
+ }
+
+ return mIOSurface->GetIOSurfaceID();
+}
+
+nsresult nsCARenderer::Render(int aWidth, int aHeight,
+ double aContentsScaleFactor,
+ CGImageRef *aOutCGImage) {
+ if (!aOutCGImage && !mIOSurface) {
+ NS_ERROR("No target destination for rendering");
+ } else if (aOutCGImage) {
+ // We are expected to return a CGImageRef, we will set
+ // it to nullptr in case we fail before the image is ready.
+ *aOutCGImage = nullptr;
+ }
+
+ if (aWidth == 0 || aHeight == 0 || aContentsScaleFactor <= 0)
+ return NS_OK;
+
+ if (!mCARenderer || !mWrapperCALayer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CARenderer* caRenderer = (CARenderer*)mCARenderer;
+ CALayer* wrapperLayer = (CALayer*)mWrapperCALayer;
+ size_t intScaleFactor = ceil(aContentsScaleFactor);
+ int renderer_width = caRenderer.bounds.size.width / intScaleFactor;
+ int renderer_height = caRenderer.bounds.size.height / intScaleFactor;
+
+ if (renderer_width != aWidth || renderer_height != aHeight ||
+ mContentsScaleFactor != aContentsScaleFactor) {
+ // XXX: This should be optimized to not rescale the buffer
+ // if we are resizing down.
+ // caLayer may be the CALayer* provided by the plugin, so we need to
+ // preserve it across the call to Destroy().
+ NSArray* sublayers = [wrapperLayer sublayers];
+ CALayer* caLayer = (CALayer*) [sublayers objectAtIndex:0];
+ // mIOSurface is set by AttachIOSurface(), not by SetupRenderer(). So
+ // since it may have been set by a prior call to AttachIOSurface(), we
+ // need to preserve it across the call to Destroy().
+ RefPtr<MacIOSurface> ioSurface = mIOSurface;
+ Destroy();
+ mIOSurface = ioSurface;
+ if (SetupRenderer(caLayer, aWidth, aHeight, aContentsScaleFactor,
+ mAllowOfflineRenderer) != NS_OK) {
+ return NS_ERROR_FAILURE;
+ }
+
+ caRenderer = (CARenderer*)mCARenderer;
+ }
+
+ CGLContextObj oldContext = ::CGLGetCurrentContext();
+ ::CGLSetCurrentContext(mOpenGLContext);
+ if (!mIOSurface) {
+ // If no shared IOSurface is given render to our own
+ // texture for readback.
+ ::glGenTextures(1, &mFBOTexture);
+ }
+
+ GLenum result = ::glGetError();
+ if (result != GL_NO_ERROR) {
+ NS_ERROR("Unexpected OpenGL Error");
+ Destroy();
+ if (oldContext)
+ ::CGLSetCurrentContext(oldContext);
+ return NS_ERROR_FAILURE;
+ }
+
+ ::glClearColor(0.0, 0.0, 0.0, 0.0);
+ ::glClear(GL_COLOR_BUFFER_BIT);
+
+ [CATransaction commit];
+ double caTime = ::CACurrentMediaTime();
+ [caRenderer beginFrameAtTime:caTime timeStamp:nullptr];
+ [caRenderer addUpdateRect:CGRectMake(0,0, aWidth * intScaleFactor,
+ aHeight * intScaleFactor)];
+ [caRenderer render];
+ [caRenderer endFrame];
+
+ // Read the data back either to the IOSurface or mCGImage.
+ if (mIOSurface) {
+ ::glFlush();
+ } else {
+ ::glPixelStorei(GL_PACK_ALIGNMENT, 4);
+ ::glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+ ::glPixelStorei(GL_PACK_SKIP_ROWS, 0);
+ ::glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
+
+ ::glReadPixels(0.0f, 0.0f, aWidth * intScaleFactor,
+ aHeight * intScaleFactor,
+ GL_BGRA, GL_UNSIGNED_BYTE,
+ mCGData);
+
+ *aOutCGImage = mCGImage;
+ }
+
+ if (oldContext) {
+ ::CGLSetCurrentContext(oldContext);
+ }
+
+ return NS_OK;
+}
+
+nsresult nsCARenderer::DrawSurfaceToCGContext(CGContextRef aContext,
+ MacIOSurface *surf,
+ CGColorSpaceRef aColorSpace,
+ int aX, int aY,
+ size_t aWidth, size_t aHeight) {
+ surf->Lock();
+ size_t bytesPerRow = surf->GetBytesPerRow();
+ size_t ioWidth = surf->GetWidth();
+ size_t ioHeight = surf->GetHeight();
+
+ // We get rendering glitches if we use a width/height that falls
+ // outside of the IOSurface.
+ if (aWidth + aX > ioWidth)
+ aWidth = ioWidth - aX;
+ if (aHeight + aY > ioHeight)
+ aHeight = ioHeight - aY;
+
+ if (aX < 0 || static_cast<size_t>(aX) >= ioWidth ||
+ aY < 0 || static_cast<size_t>(aY) >= ioHeight) {
+ surf->Unlock();
+ return NS_ERROR_FAILURE;
+ }
+
+ void* ioData = surf->GetBaseAddress();
+ double scaleFactor = surf->GetContentsScaleFactor();
+ size_t intScaleFactor = ceil(surf->GetContentsScaleFactor());
+ CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData,
+ ioData, ioHeight*intScaleFactor*(bytesPerRow)*4,
+ nullptr); //No release callback
+ if (!dataProvider) {
+ surf->Unlock();
+ return NS_ERROR_FAILURE;
+ }
+
+ CGImageRef cgImage = ::CGImageCreate(ioWidth * intScaleFactor,
+ ioHeight * intScaleFactor, 8, 32, bytesPerRow, aColorSpace,
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
+ dataProvider, nullptr, true, kCGRenderingIntentDefault);
+ ::CGDataProviderRelease(dataProvider);
+ if (!cgImage) {
+ surf->Unlock();
+ return NS_ERROR_FAILURE;
+ }
+ CGImageRef subImage = ::CGImageCreateWithImageInRect(cgImage,
+ ::CGRectMake(aX * scaleFactor,
+ aY * scaleFactor,
+ aWidth * scaleFactor,
+ aHeight * scaleFactor));
+ if (!subImage) {
+ ::CGImageRelease(cgImage);
+ surf->Unlock();
+ return NS_ERROR_FAILURE;
+ }
+
+ ::CGContextScaleCTM(aContext, 1.0f, -1.0f);
+ ::CGContextDrawImage(aContext,
+ CGRectMake(aX * scaleFactor,
+ (-(CGFloat)aY - (CGFloat)aHeight) * scaleFactor,
+ aWidth * scaleFactor,
+ aHeight * scaleFactor),
+ subImage);
+
+ ::CGImageRelease(subImage);
+ ::CGImageRelease(cgImage);
+ surf->Unlock();
+ return NS_OK;
+}
+
+void nsCARenderer::DetachCALayer() {
+ CALayer* wrapperLayer = (CALayer*)mWrapperCALayer;
+ NSArray* sublayers = [wrapperLayer sublayers];
+ CALayer* oldLayer = (CALayer*) [sublayers objectAtIndex:0];
+ [oldLayer removeFromSuperlayer];
+}
+
+void nsCARenderer::AttachCALayer(void *aCALayer) {
+ CALayer* wrapperLayer = (CALayer*)mWrapperCALayer;
+ NSArray* sublayers = [wrapperLayer sublayers];
+ CALayer* oldLayer = (CALayer*) [sublayers objectAtIndex:0];
+ [oldLayer removeFromSuperlayer];
+ [wrapperLayer addSublayer:(CALayer*)aCALayer];
+}
+
+#ifdef DEBUG
+
+int sSaveToDiskSequence = 0;
+void nsCARenderer::SaveToDisk(MacIOSurface *surf) {
+ surf->Lock();
+ size_t bytesPerRow = surf->GetBytesPerRow();
+ size_t ioWidth = surf->GetWidth();
+ size_t ioHeight = surf->GetHeight();
+ void* ioData = surf->GetBaseAddress();
+ CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData,
+ ioData, ioHeight*(bytesPerRow)*4,
+ nullptr); //No release callback
+ if (!dataProvider) {
+ surf->Unlock();
+ return;
+ }
+
+ CGColorSpaceRef colorSpace = CreateSystemColorSpace();
+ CGImageRef cgImage = ::CGImageCreate(ioWidth, ioHeight, 8, 32, bytesPerRow,
+ colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
+ dataProvider, nullptr, true, kCGRenderingIntentDefault);
+ ::CGDataProviderRelease(dataProvider);
+ ::CGColorSpaceRelease(colorSpace);
+ if (!cgImage) {
+ surf->Unlock();
+ return;
+ }
+
+ char cstr[1000];
+ SprintfLiteral(cstr, "file:///Users/benoitgirard/debug/iosurface_%i.png", ++sSaveToDiskSequence);
+
+ CFStringRef cfStr = ::CFStringCreateWithCString(kCFAllocatorDefault, cstr, kCFStringEncodingMacRoman);
+
+ printf("Exporting: %s\n", cstr);
+ CFURLRef url = ::CFURLCreateWithString( nullptr, cfStr, nullptr);
+ ::CFRelease(cfStr);
+
+ CFStringRef type = kUTTypePNG;
+ size_t count = 1;
+ CFDictionaryRef options = nullptr;
+ CGImageDestinationRef dest = ::CGImageDestinationCreateWithURL(url, type, count, options);
+ ::CFRelease(url);
+
+ ::CGImageDestinationAddImage(dest, cgImage, nullptr);
+
+ ::CGImageDestinationFinalize(dest);
+ ::CFRelease(dest);
+ ::CGImageRelease(cgImage);
+
+ surf->Unlock();
+
+ return;
+
+}
+
+#endif
diff --git a/gfx/2d/Quaternion.cpp b/gfx/2d/Quaternion.cpp
new file mode 100644
index 000000000..63842d827
--- /dev/null
+++ b/gfx/2d/Quaternion.cpp
@@ -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 "Quaternion.h"
+#include "Matrix.h"
+#include "Tools.h"
+#include <algorithm>
+#include <ostream>
+#include <math.h>
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+std::ostream&
+operator<<(std::ostream& aStream, const Quaternion& aQuat)
+{
+ return aStream << "< " << aQuat.x << " " << aQuat.y << " " << aQuat.z << " " << aQuat.w << ">";
+}
+
+void
+Quaternion::SetFromRotationMatrix(const Matrix4x4& m)
+{
+ // see http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+ const Float trace = m._11 + m._22 + m._33;
+ if (trace > 0.0) {
+ const Float s = 0.5f / sqrt(trace + 1.0f);
+ w = 0.25f / s;
+ x = (m._32 - m._23) * s;
+ y = (m._13 - m._31) * s;
+ z = (m._21 - m._12) * s;
+ } else if (m._11 > m._22 && m._11 > m._33) {
+ const Float s = 2.0f * sqrt(1.0f + m._11 - m._22 - m._33);
+ w = (m._32 - m._23) / s;
+ x = 0.25f * s;
+ y = (m._12 + m._21) / s;
+ z = (m._13 + m._31) / s;
+ } else if (m._22 > m._33) {
+ const Float s = 2.0 * sqrt(1.0f + m._22 - m._11 - m._33);
+ w = (m._13 - m._31) / s;
+ x = (m._12 + m._21) / s;
+ y = 0.25f * s;
+ z = (m._23 + m._32) / s;
+ } else {
+ const Float s = 2.0 * sqrt(1.0f + m._33 - m._11 - m._22);
+ w = (m._21 - m._12) / s;
+ x = (m._13 + m._31) / s;
+ y = (m._23 + m._32) / s;
+ z = 0.25f * s;
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/Quaternion.h b/gfx/2d/Quaternion.h
new file mode 100644
index 000000000..f14d11348
--- /dev/null
+++ b/gfx/2d/Quaternion.h
@@ -0,0 +1,111 @@
+/* -*- 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_QUATERNION_H_
+#define MOZILLA_GFX_QUATERNION_H_
+
+#include "Types.h"
+#include <math.h>
+#include <ostream>
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/MatrixFwd.h"
+#include "mozilla/gfx/Point.h"
+
+namespace mozilla {
+namespace gfx {
+
+class Quaternion
+{
+public:
+ Quaternion()
+ : x(0.0f), y(0.0f), z(0.0f), w(1.0f)
+ {}
+
+ Quaternion(Float aX, Float aY, Float aZ, Float aW)
+ : x(aX), y(aY), z(aZ), w(aW)
+ {}
+
+
+ Quaternion(const Quaternion& aOther)
+ {
+ memcpy(this, &aOther, sizeof(*this));
+ }
+
+ Float x, y, z, w;
+
+ friend std::ostream& operator<<(std::ostream& aStream, const Quaternion& aQuat);
+
+ void Set(Float aX, Float aY, Float aZ, Float aW)
+ {
+ x = aX; y = aY; z = aZ; w = aW;
+ }
+
+ // Assumes upper 3x3 of aMatrix is a pure rotation matrix (no scaling)
+ void SetFromRotationMatrix(const Matrix4x4& aMatrix);
+
+ // result = this * aQuat
+ Quaternion operator*(const Quaternion &aQuat) const
+ {
+ Quaternion o;
+ const Float bx = aQuat.x, by = aQuat.y, bz = aQuat.z, bw = aQuat.w;
+
+ o.x = x*bw + w*bx + y*bz - z*by;
+ o.y = y*bw + w*by + z*bx - x*bz;
+ o.z = z*bw + w*bz + x*by - y*bx;
+ o.w = w*bw - x*bx - y*by - z*bz;
+ return o;
+ }
+
+ Quaternion& operator*=(const Quaternion &aQuat)
+ {
+ *this = *this * aQuat;
+ return *this;
+ }
+
+ Float Length() const
+ {
+ return sqrt(x*x + y*y + z*z + w*w);
+ }
+
+ Quaternion& Conjugate()
+ {
+ x *= -1.f; y *= -1.f; z *= -1.f;
+ return *this;
+ }
+
+ Quaternion& Normalize()
+ {
+ Float l = Length();
+ if (l) {
+ l = 1.0f / l;
+ x *= l; y *= l; z *= l; w *= l;
+ } else {
+ x = y = z = 0.f;
+ w = 1.f;
+ }
+ return *this;
+ }
+
+ Quaternion& Invert()
+ {
+ return Conjugate().Normalize();
+ }
+
+ Point3D RotatePoint(const Point3D& aPoint) {
+ Float uvx = Float(2.0) * (y*aPoint.z - z*aPoint.y);
+ Float uvy = Float(2.0) * (z*aPoint.x - x*aPoint.z);
+ Float uvz = Float(2.0) * (x*aPoint.y - y*aPoint.x);
+
+ return Point3D(aPoint.x + w*uvx + y*uvz - z*uvy,
+ aPoint.y + w*uvy + z*uvx - x*uvz,
+ aPoint.z + w*uvz + x*uvy - y*uvx);
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/gfx/2d/RadialGradientEffectD2D1.cpp b/gfx/2d/RadialGradientEffectD2D1.cpp
new file mode 100644
index 000000000..8f929d8e9
--- /dev/null
+++ b/gfx/2d/RadialGradientEffectD2D1.cpp
@@ -0,0 +1,398 @@
+/* -*- 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 "RadialGradientEffectD2D1.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='RadialGradientEffect'/>
+ <Property name='Author' type='string' value='Mozilla'/>
+ <Property name='Category' type='string' value='Pattern effects'/>
+ <Property name='Description' type='string' value='This effect is used to render radial gradients in a manner compliant with the 2D Canvas specification.'/>
+ <Inputs>
+ <Input name='Geometry'/>
+ </Inputs>
+ <Property name='StopCollection' type='iunknown'>
+ <Property name='DisplayName' type='string' value='Gradient stop collection'/>
+ </Property>
+ <Property name='Center1' type='vector2'>
+ <Property name='DisplayName' type='string' value='Inner circle center'/>
+ </Property>
+ <Property name='Center2' type='vector2'>
+ <Property name='DisplayName' type='string' value='Outer circle center'/>
+ </Property>
+ <Property name='Radius1' type='float'>
+ <Property name='DisplayName' type='string' value='Inner circle radius'/>
+ </Property>
+ <Property name='Radius2' type='float'>
+ <Property name='DisplayName' type='string' value='Outer circle radius'/>
+ </Property>
+ <Property name='Transform' type='matrix3x2'>
+ <Property name='DisplayName' type='string' value='Transform applied to the pattern'/>
+ </Property>
+
+ </Effect>
+ );
+
+// {FB947CDA-718E-40CC-AE7B-D255830D7D14}
+static const GUID GUID_SampleRadialGradientPS =
+ {0xfb947cda, 0x718e, 0x40cc, {0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14}};
+// {2C468128-6546-453C-8E25-F2DF0DE10A0F}
+static const GUID GUID_SampleRadialGradientA0PS =
+ {0x2c468128, 0x6546, 0x453c, {0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf}};
+
+namespace mozilla {
+namespace gfx {
+
+RadialGradientEffectD2D1::RadialGradientEffectD2D1()
+ : mRefCount(0)
+ , mCenter1(D2D1::Vector2F(0, 0))
+ , mCenter2(D2D1::Vector2F(0, 0))
+ , mRadius1(0)
+ , mRadius2(0)
+ , mTransform(D2D1::IdentityMatrix())
+
+{
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph)
+{
+ HRESULT hr;
+
+ hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientPS, SampleRadialGradientPS, sizeof(SampleRadialGradientPS));
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientA0PS, SampleRadialGradientA0PS, sizeof(SampleRadialGradientA0PS));
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pTransformGraph->SetSingleTransformNode(this);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ mEffectContext = pContextInternal;
+
+ return S_OK;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
+{
+ if (changeType == D2D1_CHANGE_TYPE_NONE) {
+ return S_OK;
+ }
+
+ // We'll need to inverse transform our pixel, precompute inverse here.
+ Matrix mat = ToMatrix(mTransform);
+ if (!mat.Invert()) {
+ // Singular
+ return S_OK;
+ }
+
+ if (!mStopCollection) {
+ return S_OK;
+ }
+
+ 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;
+
+ HRESULT hr;
+
+ if (A == 0) {
+ hr = mDrawInfo->SetPixelShader(GUID_SampleRadialGradientA0PS);
+ } else {
+ hr = mDrawInfo->SetPixelShader(GUID_SampleRadialGradientPS);
+ }
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ RefPtr<ID2D1ResourceTexture> tex = CreateGradientTexture();
+ hr = mDrawInfo->SetResourceTexture(1, tex);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ struct PSConstantBuffer
+ {
+ float diff[3];
+ float padding;
+ float center1[2];
+ float A;
+ float radius1;
+ float sq_radius1;
+ float repeat_correct;
+ float allow_odd;
+ float padding2[1];
+ float transform[8];
+ };
+
+ PSConstantBuffer buffer = { { dc.x, dc.y, dr }, 0.0f,
+ { mCenter1.x, mCenter1.y },
+ A, mRadius1, mRadius1 * mRadius1,
+ mStopCollection->GetExtendMode() != D2D1_EXTEND_MODE_CLAMP ? 1.0f : 0.0f,
+ mStopCollection->GetExtendMode() == D2D1_EXTEND_MODE_MIRROR ? 1.0f : 0.0f,
+ { 0.0f }, { mat._11, mat._21, mat._31, 0.0f,
+ mat._12, mat._22, mat._32, 0.0f } };
+
+ hr = mDrawInfo->SetPixelShaderConstantBuffer((BYTE*)&buffer, sizeof(buffer));
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return S_OK;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph)
+{
+ return pGraph->SetSingleTransformNode(this);
+}
+
+IFACEMETHODIMP_(ULONG)
+RadialGradientEffectD2D1::AddRef()
+{
+ return ++mRefCount;
+}
+
+IFACEMETHODIMP_(ULONG)
+RadialGradientEffectD2D1::Release()
+{
+ if (!--mRefCount) {
+ delete this;
+ return 0;
+ }
+ return mRefCount;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::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;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects,
+ const D2D1_RECT_L* pInputOpaqueSubRects,
+ UINT32 inputRectCount,
+ D2D1_RECT_L* pOutputRect,
+ D2D1_RECT_L* pOutputOpaqueSubRect)
+{
+ if (inputRectCount != 1) {
+ return E_INVALIDARG;
+ }
+
+ *pOutputRect = *pInputRects;
+ *pOutputOpaqueSubRect = *pInputOpaqueSubRects;
+ return S_OK;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::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
+RadialGradientEffectD2D1::MapInvalidRect(UINT32 inputIndex,
+ D2D1_RECT_L invalidInputRect,
+ D2D1_RECT_L* pInvalidOutputRect) const
+{
+ MOZ_ASSERT(inputIndex == 0);
+
+ *pInvalidOutputRect = invalidInputRect;
+ return S_OK;
+}
+
+IFACEMETHODIMP
+RadialGradientEffectD2D1::SetDrawInfo(ID2D1DrawInfo *pDrawInfo)
+{
+ mDrawInfo = pDrawInfo;
+ return S_OK;
+}
+
+HRESULT
+RadialGradientEffectD2D1::Register(ID2D1Factory1 *aFactory)
+{
+ D2D1_PROPERTY_BINDING bindings[] = {
+ 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);
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to register radial gradient effect.";
+ }
+ return hr;
+}
+
+void
+RadialGradientEffectD2D1::Unregister(ID2D1Factory1 *aFactory)
+{
+ aFactory->UnregisterEffect(CLSID_RadialGradientEffect);
+}
+
+HRESULT __stdcall
+RadialGradientEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
+{
+ *aEffectImpl = static_cast<ID2D1EffectImpl*>(new RadialGradientEffectD2D1());
+ (*aEffectImpl)->AddRef();
+
+ return S_OK;
+}
+
+HRESULT
+RadialGradientEffectD2D1::SetStopCollection(IUnknown *aStopCollection)
+{
+ if (SUCCEEDED(aStopCollection->QueryInterface((ID2D1GradientStopCollection**)getter_AddRefs(mStopCollection)))) {
+ return S_OK;
+ }
+
+ return E_INVALIDARG;
+}
+
+already_AddRefed<ID2D1ResourceTexture>
+RadialGradientEffectD2D1::CreateGradientTexture()
+{
+ std::vector<D2D1_GRADIENT_STOP> rawStops;
+ rawStops.resize(mStopCollection->GetGradientStopCount());
+ mStopCollection->GetGradientStops(&rawStops.front(), rawStops.size());
+
+ std::vector<unsigned char> textureData;
+ textureData.resize(4096 * 4);
+ unsigned char *texData = &textureData.front();
+
+ float prevColorPos = 0;
+ float nextColorPos = 1.0f;
+ D2D1_COLOR_F prevColor = rawStops[0].color;
+ D2D1_COLOR_F nextColor = prevColor;
+
+ if (rawStops.size() >= 2) {
+ nextColor = rawStops[1].color;
+ nextColorPos = rawStops[1].position;
+ }
+
+ uint32_t stopPosition = 2;
+
+ // Not the most optimized way but this will do for now.
+ for (int i = 0; i < 4096; i++) {
+ // The 4095 seems a little counter intuitive, but we want the gradient
+ // color at offset 0 at the first pixel, and at offset 1.0f at the last
+ // pixel.
+ float pos = float(i) / 4095;
+
+ while (pos > nextColorPos) {
+ prevColor = nextColor;
+ prevColorPos = nextColorPos;
+ if (rawStops.size() > stopPosition) {
+ nextColor = rawStops[stopPosition].color;
+ nextColorPos = rawStops[stopPosition++].position;
+ } else {
+ nextColorPos = 1.0f;
+ }
+ }
+
+ float interp;
+
+ if (nextColorPos != prevColorPos) {
+ interp = (pos - prevColorPos) / (nextColorPos - prevColorPos);
+ } else {
+ interp = 0;
+ }
+
+ Color newColor(prevColor.r + (nextColor.r - prevColor.r) * interp,
+ prevColor.g + (nextColor.g - prevColor.g) * interp,
+ prevColor.b + (nextColor.b - prevColor.b) * interp,
+ prevColor.a + (nextColor.a - prevColor.a) * interp);
+
+ // Note D2D expects RGBA here!!
+ texData[i * 4] = (char)(255.0f * newColor.r);
+ texData[i * 4 + 1] = (char)(255.0f * newColor.g);
+ texData[i * 4 + 2] = (char)(255.0f * newColor.b);
+ texData[i * 4 + 3] = (char)(255.0f * newColor.a);
+ }
+
+ RefPtr<ID2D1ResourceTexture> tex;
+
+ UINT32 width = 4096;
+ UINT32 stride = 4096 * 4;
+ D2D1_RESOURCE_TEXTURE_PROPERTIES props;
+ // 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(), mStopCollection->GetExtendMode() };
+ props.extendModes = extendMode;
+
+ HRESULT hr = mEffectContext->CreateResourceTexture(nullptr, &props, &textureData.front(), &stride, 4096 * 4, getter_AddRefs(tex));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create resource texture: " << hexa(hr);
+ }
+
+ return tex.forget();
+}
+
+}
+}
diff --git a/gfx/2d/RadialGradientEffectD2D1.h b/gfx/2d/RadialGradientEffectD2D1.h
new file mode 100644
index 000000000..baa02aafb
--- /dev/null
+++ b/gfx/2d/RadialGradientEffectD2D1.h
@@ -0,0 +1,101 @@
+/* -*- 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_RADIALGRADIENTEFFECTD2D1_H_
+#define MOZILLA_GFX_RADIALGRADIENTEFFECTD2D1_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_RadialGradientEffect,
+0x97143dc6, 0xcbc4, 0x4dd4, 0xa8, 0xba, 0x13, 0x34, 0x2b, 0xb, 0xa4, 0x6d);
+
+// Macro to keep our class nice and clean.
+#define SIMPLE_PROP(type, name) \
+public: \
+ HRESULT Set##name(type a##name) \
+ { m##name = a##name; return S_OK; } \
+ type Get##name() const { return m##name; } \
+private: \
+ type m##name;
+
+namespace mozilla {
+namespace gfx {
+
+enum {
+ RADIAL_PROP_STOP_COLLECTION = 0,
+ RADIAL_PROP_CENTER_1,
+ RADIAL_PROP_CENTER_2,
+ RADIAL_PROP_RADIUS_1,
+ RADIAL_PROP_RADIUS_2,
+ RADIAL_PROP_TRANSFORM
+};
+
+class RadialGradientEffectD2D1 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);
+
+ static HRESULT Register(ID2D1Factory1* aFactory);
+ static void Unregister(ID2D1Factory1* aFactory);
+ static HRESULT __stdcall CreateEffect(IUnknown** aEffectImpl);
+
+ HRESULT SetStopCollection(IUnknown *aStopCollection);
+ IUnknown *GetStopCollection() const { return mStopCollection; }
+
+private:
+ already_AddRefed<ID2D1ResourceTexture> CreateGradientTexture();
+
+ RadialGradientEffectD2D1();
+
+ uint32_t mRefCount;
+ RefPtr<ID2D1GradientStopCollection> mStopCollection;
+ RefPtr<ID2D1EffectContext> mEffectContext;
+ RefPtr<ID2D1DrawInfo> mDrawInfo;
+ SIMPLE_PROP(D2D1_VECTOR_2F, Center1);
+ SIMPLE_PROP(D2D1_VECTOR_2F, Center2);
+ SIMPLE_PROP(FLOAT, Radius1);
+ SIMPLE_PROP(FLOAT, Radius2);
+ SIMPLE_PROP(D2D_MATRIX_3X2_F, Transform);
+};
+
+}
+}
+#undef SIMPLE_PROP
+
+#endif
diff --git a/gfx/2d/RecordedEvent.cpp b/gfx/2d/RecordedEvent.cpp
new file mode 100644
index 000000000..3bfc5c8f6
--- /dev/null
+++ b/gfx/2d/RecordedEvent.cpp
@@ -0,0 +1,1883 @@
+/* -*- 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 "RecordedEvent.h"
+
+#include "PathRecording.h"
+#include "RecordingTypes.h"
+#include "Tools.h"
+#include "Filters.h"
+#include "Logging.h"
+#include "ScaledFontBase.h"
+#include "SFNTData.h"
+
+namespace mozilla {
+namespace gfx {
+
+using namespace std;
+
+static std::string NameFromBackend(BackendType aType)
+{
+ switch (aType) {
+ case BackendType::NONE:
+ return "None";
+ case BackendType::DIRECT2D:
+ return "Direct2D";
+ default:
+ return "Unknown";
+ }
+}
+
+already_AddRefed<DrawTarget>
+Translator::CreateDrawTarget(ReferencePtr aRefPtr, const IntSize &aSize,
+ SurfaceFormat aFormat)
+{
+ RefPtr<DrawTarget> newDT =
+ GetReferenceDrawTarget()->CreateSimilarDrawTarget(aSize, aFormat);
+ AddDrawTarget(aRefPtr, newDT);
+ return newDT.forget();
+}
+
+#define LOAD_EVENT_TYPE(_typeenum, _class) \
+ case _typeenum: return new _class(aStream)
+
+RecordedEvent *
+RecordedEvent::LoadEventFromStream(std::istream &aStream, EventType aType)
+{
+ switch (aType) {
+ LOAD_EVENT_TYPE(DRAWTARGETCREATION, RecordedDrawTargetCreation);
+ LOAD_EVENT_TYPE(DRAWTARGETDESTRUCTION, RecordedDrawTargetDestruction);
+ LOAD_EVENT_TYPE(FILLRECT, RecordedFillRect);
+ LOAD_EVENT_TYPE(STROKERECT, RecordedStrokeRect);
+ LOAD_EVENT_TYPE(STROKELINE, RecordedStrokeLine);
+ LOAD_EVENT_TYPE(CLEARRECT, RecordedClearRect);
+ LOAD_EVENT_TYPE(COPYSURFACE, RecordedCopySurface);
+ LOAD_EVENT_TYPE(SETTRANSFORM, RecordedSetTransform);
+ LOAD_EVENT_TYPE(PUSHCLIPRECT, RecordedPushClipRect);
+ LOAD_EVENT_TYPE(PUSHCLIP, RecordedPushClip);
+ LOAD_EVENT_TYPE(POPCLIP, RecordedPopClip);
+ LOAD_EVENT_TYPE(FILL, RecordedFill);
+ LOAD_EVENT_TYPE(FILLGLYPHS, RecordedFillGlyphs);
+ LOAD_EVENT_TYPE(MASK, RecordedMask);
+ 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);
+ LOAD_EVENT_TYPE(CREATESIMILARDRAWTARGET, RecordedCreateSimilarDrawTarget);
+ LOAD_EVENT_TYPE(FONTDATA, RecordedFontData);
+ LOAD_EVENT_TYPE(FONTDESC, RecordedFontDescriptor);
+ LOAD_EVENT_TYPE(PUSHLAYER, RecordedPushLayer);
+ LOAD_EVENT_TYPE(POPLAYER, RecordedPopLayer);
+ default:
+ 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";
+ case CREATESIMILARDRAWTARGET:
+ return "CreateSimilarDrawTarget";
+ case FONTDATA:
+ return "FontData";
+ case FONTDESC:
+ return "FontDescriptor";
+ case PUSHLAYER:
+ return "PushLayer";
+ case POPLAYER:
+ return "PopLayer";
+ default:
+ return "Unknown";
+ }
+}
+
+void
+RecordedEvent::RecordPatternData(std::ostream &aStream, const PatternStorage &aPattern) const
+{
+ WriteElement(aStream, aPattern.mType);
+
+ switch (aPattern.mType) {
+ case PatternType::COLOR:
+ {
+ WriteElement(aStream, *reinterpret_cast<const ColorPatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ {
+ WriteElement(aStream, *reinterpret_cast<const LinearGradientPatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ WriteElement(aStream, *reinterpret_cast<const RadialGradientPatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ case PatternType::SURFACE:
+ {
+ WriteElement(aStream, *reinterpret_cast<const SurfacePatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ default:
+ return;
+ }
+}
+
+void
+RecordedEvent::ReadPatternData(std::istream &aStream, PatternStorage &aPattern) const
+{
+ ReadElement(aStream, aPattern.mType);
+
+ switch (aPattern.mType) {
+ case PatternType::COLOR:
+ {
+ ReadElement(aStream, *reinterpret_cast<ColorPatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ {
+ ReadElement(aStream, *reinterpret_cast<LinearGradientPatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ ReadElement(aStream, *reinterpret_cast<RadialGradientPatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ case PatternType::SURFACE:
+ {
+ ReadElement(aStream, *reinterpret_cast<SurfacePatternStorage*>(&aPattern.mStorage));
+ return;
+ }
+ default:
+ return;
+ }
+}
+
+void
+RecordedEvent::StorePattern(PatternStorage &aDestination, const Pattern &aSource) const
+{
+ aDestination.mType = aSource.GetType();
+
+ switch (aSource.GetType()) {
+ case PatternType::COLOR:
+ {
+ reinterpret_cast<ColorPatternStorage*>(&aDestination.mStorage)->mColor =
+ static_cast<const ColorPattern*>(&aSource)->mColor;
+ return;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ {
+ LinearGradientPatternStorage *store =
+ reinterpret_cast<LinearGradientPatternStorage*>(&aDestination.mStorage);
+ const LinearGradientPattern *pat =
+ static_cast<const LinearGradientPattern*>(&aSource);
+ store->mBegin = pat->mBegin;
+ store->mEnd = pat->mEnd;
+ store->mMatrix = pat->mMatrix;
+ store->mStops = pat->mStops.get();
+ return;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ RadialGradientPatternStorage *store =
+ reinterpret_cast<RadialGradientPatternStorage*>(&aDestination.mStorage);
+ const RadialGradientPattern *pat =
+ static_cast<const RadialGradientPattern*>(&aSource);
+ store->mCenter1 = pat->mCenter1;
+ store->mCenter2 = pat->mCenter2;
+ store->mRadius1 = pat->mRadius1;
+ store->mRadius2 = pat->mRadius2;
+ store->mMatrix = pat->mMatrix;
+ store->mStops = pat->mStops.get();
+ return;
+ }
+ case PatternType::SURFACE:
+ {
+ SurfacePatternStorage *store =
+ reinterpret_cast<SurfacePatternStorage*>(&aDestination.mStorage);
+ const SurfacePattern *pat =
+ static_cast<const SurfacePattern*>(&aSource);
+ store->mExtend = pat->mExtendMode;
+ store->mSamplingFilter = pat->mSamplingFilter;
+ store->mMatrix = pat->mMatrix;
+ store->mSurface = pat->mSurface;
+ store->mSamplingRect = pat->mSamplingRect;
+ return;
+ }
+ }
+}
+
+void
+RecordedEvent::RecordStrokeOptions(std::ostream &aStream, const StrokeOptions &aStrokeOptions) const
+{
+ JoinStyle joinStyle = aStrokeOptions.mLineJoin;
+ CapStyle capStyle = aStrokeOptions.mLineCap;
+
+ WriteElement(aStream, uint64_t(aStrokeOptions.mDashLength));
+ WriteElement(aStream, aStrokeOptions.mDashOffset);
+ WriteElement(aStream, aStrokeOptions.mLineWidth);
+ WriteElement(aStream, aStrokeOptions.mMiterLimit);
+ WriteElement(aStream, joinStyle);
+ WriteElement(aStream, capStyle);
+
+ if (!aStrokeOptions.mDashPattern) {
+ return;
+ }
+
+ aStream.write((char*)aStrokeOptions.mDashPattern, sizeof(Float) * aStrokeOptions.mDashLength);
+}
+
+void
+RecordedEvent::ReadStrokeOptions(std::istream &aStream, StrokeOptions &aStrokeOptions)
+{
+ uint64_t dashLength;
+ JoinStyle joinStyle;
+ CapStyle capStyle;
+
+ ReadElement(aStream, dashLength);
+ ReadElement(aStream, aStrokeOptions.mDashOffset);
+ ReadElement(aStream, aStrokeOptions.mLineWidth);
+ ReadElement(aStream, aStrokeOptions.mMiterLimit);
+ ReadElement(aStream, joinStyle);
+ ReadElement(aStream, capStyle);
+ // On 32 bit we truncate the value of dashLength.
+ // See also bug 811850 for history.
+ aStrokeOptions.mDashLength = size_t(dashLength);
+ aStrokeOptions.mLineJoin = joinStyle;
+ aStrokeOptions.mLineCap = capStyle;
+
+ if (!aStrokeOptions.mDashLength) {
+ return;
+ }
+
+ mDashPatternStorage.resize(aStrokeOptions.mDashLength);
+ aStrokeOptions.mDashPattern = &mDashPatternStorage.front();
+ aStream.read((char*)aStrokeOptions.mDashPattern, sizeof(Float) * aStrokeOptions.mDashLength);
+}
+
+void
+RecordedEvent::OutputSimplePatternInfo(const PatternStorage &aStorage, std::stringstream &aOutput) const
+{
+ switch (aStorage.mType) {
+ 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 PatternType::LINEAR_GRADIENT:
+ {
+ const LinearGradientPatternStorage *store =
+ reinterpret_cast<const LinearGradientPatternStorage*>(&aStorage.mStorage);
+
+ aOutput << "LinearGradient (" << store->mBegin.x << ", " << store->mBegin.y <<
+ ") - (" << store->mEnd.x << ", " << store->mEnd.y << ") Stops: " << store->mStops;
+ return;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ const RadialGradientPatternStorage *store =
+ reinterpret_cast<const RadialGradientPatternStorage*>(&aStorage.mStorage);
+ aOutput << "RadialGradient (Center 1: (" << store->mCenter1.x << ", " <<
+ store->mCenter2.y << ") Radius 2: " << store->mRadius2;
+ return;
+ }
+ case PatternType::SURFACE:
+ {
+ const SurfacePatternStorage *store =
+ reinterpret_cast<const SurfacePatternStorage*>(&aStorage.mStorage);
+ aOutput << "Surface (0x" << store->mSurface << ")";
+ return;
+ }
+ }
+}
+
+RecordedDrawingEvent::RecordedDrawingEvent(EventType aType, std::istream &aStream)
+ : RecordedEvent(aType)
+{
+ ReadElement(aStream, mDT);
+}
+
+void
+RecordedDrawingEvent::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mDT);
+}
+
+ReferencePtr
+RecordedDrawingEvent::GetObjectRef() const
+{
+ return mDT;
+}
+
+bool
+RecordedDrawTargetCreation::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<DrawTarget> newDT =
+ aTranslator->CreateDrawTarget(mRefPtr, mSize, mFormat);
+
+ // If we couldn't create a DrawTarget this will probably cause us to crash
+ // with nullptr later in the playback, so return false to abort.
+ if (!newDT) {
+ return false;
+ }
+
+ if (mHasExistingData) {
+ Rect dataRect(0, 0, mExistingData->GetSize().width, mExistingData->GetSize().height);
+ newDT->DrawSurface(mExistingData, dataRect, dataRect);
+ }
+
+ return true;
+}
+
+void
+RecordedDrawTargetCreation::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ 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
+RecordedDrawTargetCreation::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] DrawTarget Creation (Type: " << NameFromBackend(mBackendType) << ", Size: " << mSize.width << "x" << mSize.height << ")";
+}
+
+
+bool
+RecordedDrawTargetDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemoveDrawTarget(mRefPtr);
+ return true;
+}
+
+void
+RecordedDrawTargetDestruction::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+}
+
+RecordedDrawTargetDestruction::RecordedDrawTargetDestruction(istream &aStream)
+ : RecordedEvent(DRAWTARGETDESTRUCTION)
+{
+ ReadElement(aStream, mRefPtr);
+}
+
+void
+RecordedDrawTargetDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] DrawTarget Destruction";
+}
+
+bool
+RecordedCreateSimilarDrawTarget::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<DrawTarget> newDT =
+ aTranslator->GetReferenceDrawTarget()->CreateSimilarDrawTarget(mSize, mFormat);
+
+ // If we couldn't create a DrawTarget this will probably cause us to crash
+ // with nullptr later in the playback, so return false to abort.
+ if (!newDT) {
+ return false;
+ }
+
+ aTranslator->AddDrawTarget(mRefPtr, newDT);
+ return true;
+}
+
+void
+RecordedCreateSimilarDrawTarget::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mSize);
+ WriteElement(aStream, mFormat);
+}
+
+RecordedCreateSimilarDrawTarget::RecordedCreateSimilarDrawTarget(istream &aStream)
+ : RecordedEvent(CREATESIMILARDRAWTARGET)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mSize);
+ ReadElement(aStream, mFormat);
+}
+
+void
+RecordedCreateSimilarDrawTarget::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] CreateSimilarDrawTarget (Size: " << mSize.width << "x" << mSize.height << ")";
+}
+
+struct GenericPattern
+{
+ GenericPattern(const PatternStorage &aStorage, Translator *aTranslator)
+ : mPattern(nullptr), mTranslator(aTranslator)
+ {
+ mStorage = const_cast<PatternStorage*>(&aStorage);
+ }
+
+ ~GenericPattern() {
+ if (mPattern) {
+ mPattern->~Pattern();
+ }
+ }
+
+ operator Pattern*()
+ {
+ switch(mStorage->mType) {
+ case PatternType::COLOR:
+ return new (mColPat) ColorPattern(reinterpret_cast<ColorPatternStorage*>(&mStorage->mStorage)->mColor);
+ case PatternType::SURFACE:
+ {
+ SurfacePatternStorage *storage = reinterpret_cast<SurfacePatternStorage*>(&mStorage->mStorage);
+ mPattern =
+ new (mSurfPat) SurfacePattern(mTranslator->LookupSourceSurface(storage->mSurface),
+ storage->mExtend, storage->mMatrix,
+ storage->mSamplingFilter,
+ storage->mSamplingRect);
+ return mPattern;
+ }
+ case PatternType::LINEAR_GRADIENT:
+ {
+ LinearGradientPatternStorage *storage = reinterpret_cast<LinearGradientPatternStorage*>(&mStorage->mStorage);
+ mPattern =
+ new (mLinGradPat) LinearGradientPattern(storage->mBegin, storage->mEnd,
+ mTranslator->LookupGradientStops(storage->mStops),
+ storage->mMatrix);
+ return mPattern;
+ }
+ case PatternType::RADIAL_GRADIENT:
+ {
+ RadialGradientPatternStorage *storage = reinterpret_cast<RadialGradientPatternStorage*>(&mStorage->mStorage);
+ mPattern =
+ new (mRadGradPat) RadialGradientPattern(storage->mCenter1, storage->mCenter2,
+ storage->mRadius1, storage->mRadius2,
+ mTranslator->LookupGradientStops(storage->mStops),
+ storage->mMatrix);
+ return mPattern;
+ }
+ default:
+ return new (mColPat) ColorPattern(Color());
+ }
+
+ return mPattern;
+ }
+
+ union {
+ char mColPat[sizeof(ColorPattern)];
+ char mLinGradPat[sizeof(LinearGradientPattern)];
+ char mRadGradPat[sizeof(RadialGradientPattern)];
+ char mSurfPat[sizeof(SurfacePattern)];
+ };
+
+ PatternStorage *mStorage;
+ Pattern *mPattern;
+ Translator *mTranslator;
+};
+
+bool
+RecordedFillRect::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->FillRect(mRect, *GenericPattern(mPattern, aTranslator), mOptions);
+ return true;
+}
+
+void
+RecordedFillRect::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mRect);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mPattern);
+}
+
+RecordedFillRect::RecordedFillRect(istream &aStream)
+ : RecordedDrawingEvent(FILLRECT, aStream)
+{
+ ReadElement(aStream, mRect);
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mPattern);
+}
+
+void
+RecordedFillRect::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] FillRect (" << mRect.x << ", " << mRect.y << " - " << mRect.width << " x " << mRect.height << ") ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+bool
+RecordedStrokeRect::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->StrokeRect(mRect, *GenericPattern(mPattern, aTranslator), mStrokeOptions, mOptions);
+ return true;
+}
+
+void
+RecordedStrokeRect::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mRect);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mPattern);
+ RecordStrokeOptions(aStream, mStrokeOptions);
+}
+
+RecordedStrokeRect::RecordedStrokeRect(istream &aStream)
+ : RecordedDrawingEvent(STROKERECT, aStream)
+{
+ ReadElement(aStream, mRect);
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mPattern);
+ ReadStrokeOptions(aStream, mStrokeOptions);
+}
+
+void
+RecordedStrokeRect::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] StrokeRect (" << mRect.x << ", " << mRect.y << " - " << mRect.width << " x " << mRect.height
+ << ") LineWidth: " << mStrokeOptions.mLineWidth << "px ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+bool
+RecordedStrokeLine::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->StrokeLine(mBegin, mEnd, *GenericPattern(mPattern, aTranslator), mStrokeOptions, mOptions);
+ return true;
+}
+
+void
+RecordedStrokeLine::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mBegin);
+ WriteElement(aStream, mEnd);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mPattern);
+ RecordStrokeOptions(aStream, mStrokeOptions);
+}
+
+RecordedStrokeLine::RecordedStrokeLine(istream &aStream)
+ : RecordedDrawingEvent(STROKELINE, aStream)
+{
+ ReadElement(aStream, mBegin);
+ ReadElement(aStream, mEnd);
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mPattern);
+ ReadStrokeOptions(aStream, mStrokeOptions);
+}
+
+void
+RecordedStrokeLine::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] StrokeLine (" << mBegin.x << ", " << mBegin.y << " - " << mEnd.x << ", " << mEnd.y
+ << ") LineWidth: " << mStrokeOptions.mLineWidth << "px ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+bool
+RecordedFill::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->Fill(aTranslator->LookupPath(mPath), *GenericPattern(mPattern, aTranslator), mOptions);
+ return true;
+}
+
+RecordedFill::RecordedFill(istream &aStream)
+ : RecordedDrawingEvent(FILL, aStream)
+{
+ ReadElement(aStream, mPath);
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mPattern);
+}
+
+void
+RecordedFill::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mPath);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mPattern);
+}
+
+void
+RecordedFill::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] Fill (" << mPath << ") ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+RecordedFillGlyphs::~RecordedFillGlyphs()
+{
+ delete [] mGlyphs;
+}
+
+bool
+RecordedFillGlyphs::PlayEvent(Translator *aTranslator) const
+{
+ GlyphBuffer buffer;
+ buffer.mGlyphs = mGlyphs;
+ buffer.mNumGlyphs = mNumGlyphs;
+ aTranslator->LookupDrawTarget(mDT)->FillGlyphs(aTranslator->LookupScaledFont(mScaledFont), buffer, *GenericPattern(mPattern, aTranslator), mOptions);
+ return true;
+}
+
+RecordedFillGlyphs::RecordedFillGlyphs(istream &aStream)
+ : RecordedDrawingEvent(FILLGLYPHS, aStream)
+{
+ ReadElement(aStream, mScaledFont);
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mPattern);
+ ReadElement(aStream, mNumGlyphs);
+ mGlyphs = new Glyph[mNumGlyphs];
+ aStream.read((char*)mGlyphs, sizeof(Glyph) * mNumGlyphs);
+}
+
+void
+RecordedFillGlyphs::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mScaledFont);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mPattern);
+ WriteElement(aStream, mNumGlyphs);
+ aStream.write((char*)mGlyphs, sizeof(Glyph) * mNumGlyphs);
+}
+
+void
+RecordedFillGlyphs::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] FillGlyphs (" << mScaledFont << ") ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+bool
+RecordedMask::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->Mask(*GenericPattern(mSource, aTranslator), *GenericPattern(mMask, aTranslator), mOptions);
+ return true;
+}
+
+RecordedMask::RecordedMask(istream &aStream)
+ : RecordedDrawingEvent(MASK, aStream)
+{
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mSource);
+ ReadPatternData(aStream, mMask);
+}
+
+void
+RecordedMask::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mSource);
+ RecordPatternData(aStream, mMask);
+}
+
+void
+RecordedMask::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] Mask (Source: ";
+ OutputSimplePatternInfo(mSource, aStringStream);
+ aStringStream << " Mask: ";
+ OutputSimplePatternInfo(mMask, aStringStream);
+}
+
+bool
+RecordedStroke::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->Stroke(aTranslator->LookupPath(mPath), *GenericPattern(mPattern, aTranslator), mStrokeOptions, mOptions);
+ return true;
+}
+
+void
+RecordedStroke::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mPath);
+ WriteElement(aStream, mOptions);
+ RecordPatternData(aStream, mPattern);
+ RecordStrokeOptions(aStream, mStrokeOptions);
+}
+
+RecordedStroke::RecordedStroke(istream &aStream)
+ : RecordedDrawingEvent(STROKE, aStream)
+{
+ ReadElement(aStream, mPath);
+ ReadElement(aStream, mOptions);
+ ReadPatternData(aStream, mPattern);
+ ReadStrokeOptions(aStream, mStrokeOptions);
+}
+
+void
+RecordedStroke::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] Stroke ("<< mPath << ") LineWidth: " << mStrokeOptions.mLineWidth << "px ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+bool
+RecordedClearRect::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->ClearRect(mRect);
+ return true;
+}
+
+void
+RecordedClearRect::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mRect);
+}
+
+RecordedClearRect::RecordedClearRect(istream &aStream)
+ : RecordedDrawingEvent(CLEARRECT, aStream)
+{
+ ReadElement(aStream, mRect);
+}
+
+void
+RecordedClearRect::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT<< "] ClearRect (" << mRect.x << ", " << mRect.y << " - " << mRect.width << " x " << mRect.height << ") ";
+}
+
+bool
+RecordedCopySurface::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->CopySurface(aTranslator->LookupSourceSurface(mSourceSurface),
+ mSourceRect, mDest);
+ return true;
+}
+
+void
+RecordedCopySurface::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mSourceSurface);
+ WriteElement(aStream, mSourceRect);
+ WriteElement(aStream, mDest);
+}
+
+RecordedCopySurface::RecordedCopySurface(istream &aStream)
+ : RecordedDrawingEvent(COPYSURFACE, aStream)
+{
+ ReadElement(aStream, mSourceSurface);
+ ReadElement(aStream, mSourceRect);
+ ReadElement(aStream, mDest);
+}
+
+void
+RecordedCopySurface::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT<< "] CopySurface (" << mSourceSurface << ")";
+}
+
+bool
+RecordedPushClip::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->PushClip(aTranslator->LookupPath(mPath));
+ return true;
+}
+
+void
+RecordedPushClip::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mPath);
+}
+
+RecordedPushClip::RecordedPushClip(istream &aStream)
+ : RecordedDrawingEvent(PUSHCLIP, aStream)
+{
+ ReadElement(aStream, mPath);
+}
+
+void
+RecordedPushClip::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] PushClip (" << mPath << ") ";
+}
+
+bool
+RecordedPushClipRect::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->PushClipRect(mRect);
+ return true;
+}
+
+void
+RecordedPushClipRect::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mRect);
+}
+
+RecordedPushClipRect::RecordedPushClipRect(istream &aStream)
+ : RecordedDrawingEvent(PUSHCLIPRECT, aStream)
+{
+ ReadElement(aStream, mRect);
+}
+
+void
+RecordedPushClipRect::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] PushClipRect (" << mRect.x << ", " << mRect.y << " - " << mRect.width << " x " << mRect.height << ") ";
+}
+
+bool
+RecordedPopClip::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->PopClip();
+ return true;
+}
+
+void
+RecordedPopClip::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+}
+
+RecordedPopClip::RecordedPopClip(istream &aStream)
+ : RecordedDrawingEvent(POPCLIP, aStream)
+{
+}
+
+void
+RecordedPopClip::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] PopClip";
+}
+
+bool
+RecordedPushLayer::PlayEvent(Translator *aTranslator) const
+{
+ SourceSurface* mask = mMask ? aTranslator->LookupSourceSurface(mMask)
+ : nullptr;
+ aTranslator->LookupDrawTarget(mDT)->
+ PushLayer(mOpaque, mOpacity, mask, mMaskTransform, mBounds, mCopyBackground);
+ return true;
+}
+
+void
+RecordedPushLayer::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mOpaque);
+ WriteElement(aStream, mOpacity);
+ WriteElement(aStream, mMask);
+ WriteElement(aStream, mMaskTransform);
+ WriteElement(aStream, mBounds);
+ WriteElement(aStream, mCopyBackground);
+}
+
+RecordedPushLayer::RecordedPushLayer(istream &aStream)
+ : RecordedDrawingEvent(PUSHLAYER, aStream)
+{
+ ReadElement(aStream, mOpaque);
+ ReadElement(aStream, mOpacity);
+ ReadElement(aStream, mMask);
+ ReadElement(aStream, mMaskTransform);
+ ReadElement(aStream, mBounds);
+ ReadElement(aStream, mCopyBackground);
+}
+
+void
+RecordedPushLayer::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] PushPLayer (Opaque=" << mOpaque <<
+ ", Opacity=" << mOpacity << ", Mask Ref=" << mMask << ") ";
+}
+
+bool
+RecordedPopLayer::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->PopLayer();
+ return true;
+}
+
+void
+RecordedPopLayer::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+}
+
+RecordedPopLayer::RecordedPopLayer(istream &aStream)
+ : RecordedDrawingEvent(POPLAYER, aStream)
+{
+}
+
+void
+RecordedPopLayer::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] PopLayer";
+}
+
+bool
+RecordedSetTransform::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->SetTransform(mTransform);
+ return true;
+}
+
+void
+RecordedSetTransform::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mTransform);
+}
+
+RecordedSetTransform::RecordedSetTransform(istream &aStream)
+ : RecordedDrawingEvent(SETTRANSFORM, aStream)
+{
+ ReadElement(aStream, mTransform);
+}
+
+void
+RecordedSetTransform::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] SetTransform [ " << mTransform._11 << " " << mTransform._12 << " ; " <<
+ mTransform._21 << " " << mTransform._22 << " ; " << mTransform._31 << " " << mTransform._32 << " ]";
+}
+
+bool
+RecordedDrawSurface::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->
+ DrawSurface(aTranslator->LookupSourceSurface(mRefSource), mDest, mSource,
+ mDSOptions, mOptions);
+ return true;
+}
+
+void
+RecordedDrawSurface::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mRefSource);
+ WriteElement(aStream, mDest);
+ WriteElement(aStream, mSource);
+ WriteElement(aStream, mDSOptions);
+ WriteElement(aStream, mOptions);
+}
+
+RecordedDrawSurface::RecordedDrawSurface(istream &aStream)
+ : RecordedDrawingEvent(DRAWSURFACE, aStream)
+{
+ ReadElement(aStream, mRefSource);
+ ReadElement(aStream, mDest);
+ ReadElement(aStream, mSource);
+ ReadElement(aStream, mDSOptions);
+ ReadElement(aStream, mOptions);
+}
+
+void
+RecordedDrawSurface::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] DrawSurface (" << mRefSource << ")";
+}
+
+bool
+RecordedDrawFilter::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->
+ DrawFilter(aTranslator->LookupFilterNode(mNode), mSourceRect,
+ mDestPoint, mOptions);
+ return true;
+}
+
+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 << ")";
+}
+
+bool
+RecordedDrawSurfaceWithShadow::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->
+ DrawSurfaceWithShadow(aTranslator->LookupSourceSurface(mRefSource),
+ mDest, mColor, mOffset, mSigma, mOp);
+ return true;
+}
+
+void
+RecordedDrawSurfaceWithShadow::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ WriteElement(aStream, mRefSource);
+ WriteElement(aStream, mDest);
+ WriteElement(aStream, mColor);
+ WriteElement(aStream, mOffset);
+ WriteElement(aStream, mSigma);
+ WriteElement(aStream, mOp);
+}
+
+RecordedDrawSurfaceWithShadow::RecordedDrawSurfaceWithShadow(istream &aStream)
+ : RecordedDrawingEvent(DRAWSURFACEWITHSHADOW, aStream)
+{
+ ReadElement(aStream, mRefSource);
+ ReadElement(aStream, mDest);
+ ReadElement(aStream, mColor);
+ ReadElement(aStream, mOffset);
+ ReadElement(aStream, mSigma);
+ ReadElement(aStream, mOp);
+}
+
+void
+RecordedDrawSurfaceWithShadow::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] DrawSurfaceWithShadow (" << mRefSource << ") Color: (" <<
+ mColor.r << ", " << mColor.g << ", " << mColor.b << ", " << mColor.a << ")";
+}
+
+RecordedPathCreation::RecordedPathCreation(PathRecording *aPath)
+ : RecordedEvent(PATHCREATION), mRefPtr(aPath), mFillRule(aPath->mFillRule), mPathOps(aPath->mPathOps)
+{
+}
+
+RecordedPathCreation::~RecordedPathCreation()
+{
+}
+
+bool
+RecordedPathCreation::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<PathBuilder> builder =
+ aTranslator->GetReferenceDrawTarget()->CreatePathBuilder(mFillRule);
+
+ for (size_t i = 0; i < mPathOps.size(); i++) {
+ const PathOp &op = mPathOps[i];
+ switch (op.mType) {
+ case PathOp::OP_MOVETO:
+ builder->MoveTo(op.mP1);
+ break;
+ case PathOp::OP_LINETO:
+ builder->LineTo(op.mP1);
+ break;
+ case PathOp::OP_BEZIERTO:
+ builder->BezierTo(op.mP1, op.mP2, op.mP3);
+ break;
+ case PathOp::OP_QUADRATICBEZIERTO:
+ builder->QuadraticBezierTo(op.mP1, op.mP2);
+ break;
+ case PathOp::OP_CLOSE:
+ builder->Close();
+ break;
+ }
+ }
+
+ RefPtr<Path> path = builder->Finish();
+ aTranslator->AddPath(mRefPtr, path);
+ return true;
+}
+
+void
+RecordedPathCreation::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, uint64_t(mPathOps.size()));
+ WriteElement(aStream, mFillRule);
+ typedef std::vector<PathOp> pathOpVec;
+ for (pathOpVec::const_iterator iter = mPathOps.begin(); iter != mPathOps.end(); iter++) {
+ WriteElement(aStream, iter->mType);
+ if (sPointCount[iter->mType] >= 1) {
+ WriteElement(aStream, iter->mP1);
+ }
+ if (sPointCount[iter->mType] >= 2) {
+ WriteElement(aStream, iter->mP2);
+ }
+ if (sPointCount[iter->mType] >= 3) {
+ WriteElement(aStream, iter->mP3);
+ }
+ }
+
+}
+
+RecordedPathCreation::RecordedPathCreation(istream &aStream)
+ : RecordedEvent(PATHCREATION)
+{
+ uint64_t size;
+
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, size);
+ ReadElement(aStream, mFillRule);
+
+ for (uint64_t i = 0; i < size; i++) {
+ PathOp newPathOp;
+ ReadElement(aStream, newPathOp.mType);
+ if (sPointCount[newPathOp.mType] >= 1) {
+ ReadElement(aStream, newPathOp.mP1);
+ }
+ if (sPointCount[newPathOp.mType] >= 2) {
+ ReadElement(aStream, newPathOp.mP2);
+ }
+ if (sPointCount[newPathOp.mType] >= 3) {
+ ReadElement(aStream, newPathOp.mP3);
+ }
+
+ mPathOps.push_back(newPathOp);
+ }
+
+}
+
+void
+RecordedPathCreation::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] Path created (OpCount: " << mPathOps.size() << ")";
+}
+bool
+RecordedPathDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemovePath(mRefPtr);
+ return true;
+}
+
+void
+RecordedPathDestruction::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+}
+
+RecordedPathDestruction::RecordedPathDestruction(istream &aStream)
+ : RecordedEvent(PATHDESTRUCTION)
+{
+ ReadElement(aStream, mRefPtr);
+}
+
+void
+RecordedPathDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] Path Destroyed";
+}
+
+RecordedSourceSurfaceCreation::~RecordedSourceSurfaceCreation()
+{
+ if (mDataOwned) {
+ delete [] mData;
+ }
+}
+
+bool
+RecordedSourceSurfaceCreation::PlayEvent(Translator *aTranslator) const
+{
+ if (!mData) {
+ return false;
+ }
+
+ RefPtr<SourceSurface> src = aTranslator->GetReferenceDrawTarget()->
+ CreateSourceSurfaceFromData(mData, mSize, mSize.width * BytesPerPixel(mFormat), mFormat);
+ aTranslator->AddSourceSurface(mRefPtr, src);
+ return true;
+}
+
+void
+RecordedSourceSurfaceCreation::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mSize);
+ WriteElement(aStream, mFormat);
+ MOZ_ASSERT(mData);
+ for (int y = 0; y < mSize.height; y++) {
+ aStream.write((const char*)mData + y * mStride, BytesPerPixel(mFormat) * mSize.width);
+ }
+}
+
+RecordedSourceSurfaceCreation::RecordedSourceSurfaceCreation(istream &aStream)
+ : RecordedEvent(SOURCESURFACECREATION), mDataOwned(true)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mSize);
+ ReadElement(aStream, mFormat);
+ mData = (uint8_t*)new (fallible) char[mSize.width * mSize.height * BytesPerPixel(mFormat)];
+ if (!mData) {
+ gfxWarning() << "RecordedSourceSurfaceCreation failed to allocate data";
+ } else {
+ aStream.read((char*)mData, mSize.width * mSize.height * BytesPerPixel(mFormat));
+ }
+}
+
+void
+RecordedSourceSurfaceCreation::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] SourceSurface created (Size: " << mSize.width << "x" << mSize.height << ")";
+}
+
+bool
+RecordedSourceSurfaceDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemoveSourceSurface(mRefPtr);
+ return true;
+}
+
+void
+RecordedSourceSurfaceDestruction::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+}
+
+RecordedSourceSurfaceDestruction::RecordedSourceSurfaceDestruction(istream &aStream)
+ : RecordedEvent(SOURCESURFACEDESTRUCTION)
+{
+ ReadElement(aStream, mRefPtr);
+}
+
+void
+RecordedSourceSurfaceDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] SourceSurface Destroyed";
+}
+
+RecordedFilterNodeCreation::~RecordedFilterNodeCreation()
+{
+}
+
+bool
+RecordedFilterNodeCreation::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<FilterNode> node = aTranslator->GetReferenceDrawTarget()->
+ CreateFilter(mType);
+ aTranslator->AddFilterNode(mRefPtr, node);
+ return true;
+}
+
+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) << ")";
+}
+
+bool
+RecordedFilterNodeDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemoveFilterNode(mRefPtr);
+ return true;
+}
+
+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) {
+ delete [] mStops;
+ }
+}
+
+bool
+RecordedGradientStopsCreation::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<GradientStops> src = aTranslator->GetReferenceDrawTarget()->
+ CreateGradientStops(mStops, mNumStops, mExtendMode);
+ aTranslator->AddGradientStops(mRefPtr, src);
+ return true;
+}
+
+void
+RecordedGradientStopsCreation::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mExtendMode);
+ WriteElement(aStream, mNumStops);
+ aStream.write((const char*)mStops, mNumStops * sizeof(GradientStop));
+}
+
+RecordedGradientStopsCreation::RecordedGradientStopsCreation(istream &aStream)
+ : RecordedEvent(GRADIENTSTOPSCREATION), mDataOwned(true)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mExtendMode);
+ ReadElement(aStream, mNumStops);
+ mStops = new GradientStop[mNumStops];
+
+ aStream.read((char*)mStops, mNumStops * sizeof(GradientStop));
+}
+
+void
+RecordedGradientStopsCreation::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] GradientStops created (Stops: " << mNumStops << ")";
+}
+
+bool
+RecordedGradientStopsDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemoveGradientStops(mRefPtr);
+ return true;
+}
+
+void
+RecordedGradientStopsDestruction::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+}
+
+RecordedGradientStopsDestruction::RecordedGradientStopsDestruction(istream &aStream)
+ : RecordedEvent(GRADIENTSTOPSDESTRUCTION)
+{
+ ReadElement(aStream, mRefPtr);
+}
+
+void
+RecordedGradientStopsDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] GradientStops Destroyed";
+}
+
+bool
+RecordedSnapshot::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<SourceSurface> src = aTranslator->LookupDrawTarget(mDT)->Snapshot();
+ aTranslator->AddSourceSurface(mRefPtr, src);
+ return true;
+}
+
+void
+RecordedSnapshot::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mDT);
+}
+
+RecordedSnapshot::RecordedSnapshot(istream &aStream)
+ : RecordedEvent(SNAPSHOT)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mDT);
+}
+
+void
+RecordedSnapshot::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] Snapshot Created (DT: " << mDT << ")";
+}
+
+RecordedFontData::~RecordedFontData()
+{
+ delete[] mData;
+}
+
+bool
+RecordedFontData::PlayEvent(Translator *aTranslator) const
+{
+ RefPtr<NativeFontResource> fontResource =
+ Factory::CreateNativeFontResource(mData, mFontDetails.size,
+ aTranslator->GetDesiredFontType());
+ if (!fontResource) {
+ return false;
+ }
+
+ aTranslator->AddNativeFontResource(mFontDetails.fontDataKey, fontResource);
+ return true;
+}
+
+void
+RecordedFontData::RecordToStream(std::ostream &aStream) const
+{
+ MOZ_ASSERT(mGetFontFileDataSucceeded);
+
+ WriteElement(aStream, mFontDetails.fontDataKey);
+ WriteElement(aStream, mFontDetails.size);
+ aStream.write((const char*)mData, mFontDetails.size);
+}
+
+void
+RecordedFontData::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "Font Data of size " << mFontDetails.size;
+}
+
+void
+RecordedFontData::SetFontData(const uint8_t *aData, uint32_t aSize, uint32_t aIndex, Float aGlyphSize)
+{
+ mData = new uint8_t[aSize];
+ memcpy(mData, aData, aSize);
+ mFontDetails.fontDataKey = SFNTData::GetUniqueKey(aData, aSize);
+ mFontDetails.size = aSize;
+ mFontDetails.index = aIndex;
+ mFontDetails.glyphSize = aGlyphSize;
+}
+
+bool
+RecordedFontData::GetFontDetails(RecordedFontDetails& fontDetails)
+{
+ if (!mGetFontFileDataSucceeded) {
+ return false;
+ }
+
+ fontDetails.fontDataKey = mFontDetails.fontDataKey;
+ fontDetails.size = mFontDetails.size;
+ fontDetails.glyphSize = mFontDetails.glyphSize;
+ fontDetails.index = mFontDetails.index;
+ return true;
+}
+
+RecordedFontData::RecordedFontData(istream &aStream)
+ : RecordedEvent(FONTDATA)
+{
+ ReadElement(aStream, mFontDetails.fontDataKey);
+ ReadElement(aStream, mFontDetails.size);
+ mData = new uint8_t[mFontDetails.size];
+ aStream.read((char*)mData, mFontDetails.size);
+}
+
+RecordedFontDescriptor::~RecordedFontDescriptor()
+{
+}
+
+bool
+RecordedFontDescriptor::PlayEvent(Translator *aTranslator) const
+{
+ MOZ_ASSERT(mType == FontType::GDI);
+
+ NativeFont nativeFont;
+ nativeFont.mType = (NativeFontType)mType;
+ nativeFont.mFont = (void*)&mData[0];
+
+ RefPtr<ScaledFont> font =
+ Factory::CreateScaledFontForNativeFont(nativeFont, mFontSize);
+
+#ifdef USE_CAIRO_SCALED_FONT
+ static_cast<ScaledFontBase*>(font.get())->PopulateCairoScaledFont();
+#endif
+
+ aTranslator->AddScaledFont(mRefPtr, font);
+ return true;
+}
+
+void
+RecordedFontDescriptor::RecordToStream(std::ostream &aStream) const
+{
+ MOZ_ASSERT(mHasDesc);
+ WriteElement(aStream, mType);
+ WriteElement(aStream, mFontSize);
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, (size_t)mData.size());
+ aStream.write((char*)&mData[0], mData.size());
+}
+
+void
+RecordedFontDescriptor::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] Font Descriptor";
+}
+
+void
+RecordedFontDescriptor::SetFontDescriptor(const uint8_t* aData, uint32_t aSize, Float aFontSize)
+{
+ mData.assign(aData, aData + aSize);
+ mFontSize = aFontSize;
+}
+
+RecordedFontDescriptor::RecordedFontDescriptor(istream &aStream)
+ : RecordedEvent(FONTDATA)
+{
+ ReadElement(aStream, mType);
+ ReadElement(aStream, mFontSize);
+ ReadElement(aStream, mRefPtr);
+
+ size_t size;
+ ReadElement(aStream, size);
+ mData.resize(size);
+ aStream.read((char*)&mData[0], size);
+}
+
+bool
+RecordedScaledFontCreation::PlayEvent(Translator *aTranslator) const
+{
+ NativeFontResource *fontResource = aTranslator->LookupNativeFontResource(mFontDataKey);
+ if (!fontResource) {
+ gfxDevCrash(LogReason::NativeFontResourceNotFound) <<
+ "NativeFontResource lookup failed for key |" << hexa(mFontDataKey) << "|.";
+ return false;
+ }
+
+ RefPtr<ScaledFont> scaledFont =
+ fontResource->CreateScaledFont(mIndex, mGlyphSize, mInstanceData.data(), mInstanceData.size());
+ aTranslator->AddScaledFont(mRefPtr, scaledFont);
+ return true;
+}
+
+void
+RecordedScaledFontCreation::RecordToStream(std::ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+ WriteElement(aStream, mFontDataKey);
+ WriteElement(aStream, mIndex);
+ WriteElement(aStream, mGlyphSize);
+ WriteElement(aStream, (size_t)mInstanceData.size());
+ aStream.write((char*)mInstanceData.data(), mInstanceData.size());
+}
+
+void
+RecordedScaledFontCreation::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] ScaledFont Created";
+}
+
+void
+RecordedScaledFontCreation::SetFontInstanceData(const uint8_t *aData, uint32_t aSize)
+{
+ mInstanceData.assign(aData, aData + aSize);
+}
+
+RecordedScaledFontCreation::RecordedScaledFontCreation(istream &aStream)
+ : RecordedEvent(SCALEDFONTCREATION)
+{
+ ReadElement(aStream, mRefPtr);
+ ReadElement(aStream, mFontDataKey);
+ ReadElement(aStream, mIndex);
+ ReadElement(aStream, mGlyphSize);
+
+ size_t size;
+ ReadElement(aStream, size);
+ mInstanceData.resize(size);
+ aStream.read((char*)mInstanceData.data(), size);
+}
+
+bool
+RecordedScaledFontDestruction::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->RemoveScaledFont(mRefPtr);
+ return true;
+}
+
+void
+RecordedScaledFontDestruction::RecordToStream(ostream &aStream) const
+{
+ WriteElement(aStream, mRefPtr);
+}
+
+RecordedScaledFontDestruction::RecordedScaledFontDestruction(istream &aStream)
+ : RecordedEvent(SCALEDFONTDESTRUCTION)
+{
+ ReadElement(aStream, mRefPtr);
+}
+
+void
+RecordedScaledFontDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mRefPtr << "] ScaledFont Destroyed";
+}
+
+bool
+RecordedMaskSurface::PlayEvent(Translator *aTranslator) const
+{
+ aTranslator->LookupDrawTarget(mDT)->
+ MaskSurface(*GenericPattern(mPattern, aTranslator),
+ aTranslator->LookupSourceSurface(mRefMask),
+ mOffset, mOptions);
+ return true;
+}
+
+void
+RecordedMaskSurface::RecordToStream(ostream &aStream) const
+{
+ RecordedDrawingEvent::RecordToStream(aStream);
+ RecordPatternData(aStream, mPattern);
+ WriteElement(aStream, mRefMask);
+ WriteElement(aStream, mOffset);
+ WriteElement(aStream, mOptions);
+}
+
+RecordedMaskSurface::RecordedMaskSurface(istream &aStream)
+ : RecordedDrawingEvent(MASKSURFACE, aStream)
+{
+ ReadPatternData(aStream, mPattern);
+ ReadElement(aStream, mRefMask);
+ ReadElement(aStream, mOffset);
+ ReadElement(aStream, mOptions);
+}
+
+void
+RecordedMaskSurface::OutputSimpleEventInfo(stringstream &aStringStream) const
+{
+ aStringStream << "[" << mDT << "] MaskSurface (" << mRefMask << ") Offset: (" << mOffset.x << "x" << mOffset.y << ") Pattern: ";
+ OutputSimplePatternInfo(mPattern, aStringStream);
+}
+
+template<typename T>
+void
+ReplaySetAttribute(FilterNode *aNode, uint32_t aIndex, T aValue)
+{
+ aNode->SetAttribute(aIndex, aValue);
+}
+
+bool
+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(Matrix, MATRIX);
+ 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;
+ }
+
+ return true;
+}
+
+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 << ")";
+}
+
+bool
+RecordedFilterNodeSetInput::PlayEvent(Translator *aTranslator) const
+{
+ if (mInputFilter) {
+ aTranslator->LookupFilterNode(mNode)->SetInput(
+ mIndex, aTranslator->LookupFilterNode(mInputFilter));
+ } else {
+ aTranslator->LookupFilterNode(mNode)->SetInput(
+ mIndex, aTranslator->LookupSourceSurface(mInputSurface));
+ }
+
+ return true;
+}
+
+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 << ")";
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/RecordedEvent.h b/gfx/2d/RecordedEvent.h
new file mode 100644
index 000000000..bf660ba24
--- /dev/null
+++ b/gfx/2d/RecordedEvent.h
@@ -0,0 +1,1281 @@
+/* -*- 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_RECORDEDEVENT_H_
+#define MOZILLA_GFX_RECORDEDEVENT_H_
+
+#include "2D.h"
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+struct PathOp;
+class PathRecording;
+
+const uint32_t kMagicInt = 0xc001feed;
+
+// A change in major revision means a change in event binary format, causing
+// 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 = 6;
+// A change in minor revision means additions of new events. New streams will
+// not play in older players.
+const uint16_t kMinorRevision = 0;
+
+struct ReferencePtr
+{
+ ReferencePtr()
+ : mLongPtr(0)
+ {}
+
+ MOZ_IMPLICIT ReferencePtr(const void* aLongPtr)
+ : mLongPtr(uint64_t(aLongPtr))
+ {}
+
+ template <typename T>
+ MOZ_IMPLICIT ReferencePtr(const RefPtr<T>& aPtr)
+ : mLongPtr(uint64_t(aPtr.get()))
+ {}
+
+ ReferencePtr &operator =(const void* aLongPtr) {
+ mLongPtr = uint64_t(aLongPtr);
+ return *this;
+ }
+
+ template <typename T>
+ ReferencePtr &operator =(const RefPtr<T>& aPtr) {
+ mLongPtr = uint64_t(aPtr.get());
+ return *this;
+ }
+
+ operator void*() const {
+ return (void*)mLongPtr;
+ }
+
+ uint64_t mLongPtr;
+};
+
+struct RecordedFontDetails
+{
+ uint64_t fontDataKey;
+ uint32_t size;
+ uint32_t index;
+ Float glyphSize;
+};
+
+// Used by the Azure drawing debugger (player2d)
+inline std::string StringFromPtr(ReferencePtr aPtr)
+{
+ std::stringstream stream;
+ stream << aPtr;
+ return stream.str();
+}
+
+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 NativeFontResource *LookupNativeFontResource(uint64_t aKey) = 0;
+ virtual void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget *aDT) = 0;
+ virtual void RemoveDrawTarget(ReferencePtr aRefPtr) = 0;
+ virtual void AddPath(ReferencePtr aRefPtr, Path *aPath) = 0;
+ 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;
+ virtual void RemoveScaledFont(ReferencePtr aRefPtr) = 0;
+ virtual void AddNativeFontResource(uint64_t aKey,
+ NativeFontResource *aNativeFontResource) = 0;
+
+ virtual already_AddRefed<DrawTarget> CreateDrawTarget(ReferencePtr aRefPtr,
+ const IntSize &aSize,
+ SurfaceFormat aFormat);
+ virtual DrawTarget *GetReferenceDrawTarget() = 0;
+ virtual FontType GetDesiredFontType() = 0;
+};
+
+struct ColorPatternStorage
+{
+ Color mColor;
+};
+
+struct LinearGradientPatternStorage
+{
+ Point mBegin;
+ Point mEnd;
+ ReferencePtr mStops;
+ Matrix mMatrix;
+};
+
+struct RadialGradientPatternStorage
+{
+ Point mCenter1;
+ Point mCenter2;
+ Float mRadius1;
+ Float mRadius2;
+ ReferencePtr mStops;
+ Matrix mMatrix;
+};
+
+struct SurfacePatternStorage
+{
+ ExtendMode mExtend;
+ SamplingFilter mSamplingFilter;
+ ReferencePtr mSurface;
+ Matrix mMatrix;
+ IntRect mSamplingRect;
+};
+
+struct PatternStorage
+{
+ PatternType mType;
+ union {
+ char *mStorage;
+ char mColor[sizeof(ColorPatternStorage)];
+ char mLinear[sizeof(LinearGradientPatternStorage)];
+ char mRadial[sizeof(RadialGradientPatternStorage)];
+ char mSurface[sizeof(SurfacePatternStorage)];
+ };
+};
+
+class RecordedEvent {
+public:
+ enum EventType {
+ DRAWTARGETCREATION = 0,
+ DRAWTARGETDESTRUCTION,
+ FILLRECT,
+ STROKERECT,
+ STROKELINE,
+ CLEARRECT,
+ COPYSURFACE,
+ SETTRANSFORM,
+ PUSHCLIP,
+ PUSHCLIPRECT,
+ POPCLIP,
+ FILL,
+ FILLGLYPHS,
+ MASK,
+ STROKE,
+ DRAWSURFACE,
+ DRAWSURFACEWITHSHADOW,
+ PATHCREATION,
+ PATHDESTRUCTION,
+ SOURCESURFACECREATION,
+ SOURCESURFACEDESTRUCTION,
+ GRADIENTSTOPSCREATION,
+ GRADIENTSTOPSDESTRUCTION,
+ SNAPSHOT,
+ SCALEDFONTCREATION,
+ SCALEDFONTDESTRUCTION,
+ MASKSURFACE,
+ FILTERNODECREATION,
+ FILTERNODEDESTRUCTION,
+ DRAWFILTER,
+ FILTERNODESETATTRIBUTE,
+ FILTERNODESETINPUT,
+ CREATESIMILARDRAWTARGET,
+ FONTDATA,
+ FONTDESC,
+ PUSHLAYER,
+ POPLAYER,
+ };
+ static const uint32_t kTotalEventTypes = RecordedEvent::FILTERNODESETINPUT + 1;
+
+ virtual ~RecordedEvent() {}
+
+ static std::string GetEventName(EventType aType);
+
+ /**
+ * Play back this event using the translator. Note that derived classes should
+ * only return false when there is a fatal error, as it will probably mean the
+ * translation will abort.
+ * @param aTranslator Translator to be used for retrieving other referenced
+ * objects and making playback decisions.
+ * @return true unless a fatal problem has occurred and playback should abort.
+ */
+ virtual bool PlayEvent(Translator *aTranslator) const { return true; }
+
+ virtual void RecordToStream(std::ostream &aStream) const {}
+
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const { }
+
+ void RecordPatternData(std::ostream &aStream, const PatternStorage &aPatternStorage) const;
+ void ReadPatternData(std::istream &aStream, PatternStorage &aPatternStorage) const;
+ void StorePattern(PatternStorage &aDestination, const Pattern &aSource) const;
+ void RecordStrokeOptions(std::ostream &aStream, const StrokeOptions &aStrokeOptions) const;
+ void ReadStrokeOptions(std::istream &aStream, StrokeOptions &aStrokeOptions);
+
+ virtual std::string GetName() const = 0;
+
+ virtual ReferencePtr GetObjectRef() const = 0;
+
+ virtual ReferencePtr GetDestinedDT() { return nullptr; }
+
+ void OutputSimplePatternInfo(const PatternStorage &aStorage, std::stringstream &aOutput) const;
+
+ static RecordedEvent *LoadEventFromStream(std::istream &aStream, EventType aType);
+
+ EventType GetType() { return (EventType)mType; }
+protected:
+ friend class DrawEventRecorderPrivate;
+
+ MOZ_IMPLICIT RecordedEvent(int32_t aType) : mType(aType)
+ {}
+
+ int32_t mType;
+ std::vector<Float> mDashPatternStorage;
+};
+
+class RecordedDrawingEvent : public RecordedEvent
+{
+public:
+ virtual ReferencePtr GetDestinedDT() { return mDT; }
+
+protected:
+ RecordedDrawingEvent(EventType aType, DrawTarget *aTarget)
+ : RecordedEvent(aType), mDT(aTarget)
+ {
+ }
+
+ RecordedDrawingEvent(EventType aType, std::istream &aStream);
+ virtual void RecordToStream(std::ostream &aStream) const;
+
+ virtual ReferencePtr GetObjectRef() const;
+
+ ReferencePtr mDT;
+};
+
+class RecordedDrawTargetCreation : public RecordedEvent {
+public:
+ 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 bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "DrawTarget Creation"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+
+ ReferencePtr mRefPtr;
+ BackendType mBackendType;
+ IntSize mSize;
+ SurfaceFormat mFormat;
+ bool mHasExistingData;
+ RefPtr<SourceSurface> mExistingData;
+
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedDrawTargetCreation(std::istream &aStream);
+};
+
+class RecordedDrawTargetDestruction : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedDrawTargetDestruction(ReferencePtr aRefPtr)
+ : RecordedEvent(DRAWTARGETDESTRUCTION), mRefPtr(aRefPtr)
+ {}
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "DrawTarget Destruction"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+
+ ReferencePtr mRefPtr;
+
+ BackendType mBackendType;
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedDrawTargetDestruction(std::istream &aStream);
+};
+
+class RecordedCreateSimilarDrawTarget : public RecordedEvent
+{
+public:
+ RecordedCreateSimilarDrawTarget(ReferencePtr aRefPtr, const IntSize &aSize,
+ SurfaceFormat aFormat)
+ : RecordedEvent(CREATESIMILARDRAWTARGET)
+ , mRefPtr(aRefPtr) , mSize(aSize), mFormat(aFormat)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "CreateSimilarDrawTarget"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+
+ ReferencePtr mRefPtr;
+ IntSize mSize;
+ SurfaceFormat mFormat;
+
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedCreateSimilarDrawTarget(std::istream &aStream);
+};
+
+class RecordedFillRect : public RecordedDrawingEvent {
+public:
+ RecordedFillRect(DrawTarget *aDT, const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions)
+ : RecordedDrawingEvent(FILLRECT, aDT), mRect(aRect), mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "FillRect"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedFillRect(std::istream &aStream);
+
+ Rect mRect;
+ PatternStorage mPattern;
+ DrawOptions mOptions;
+};
+
+class RecordedStrokeRect : public RecordedDrawingEvent {
+public:
+ RecordedStrokeRect(DrawTarget *aDT, const Rect &aRect, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
+ : RecordedDrawingEvent(STROKERECT, aDT), mRect(aRect),
+ mStrokeOptions(aStrokeOptions), mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "StrokeRect"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedStrokeRect(std::istream &aStream);
+
+ Rect mRect;
+ PatternStorage mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+};
+
+class RecordedStrokeLine : public RecordedDrawingEvent {
+public:
+ RecordedStrokeLine(DrawTarget *aDT, const Point &aBegin, const Point &aEnd,
+ const Pattern &aPattern, const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions)
+ : RecordedDrawingEvent(STROKELINE, aDT), mBegin(aBegin), mEnd(aEnd),
+ mStrokeOptions(aStrokeOptions), mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "StrokeLine"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedStrokeLine(std::istream &aStream);
+
+ Point mBegin;
+ Point mEnd;
+ PatternStorage mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+};
+
+class RecordedFill : public RecordedDrawingEvent {
+public:
+ RecordedFill(DrawTarget *aDT, ReferencePtr aPath, const Pattern &aPattern, const DrawOptions &aOptions)
+ : RecordedDrawingEvent(FILL, aDT), mPath(aPath), mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Fill"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedFill(std::istream &aStream);
+
+ ReferencePtr mPath;
+ PatternStorage mPattern;
+ DrawOptions mOptions;
+};
+
+class RecordedFillGlyphs : public RecordedDrawingEvent {
+public:
+ RecordedFillGlyphs(DrawTarget *aDT, ReferencePtr aScaledFont, const Pattern &aPattern, const DrawOptions &aOptions,
+ const Glyph *aGlyphs, uint32_t aNumGlyphs)
+ : RecordedDrawingEvent(FILLGLYPHS, aDT), mScaledFont(aScaledFont), mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ mNumGlyphs = aNumGlyphs;
+ mGlyphs = new Glyph[aNumGlyphs];
+ memcpy(mGlyphs, aGlyphs, sizeof(Glyph) * aNumGlyphs);
+ }
+ virtual ~RecordedFillGlyphs();
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "FillGlyphs"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedFillGlyphs(std::istream &aStream);
+
+ ReferencePtr mScaledFont;
+ PatternStorage mPattern;
+ DrawOptions mOptions;
+ Glyph *mGlyphs;
+ uint32_t mNumGlyphs;
+};
+
+class RecordedMask : public RecordedDrawingEvent {
+public:
+ RecordedMask(DrawTarget *aDT, const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions)
+ : RecordedDrawingEvent(MASK, aDT), mOptions(aOptions)
+ {
+ StorePattern(mSource, aSource);
+ StorePattern(mMask, aMask);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Mask"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedMask(std::istream &aStream);
+
+ PatternStorage mSource;
+ PatternStorage mMask;
+ DrawOptions mOptions;
+};
+
+class RecordedStroke : public RecordedDrawingEvent {
+public:
+ RecordedStroke(DrawTarget *aDT, ReferencePtr aPath, const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
+ : RecordedDrawingEvent(STROKE, aDT), mPath(aPath),
+ mStrokeOptions(aStrokeOptions), mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Stroke"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedStroke(std::istream &aStream);
+
+ ReferencePtr mPath;
+ PatternStorage mPattern;
+ StrokeOptions mStrokeOptions;
+ DrawOptions mOptions;
+};
+
+class RecordedClearRect : public RecordedDrawingEvent {
+public:
+ RecordedClearRect(DrawTarget *aDT, const Rect &aRect)
+ : RecordedDrawingEvent(CLEARRECT, aDT), mRect(aRect)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "ClearRect"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedClearRect(std::istream &aStream);
+
+ Rect mRect;
+};
+
+class RecordedCopySurface : public RecordedDrawingEvent {
+public:
+ RecordedCopySurface(DrawTarget *aDT, ReferencePtr aSourceSurface,
+ const IntRect &aSourceRect, const IntPoint &aDest)
+ : RecordedDrawingEvent(COPYSURFACE, aDT), mSourceSurface(aSourceSurface),
+ mSourceRect(aSourceRect), mDest(aDest)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "CopySurface"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedCopySurface(std::istream &aStream);
+
+ ReferencePtr mSourceSurface;
+ IntRect mSourceRect;
+ IntPoint mDest;
+};
+
+class RecordedPushClip : public RecordedDrawingEvent {
+public:
+ RecordedPushClip(DrawTarget *aDT, ReferencePtr aPath)
+ : RecordedDrawingEvent(PUSHCLIP, aDT), mPath(aPath)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "PushClip"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedPushClip(std::istream &aStream);
+
+ ReferencePtr mPath;
+};
+
+class RecordedPushClipRect : public RecordedDrawingEvent {
+public:
+ RecordedPushClipRect(DrawTarget *aDT, const Rect &aRect)
+ : RecordedDrawingEvent(PUSHCLIPRECT, aDT), mRect(aRect)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "PushClipRect"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedPushClipRect(std::istream &aStream);
+
+ Rect mRect;
+};
+
+class RecordedPopClip : public RecordedDrawingEvent {
+public:
+ MOZ_IMPLICIT RecordedPopClip(DrawTarget *aDT)
+ : RecordedDrawingEvent(POPCLIP, aDT)
+ {}
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "PopClip"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedPopClip(std::istream &aStream);
+};
+
+class RecordedPushLayer : public RecordedDrawingEvent {
+public:
+ RecordedPushLayer(DrawTarget* aDT, bool aOpaque, Float aOpacity,
+ SourceSurface* aMask, const Matrix& aMaskTransform,
+ const IntRect& aBounds, bool aCopyBackground)
+ : RecordedDrawingEvent(PUSHLAYER, aDT), mOpaque(aOpaque)
+ , mOpacity(aOpacity), mMask(aMask), mMaskTransform(aMaskTransform)
+ , mBounds(aBounds), mCopyBackground(aCopyBackground)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "PushLayer"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedPushLayer(std::istream &aStream);
+
+ bool mOpaque;
+ Float mOpacity;
+ ReferencePtr mMask;
+ Matrix mMaskTransform;
+ IntRect mBounds;
+ bool mCopyBackground;
+};
+
+class RecordedPopLayer : public RecordedDrawingEvent {
+public:
+ MOZ_IMPLICIT RecordedPopLayer(DrawTarget* aDT)
+ : RecordedDrawingEvent(POPLAYER, aDT)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "PopLayer"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedPopLayer(std::istream &aStream);
+};
+
+class RecordedSetTransform : public RecordedDrawingEvent {
+public:
+ RecordedSetTransform(DrawTarget *aDT, const Matrix &aTransform)
+ : RecordedDrawingEvent(SETTRANSFORM, aDT), mTransform(aTransform)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "SetTransform"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedSetTransform(std::istream &aStream);
+
+ Matrix mTransform;
+};
+
+class RecordedDrawSurface : public RecordedDrawingEvent {
+public:
+ RecordedDrawSurface(DrawTarget *aDT, ReferencePtr aRefSource, const Rect &aDest,
+ const Rect &aSource, const DrawSurfaceOptions &aDSOptions,
+ const DrawOptions &aOptions)
+ : RecordedDrawingEvent(DRAWSURFACE, aDT), mRefSource(aRefSource), mDest(aDest)
+ , mSource(aSource), mDSOptions(aDSOptions), mOptions(aOptions)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "DrawSurface"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedDrawSurface(std::istream &aStream);
+
+ ReferencePtr mRefSource;
+ Rect mDest;
+ Rect mSource;
+ DrawSurfaceOptions mDSOptions;
+ DrawOptions mOptions;
+};
+
+class RecordedDrawSurfaceWithShadow : public RecordedDrawingEvent {
+public:
+ RecordedDrawSurfaceWithShadow(DrawTarget *aDT, ReferencePtr aRefSource, const Point &aDest,
+ const Color &aColor, const Point &aOffset,
+ Float aSigma, CompositionOp aOp)
+ : RecordedDrawingEvent(DRAWSURFACEWITHSHADOW, aDT), mRefSource(aRefSource), mDest(aDest)
+ , mColor(aColor), mOffset(aOffset), mSigma(aSigma), mOp(aOp)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "DrawSurfaceWithShadow"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedDrawSurfaceWithShadow(std::istream &aStream);
+
+ ReferencePtr mRefSource;
+ Point mDest;
+ Color mColor;
+ Point mOffset;
+ Float mSigma;
+ 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 bool 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:
+ MOZ_IMPLICIT RecordedPathCreation(PathRecording *aPath);
+ ~RecordedPathCreation();
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Path Creation"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+ FillRule mFillRule;
+ std::vector<PathOp> mPathOps;
+
+ MOZ_IMPLICIT RecordedPathCreation(std::istream &aStream);
+};
+
+class RecordedPathDestruction : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedPathDestruction(PathRecording *aPath)
+ : RecordedEvent(PATHDESTRUCTION), mRefPtr(aPath)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Path Destruction"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+
+ MOZ_IMPLICIT RecordedPathDestruction(std::istream &aStream);
+};
+
+class RecordedSourceSurfaceCreation : public RecordedEvent {
+public:
+ RecordedSourceSurfaceCreation(ReferencePtr aRefPtr, uint8_t *aData, int32_t aStride,
+ const IntSize &aSize, SurfaceFormat aFormat)
+ : RecordedEvent(SOURCESURFACECREATION), mRefPtr(aRefPtr), mData(aData)
+ , mStride(aStride), mSize(aSize), mFormat(aFormat), mDataOwned(false)
+ {
+ }
+
+ ~RecordedSourceSurfaceCreation();
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "SourceSurface Creation"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+ uint8_t *mData;
+ int32_t mStride;
+ IntSize mSize;
+ SurfaceFormat mFormat;
+ bool mDataOwned;
+
+ MOZ_IMPLICIT RecordedSourceSurfaceCreation(std::istream &aStream);
+};
+
+class RecordedSourceSurfaceDestruction : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedSourceSurfaceDestruction(ReferencePtr aRefPtr)
+ : RecordedEvent(SOURCESURFACEDESTRUCTION), mRefPtr(aRefPtr)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "SourceSurface Destruction"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+
+ MOZ_IMPLICIT RecordedSourceSurfaceDestruction(std::istream &aStream);
+};
+
+class RecordedFilterNodeCreation : public RecordedEvent {
+public:
+ RecordedFilterNodeCreation(ReferencePtr aRefPtr, FilterType aType)
+ : RecordedEvent(FILTERNODECREATION), mRefPtr(aRefPtr), mType(aType)
+ {
+ }
+
+ ~RecordedFilterNodeCreation();
+
+ virtual bool 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 bool 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 {
+public:
+ RecordedGradientStopsCreation(ReferencePtr aRefPtr, GradientStop *aStops,
+ uint32_t aNumStops, ExtendMode aExtendMode)
+ : RecordedEvent(GRADIENTSTOPSCREATION), mRefPtr(aRefPtr), mStops(aStops)
+ , mNumStops(aNumStops), mExtendMode(aExtendMode), mDataOwned(false)
+ {
+ }
+
+ ~RecordedGradientStopsCreation();
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "GradientStops Creation"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+ GradientStop *mStops;
+ uint32_t mNumStops;
+ ExtendMode mExtendMode;
+ bool mDataOwned;
+
+ MOZ_IMPLICIT RecordedGradientStopsCreation(std::istream &aStream);
+};
+
+class RecordedGradientStopsDestruction : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedGradientStopsDestruction(ReferencePtr aRefPtr)
+ : RecordedEvent(GRADIENTSTOPSDESTRUCTION), mRefPtr(aRefPtr)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "GradientStops Destruction"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+
+ MOZ_IMPLICIT RecordedGradientStopsDestruction(std::istream &aStream);
+};
+
+class RecordedSnapshot : public RecordedEvent {
+public:
+ RecordedSnapshot(ReferencePtr aRefPtr, DrawTarget *aDT)
+ : RecordedEvent(SNAPSHOT), mRefPtr(aRefPtr), mDT(aDT)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Snapshot"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+ ReferencePtr mDT;
+
+ MOZ_IMPLICIT RecordedSnapshot(std::istream &aStream);
+};
+
+class RecordedFontData : public RecordedEvent {
+public:
+
+ static void FontDataProc(const uint8_t *aData, uint32_t aSize,
+ uint32_t aIndex, Float aGlyphSize, void* aBaton)
+ {
+ auto recordedFontData = static_cast<RecordedFontData*>(aBaton);
+ recordedFontData->SetFontData(aData, aSize, aIndex, aGlyphSize);
+ }
+
+ explicit RecordedFontData(ScaledFont *aScaledFont)
+ : RecordedEvent(FONTDATA), mData(nullptr)
+ {
+ mGetFontFileDataSucceeded = aScaledFont->GetFontFileData(&FontDataProc, this);
+ }
+
+ ~RecordedFontData();
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Font Data"; }
+ virtual ReferencePtr GetObjectRef() const { return nullptr; };
+
+ void SetFontData(const uint8_t *aData, uint32_t aSize, uint32_t aIndex,
+ Float aGlyphSize);
+
+ bool GetFontDetails(RecordedFontDetails& fontDetails);
+
+private:
+ friend class RecordedEvent;
+
+ uint8_t *mData;
+ RecordedFontDetails mFontDetails;
+
+ bool mGetFontFileDataSucceeded = false;
+
+ MOZ_IMPLICIT RecordedFontData(std::istream &aStream);
+};
+
+class RecordedFontDescriptor : public RecordedEvent {
+public:
+
+ static void FontDescCb(const uint8_t *aData, uint32_t aSize,
+ Float aFontSize, void* aBaton)
+ {
+ auto recordedFontDesc = static_cast<RecordedFontDescriptor*>(aBaton);
+ recordedFontDesc->SetFontDescriptor(aData, aSize, aFontSize);
+ }
+
+ explicit RecordedFontDescriptor(ScaledFont* aScaledFont)
+ : RecordedEvent(FONTDESC)
+ , mType(aScaledFont->GetType())
+ , mRefPtr(aScaledFont)
+ {
+ mHasDesc = aScaledFont->GetFontDescriptor(FontDescCb, this);
+ }
+
+ ~RecordedFontDescriptor();
+
+ bool IsValid() const { return mHasDesc; }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "Font Desc"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+
+private:
+ friend class RecordedEvent;
+
+ void SetFontDescriptor(const uint8_t* aData, uint32_t aSize, Float aFontSize);
+
+ bool mHasDesc;
+
+ FontType mType;
+ Float mFontSize;
+ std::vector<uint8_t> mData;
+ ReferencePtr mRefPtr;
+
+ MOZ_IMPLICIT RecordedFontDescriptor(std::istream &aStream);
+};
+
+class RecordedScaledFontCreation : public RecordedEvent {
+public:
+
+ static void FontInstanceDataProc(const uint8_t* aData, uint32_t aSize, void* aBaton)
+ {
+ auto recordedScaledFontCreation = static_cast<RecordedScaledFontCreation*>(aBaton);
+ recordedScaledFontCreation->SetFontInstanceData(aData, aSize);
+ }
+
+ RecordedScaledFontCreation(ScaledFont* aScaledFont,
+ RecordedFontDetails aFontDetails)
+ : RecordedEvent(SCALEDFONTCREATION), mRefPtr(aScaledFont)
+ , mFontDataKey(aFontDetails.fontDataKey)
+ , mGlyphSize(aFontDetails.glyphSize) , mIndex(aFontDetails.index)
+ {
+ aScaledFont->GetFontInstanceData(FontInstanceDataProc, this);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "ScaledFont Creation"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+
+ void SetFontInstanceData(const uint8_t *aData, uint32_t aSize);
+
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+ uint64_t mFontDataKey;
+ Float mGlyphSize;
+ uint32_t mIndex;
+ std::vector<uint8_t> mInstanceData;
+
+ MOZ_IMPLICIT RecordedScaledFontCreation(std::istream &aStream);
+};
+
+class RecordedScaledFontDestruction : public RecordedEvent {
+public:
+ MOZ_IMPLICIT RecordedScaledFontDestruction(ReferencePtr aRefPtr)
+ : RecordedEvent(SCALEDFONTDESTRUCTION), mRefPtr(aRefPtr)
+ {
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "ScaledFont Destruction"; }
+ virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+ friend class RecordedEvent;
+
+ ReferencePtr mRefPtr;
+
+ MOZ_IMPLICIT RecordedScaledFontDestruction(std::istream &aStream);
+};
+
+class RecordedMaskSurface : public RecordedDrawingEvent {
+public:
+ RecordedMaskSurface(DrawTarget *aDT, const Pattern &aPattern, ReferencePtr aRefMask,
+ const Point &aOffset, const DrawOptions &aOptions)
+ : RecordedDrawingEvent(MASKSURFACE, aDT), mRefMask(aRefMask), mOffset(aOffset)
+ , mOptions(aOptions)
+ {
+ StorePattern(mPattern, aPattern);
+ }
+
+ virtual bool PlayEvent(Translator *aTranslator) const;
+
+ virtual void RecordToStream(std::ostream &aStream) const;
+ virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+ virtual std::string GetName() const { return "MaskSurface"; }
+private:
+ friend class RecordedEvent;
+
+ MOZ_IMPLICIT RecordedMaskSurface(std::istream &aStream);
+
+ PatternStorage mPattern;
+ ReferencePtr mRefMask;
+ Point mOffset;
+ 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_MATRIX,
+ 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 bool 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 bool 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);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
diff --git a/gfx/2d/RecordingTypes.h b/gfx/2d/RecordingTypes.h
new file mode 100644
index 000000000..93fc67a68
--- /dev/null
+++ b/gfx/2d/RecordingTypes.h
@@ -0,0 +1,41 @@
+/* -*- 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_RECORDINGTYPES_H_
+#define MOZILLA_GFX_RECORDINGTYPES_H_
+
+#include <ostream>
+
+namespace mozilla {
+namespace gfx {
+
+template<class T>
+struct ElementStreamFormat
+{
+ static void Write(std::ostream &aStream, const T &aElement)
+ {
+ aStream.write(reinterpret_cast<const char*>(&aElement), sizeof(T));
+ }
+ static void Read(std::istream &aStream, T &aElement)
+ {
+ aStream.read(reinterpret_cast<char *>(&aElement), sizeof(T));
+ }
+};
+
+template<class T>
+void WriteElement(std::ostream &aStream, const T &aElement)
+{
+ ElementStreamFormat<T>::Write(aStream, aElement);
+}
+template<class T>
+void ReadElement(std::istream &aStream, T &aElement)
+{
+ ElementStreamFormat<T>::Read(aStream, aElement);
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_RECORDINGTYPES_H_ */
diff --git a/gfx/2d/Rect.h b/gfx/2d/Rect.h
new file mode 100644
index 000000000..942cb200d
--- /dev/null
+++ b/gfx/2d/Rect.h
@@ -0,0 +1,334 @@
+/* -*- 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_RECT_H_
+#define MOZILLA_GFX_RECT_H_
+
+#include "BaseRect.h"
+#include "BaseMargin.h"
+#include "NumericTools.h"
+#include "Point.h"
+#include "Tools.h"
+#include "mozilla/Maybe.h"
+
+#include <cmath>
+
+namespace mozilla {
+
+template <typename> struct IsPixel;
+
+namespace gfx {
+
+template<class units, class F> struct RectTyped;
+
+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) {}
+
+ // XXX When all of the code is ported, the following functions to convert
+ // to and from unknown types should be removed.
+
+ static IntMarginTyped<units> FromUnknownMargin(const IntMarginTyped<UnknownUnits>& aMargin) {
+ return IntMarginTyped<units>(aMargin.top, aMargin.right,
+ aMargin.bottom, aMargin.left);
+ }
+
+ IntMarginTyped<UnknownUnits> ToUnknownMargin() const {
+ return IntMarginTyped<UnknownUnits>(this->top, this->right,
+ this->bottom, this->left);
+ }
+};
+typedef IntMarginTyped<UnknownUnits> IntMargin;
+
+template<class units, class F = Float>
+struct MarginTyped:
+ public BaseMargin<F, MarginTyped<units> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseMargin<F, MarginTyped<units, F> > Super;
+
+ MarginTyped() : Super() {}
+ MarginTyped(F aTop, F aRight, F aBottom, F aLeft) :
+ Super(aTop, aRight, aBottom, aLeft) {}
+ explicit MarginTyped(const IntMarginTyped<units>& aMargin) :
+ Super(F(aMargin.top), F(aMargin.right),
+ F(aMargin.bottom), F(aMargin.left)) {}
+};
+typedef MarginTyped<UnknownUnits> Margin;
+typedef MarginTyped<UnknownUnits, double> MarginDouble;
+
+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>, IntMarginTyped<units> >,
+ public units {
+ 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;
+ typedef IntRectTyped<units> Self;
+ typedef IntParam<int32_t> ToInt;
+
+ IntRectTyped() : Super() {}
+ IntRectTyped(const IntPointTyped<units>& aPos, const IntSizeTyped<units>& aSize) :
+ Super(aPos, aSize) {}
+
+ IntRectTyped(ToInt aX, ToInt aY, ToInt aWidth, ToInt aHeight) :
+ Super(aX.value, aY.value, aWidth.value, aHeight.value) {}
+
+ static IntRectTyped<units> RoundIn(float aX, float aY, float aW, float aH) {
+ return IntRectTyped<units>::RoundIn(RectTyped<units, float>(aX, aY, aW, aH));
+ }
+
+ static IntRectTyped<units> RoundOut(float aX, float aY, float aW, float aH) {
+ return IntRectTyped<units>::RoundOut(RectTyped<units, float>(aX, aY, aW, aH));
+ }
+
+ static IntRectTyped<units> Round(float aX, float aY, float aW, float aH) {
+ return IntRectTyped<units>::Round(RectTyped<units, float>(aX, aY, aW, aH));
+ }
+
+ static IntRectTyped<units> Truncate(float aX, float aY, float aW, float aH) {
+ return IntRectTyped<units>(IntPointTyped<units>::Truncate(aX, aY),
+ IntSizeTyped<units>::Truncate(aW, aH));
+ }
+
+ static IntRectTyped<units> RoundIn(const RectTyped<units, float>& aRect) {
+ auto tmp(aRect);
+ tmp.RoundIn();
+ return IntRectTyped(int32_t(tmp.x), int32_t(tmp.y),
+ int32_t(tmp.width), int32_t(tmp.height));
+ }
+
+ static IntRectTyped<units> RoundOut(const RectTyped<units, float>& aRect) {
+ auto tmp(aRect);
+ tmp.RoundOut();
+ return IntRectTyped(int32_t(tmp.x), int32_t(tmp.y),
+ int32_t(tmp.width), int32_t(tmp.height));
+ }
+
+ static IntRectTyped<units> Round(const RectTyped<units, float>& aRect) {
+ auto tmp(aRect);
+ tmp.Round();
+ return IntRectTyped(int32_t(tmp.x), int32_t(tmp.y),
+ int32_t(tmp.width), int32_t(tmp.height));
+ }
+
+ static IntRectTyped<units> Truncate(const RectTyped<units, float>& aRect) {
+ return IntRectTyped::Truncate(aRect.x, aRect.y, aRect.width, aRect.height);
+ }
+
+ // Rounding isn't meaningful on an integer rectangle.
+ void Round() {}
+ void RoundIn() {}
+ void RoundOut() {}
+
+ // XXX When all of the code is ported, the following functions to convert
+ // to and from unknown types should be removed.
+
+ static IntRectTyped<units> FromUnknownRect(const IntRectTyped<UnknownUnits>& rect) {
+ return IntRectTyped<units>(rect.x, rect.y, rect.width, rect.height);
+ }
+
+ 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();
+ }
+
+ // Same as Union(), but in the cases where aRect is non-empty, the union is
+ // done while guarding against overflow. If an overflow is detected, Nothing
+ // is returned.
+ MOZ_MUST_USE Maybe<Self> SafeUnion(const Self& aRect) const
+ {
+ if (this->IsEmpty()) {
+ return aRect.Overflows() ? Nothing() : Some(aRect);
+ } else if (aRect.IsEmpty()) {
+ return Some(*static_cast<const Self*>(this));
+ } else {
+ return this->SafeUnionEdges(aRect);
+ }
+ }
+
+ // Same as UnionEdges, but guards against overflow. If an overflow is detected,
+ // Nothing is returned.
+ MOZ_MUST_USE Maybe<Self> SafeUnionEdges(const Self& aRect) const
+ {
+ if (this->Overflows() || aRect.Overflows()) {
+ return Nothing();
+ }
+ // If neither |this| nor |aRect| overflow, then their XMost/YMost values
+ // should be safe to use.
+ CheckedInt<int32_t> newX = std::min(this->x, aRect.x);
+ CheckedInt<int32_t> newY = std::min(this->y, aRect.y);
+ CheckedInt<int32_t> newXMost = std::max(this->XMost(), aRect.XMost());
+ CheckedInt<int32_t> newYMost = std::max(this->YMost(), aRect.YMost());
+ CheckedInt<int32_t> newW = newXMost - newX;
+ CheckedInt<int32_t> newH = newYMost - newY;
+ if (!newW.isValid() || !newH.isValid()) {
+ return Nothing();
+ }
+ return Some(Self(newX.value(), newY.value(), newW.value(), newH.value()));
+ }
+
+ // This is here only to keep IPDL-generated code happy. DO NOT USE.
+ bool operator==(const IntRectTyped<units>& aRect) const
+ {
+ return IntRectTyped<units>::IsEqualEdges(aRect);
+ }
+
+ void InflateToMultiple(const IntSizeTyped<units>& aTileSize)
+ {
+ if (this->IsEmpty()) {
+ return;
+ }
+
+ int32_t yMost = this->YMost();
+ int32_t xMost = this->XMost();
+
+ this->x = mozilla::RoundDownToMultiple(this->x, aTileSize.width);
+ this->y = mozilla::RoundDownToMultiple(this->y, aTileSize.height);
+ xMost = mozilla::RoundUpToMultiple(xMost, aTileSize.width);
+ yMost = mozilla::RoundUpToMultiple(yMost, aTileSize.height);
+
+ this->width = xMost - this->x;
+ this->height = yMost - this->y;
+ }
+
+};
+typedef IntRectTyped<UnknownUnits> IntRect;
+
+template<class units, class F = Float>
+struct RectTyped :
+ public BaseRect<F, RectTyped<units, F>, PointTyped<units, F>, SizeTyped<units, F>, MarginTyped<units, F> >,
+ public units {
+ static_assert(IsPixel<units>::value,
+ "'units' must be a coordinate system tag");
+
+ typedef BaseRect<F, RectTyped<units, F>, PointTyped<units, F>, SizeTyped<units, F>, MarginTyped<units, F> > Super;
+
+ RectTyped() : Super() {}
+ RectTyped(const PointTyped<units, F>& aPos, const SizeTyped<units, F>& aSize) :
+ Super(aPos, aSize) {}
+ RectTyped(F _x, F _y, F _width, F _height) :
+ Super(_x, _y, _width, _height) {}
+ explicit RectTyped(const IntRectTyped<units>& rect) :
+ Super(F(rect.x), F(rect.y),
+ F(rect.width), F(rect.height)) {}
+
+ void NudgeToIntegers()
+ {
+ NudgeToInteger(&(this->x));
+ NudgeToInteger(&(this->y));
+ NudgeToInteger(&(this->width));
+ NudgeToInteger(&(this->height));
+ }
+
+ bool ToIntRect(IntRectTyped<units> *aOut) const
+ {
+ *aOut = IntRectTyped<units>(int32_t(this->X()), int32_t(this->Y()),
+ int32_t(this->Width()), int32_t(this->Height()));
+ return RectTyped<units, F>(F(aOut->x), F(aOut->y),
+ F(aOut->width), F(aOut->height))
+ .IsEqualEdges(*this);
+ }
+
+ // XXX When all of the code is ported, the following functions to convert to and from
+ // unknown types should be removed.
+
+ static RectTyped<units, F> FromUnknownRect(const RectTyped<UnknownUnits, F>& rect) {
+ return RectTyped<units, F>(rect.x, rect.y, rect.width, rect.height);
+ }
+
+ RectTyped<UnknownUnits, F> ToUnknownRect() const {
+ return RectTyped<UnknownUnits, F>(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, F>& aRect) const
+ {
+ return RectTyped<units, F>::IsEqualEdges(aRect);
+ }
+};
+typedef RectTyped<UnknownUnits> Rect;
+typedef RectTyped<UnknownUnits, double> RectDouble;
+
+template<class units>
+IntRectTyped<units> RoundedToInt(const RectTyped<units>& aRect)
+{
+ RectTyped<units> copy(aRect);
+ copy.Round();
+ return IntRectTyped<units>(int32_t(copy.x),
+ int32_t(copy.y),
+ int32_t(copy.width),
+ int32_t(copy.height));
+}
+
+template<class units>
+IntRectTyped<units> RoundedIn(const RectTyped<units>& aRect)
+{
+ return IntRectTyped<units>::RoundIn(aRect);
+}
+
+template<class units>
+IntRectTyped<units> RoundedOut(const RectTyped<units>& aRect)
+{
+ return IntRectTyped<units>::RoundOut(aRect);
+}
+
+template<class units>
+IntRectTyped<units> TruncatedToInt(const RectTyped<units>& aRect) {
+ return IntRectTyped<units>::Truncate(aRect);
+}
+
+template<class units>
+RectTyped<units> IntRectToRect(const IntRectTyped<units>& aRect)
+{
+ return RectTyped<units>(aRect.x, aRect.y, aRect.width, aRect.height);
+}
+
+// Convenience function for intersecting two rectangles wrapped in Maybes.
+template <typename T>
+Maybe<T>
+IntersectMaybeRects(const Maybe<T>& a, const Maybe<T>& b)
+{
+ if (!a) {
+ return b;
+ } else if (!b) {
+ return a;
+ } else {
+ return Some(a->Intersect(*b));
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_RECT_H_ */
diff --git a/gfx/2d/SFNTData.cpp b/gfx/2d/SFNTData.cpp
new file mode 100644
index 000000000..fd29f6694
--- /dev/null
+++ b/gfx/2d/SFNTData.cpp
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "SFNTData.h"
+
+#include <algorithm>
+
+#include "BigEndianInts.h"
+#include "Logging.h"
+#include "mozilla/HashFunctions.h"
+#include "SFNTNameTable.h"
+
+namespace mozilla {
+namespace gfx {
+
+#define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
+
+#pragma pack(push, 1)
+
+struct TTCHeader
+{
+ BigEndianUint32 ttcTag; // Always 'ttcf'
+ BigEndianUint32 version; // Fixed, 0x00010000
+ BigEndianUint32 numFonts;
+};
+
+struct OffsetTable
+{
+ BigEndianUint32 sfntVersion; // Fixed, 0x00010000 for version 1.0.
+ BigEndianUint16 numTables;
+ BigEndianUint16 searchRange; // (Maximum power of 2 <= numTables) x 16.
+ BigEndianUint16 entrySelector; // Log2(maximum power of 2 <= numTables).
+ BigEndianUint16 rangeShift; // NumTables x 16-searchRange.
+};
+
+struct TableDirEntry
+{
+ BigEndianUint32 tag; // 4 -byte identifier.
+ BigEndianUint32 checkSum; // CheckSum for this table.
+ BigEndianUint32 offset; // Offset from beginning of TrueType font file.
+ BigEndianUint32 length; // Length of this table.
+
+ friend bool operator<(const TableDirEntry& lhs, const uint32_t aTag)
+ {
+ return lhs.tag < aTag;
+ }
+};
+
+#pragma pack(pop)
+
+class SFNTData::Font
+{
+public:
+ Font(const OffsetTable *aOffsetTable, const uint8_t *aFontData,
+ uint32_t aDataLength)
+ : mFontData(aFontData)
+ , mFirstDirEntry(reinterpret_cast<const TableDirEntry*>(aOffsetTable + 1))
+ , mEndOfDirEntries(mFirstDirEntry + aOffsetTable->numTables)
+ , mDataLength(aDataLength)
+ {
+ }
+
+ bool GetU16FullName(mozilla::u16string& aU16FullName)
+ {
+ const TableDirEntry* dirEntry =
+ GetDirEntry(TRUETYPE_TAG('n', 'a', 'm', 'e'));
+ if (!dirEntry) {
+ gfxWarning() << "Name table entry not found.";
+ return false;
+ }
+
+ UniquePtr<SFNTNameTable> nameTable =
+ SFNTNameTable::Create((mFontData + dirEntry->offset), dirEntry->length);
+ if (!nameTable) {
+ return false;
+ }
+
+ return nameTable->GetU16FullName(aU16FullName);
+ }
+
+private:
+
+ const TableDirEntry*
+ GetDirEntry(const uint32_t aTag)
+ {
+ const TableDirEntry* foundDirEntry =
+ std::lower_bound(mFirstDirEntry, mEndOfDirEntries, aTag);
+
+ if (foundDirEntry == mEndOfDirEntries || foundDirEntry->tag != aTag) {
+ gfxWarning() << "Font data does not contain tag.";
+ return nullptr;
+ }
+
+ if (mDataLength < (foundDirEntry->offset + foundDirEntry->length)) {
+ gfxWarning() << "Font data too short to contain table.";
+ return nullptr;
+ }
+
+ return foundDirEntry;
+ }
+
+ const uint8_t *mFontData;
+ const TableDirEntry *mFirstDirEntry;
+ const TableDirEntry *mEndOfDirEntries;
+ uint32_t mDataLength;
+};
+
+/* static */
+UniquePtr<SFNTData>
+SFNTData::Create(const uint8_t *aFontData, uint32_t aDataLength)
+{
+ MOZ_ASSERT(aFontData);
+
+ // Check to see if this is a font collection.
+ if (aDataLength < sizeof(TTCHeader)) {
+ gfxWarning() << "Font data too short.";
+ return nullptr;
+ }
+
+ const TTCHeader *ttcHeader = reinterpret_cast<const TTCHeader*>(aFontData);
+ if (ttcHeader->ttcTag == TRUETYPE_TAG('t', 't', 'c', 'f')) {
+ uint32_t numFonts = ttcHeader->numFonts;
+ if (aDataLength < sizeof(TTCHeader) + (numFonts * sizeof(BigEndianUint32))) {
+ gfxWarning() << "Font data too short to contain full TTC Header.";
+ return nullptr;
+ }
+
+ UniquePtr<SFNTData> sfntData(new SFNTData);
+ const BigEndianUint32* offset =
+ reinterpret_cast<const BigEndianUint32*>(aFontData + sizeof(TTCHeader));
+ const BigEndianUint32* endOfOffsets = offset + numFonts;
+ while (offset != endOfOffsets) {
+ if (!sfntData->AddFont(aFontData, aDataLength, *offset)) {
+ return nullptr;
+ }
+ ++offset;
+ }
+
+ return Move(sfntData);
+ }
+
+ UniquePtr<SFNTData> sfntData(new SFNTData);
+ if (!sfntData->AddFont(aFontData, aDataLength, 0)) {
+ return nullptr;
+ }
+
+ return Move(sfntData);
+}
+
+/* static */
+uint64_t
+SFNTData::GetUniqueKey(const uint8_t *aFontData, uint32_t aDataLength)
+{
+ uint64_t hash;
+ UniquePtr<SFNTData> sfntData = SFNTData::Create(aFontData, aDataLength);
+ mozilla::u16string firstName;
+ if (sfntData && sfntData->GetU16FullName(0, firstName)) {
+ hash = HashString(firstName.c_str(), firstName.length());
+ } else {
+ gfxWarning() << "Failed to get name from font data hashing whole font.";
+ hash = HashString(aFontData, aDataLength);
+ }
+
+ return hash << 32 | aDataLength;;
+}
+
+SFNTData::~SFNTData()
+{
+ for (size_t i = 0; i < mFonts.length(); ++i) {
+ delete mFonts[i];
+ }
+}
+
+bool
+SFNTData::GetU16FullName(uint32_t aIndex, mozilla::u16string& aU16FullName)
+{
+ if (aIndex >= mFonts.length()) {
+ gfxWarning() << "aIndex to font data too high.";
+ return false;
+ }
+
+ return mFonts[aIndex]->GetU16FullName(aU16FullName);
+}
+
+bool
+SFNTData::GetU16FullNames(Vector<mozilla::u16string>& aU16FullNames)
+{
+ bool fontFound = false;
+ for (size_t i = 0; i < mFonts.length(); ++i) {
+ mozilla::u16string name;
+ if (mFonts[i]->GetU16FullName(name)) {
+ fontFound = true;
+ }
+ if (!aU16FullNames.append(Move(name))) {
+ return false;
+ }
+ }
+
+ return fontFound;
+}
+
+bool
+SFNTData::GetIndexForU16Name(const mozilla::u16string& aU16FullName,
+ uint32_t* aIndex, size_t aTruncatedLen)
+{
+ for (size_t i = 0; i < mFonts.length(); ++i) {
+ mozilla::u16string name;
+ if (!mFonts[i]->GetU16FullName(name)) {
+ continue;
+ }
+
+ if (aTruncatedLen) {
+ MOZ_ASSERT(aU16FullName.length() <= aTruncatedLen);
+ name = name.substr(0, aTruncatedLen);
+ }
+
+ if (name == aU16FullName) {
+ *aIndex = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+SFNTData::AddFont(const uint8_t *aFontData, uint32_t aDataLength,
+ uint32_t aOffset)
+{
+ uint32_t remainingLength = aDataLength - aOffset;
+ if (remainingLength < sizeof(OffsetTable)) {
+ gfxWarning() << "Font data too short to contain OffsetTable " << aOffset;
+ return false;
+ }
+
+ const OffsetTable *offsetTable =
+ reinterpret_cast<const OffsetTable*>(aFontData + aOffset);
+ if (remainingLength <
+ sizeof(OffsetTable) + (offsetTable->numTables * sizeof(TableDirEntry))) {
+ gfxWarning() << "Font data too short to contain tables.";
+ return false;
+ }
+
+ return mFonts.append(new Font(offsetTable, aFontData, aDataLength));
+}
+
+} // gfx
+} // mozilla
diff --git a/gfx/2d/SFNTData.h b/gfx/2d/SFNTData.h
new file mode 100644
index 000000000..e10d63cae
--- /dev/null
+++ b/gfx/2d/SFNTData.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_SFNTData_h
+#define mozilla_gfx_SFNTData_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+#include "u16string.h"
+
+namespace mozilla {
+namespace gfx {
+
+class SFNTData final
+{
+public:
+
+ /**
+ * Creates an SFNTData if the header is a format that we understand and
+ * aDataLength is sufficient for the length information in the header data.
+ * Note that the data is NOT copied, so must exist the SFNTData's lifetime.
+ *
+ * @param aFontData the SFNT data.
+ * @param aDataLength length
+ * @return UniquePtr to a SFNTData or nullptr if the header is invalid.
+ */
+ static UniquePtr<SFNTData> Create(const uint8_t *aFontData,
+ uint32_t aDataLength);
+
+ /**
+ * Creates a unique key for the given font data.
+ *
+ * @param aFontData the SFNT data
+ * @param aDataLength length
+ * @return unique key to be used for caching
+ */
+ static uint64_t GetUniqueKey(const uint8_t *aFontData, uint32_t aDataLength);
+
+ ~SFNTData();
+
+ /**
+ * Gets the full name from the name table of the font corresponding to the
+ * index. If the full name string is not present it will use the family space
+ * concatenated with the style.
+ * This will only read names that are already UTF16.
+ *
+ * @param aFontData SFNT data.
+ * @param aDataLength length of aFontData.
+ * @param aU16FullName string to be populated with the full name.
+ * @return true if the full name is successfully read.
+ */
+ bool GetU16FullName(uint32_t aIndex, mozilla::u16string& aU16FullName);
+
+ /**
+ * Populate a Vector with the first UTF16 full name from each name table of
+ * the fonts. If the full name string is not present it will use the family
+ * space concatenated with the style.
+ * This will only read names that are already UTF16.
+ *
+ * @param aU16FullNames the Vector to be populated.
+ * @return true if at least one name found otherwise false.
+ */
+ bool GetU16FullNames(Vector<mozilla::u16string>& aU16FullNames);
+
+ /**
+ * Returns the index for the first UTF16 name matching aU16FullName.
+ *
+ * @param aU16FullName full name to find.
+ * @param aIndex out param for the index if found.
+ * @param aTruncatedLen length to truncate the compared font name to.
+ * @return true if the full name is successfully read.
+ */
+ bool GetIndexForU16Name(const mozilla::u16string& aU16FullName, uint32_t* aIndex,
+ size_t aTruncatedLen = 0);
+
+private:
+
+ SFNTData() {}
+
+ bool AddFont(const uint8_t *aFontData, uint32_t aDataLength,
+ uint32_t aOffset);
+
+ // Internal representation of single font in font file.
+ class Font;
+
+ Vector<Font*> mFonts;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_SFNTData_h
diff --git a/gfx/2d/SFNTNameTable.cpp b/gfx/2d/SFNTNameTable.cpp
new file mode 100644
index 000000000..a30c6bd97
--- /dev/null
+++ b/gfx/2d/SFNTNameTable.cpp
@@ -0,0 +1,357 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "SFNTNameTable.h"
+
+#include "BigEndianInts.h"
+#include "Logging.h"
+#include "mozilla/Move.h"
+
+#if defined(XP_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+static const BigEndianUint16 FORMAT_0 = 0;
+
+static const BigEndianUint16 NAME_ID_FAMILY = 1;
+static const BigEndianUint16 NAME_ID_STYLE = 2;
+static const BigEndianUint16 NAME_ID_FULL = 4;
+
+static const BigEndianUint16 PLATFORM_ID_UNICODE = 0;
+static const BigEndianUint16 PLATFORM_ID_MAC = 1;
+static const BigEndianUint16 PLATFORM_ID_MICROSOFT = 3;
+
+static const BigEndianUint16 ENCODING_ID_MICROSOFT_SYMBOL = 0;
+static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEBMP = 1;
+static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEFULL = 10;
+
+static const BigEndianUint16 ENCODING_ID_MAC_ROMAN = 0;
+
+static const BigEndianUint16 LANG_ID_MAC_ENGLISH = 0;
+
+static const BigEndianUint16 LANG_ID_MICROSOFT_EN_US = 0x0409;
+
+#pragma pack(push, 1)
+
+// Name table has a header, followed by name records, followed by string data.
+struct NameHeader
+{
+ BigEndianUint16 format; // Format selector (=0).
+ BigEndianUint16 count; // Number of name records.
+ BigEndianUint16 stringOffset; // Offset to string storage from start of table.
+};
+
+struct NameRecord
+{
+ BigEndianUint16 platformID;
+ BigEndianUint16 encodingID; // Platform-specific encoding ID
+ BigEndianUint16 languageID;
+ BigEndianUint16 nameID;
+ BigEndianUint16 length; // String length in bytes.
+ BigEndianUint16 offset; // String offset from start of storage in bytes.
+};
+
+#pragma pack(pop)
+
+enum ENameDecoder : int
+{
+ eNameDecoderUTF16,
+#if defined(XP_MACOSX)
+ eNameDecoderMacRoman,
+#endif
+ eNameDecoderNone
+};
+
+/* static */
+UniquePtr<SFNTNameTable>
+SFNTNameTable::Create(const uint8_t *aNameData, uint32_t aDataLength)
+{
+ MOZ_ASSERT(aNameData);
+
+ if (aDataLength < sizeof(NameHeader)) {
+ gfxWarning() << "Name data too short to contain NameHeader.";
+ return nullptr;
+ }
+
+ const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(aNameData);
+ if (nameHeader->format != FORMAT_0) {
+ gfxWarning() << "Only Name Table Format 0 is supported.";
+ return nullptr;
+ }
+
+ uint16_t stringOffset = nameHeader->stringOffset;
+
+ if (stringOffset !=
+ sizeof(NameHeader) + (nameHeader->count * sizeof(NameRecord))) {
+ gfxWarning() << "Name table string offset is incorrect.";
+ return nullptr;
+ }
+
+ if (aDataLength < stringOffset) {
+ gfxWarning() << "Name data too short to contain name records.";
+ return nullptr;
+ }
+
+ return UniquePtr<SFNTNameTable>(
+ new SFNTNameTable(nameHeader, aNameData, aDataLength));
+}
+
+SFNTNameTable::SFNTNameTable(const NameHeader *aNameHeader,
+ const uint8_t *aNameData, uint32_t aDataLength)
+ : mFirstRecord(reinterpret_cast<const NameRecord*>(aNameData
+ + sizeof(NameHeader)))
+ , mEndOfRecords(mFirstRecord + aNameHeader->count)
+ , mStringData(aNameData + aNameHeader->stringOffset)
+ , mStringDataLength(aDataLength - aNameHeader->stringOffset)
+{
+ MOZ_ASSERT(reinterpret_cast<const uint8_t*>(aNameHeader) == aNameData);
+}
+
+static bool
+IsUTF16Encoding(const NameRecord *aNameRecord)
+{
+ if (aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
+ (aNameRecord->encodingID == ENCODING_ID_MICROSOFT_UNICODEBMP ||
+ aNameRecord->encodingID == ENCODING_ID_MICROSOFT_SYMBOL)) {
+ return true;
+ }
+
+ if (aNameRecord->platformID == PLATFORM_ID_UNICODE) {
+ return true;
+ }
+
+ return false;
+}
+
+#if defined(XP_MACOSX)
+static bool
+IsMacRomanEncoding(const NameRecord *aNameRecord)
+{
+ if (aNameRecord->platformID == PLATFORM_ID_MAC &&
+ aNameRecord->encodingID == ENCODING_ID_MAC_ROMAN) {
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+static NameRecordMatchers*
+CreateCanonicalMatchers(const BigEndianUint16& aNameID)
+{
+ // For Windows, we return only Microsoft platform name record
+ // matchers. On Mac, we return matchers for both Microsoft platform
+ // records and Mac platform records.
+ NameRecordMatchers *matchers = new NameRecordMatchers();
+
+#if defined(XP_MACOSX)
+ // First, look for the English name.
+ if (!matchers->append(
+ [=](const NameRecord *aNameRecord) {
+ if (aNameRecord->nameID == aNameID &&
+ aNameRecord->languageID == LANG_ID_MAC_ENGLISH &&
+ aNameRecord->platformID == PLATFORM_ID_MAC &&
+ IsMacRomanEncoding(aNameRecord)) {
+ return eNameDecoderMacRoman;
+ } else {
+ return eNameDecoderNone;
+ }
+ })) {
+ MOZ_CRASH();
+ }
+
+ // Second, look for all languages.
+ if (!matchers->append(
+ [=](const NameRecord *aNameRecord) {
+ if (aNameRecord->nameID == aNameID &&
+ aNameRecord->platformID == PLATFORM_ID_MAC &&
+ IsMacRomanEncoding(aNameRecord)) {
+ return eNameDecoderMacRoman;
+ } else {
+ return eNameDecoderNone;
+ }
+ })) {
+ MOZ_CRASH();
+ }
+#endif /* defined(XP_MACOSX) */
+
+ // First, look for the English name (this will normally succeed).
+ if (!matchers->append(
+ [=](const NameRecord *aNameRecord) {
+ if (aNameRecord->nameID == aNameID &&
+ aNameRecord->languageID == LANG_ID_MICROSOFT_EN_US &&
+ aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
+ IsUTF16Encoding(aNameRecord)) {
+ return eNameDecoderUTF16;
+ } else {
+ return eNameDecoderNone;
+ }
+ })) {
+ MOZ_CRASH();
+ }
+
+ // Second, look for all languages.
+ if (!matchers->append(
+ [=](const NameRecord *aNameRecord) {
+ if (aNameRecord->nameID == aNameID &&
+ aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
+ IsUTF16Encoding(aNameRecord)) {
+ return eNameDecoderUTF16;
+ } else {
+ return eNameDecoderNone;
+ }
+ })) {
+ MOZ_CRASH();
+ }
+
+ return matchers;
+}
+
+static const NameRecordMatchers&
+FullNameMatchers()
+{
+ static const NameRecordMatchers *sFullNameMatchers =
+ CreateCanonicalMatchers(NAME_ID_FULL);
+ return *sFullNameMatchers;
+}
+
+static const NameRecordMatchers&
+FamilyMatchers()
+{
+ static const NameRecordMatchers *sFamilyMatchers =
+ CreateCanonicalMatchers(NAME_ID_FAMILY);
+ return *sFamilyMatchers;
+}
+
+static const NameRecordMatchers&
+StyleMatchers()
+{
+ static const NameRecordMatchers *sStyleMatchers =
+ CreateCanonicalMatchers(NAME_ID_STYLE);
+ return *sStyleMatchers;
+}
+
+bool
+SFNTNameTable::GetU16FullName(mozilla::u16string& aU16FullName)
+{
+ if (ReadU16Name(FullNameMatchers(), aU16FullName)) {
+ return true;
+ }
+
+ // If the full name record doesn't exist create the name from the family space
+ // concatenated with the style.
+ mozilla::u16string familyName;
+ if (!ReadU16Name(FamilyMatchers(), familyName)) {
+ return false;
+ }
+
+ mozilla::u16string styleName;
+ if (!ReadU16Name(StyleMatchers(), styleName)) {
+ return false;
+ }
+
+ aU16FullName.assign(Move(familyName));
+ aU16FullName.append(u" ");
+ aU16FullName.append(styleName);
+ return true;
+}
+
+bool
+SFNTNameTable::ReadU16Name(const NameRecordMatchers& aMatchers,
+ mozilla::u16string& aU16Name)
+{
+ MOZ_ASSERT(!aMatchers.empty());
+
+ for (size_t i = 0; i < aMatchers.length(); ++i) {
+ const NameRecord* record = mFirstRecord;
+ while (record != mEndOfRecords) {
+ switch (aMatchers[i](record)) {
+ case eNameDecoderUTF16:
+ return ReadU16NameFromU16Record(record, aU16Name);
+#if defined(XP_MACOSX)
+ case eNameDecoderMacRoman:
+ return ReadU16NameFromMacRomanRecord(record, aU16Name);
+#endif
+ case eNameDecoderNone:
+ break;
+ default:
+ MOZ_CRASH("Invalid matcher encoding type");
+ break;
+ }
+ ++record;
+ }
+ }
+
+ return false;
+}
+
+bool
+SFNTNameTable::ReadU16NameFromU16Record(const NameRecord *aNameRecord,
+ mozilla::u16string& aU16Name)
+{
+ uint32_t offset = aNameRecord->offset;
+ uint32_t length = aNameRecord->length;
+ if (mStringDataLength < offset + length) {
+ gfxWarning() << "Name data too short to contain name string.";
+ return false;
+ }
+
+ const uint8_t *startOfName = mStringData + offset;
+ size_t actualLength = length / sizeof(char16_t);
+ UniquePtr<char16_t[]> nameData(new char16_t[actualLength]);
+ NativeEndian::copyAndSwapFromBigEndian(nameData.get(), startOfName,
+ actualLength);
+
+ aU16Name.assign(nameData.get(), actualLength);
+ return true;
+}
+
+#if defined(XP_MACOSX)
+bool
+SFNTNameTable::ReadU16NameFromMacRomanRecord(const NameRecord *aNameRecord,
+ mozilla::u16string& aU16Name)
+{
+ uint32_t offset = aNameRecord->offset;
+ uint32_t length = aNameRecord->length;
+ if (mStringDataLength < offset + length) {
+ gfxWarning() << "Name data too short to contain name string.";
+ return false;
+ }
+ if (length > INT_MAX) {
+ gfxWarning() << "Name record too long to decode.";
+ return false;
+ }
+
+ // pointer to the Mac Roman encoded string in the name record
+ const uint8_t *encodedStr = mStringData + offset;
+
+ CFStringRef cfString;
+ cfString = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, encodedStr,
+ length, kCFStringEncodingMacRoman,
+ false, kCFAllocatorNull);
+
+ // length (in UTF-16 code pairs) of the decoded string
+ CFIndex decodedLength = CFStringGetLength(cfString);
+
+ // temporary buffer
+ UniquePtr<UniChar[]> u16Buffer = MakeUnique<UniChar[]>(decodedLength);
+
+ CFStringGetCharacters(cfString, CFRangeMake(0, decodedLength),
+ u16Buffer.get());
+
+ CFRelease(cfString);
+
+ aU16Name.assign(reinterpret_cast<char16_t*>(u16Buffer.get()), decodedLength);
+
+ return true;
+}
+#endif
+
+} // gfx
+} // mozilla
diff --git a/gfx/2d/SFNTNameTable.h b/gfx/2d/SFNTNameTable.h
new file mode 100644
index 000000000..3735cca96
--- /dev/null
+++ b/gfx/2d/SFNTNameTable.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_SFNTNameTable_h
+#define mozilla_gfx_SFNTNameTable_h
+
+#include "mozilla/Function.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+#include "u16string.h"
+
+namespace mozilla {
+namespace gfx {
+
+struct NameHeader;
+struct NameRecord;
+enum ENameDecoder : int;
+
+typedef Vector<function<ENameDecoder(const NameRecord*)>> NameRecordMatchers;
+
+class SFNTNameTable final
+{
+public:
+
+ /**
+ * Creates a SFNTNameTable if the header data is valid. Note that the data is
+ * NOT copied, so must exist for the lifetime of the table.
+ *
+ * @param aNameData the Name Table data.
+ * @param aDataLength length
+ * @return UniquePtr to a SFNTNameTable or nullptr if the header is invalid.
+ */
+ static UniquePtr<SFNTNameTable> Create(const uint8_t *aNameData,
+ uint32_t aDataLength);
+
+ /**
+ * Gets the full name from the name table. If the full name string is not
+ * present it will use the family space concatenated with the style.
+ * This will only read names that are already UTF16 or Mac OS Roman.
+ *
+ * @param aU16FullName string to be populated with the full name.
+ * @return true if the full name is successfully read.
+ */
+ bool GetU16FullName(mozilla::u16string& aU16FullName);
+
+private:
+
+ SFNTNameTable(const NameHeader *aNameHeader, const uint8_t *aNameData,
+ uint32_t aDataLength);
+
+ bool ReadU16Name(const NameRecordMatchers& aMatchers, mozilla::u16string& aU16Name);
+
+ bool ReadU16NameFromU16Record(const NameRecord *aNameRecord,
+ mozilla::u16string& aU16Name);
+
+#if defined(XP_MACOSX)
+ bool ReadU16NameFromMacRomanRecord(const NameRecord *aNameRecord,
+ mozilla::u16string& aU16Name);
+#endif
+
+ const NameRecord *mFirstRecord;
+ const NameRecord *mEndOfRecords;
+ const uint8_t *mStringData;
+ const uint32_t mStringDataLength;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_SFNTNameTable_h
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/SSEHelpers.h b/gfx/2d/SSEHelpers.h
new file mode 100644
index 000000000..61b53d86e
--- /dev/null
+++ b/gfx/2d/SSEHelpers.h
@@ -0,0 +1,17 @@
+/* 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 <xmmintrin.h>
+#include <emmintrin.h>
+
+/* Before Nehalem _mm_loadu_si128 could be very slow, this trick is a little
+ * faster. Once enough people are on architectures where _mm_loadu_si128 is
+ * fast we can migrate to it.
+ */
+MOZ_ALWAYS_INLINE __m128i loadUnaligned128(const __m128i *aSource)
+{
+ // Yes! We use uninitialized memory here, we'll overwrite it though!
+ __m128 res = _mm_loadl_pi(_mm_set1_ps(0), (const __m64*)aSource);
+ return _mm_castps_si128(_mm_loadh_pi(res, ((const __m64*)(aSource)) + 1));
+}
diff --git a/gfx/2d/SVGTurbulenceRenderer-inl.h b/gfx/2d/SVGTurbulenceRenderer-inl.h
new file mode 100644
index 000000000..7b18903e8
--- /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);
+
+ already_AddRefed<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>
+already_AddRefed<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.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/Scale.cpp b/gfx/2d/Scale.cpp
new file mode 100644
index 000000000..f1fec03d0
--- /dev/null
+++ b/gfx/2d/Scale.cpp
@@ -0,0 +1,44 @@
+/* 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 "Scale.h"
+
+#ifdef USE_SKIA
+#include "HelpersSkia.h"
+#include "skia/include/core/SkBitmap.h"
+#include "image_operations.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+bool Scale(uint8_t* srcData, int32_t srcWidth, int32_t srcHeight, int32_t srcStride,
+ uint8_t* dstData, int32_t dstWidth, int32_t dstHeight, int32_t dstStride,
+ SurfaceFormat format)
+{
+#ifdef USE_SKIA
+ SkBitmap imgSrc;
+ imgSrc.installPixels(MakeSkiaImageInfo(IntSize(srcWidth, srcHeight), format),
+ srcData, srcStride);
+
+ // Rescaler is compatible with 32 bpp only. Convert to RGB32 if needed.
+ if (imgSrc.colorType() != kBGRA_8888_SkColorType) {
+ imgSrc.copyTo(&imgSrc, kBGRA_8888_SkColorType);
+ }
+
+ // This returns an SkBitmap backed by dstData; since it also wrote to dstData,
+ // we don't need to look at that SkBitmap.
+ SkBitmap result = skia::ImageOperations::Resize(imgSrc,
+ skia::ImageOperations::RESIZE_BEST,
+ dstWidth, dstHeight,
+ dstData);
+
+ return !result.isNull();
+#else
+ return false;
+#endif
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/Scale.h b/gfx/2d/Scale.h
new file mode 100644
index 000000000..75465da38
--- /dev/null
+++ b/gfx/2d/Scale.h
@@ -0,0 +1,36 @@
+/* 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_SCALE_H_
+#define MOZILLA_GFX_SCALE_H_
+
+#include "Types.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Scale an image using a high-quality filter.
+ *
+ * Synchronously scales an image and writes the output to the destination in
+ * 32-bit format. The destination must be pre-allocated by the caller.
+ *
+ * Returns true if scaling was successful, and false otherwise. Currently, this
+ * function is implemented using Skia. If Skia is not enabled when building,
+ * calling this function will always return false.
+ *
+ * IMPLEMTATION NOTES:
+ * This API is not currently easily hardware acceleratable. A better API might
+ * take a SourceSurface and return a SourceSurface; the Direct2D backend, for
+ * example, could simply set a status bit on a copy of the image, and use
+ * Direct2D's high-quality scaler at draw time.
+ */
+GFX2D_API bool Scale(uint8_t* srcData, int32_t srcWidth, int32_t srcHeight, int32_t srcStride,
+ uint8_t* dstData, int32_t dstWidth, int32_t dstHeight, int32_t dstStride,
+ SurfaceFormat format);
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BLUR_H_ */
diff --git a/gfx/2d/ScaleFactor.h b/gfx/2d/ScaleFactor.h
new file mode 100644
index 000000000..acf5bb739
--- /dev/null
+++ b/gfx/2d/ScaleFactor.h
@@ -0,0 +1,85 @@
+/* -*- 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_SCALEFACTOR_H_
+#define MOZILLA_GFX_SCALEFACTOR_H_
+
+#include "mozilla/Attributes.h"
+
+#include "gfxPoint.h"
+
+namespace mozilla {
+namespace gfx {
+
+/*
+ * This class represents a scaling factor between two different pixel unit
+ * systems. This is effectively a type-safe float, intended to be used in
+ * combination with the known-type instances of gfx::Point, gfx::Rect, etc.
+ *
+ * This class is meant to be used in cases where a single scale applies to
+ * both the x and y axes. For cases where two diferent scales apply, use
+ * ScaleFactors2D.
+ */
+template<class src, class dst>
+struct ScaleFactor {
+ float scale;
+
+ constexpr ScaleFactor() : scale(1.0) {}
+ constexpr ScaleFactor(const ScaleFactor<src, dst>& aCopy) : scale(aCopy.scale) {}
+ explicit constexpr ScaleFactor(float aScale) : scale(aScale) {}
+
+ ScaleFactor<dst, src> Inverse() {
+ return ScaleFactor<dst, src>(1 / scale);
+ }
+
+ bool operator==(const ScaleFactor<src, dst>& aOther) const {
+ return scale == aOther.scale;
+ }
+
+ bool operator!=(const ScaleFactor<src, dst>& aOther) const {
+ 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);
+ }
+
+ template<class other>
+ ScaleFactor<src, other> operator/(const ScaleFactor<other, dst>& aOther) const {
+ return ScaleFactor<src, other>(scale / aOther.scale);
+ }
+
+ template<class other>
+ ScaleFactor<src, other> operator*(const ScaleFactor<dst, other>& aOther) const {
+ return ScaleFactor<src, other>(scale * aOther.scale);
+ }
+
+ template<class other>
+ ScaleFactor<other, dst> operator*(const ScaleFactor<other, src>& aOther) const {
+ return ScaleFactor<other, dst>(scale * aOther.scale);
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SCALEFACTOR_H_ */
diff --git a/gfx/2d/ScaleFactors2D.h b/gfx/2d/ScaleFactors2D.h
new file mode 100644
index 000000000..6de32d7f2
--- /dev/null
+++ b/gfx/2d/ScaleFactors2D.h
@@ -0,0 +1,135 @@
+/* -*- 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_SCALEFACTORS2D_H_
+#define MOZILLA_GFX_SCALEFACTORS2D_H_
+
+#include <ostream>
+
+#include "mozilla/Attributes.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/gfx/ScaleFactor.h"
+
+#include "gfxPoint.h"
+
+namespace mozilla {
+namespace gfx {
+
+/*
+ * This class is like ScaleFactor, but allows different scales on the x and
+ * y axes.
+ */
+template<class src, class dst>
+struct ScaleFactors2D {
+ float xScale;
+ float yScale;
+
+ constexpr ScaleFactors2D() : xScale(1.0), yScale(1.0) {}
+ constexpr ScaleFactors2D(const ScaleFactors2D<src, dst>& aCopy)
+ : xScale(aCopy.xScale), yScale(aCopy.yScale) {}
+ constexpr ScaleFactors2D(float aXScale, float aYScale)
+ : xScale(aXScale), yScale(aYScale) {}
+ // Layout code often uses gfxSize to represent a pair of x/y scales.
+ explicit constexpr ScaleFactors2D(const gfxSize& aSize)
+ : xScale(aSize.width), yScale(aSize.height) {}
+
+ // "Upgrade" from a ScaleFactor.
+ // This is deliberately 'explicit' so that the treatment of a single scale
+ // number as both the x- and y-scale in a context where they are allowed to
+ // be different, is more visible.
+ explicit constexpr ScaleFactors2D(const ScaleFactor<src, dst>& aScale)
+ : xScale(aScale.scale), yScale(aScale.scale) {}
+
+ bool AreScalesSame() const {
+ return FuzzyEqualsMultiplicative(xScale, yScale);
+ }
+
+ // Convert to a ScaleFactor. Asserts that the scales are, in fact, equal.
+ ScaleFactor<src, dst> ToScaleFactor() const {
+ MOZ_ASSERT(AreScalesSame());
+ return ScaleFactor<src, dst>(xScale);
+ }
+
+ bool operator==(const ScaleFactors2D<src, dst>& aOther) const {
+ return xScale == aOther.xScale && yScale == aOther.yScale;
+ }
+
+ bool operator!=(const ScaleFactors2D<src, dst>& aOther) const {
+ return !(*this == aOther);
+ }
+
+ friend std::ostream& operator<<(std::ostream& aStream,
+ const ScaleFactors2D<src, dst>& aScale) {
+ if (aScale.AreScalesSame()) {
+ return aStream << aScale.xScale;
+ } else {
+ return aStream << '(' << aScale.xScale << ',' << aScale.yScale << ')';
+ }
+ }
+
+ template<class other>
+ ScaleFactors2D<other, dst> operator/(const ScaleFactors2D<src, other>& aOther) const {
+ return ScaleFactors2D<other, dst>(xScale / aOther.xScale, yScale / aOther.yScale);
+ }
+
+ template<class other>
+ ScaleFactors2D<src, other> operator/(const ScaleFactors2D<other, dst>& aOther) const {
+ return ScaleFactors2D<src, other>(xScale / aOther.xScale, yScale / aOther.yScale);
+ }
+
+ template<class other>
+ ScaleFactors2D<src, other> operator*(const ScaleFactors2D<dst, other>& aOther) const {
+ return ScaleFactors2D<src, other>(xScale * aOther.xScale, yScale * aOther.yScale);
+ }
+
+ template<class other>
+ ScaleFactors2D<other, dst> operator*(const ScaleFactors2D<other, src>& aOther) const {
+ return ScaleFactors2D<other, dst>(xScale * aOther.xScale, yScale * aOther.yScale);
+ }
+
+ template<class other>
+ ScaleFactors2D<src, other> operator*(const ScaleFactor<dst, other>& aOther) const {
+ return *this * ScaleFactors2D<dst, other>(aOther);
+ }
+
+ template<class other>
+ ScaleFactors2D<other, dst> operator*(const ScaleFactor<other, src>& aOther) const {
+ return *this * ScaleFactors2D<other, src>(aOther);
+ }
+
+ template<class other>
+ ScaleFactors2D<src, other> operator/(const ScaleFactor<other, dst>& aOther) const {
+ return *this / ScaleFactors2D<other, dst>(aOther);
+ }
+
+ template<class other>
+ ScaleFactors2D<other, dst> operator/(const ScaleFactor<src, other>& aOther) const {
+ return *this / ScaleFactors2D<src, other>(aOther);
+ }
+
+ template<class other>
+ friend ScaleFactors2D<other, dst> operator*(const ScaleFactor<other, src>& aA,
+ const ScaleFactors2D<src, dst>& aB) {
+ return ScaleFactors2D<other, src>(aA) * aB;
+ }
+
+ template<class other>
+ friend ScaleFactors2D<other, src> operator/(const ScaleFactor<other, dst>& aA,
+ const ScaleFactors2D<src, dst>& aB) {
+ return ScaleFactors2D<other, src>(aA) / aB;
+ }
+
+ // Divide two scales of the same units, yielding a scale with no units,
+ // represented as a gfxSize. This can mean e.g. the cahnge in a particular
+ // scale from one frame to the next.
+ gfxSize operator/(const ScaleFactors2D& aOther) const {
+ return gfxSize(xScale / aOther.xScale, yScale / aOther.yScale);
+ }
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SCALEFACTORS2D_H_ */
diff --git a/gfx/2d/ScaledFontBase.cpp b/gfx/2d/ScaledFontBase.cpp
new file mode 100644
index 000000000..ca9b2a188
--- /dev/null
+++ b/gfx/2d/ScaledFontBase.cpp
@@ -0,0 +1,266 @@
+/* -*- 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 "ScaledFontBase.h"
+
+#ifdef USE_SKIA
+#include "PathSkia.h"
+#include "skia/include/core/SkPaint.h"
+#endif
+
+#ifdef USE_CAIRO
+#include "PathCairo.h"
+#include "DrawTargetCairo.h"
+#include "HelpersCairo.h"
+#endif
+
+#include <vector>
+#include <cmath>
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+ScaledFontBase::~ScaledFontBase()
+{
+#ifdef USE_SKIA
+ SkSafeUnref(mTypeface);
+#endif
+#ifdef USE_CAIRO_SCALED_FONT
+ cairo_scaled_font_destroy(mScaledFont);
+#endif
+}
+
+ScaledFontBase::ScaledFontBase(Float aSize)
+ : mSize(aSize)
+{
+#ifdef USE_SKIA
+ mTypeface = nullptr;
+#endif
+#ifdef USE_CAIRO_SCALED_FONT
+ mScaledFont = nullptr;
+#endif
+}
+
+#ifdef USE_CAIRO_SCALED_FONT
+bool
+ScaledFontBase::PopulateCairoScaledFont()
+{
+ cairo_font_face_t* cairoFontFace = GetCairoFontFace();
+ if (!cairoFontFace) {
+ return false;
+ }
+
+ cairo_matrix_t sizeMatrix;
+ cairo_matrix_t identityMatrix;
+
+ cairo_matrix_init_scale(&sizeMatrix, mSize, mSize);
+ cairo_matrix_init_identity(&identityMatrix);
+
+ cairo_font_options_t *fontOptions = cairo_font_options_create();
+
+ mScaledFont = cairo_scaled_font_create(cairoFontFace, &sizeMatrix,
+ &identityMatrix, fontOptions);
+
+ cairo_font_options_destroy(fontOptions);
+ cairo_font_face_destroy(cairoFontFace);
+
+ return (cairo_scaled_font_status(mScaledFont) == CAIRO_STATUS_SUCCESS);
+}
+#endif
+
+#ifdef USE_SKIA
+SkPath
+ScaledFontBase::GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer)
+{
+ SkTypeface *typeFace = GetSkTypeface();
+ MOZ_ASSERT(typeFace);
+
+ SkPaint paint;
+ paint.setTypeface(sk_ref_sp(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
+
+already_AddRefed<Path>
+ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
+{
+#ifdef USE_SKIA
+ if (aTarget->GetBackendType() == BackendType::SKIA) {
+ SkPath path = GetSkiaPathForGlyphs(aBuffer);
+ return MakeAndAddRef<PathSkia>(path, FillRule::FILL_WINDING);
+ }
+#endif
+#ifdef USE_CAIRO
+ if (aTarget->GetBackendType() == BackendType::CAIRO) {
+ MOZ_ASSERT(mScaledFont);
+
+ DrawTarget *dt = const_cast<DrawTarget*>(aTarget);
+ cairo_t *ctx = static_cast<cairo_t*>(dt->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
+
+ 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(ctx, mScaledFont);
+
+ // 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_new_path(ctx);
+
+ cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
+
+ RefPtr<PathCairo> newPath = new PathCairo(ctx);
+ if (isNewContext) {
+ cairo_destroy(ctx);
+ }
+
+ return newPath.forget();
+ }
+#endif
+ return nullptr;
+}
+
+void
+ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint)
+{
+ BackendType backendType = aBuilder->GetBackendType();
+#ifdef USE_SKIA
+ if (backendType == BackendType::SKIA) {
+ PathBuilderSkia *builder = static_cast<PathBuilderSkia*>(aBuilder);
+ builder->AppendPath(GetSkiaPathForGlyphs(aBuffer));
+ return;
+ }
+#endif
+#ifdef USE_CAIRO
+ if (backendType == 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
+}
+
+void
+ScaledFontBase::GetGlyphDesignMetrics(const uint16_t* aGlyphs, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics)
+{
+#ifdef USE_CAIRO_SCALED_FONT
+ if (mScaledFont) {
+ for (uint32_t i = 0; i < aNumGlyphs; i++) {
+ cairo_glyph_t glyph;
+ cairo_text_extents_t extents;
+ glyph.index = aGlyphs[i];
+ glyph.x = 0;
+ glyph.y = 0;
+
+ cairo_scaled_font_glyph_extents(mScaledFont, &glyph, 1, &extents);
+
+ aGlyphMetrics[i].mXBearing = extents.x_bearing;
+ aGlyphMetrics[i].mXAdvance = extents.x_advance;
+ aGlyphMetrics[i].mYBearing = extents.y_bearing;
+ aGlyphMetrics[i].mYAdvance = extents.y_advance;
+ aGlyphMetrics[i].mWidth = extents.width;
+ aGlyphMetrics[i].mHeight = extents.height;
+
+ cairo_font_options_t *options = cairo_font_options_create();
+ cairo_scaled_font_get_font_options(mScaledFont, options);
+
+ if (cairo_font_options_get_antialias(options) != CAIRO_ANTIALIAS_NONE) {
+ if (cairo_scaled_font_get_type(mScaledFont) == CAIRO_FONT_TYPE_WIN32) {
+ if (aGlyphMetrics[i].mWidth > 0 && aGlyphMetrics[i].mHeight > 0) {
+ aGlyphMetrics[i].mWidth -= 3.0f;
+ aGlyphMetrics[i].mXBearing += 1.0f;
+ }
+ }
+#if defined(MOZ2D_HAS_MOZ_CAIRO) && defined(CAIRO_HAS_DWRITE_FONT)
+ else if (cairo_scaled_font_get_type(mScaledFont) == CAIRO_FONT_TYPE_DWRITE) {
+ if (aGlyphMetrics[i].mWidth > 0 && aGlyphMetrics[i].mHeight > 0) {
+ aGlyphMetrics[i].mWidth -= 2.0f;
+ aGlyphMetrics[i].mXBearing += 1.0f;
+ }
+ }
+#endif
+ }
+ cairo_font_options_destroy(options);
+ }
+
+ }
+#endif
+
+ // Don't know how to get the glyph metrics...
+ MOZ_CRASH("The specific backend type is not supported for GetGlyphDesignMetrics.");
+}
+
+
+#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);
+}
+#endif
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/ScaledFontBase.h b/gfx/2d/ScaledFontBase.h
new file mode 100644
index 000000000..e4bb4f2f8
--- /dev/null
+++ b/gfx/2d/ScaledFontBase.h
@@ -0,0 +1,72 @@
+/* -*- 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_SCALEDFONTBASE_H_
+#define MOZILLA_GFX_SCALEDFONTBASE_H_
+
+#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/include/core/SkPath.h"
+#include "skia/include/core/SkTypeface.h"
+#endif
+#ifdef USE_CAIRO_SCALED_FONT
+#include "cairo.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+class ScaledFontBase : public ScaledFont
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontBase)
+ explicit ScaledFontBase(Float aSize);
+ virtual ~ScaledFontBase();
+
+ virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
+
+ virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
+
+ virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics);
+
+ float GetSize() { return mSize; }
+
+#ifdef USE_SKIA
+ virtual SkTypeface* GetSkTypeface() { return mTypeface; }
+#endif
+
+ // Not true, but required to instantiate a ScaledFontBase.
+ virtual FontType GetType() const { return FontType::SKIA; }
+
+#ifdef USE_CAIRO_SCALED_FONT
+ bool PopulateCairoScaledFont();
+ cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; }
+ void SetCairoScaledFont(cairo_scaled_font_t* font);
+#endif
+
+protected:
+ friend class DrawTargetSkia;
+#ifdef USE_SKIA
+ SkTypeface* mTypeface;
+ SkPath GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer);
+#endif
+#ifdef USE_CAIRO_SCALED_FONT
+ // Overridders should ensure the cairo_font_face_t has been addrefed.
+ virtual cairo_font_face_t* GetCairoFontFace() { return nullptr; }
+ cairo_scaled_font_t* mScaledFont;
+#endif
+ Float mSize;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SCALEDFONTBASE_H_ */
diff --git a/gfx/2d/ScaledFontCairo.cpp b/gfx/2d/ScaledFontCairo.cpp
new file mode 100644
index 000000000..b8b52145e
--- /dev/null
+++ b/gfx/2d/ScaledFontCairo.cpp
@@ -0,0 +1,38 @@
+/* -*- 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"
+
+#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
+#include "skia/include/ports/SkTypeface_cairo.h"
+#endif
+
+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) {
+ mTypeface = SkCreateTypefaceFromCairoFTFont(mScaledFont);
+ }
+
+ return mTypeface;
+}
+#endif
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/ScaledFontCairo.h b/gfx/2d/ScaledFontCairo.h
new file mode 100644
index 000000000..7862fa1a0
--- /dev/null
+++ b/gfx/2d/ScaledFontCairo.h
@@ -0,0 +1,31 @@
+/* -*- 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
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SCALEDFONTCAIRO_H_ */
diff --git a/gfx/2d/ScaledFontDWrite.cpp b/gfx/2d/ScaledFontDWrite.cpp
new file mode 100644
index 000000000..dc8e586f5
--- /dev/null
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -0,0 +1,293 @@
+/* -*- 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 "DrawTargetD2D1.h"
+#include "ScaledFontDWrite.h"
+#include "PathD2D.h"
+#include "gfxFont.h"
+
+using namespace std;
+
+#ifdef USE_SKIA
+#include "PathSkia.h"
+#include "skia/include/core/SkPaint.h"
+#include "skia/include/core/SkPath.h"
+#include "skia/include/ports/SkTypeface_win.h"
+#endif
+
+#include <vector>
+
+#ifdef USE_CAIRO_SCALED_FONT
+#include "cairo-win32.h"
+#endif
+
+#include "HelpersWinFonts.h"
+
+namespace mozilla {
+namespace gfx {
+
+#define GASP_TAG 0x70736167
+#define GASP_DOGRAY 0x2
+
+static inline unsigned short
+readShort(const char *aBuf)
+{
+ return (*aBuf << 8) | *(aBuf + 1);
+}
+
+static bool
+DoGrayscale(IDWriteFontFace *aDWFace, Float ppem)
+{
+ void *tableContext;
+ char *tableData;
+ UINT32 tableSize;
+ BOOL exists;
+ aDWFace->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists);
+
+ if (exists) {
+ if (tableSize < 4) {
+ aDWFace->ReleaseFontTable(tableContext);
+ return true;
+ }
+ struct gaspRange {
+ unsigned short maxPPEM; // Stored big-endian
+ unsigned short behavior; // Stored big-endian
+ };
+ unsigned short numRanges = readShort(tableData + 2);
+ if (tableSize < (UINT)4 + numRanges * 4) {
+ aDWFace->ReleaseFontTable(tableContext);
+ return true;
+ }
+ gaspRange *ranges = (gaspRange *)(tableData + 4);
+ for (int i = 0; i < numRanges; i++) {
+ if (readShort((char*)&ranges[i].maxPPEM) > ppem) {
+ if (!(readShort((char*)&ranges[i].behavior) & GASP_DOGRAY)) {
+ aDWFace->ReleaseFontTable(tableContext);
+ return false;
+ }
+ break;
+ }
+ }
+ aDWFace->ReleaseFontTable(tableContext);
+ }
+ return true;
+}
+
+static inline DWRITE_FONT_STRETCH
+DWriteFontStretchFromStretch(int16_t aStretch)
+{
+ switch (aStretch) {
+ case NS_FONT_STRETCH_ULTRA_CONDENSED:
+ return DWRITE_FONT_STRETCH_ULTRA_CONDENSED;
+ case NS_FONT_STRETCH_EXTRA_CONDENSED:
+ return DWRITE_FONT_STRETCH_EXTRA_CONDENSED;
+ case NS_FONT_STRETCH_CONDENSED:
+ return DWRITE_FONT_STRETCH_CONDENSED;
+ case NS_FONT_STRETCH_SEMI_CONDENSED:
+ return DWRITE_FONT_STRETCH_SEMI_CONDENSED;
+ case NS_FONT_STRETCH_NORMAL:
+ return DWRITE_FONT_STRETCH_NORMAL;
+ case NS_FONT_STRETCH_SEMI_EXPANDED:
+ return DWRITE_FONT_STRETCH_SEMI_EXPANDED;
+ case NS_FONT_STRETCH_EXPANDED:
+ return DWRITE_FONT_STRETCH_EXPANDED;
+ case NS_FONT_STRETCH_EXTRA_EXPANDED:
+ return DWRITE_FONT_STRETCH_EXTRA_EXPANDED;
+ case NS_FONT_STRETCH_ULTRA_EXPANDED:
+ return DWRITE_FONT_STRETCH_ULTRA_EXPANDED;
+ default:
+ return DWRITE_FONT_STRETCH_UNDEFINED;
+ }
+}
+
+ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace *aFontFace, Float aSize,
+ bool aUseEmbeddedBitmap, bool aForceGDIMode,
+ const gfxFontStyle* aStyle)
+ : ScaledFontBase(aSize)
+ , mFontFace(aFontFace)
+ , mUseEmbeddedBitmap(aUseEmbeddedBitmap)
+ , mForceGDIMode(aForceGDIMode)
+{
+ mStyle = SkFontStyle(aStyle->weight,
+ DWriteFontStretchFromStretch(aStyle->stretch),
+ aStyle->style == NS_FONT_STYLE_NORMAL ?
+ SkFontStyle::kUpright_Slant : SkFontStyle::kItalic_Slant);
+}
+
+already_AddRefed<Path>
+ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
+{
+ if (aTarget->GetBackendType() != BackendType::DIRECT2D && aTarget->GetBackendType() != BackendType::DIRECT2D1_1) {
+ return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
+ }
+
+ RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder();
+
+ PathBuilderD2D *pathBuilderD2D =
+ static_cast<PathBuilderD2D*>(pathBuilder.get());
+
+ CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
+
+ return pathBuilder->Finish();
+}
+
+
+#ifdef USE_SKIA
+SkTypeface*
+ScaledFontDWrite::GetSkTypeface()
+{
+ if (!mTypeface) {
+ IDWriteFactory *factory = DrawTargetD2D1::GetDWriteFactory();
+ if (!factory) {
+ return nullptr;
+ }
+
+ mTypeface = SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle, mForceGDIMode);
+ }
+ return mTypeface;
+}
+#endif
+
+void
+ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint)
+{
+ BackendType backendType = aBuilder->GetBackendType();
+ if (backendType != BackendType::DIRECT2D && backendType != BackendType::DIRECT2D1_1) {
+ ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aTransformHint);
+ return;
+ }
+
+ PathBuilderD2D *pathBuilderD2D =
+ static_cast<PathBuilderD2D*>(aBuilder);
+
+ if (pathBuilderD2D->IsFigureActive()) {
+ gfxCriticalNote << "Attempting to copy glyphs to PathBuilderD2D with active figure.";
+ }
+
+ CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
+}
+
+void
+ScaledFontDWrite::GetGlyphDesignMetrics(const uint16_t* aGlyphs, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics)
+{
+ DWRITE_FONT_METRICS fontMetrics;
+ mFontFace->GetMetrics(&fontMetrics);
+
+ vector<DWRITE_GLYPH_METRICS> metrics(aNumGlyphs);
+ mFontFace->GetDesignGlyphMetrics(aGlyphs, aNumGlyphs, &metrics.front());
+
+ Float designUnitCorrection = 1.f / fontMetrics.designUnitsPerEm;
+
+ for (uint32_t i = 0; i < aNumGlyphs; i++) {
+ aGlyphMetrics[i].mXBearing = metrics[i].leftSideBearing * designUnitCorrection * mSize;
+ aGlyphMetrics[i].mXAdvance = metrics[i].advanceWidth * designUnitCorrection * mSize;
+ aGlyphMetrics[i].mYBearing = metrics[i].topSideBearing * designUnitCorrection * mSize;
+ aGlyphMetrics[i].mYAdvance = metrics[i].advanceHeight * designUnitCorrection * mSize;
+ aGlyphMetrics[i].mWidth = (metrics[i].advanceHeight - metrics[i].topSideBearing - metrics[i].bottomSideBearing) *
+ designUnitCorrection * mSize;
+ aGlyphMetrics[i].mHeight = (metrics[i].topSideBearing - metrics[i].verticalOriginY) * designUnitCorrection * mSize;
+ }
+}
+
+void
+ScaledFontDWrite::CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink)
+{
+ std::vector<UINT16> indices;
+ std::vector<FLOAT> advances;
+ std::vector<DWRITE_GLYPH_OFFSET> offsets;
+ indices.resize(aBuffer.mNumGlyphs);
+ advances.resize(aBuffer.mNumGlyphs);
+ offsets.resize(aBuffer.mNumGlyphs);
+
+ memset(&advances.front(), 0, sizeof(FLOAT) * aBuffer.mNumGlyphs);
+ for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+ indices[i] = aBuffer.mGlyphs[i].mIndex;
+ offsets[i].advanceOffset = aBuffer.mGlyphs[i].mPosition.x;
+ offsets[i].ascenderOffset = -aBuffer.mGlyphs[i].mPosition.y;
+ }
+
+ HRESULT hr =
+ mFontFace->GetGlyphRunOutline(mSize, &indices.front(), &advances.front(),
+ &offsets.front(), aBuffer.mNumGlyphs,
+ FALSE, FALSE, aSink);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to copy glyphs to geometry sink. Code: " << hexa(hr);
+ }
+}
+
+bool
+ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
+{
+ UINT32 fileCount = 0;
+ mFontFace->GetFiles(&fileCount, nullptr);
+
+ if (fileCount > 1) {
+ MOZ_ASSERT(false);
+ return false;
+ }
+
+ RefPtr<IDWriteFontFile> file;
+ mFontFace->GetFiles(&fileCount, getter_AddRefs(file));
+
+ const void *referenceKey;
+ UINT32 refKeySize;
+ // XXX - This can currently crash for webfonts, as when we get the reference
+ // key out of the file, that can be an invalid reference key for the loader
+ // we use it with. The fix to this is not obvious but it will probably
+ // have to happen inside thebes.
+ file->GetReferenceKey(&referenceKey, &refKeySize);
+
+ RefPtr<IDWriteFontFileLoader> loader;
+ file->GetLoader(getter_AddRefs(loader));
+
+ RefPtr<IDWriteFontFileStream> stream;
+ loader->CreateStreamFromKey(referenceKey, refKeySize, getter_AddRefs(stream));
+
+ UINT64 fileSize64;
+ stream->GetFileSize(&fileSize64);
+ if (fileSize64 > UINT32_MAX) {
+ MOZ_ASSERT(false);
+ return false;
+ }
+
+ uint32_t fileSize = static_cast<uint32_t>(fileSize64);
+ const void *fragmentStart;
+ void *context;
+ stream->ReadFileFragment(&fragmentStart, 0, fileSize, &context);
+
+ aDataCallback((uint8_t*)fragmentStart, fileSize, mFontFace->GetIndex(), mSize, aBaton);
+
+ stream->ReleaseFileFragment(context);
+
+ return true;
+}
+
+AntialiasMode
+ScaledFontDWrite::GetDefaultAAMode()
+{
+ AntialiasMode defaultMode = GetSystemDefaultAAMode();
+
+ if (defaultMode == AntialiasMode::GRAY) {
+ if (!DoGrayscale(mFontFace, mSize)) {
+ defaultMode = AntialiasMode::NONE;
+ }
+ }
+ return defaultMode;
+}
+
+#ifdef USE_CAIRO_SCALED_FONT
+cairo_font_face_t*
+ScaledFontDWrite::GetCairoFontFace()
+{
+ if (!mFontFace) {
+ return nullptr;
+ }
+
+ return cairo_dwrite_font_face_create_for_dwrite_fontface(nullptr, mFontFace);
+}
+#endif
+
+}
+}
diff --git a/gfx/2d/ScaledFontDWrite.h b/gfx/2d/ScaledFontDWrite.h
new file mode 100644
index 000000000..fc9a26c42
--- /dev/null
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -0,0 +1,84 @@
+/* -*- 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_SCALEDFONTDWRITE_H_
+#define MOZILLA_GFX_SCALEDFONTDWRITE_H_
+
+#include <dwrite.h>
+#include "ScaledFontBase.h"
+
+struct ID2D1GeometrySink;
+struct gfxFontStyle;
+
+namespace mozilla {
+namespace gfx {
+
+class ScaledFontDWrite final : public ScaledFontBase
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontDwrite)
+ ScaledFontDWrite(IDWriteFontFace *aFont, Float aSize)
+ : ScaledFontBase(aSize)
+ , mFontFace(aFont)
+ , mUseEmbeddedBitmap(false)
+ , mForceGDIMode(false)
+ {}
+
+ ScaledFontDWrite(IDWriteFontFace *aFontFace, Float aSize, bool aUseEmbeddedBitmap,
+ bool aForceGDIMode, const gfxFontStyle* aStyle);
+
+ virtual FontType GetType() const { return FontType::DWRITE; }
+
+ virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
+ virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
+
+ void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
+
+ virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics);
+
+ virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
+
+ virtual AntialiasMode GetDefaultAAMode() override;
+
+ bool UseEmbeddedBitmaps() { return mUseEmbeddedBitmap; }
+ bool ForceGDIMode() { return mForceGDIMode; }
+
+#ifdef USE_SKIA
+ virtual SkTypeface* GetSkTypeface();
+ SkFontStyle mStyle;
+#endif
+
+ RefPtr<IDWriteFontFace> mFontFace;
+ bool mUseEmbeddedBitmap;
+ bool mForceGDIMode;
+
+protected:
+#ifdef USE_CAIRO_SCALED_FONT
+ cairo_font_face_t* GetCairoFontFace() override;
+#endif
+};
+
+class GlyphRenderingOptionsDWrite : public GlyphRenderingOptions
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptionsDWrite)
+ GlyphRenderingOptionsDWrite(IDWriteRenderingParams *aParams)
+ : mParams(aParams)
+ {
+ }
+
+ virtual FontType GetType() const { return FontType::DWRITE; }
+
+private:
+ friend class DrawTargetD2D;
+ friend class DrawTargetD2D1;
+
+ RefPtr<IDWriteRenderingParams> mParams;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_SCALEDFONTDWRITE_H_ */
diff --git a/gfx/2d/ScaledFontFontconfig.cpp b/gfx/2d/ScaledFontFontconfig.cpp
new file mode 100644
index 000000000..d4751f86d
--- /dev/null
+++ b/gfx/2d/ScaledFontFontconfig.cpp
@@ -0,0 +1,47 @@
+/* -*- 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 "ScaledFontFontconfig.h"
+#include "Logging.h"
+
+#ifdef USE_SKIA
+#include "skia/include/ports/SkTypeface_cairo.h"
+#endif
+
+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
+ScaledFontFontconfig::ScaledFontFontconfig(cairo_scaled_font_t* aScaledFont,
+ FcPattern* aPattern,
+ Float aSize)
+ : ScaledFontBase(aSize),
+ mPattern(aPattern)
+{
+ SetCairoScaledFont(aScaledFont);
+ FcPatternReference(aPattern);
+}
+
+ScaledFontFontconfig::~ScaledFontFontconfig()
+{
+ FcPatternDestroy(mPattern);
+}
+
+#ifdef USE_SKIA
+SkTypeface* ScaledFontFontconfig::GetSkTypeface()
+{
+ if (!mTypeface) {
+ mTypeface = SkCreateTypefaceFromCairoFTFontWithFontconfig(mScaledFont, mPattern);
+ }
+
+ return mTypeface;
+}
+#endif
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/ScaledFontFontconfig.h b/gfx/2d/ScaledFontFontconfig.h
new file mode 100644
index 000000000..4d4e8217d
--- /dev/null
+++ b/gfx/2d/ScaledFontFontconfig.h
@@ -0,0 +1,37 @@
+/* -*- 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_SCALEDFONTFONTCONFIG_H_
+#define MOZILLA_GFX_SCALEDFONTFONTCONFIG_H_
+
+#include "ScaledFontBase.h"
+
+#include <fontconfig/fontconfig.h>
+#include <cairo.h>
+
+namespace mozilla {
+namespace gfx {
+
+class ScaledFontFontconfig : public ScaledFontBase
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontFontconfig)
+ ScaledFontFontconfig(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize);
+ ~ScaledFontFontconfig();
+
+ virtual FontType GetType() const { return FontType::FONTCONFIG; }
+
+#ifdef USE_SKIA
+ virtual SkTypeface* GetSkTypeface();
+#endif
+
+private:
+ FcPattern* mPattern;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_SCALEDFONTFONTCONFIG_H_ */
diff --git a/gfx/2d/ScaledFontMac.cpp b/gfx/2d/ScaledFontMac.cpp
new file mode 100644
index 000000000..6baf25782
--- /dev/null
+++ b/gfx/2d/ScaledFontMac.cpp
@@ -0,0 +1,247 @@
+/* -*- 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 "ScaledFontMac.h"
+#ifdef USE_SKIA
+#include "PathSkia.h"
+#include "skia/include/core/SkPaint.h"
+#include "skia/include/core/SkPath.h"
+#include "skia/include/ports/SkTypeface_mac.h"
+#endif
+#include <vector>
+#include <dlfcn.h>
+#ifdef MOZ_WIDGET_UIKIT
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+// prototype for private API
+extern "C" {
+CGPathRef CGFontGetGlyphPath(CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph);
+};
+#endif
+
+#ifdef USE_CAIRO_SCALED_FONT
+#include "cairo-quartz.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+ScaledFontMac::CTFontDrawGlyphsFuncT* ScaledFontMac::CTFontDrawGlyphsPtr = nullptr;
+bool ScaledFontMac::sSymbolLookupDone = false;
+
+ScaledFontMac::ScaledFontMac(CGFontRef aFont, Float aSize)
+ : ScaledFontBase(aSize)
+{
+ if (!sSymbolLookupDone) {
+ CTFontDrawGlyphsPtr =
+ (CTFontDrawGlyphsFuncT*)dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
+ sSymbolLookupDone = true;
+ }
+
+ // XXX: should we be taking a reference
+ mFont = CGFontRetain(aFont);
+ if (CTFontDrawGlyphsPtr != nullptr) {
+ // only create mCTFont if we're going to be using the CTFontDrawGlyphs API
+ mCTFont = CTFontCreateWithGraphicsFont(aFont, aSize, nullptr, nullptr);
+ } else {
+ mCTFont = nullptr;
+ }
+}
+
+ScaledFontMac::~ScaledFontMac()
+{
+ if (mCTFont) {
+ CFRelease(mCTFont);
+ }
+ CGFontRelease(mFont);
+}
+
+#ifdef USE_SKIA
+SkTypeface* ScaledFontMac::GetSkTypeface()
+{
+ if (!mTypeface) {
+ if (mCTFont) {
+ mTypeface = SkCreateTypefaceFromCTFont(mCTFont);
+ } else {
+ CTFontRef fontFace = CTFontCreateWithGraphicsFont(mFont, mSize, nullptr, nullptr);
+ mTypeface = SkCreateTypefaceFromCTFont(fontFace);
+ CFRelease(fontFace);
+ }
+ }
+ return mTypeface;
+}
+#endif
+
+// private API here are the public options on OS X
+// CTFontCreatePathForGlyph
+// ATSUGlyphGetCubicPaths
+// we've used this in cairo sucessfully for some time.
+// Note: cairo dlsyms it. We could do that but maybe it's
+// safe just to use?
+
+already_AddRefed<Path>
+ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
+{
+ return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
+}
+
+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;
+
+}
+
+#ifdef USE_CAIRO_SCALED_FONT
+cairo_font_face_t*
+ScaledFontMac::GetCairoFontFace()
+{
+ MOZ_ASSERT(mFont);
+ return cairo_quartz_font_face_create_for_cgfont(mFont);
+}
+#endif
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/ScaledFontMac.h b/gfx/2d/ScaledFontMac.h
new file mode 100644
index 000000000..c141f96b2
--- /dev/null
+++ b/gfx/2d/ScaledFontMac.h
@@ -0,0 +1,79 @@
+/* -*- 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_SCALEDFONTMAC_H_
+#define MOZILLA_GFX_SCALEDFONTMAC_H_
+
+#ifdef MOZ_WIDGET_COCOA
+#include <ApplicationServices/ApplicationServices.h>
+#else
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreText/CoreText.h>
+#endif
+
+#include "2D.h"
+
+#include "ScaledFontBase.h"
+
+namespace mozilla {
+namespace gfx {
+
+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 ScaledFontMac : public ScaledFontBase
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontMac)
+ ScaledFontMac(CGFontRef aFont, Float aSize);
+ virtual ~ScaledFontMac();
+
+ virtual FontType GetType() const { return FontType::MAC; }
+#ifdef USE_SKIA
+ virtual SkTypeface* GetSkTypeface();
+#endif
+ virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
+ virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
+
+#ifdef USE_CAIRO_SCALED_FONT
+ cairo_font_face_t* GetCairoFontFace();
+#endif
+
+private:
+ friend class DrawTargetSkia;
+ CGFontRef mFont;
+ CTFontRef mCTFont; // only created if CTFontDrawGlyphs is available, otherwise null
+
+ typedef void (CTFontDrawGlyphsFuncT)(CTFontRef,
+ const CGGlyph[], const CGPoint[],
+ size_t, CGContextRef);
+
+ static bool sSymbolLookupDone;
+
+public:
+ // function pointer for CTFontDrawGlyphs, if available;
+ // initialized the first time a ScaledFontMac is created,
+ // so it will be valid by the time DrawTargetCG wants to use it
+ static CTFontDrawGlyphsFuncT* CTFontDrawGlyphsPtr;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SCALEDFONTMAC_H_ */
diff --git a/gfx/2d/ScaledFontWin.cpp b/gfx/2d/ScaledFontWin.cpp
new file mode 100644
index 000000000..2ebae21e5
--- /dev/null
+++ b/gfx/2d/ScaledFontWin.cpp
@@ -0,0 +1,103 @@
+/* -*- 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 "ScaledFontWin.h"
+
+#include "AutoHelpersWin.h"
+#include "Logging.h"
+#include "nsString.h"
+
+#ifdef USE_SKIA
+#include "skia/include/ports/SkTypeface_win.h"
+#endif
+
+#ifdef USE_CAIRO_SCALED_FONT
+#include "cairo-win32.h"
+#endif
+
+#include "HelpersWinFonts.h"
+
+namespace mozilla {
+namespace gfx {
+
+ScaledFontWin::ScaledFontWin(const LOGFONT* aFont, Float aSize)
+ : ScaledFontBase(aSize)
+ , mLogFont(*aFont)
+{
+}
+
+bool
+ScaledFontWin::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
+{
+ AutoDC dc;
+ AutoSelectFont font(dc.GetDC(), &mLogFont);
+
+ // Check for a font collection first.
+ uint32_t table = 0x66637474; // 'ttcf'
+ uint32_t tableSize = ::GetFontData(dc.GetDC(), table, 0, nullptr, 0);
+ if (tableSize == GDI_ERROR) {
+ // Try as if just a single font.
+ table = 0;
+ tableSize = ::GetFontData(dc.GetDC(), table, 0, nullptr, 0);
+ if (tableSize == GDI_ERROR) {
+ return false;
+ }
+ }
+
+ UniquePtr<uint8_t[]> fontData(new uint8_t[tableSize]);
+
+ uint32_t sizeGot =
+ ::GetFontData(dc.GetDC(), table, 0, fontData.get(), tableSize);
+ if (sizeGot != tableSize) {
+ return false;
+ }
+
+ aDataCallback(fontData.get(), tableSize, 0, mSize, aBaton);
+ return true;
+}
+
+bool
+ScaledFontWin::GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton)
+{
+ aCb(reinterpret_cast<uint8_t*>(&mLogFont), sizeof(mLogFont), aBaton);
+ return true;
+}
+
+bool
+ScaledFontWin::GetFontDescriptor(FontDescriptorOutput aCb, void* aBaton)
+{
+ aCb(reinterpret_cast<uint8_t*>(&mLogFont), sizeof(mLogFont), mSize, aBaton);
+ return true;
+}
+
+AntialiasMode
+ScaledFontWin::GetDefaultAAMode()
+{
+ return GetSystemDefaultAAMode();
+}
+
+#ifdef USE_SKIA
+SkTypeface* ScaledFontWin::GetSkTypeface()
+{
+ if (!mTypeface) {
+ mTypeface = SkCreateTypefaceFromLOGFONT(mLogFont);
+ }
+ return mTypeface;
+}
+#endif
+
+#ifdef USE_CAIRO_SCALED_FONT
+cairo_font_face_t*
+ScaledFontWin::GetCairoFontFace()
+{
+ if (mLogFont.lfFaceName[0] == 0) {
+ return nullptr;
+ }
+ return cairo_win32_font_face_create_for_logfontw(&mLogFont);
+}
+#endif
+
+}
+}
diff --git a/gfx/2d/ScaledFontWin.h b/gfx/2d/ScaledFontWin.h
new file mode 100644
index 000000000..c07b263d7
--- /dev/null
+++ b/gfx/2d/ScaledFontWin.h
@@ -0,0 +1,49 @@
+/* -*- 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_SCALEDFONTWIN_H_
+#define MOZILLA_GFX_SCALEDFONTWIN_H_
+
+#include "ScaledFontBase.h"
+#include <windows.h>
+
+namespace mozilla {
+namespace gfx {
+
+class ScaledFontWin : public ScaledFontBase
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontWin)
+ ScaledFontWin(const LOGFONT* aFont, Float aSize);
+
+ virtual FontType GetType() const { return FontType::GDI; }
+
+ bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) override;
+
+ bool GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) override;
+
+ virtual bool GetFontDescriptor(FontDescriptorOutput aCb, void* aBaton) override;
+ virtual AntialiasMode GetDefaultAAMode() override;
+
+#ifdef USE_SKIA
+ virtual SkTypeface* GetSkTypeface();
+#endif
+
+protected:
+#ifdef USE_CAIRO_SCALED_FONT
+ cairo_font_face_t* GetCairoFontFace() override;
+#endif
+
+private:
+#ifdef USE_SKIA
+ friend class DrawTargetSkia;
+#endif
+ LOGFONT mLogFont;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_SCALEDFONTWIN_H_ */
diff --git a/gfx/2d/ShadersD2D.fx b/gfx/2d/ShadersD2D.fx
new file mode 100644
index 000000000..df4ced845
--- /dev/null
+++ b/gfx/2d/ShadersD2D.fx
@@ -0,0 +1,746 @@
+// We store vertex coordinates and the quad shape in a constant buffer, this is
+// easy to update and allows us to use a single call to set the x, y, w, h of
+// the quad.
+// The QuadDesc and TexCoords both work as follows:
+// The x component is the quad left point, the y component is the top point
+// the z component is the width, and the w component is the height. The quad
+// are specified in viewport coordinates, i.e. { -1.0f, 1.0f, 2.0f, -2.0f }
+// would cover the entire viewport (which runs from <-1.0f, 1.0f> left to right
+// and <-1.0f, 1.0f> -bottom- to top. The TexCoords desc is specified in texture
+// space <0, 1.0f> left to right and top to bottom. The input vertices of the
+// shader stage always form a rectangle from {0, 0} - {1, 1}
+cbuffer cb0
+{
+ float4 QuadDesc;
+ float4 TexCoords;
+ float4 MaskTexCoords;
+ float4 TextColor;
+}
+
+cbuffer cb1
+{
+ float4 BlurOffsetsH[3];
+ float4 BlurOffsetsV[3];
+ float4 BlurWeights[3];
+ float4 ShadowColor;
+}
+
+cbuffer cb2
+{
+ float3x3 DeviceSpaceToUserSpace;
+ float2 dimensions;
+ // Precalculate as much as we can!
+ float3 diff;
+ float2 center1;
+ float A;
+ float radius1;
+ float sq_radius1;
+}
+
+struct VS_OUTPUT
+{
+ float4 Position : SV_Position;
+ float2 TexCoord : TEXCOORD0;
+ float2 MaskTexCoord : TEXCOORD1;
+};
+
+struct VS_RADIAL_OUTPUT
+{
+ float4 Position : SV_Position;
+ float2 MaskTexCoord : TEXCOORD0;
+ float2 PixelCoord : TEXCOORD1;
+};
+
+struct PS_TEXT_OUTPUT
+{
+ float4 color;
+ float4 alpha;
+};
+
+Texture2D tex;
+Texture2D bcktex;
+Texture2D mask;
+uint blendop;
+
+sampler sSampler = sampler_state {
+ Filter = MIN_MAG_MIP_LINEAR;
+ Texture = tex;
+ AddressU = Clamp;
+ AddressV = Clamp;
+};
+
+sampler sBckSampler = sampler_state {
+ Filter = MIN_MAG_MIP_LINEAR;
+ Texture = bcktex;
+ AddressU = Clamp;
+ AddressV = Clamp;
+};
+
+sampler sWrapSampler = sampler_state {
+ Filter = MIN_MAG_MIP_LINEAR;
+ Texture = tex;
+ AddressU = Wrap;
+ AddressV = Wrap;
+};
+
+sampler sMirrorSampler = sampler_state {
+ Filter = MIN_MAG_MIP_LINEAR;
+ Texture = tex;
+ AddressU = Mirror;
+ AddressV = Mirror;
+};
+
+sampler sMaskSampler = sampler_state {
+ Filter = MIN_MAG_MIP_LINEAR;
+ Texture = mask;
+ AddressU = Clamp;
+ AddressV = Clamp;
+};
+
+sampler sShadowSampler = sampler_state {
+ Filter = MIN_MAG_MIP_LINEAR;
+ Texture = tex;
+ AddressU = Border;
+ AddressV = Border;
+ BorderColor = float4(0, 0, 0, 0);
+};
+
+RasterizerState TextureRast
+{
+ ScissorEnable = True;
+ CullMode = None;
+};
+
+BlendState ShadowBlendH
+{
+ BlendEnable[0] = False;
+ RenderTargetWriteMask[0] = 0xF;
+};
+
+BlendState ShadowBlendV
+{
+ BlendEnable[0] = True;
+ SrcBlend = One;
+ DestBlend = Inv_Src_Alpha;
+ BlendOp = Add;
+ SrcBlendAlpha = One;
+ DestBlendAlpha = Inv_Src_Alpha;
+ BlendOpAlpha = Add;
+ RenderTargetWriteMask[0] = 0xF;
+};
+
+BlendState bTextBlend
+{
+ AlphaToCoverageEnable = FALSE;
+ BlendEnable[0] = TRUE;
+ SrcBlend = Src1_Color;
+ DestBlend = Inv_Src1_Color;
+ BlendOp = Add;
+ SrcBlendAlpha = Src1_Alpha;
+ DestBlendAlpha = Inv_Src1_Alpha;
+ BlendOpAlpha = Add;
+ RenderTargetWriteMask[0] = 0x0F; // All
+};
+
+VS_OUTPUT SampleTextureVS(float3 pos : POSITION)
+{
+ VS_OUTPUT Output;
+ Output.Position.w = 1.0f;
+ Output.Position.x = pos.x * QuadDesc.z + QuadDesc.x;
+ Output.Position.y = pos.y * QuadDesc.w + QuadDesc.y;
+ Output.Position.z = 0;
+ Output.TexCoord.x = pos.x * TexCoords.z + TexCoords.x;
+ Output.TexCoord.y = pos.y * TexCoords.w + TexCoords.y;
+ Output.MaskTexCoord.x = pos.x * MaskTexCoords.z + MaskTexCoords.x;
+ Output.MaskTexCoord.y = pos.y * MaskTexCoords.w + MaskTexCoords.y;
+ return Output;
+}
+
+VS_RADIAL_OUTPUT SampleRadialVS(float3 pos : POSITION)
+{
+ VS_RADIAL_OUTPUT Output;
+ Output.Position.w = 1.0f;
+ Output.Position.x = pos.x * QuadDesc.z + QuadDesc.x;
+ Output.Position.y = pos.y * QuadDesc.w + QuadDesc.y;
+ Output.Position.z = 0;
+ Output.MaskTexCoord.x = pos.x * MaskTexCoords.z + MaskTexCoords.x;
+ Output.MaskTexCoord.y = pos.y * MaskTexCoords.w + MaskTexCoords.y;
+
+ // For the radial gradient pixel shader we need to pass in the pixel's
+ // coordinates in user space for the color to be correctly determined.
+
+ Output.PixelCoord.x = ((Output.Position.x + 1.0f) / 2.0f) * dimensions.x;
+ Output.PixelCoord.y = ((1.0f - Output.Position.y) / 2.0f) * dimensions.y;
+ Output.PixelCoord.xy = mul(float3(Output.PixelCoord.x, Output.PixelCoord.y, 1.0f), DeviceSpaceToUserSpace).xy;
+ return Output;
+}
+
+float Screen(float a, float b)
+{
+ return 1 - ((1 - a)*(1 - b));
+}
+
+static float RedLuminance = 0.3f;
+static float GreenLuminance = 0.59f;
+static float BlueLuminance = 0.11f;
+
+float Lum(float3 C)
+{
+ return RedLuminance * C.r + GreenLuminance * C.g + BlueLuminance * C.b;
+}
+
+float3 ClipColor(float3 C)
+{
+ float L = Lum(C);
+ float n = min(min(C.r, C.g), C.b);
+ float x = max(max(C.r, C.g), C.b);
+
+ if(n < 0)
+ C = L + (((C - L) * L) / (L - n));
+
+ if(x > 1)
+ C = L + ((C - L) * (1 - L) / (x - L));
+
+ return C;
+}
+
+float3 SetLum(float3 C, float l)
+{
+ float d = l - Lum(C);
+ C = C + d;
+ return ClipColor(C);
+}
+
+float Sat(float3 C)
+{
+ return max(C.r, max(C.g, C.b)) - min(C.r, min(C.g, C.b));
+}
+
+void SetSatComponents(inout float minComp, inout float midComp, inout float maxComp, in float satVal)
+{
+ midComp -= minComp;
+ maxComp -= minComp;
+ minComp = 0.0;
+ if (maxComp > 0.0)
+ {
+ midComp *= satVal/maxComp;
+ maxComp = satVal;
+ }
+}
+
+float3 SetSat(float3 color, in float satVal)
+{
+ if (color.x <= color.y) {
+ if (color.y <= color.z) {
+ // x <= y <= z
+ SetSatComponents(color.x, color.y, color.z, satVal);
+ }
+ else {
+ if (color.x <= color.z) {
+ // x <= z <= y
+ SetSatComponents(color.x, color.z, color.y, satVal);
+ }
+ else {
+ // z <= x <= y
+ SetSatComponents(color.z, color.x, color.y, satVal);
+ }
+ }
+ }
+ else {
+ if (color.x <= color.z) {
+ // y <= x <= z
+ SetSatComponents(color.y, color.x, color.z, satVal);
+ }
+ else {
+ if (color.y <= color.z) {
+ // y <= z <= x
+ SetSatComponents(color.y, color.z, color.x, satVal);
+ }
+ else {
+ // z <= y <= x
+ SetSatComponents(color.z, color.y, color.x, satVal);
+ }
+ }
+ }
+
+ return color;
+}
+
+float4 SampleBlendTextureSeparablePS_1( VS_OUTPUT In) : SV_Target
+{
+ float4 output = tex.Sample(sSampler, In.TexCoord);
+ float4 background = bcktex.Sample(sBckSampler, In.TexCoord);
+ if((output.a == 0) || (background.a == 0))
+ return output;
+
+ output.rgb /= output.a;
+ background.rgb /= background.a;
+
+ float4 retval = output;
+
+ if(blendop == 1) { // multiply
+ retval.rgb = output.rgb * background.rgb;
+ } else if(blendop == 2) {
+ retval.rgb = output.rgb + background.rgb - output.rgb * background.rgb;
+ } else if(blendop == 3) {
+ if(background.r <= 0.5)
+ retval.r = 2*background.r * output.r;
+ else
+ retval.r = Screen(output.r, 2 * background.r - 1);
+ if(background.g <= 0.5)
+ retval.g = 2 * background.g * output.g;
+ else
+ retval.g = Screen(output.g, 2 * background.g - 1);
+ if(background.b <= 0.5)
+ retval.b = 2 * background.b * output.b;
+ else
+ retval.b = Screen(output.b, 2 * background.b - 1);
+ } else if(blendop == 4) {
+ retval.rgb = min(output.rgb, background.rgb);
+ } else if(blendop == 5) {
+ retval.rgb = max(output.rgb, background.rgb);
+ } else {
+ 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;
+ return output;
+}
+
+float4 SampleBlendTextureSeparablePS_2( VS_OUTPUT In) : SV_Target
+{
+ float4 output = tex.Sample(sSampler, In.TexCoord);
+ float4 background = bcktex.Sample(sBckSampler, In.TexCoord);
+ if((output.a == 0) || (background.a == 0))
+ return output;
+
+ output.rgb /= output.a;
+ background.rgb /= background.a;
+
+ float4 retval = output;
+
+ if(blendop == 7) {
+ 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.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.b = 1 - min(1, (1 - background.b) / output.b);
+ } else if(blendop == 8) {
+ if(output.r <= 0.5)
+ retval.r = 2 * output.r * background.r;
+ else
+ retval.r = Screen(background.r, 2 * output.r -1);
+ if(output.g <= 0.5)
+ retval.g = 2 * output.g * background.g;
+ else
+ retval.g = Screen(background.g, 2 * output.g -1);
+ if(output.b <= 0.5)
+ retval.b = 2 * output.b * background.b;
+ else
+ retval.b = Screen(background.b, 2 * output.b -1);
+ } else if(blendop == 9){
+ float D;
+ if(background.r <= 0.25)
+ D = ((16 * background.r - 12) * background.r + 4) * background.r;
+ else
+ D = sqrt(background.r);
+ if(output.r <= 0.5)
+ retval.r = background.r - (1 - 2 * output.r) * background.r * (1 - background.r);
+ else
+ retval.r = background.r + (2 * output.r - 1) * (D - background.r);
+
+ if(background.g <= 0.25)
+ D = ((16 * background.g - 12) * background.g + 4) * background.g;
+ else
+ D = sqrt(background.g);
+ if(output.g <= 0.5)
+ retval.g = background.g - (1 - 2 * output.g) * background.g * (1 - background.g);
+ else
+ retval.g = background.g + (2 * output.g - 1) * (D - background.g);
+
+ if(background.b <= 0.25)
+ D = ((16 * background.b - 12) * background.b + 4) * background.b;
+ else
+ D = sqrt(background.b);
+
+ if(output.b <= 0.5)
+ retval.b = background.b - (1 - 2 * output.b) * background.b * (1 - background.b);
+ else
+ retval.b = background.b + (2 * output.b - 1) * (D - background.b);
+ } else if(blendop == 10) {
+ retval.rgb = abs(output.rgb - background.rgb);
+ } else {
+ retval.rgb = output.rgb + background.rgb - 2 * output.rgb * background.rgb;
+ }
+
+ output.rgb = ((1 - background.a) * output.rgb + background.a * retval.rgb) * output.a;
+ return output;
+}
+
+float4 SampleBlendTextureNonSeparablePS( VS_OUTPUT In) : SV_Target
+{
+ float4 output = tex.Sample(sSampler, In.TexCoord);
+ float4 background = bcktex.Sample(sBckSampler, In.TexCoord);
+ if((output.a == 0) || (background.a == 0))
+ return output;
+
+ output.rgb /= output.a;
+ background.rgb /= background.a;
+
+ float4 retval = output;
+
+ if(blendop == 12) {
+ retval.rgb = SetLum(SetSat(output.rgb, Sat(background.rgb)), Lum(background.rgb));
+ } else if(blendop == 13) {
+ retval.rgb = SetLum(SetSat(background.rgb, Sat(output.rgb)), Lum(background.rgb));
+ } else if(blendop == 14) {
+ retval.rgb = SetLum(output.rgb, Lum(background.rgb));
+ } else {
+ retval.rgb = SetLum(background.rgb, Lum(output.rgb));
+ }
+
+ output.rgb = ((1 - background.a) * output.rgb + background.a * retval.rgb) * output.a;
+ return output;
+}
+
+
+float4 SampleTexturePS( VS_OUTPUT In) : SV_Target
+{
+ return tex.Sample(sSampler, In.TexCoord);
+}
+
+float4 SampleMaskTexturePS( VS_OUTPUT In) : SV_Target
+{
+ return tex.Sample(sSampler, In.TexCoord) * mask.Sample(sMaskSampler, In.MaskTexCoord).a;
+}
+
+float4 SampleRadialGradientPS(VS_RADIAL_OUTPUT In, uniform sampler aSampler) : SV_Target
+{
+ // Radial gradient painting is defined as the set of circles whose centers
+ // are described by C(t) = (C2 - C1) * t + C1; with radii
+ // R(t) = (R2 - R1) * t + R1; for R(t) > 0. This shader solves the
+ // quadratic equation that arises when calculating t for pixel (x, y).
+ //
+ // A more extensive derrivation can be found in the pixman radial gradient
+ // code.
+
+ float2 p = In.PixelCoord;
+ float3 dp = float3(p - center1, radius1);
+
+ // dpx * dcx + dpy * dcy + r * dr
+ float B = dot(dp, diff);
+
+ float C = pow(dp.x, 2) + pow(dp.y, 2) - sq_radius1;
+
+ float det = pow(B, 2) - A * C;
+
+ if (det < 0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ float sqrt_det = sqrt(abs(det));
+
+ float2 t = (B + float2(sqrt_det, -sqrt_det)) / A;
+
+ float2 isValid = step(float2(-radius1, -radius1), t * diff.z);
+
+ if (max(isValid.x, isValid.y) <= 0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ float upper_t = lerp(t.y, t.x, isValid.x);
+
+ float4 output = tex.Sample(aSampler, float2(upper_t, 0.5));
+ // Premultiply
+ output.rgb *= output.a;
+ // Multiply the output color by the input mask for the operation.
+ output *= mask.Sample(sMaskSampler, In.MaskTexCoord).a;
+ return output;
+};
+
+float4 SampleRadialGradientA0PS( VS_RADIAL_OUTPUT In, uniform sampler aSampler ) : SV_Target
+{
+ // This simpler shader is used for the degenerate case where A is 0,
+ // i.e. we're actually solving a linear equation.
+
+ float2 p = In.PixelCoord;
+ float3 dp = float3(p - center1, radius1);
+
+ // dpx * dcx + dpy * dcy + r * dr
+ float B = dot(dp, diff);
+
+ float C = pow(dp.x, 2) + pow(dp.y, 2) - pow(radius1, 2);
+
+ float t = 0.5 * C / B;
+
+ if (-radius1 >= t * diff.z) {
+ return float4(0, 0, 0, 0);
+ }
+
+ float4 output = tex.Sample(aSampler, float2(t, 0.5));
+ // Premultiply
+ output.rgb *= output.a;
+ // Multiply the output color by the input mask for the operation.
+ output *= mask.Sample(sMaskSampler, In.MaskTexCoord).a;
+ return output;
+};
+
+float4 SampleShadowHPS( VS_OUTPUT In) : SV_Target
+{
+ float outputStrength = 0;
+
+ outputStrength += BlurWeights[0].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[0].x, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[0].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[0].y, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[0].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[0].z, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[0].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[0].w, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[1].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[1].x, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[1].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[1].y, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[1].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[1].z, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[1].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[1].w, In.TexCoord.y)).a;
+ outputStrength += BlurWeights[2].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x + BlurOffsetsH[2].x, In.TexCoord.y)).a;
+
+ return ShadowColor * outputStrength;
+};
+
+float4 SampleShadowVPS( VS_OUTPUT In) : SV_Target
+{
+ float4 outputColor = float4(0, 0, 0, 0);
+
+ outputColor += BlurWeights[0].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].x));
+ outputColor += BlurWeights[0].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].y));
+ outputColor += BlurWeights[0].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].z));
+ outputColor += BlurWeights[0].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].w));
+ outputColor += BlurWeights[1].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].x));
+ outputColor += BlurWeights[1].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].y));
+ outputColor += BlurWeights[1].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].z));
+ outputColor += BlurWeights[1].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].w));
+ outputColor += BlurWeights[2].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[2].x));
+
+ return outputColor;
+};
+
+float4 SampleMaskShadowVPS( VS_OUTPUT In) : SV_Target
+{
+ float4 outputColor = float4(0, 0, 0, 0);
+
+ outputColor += BlurWeights[0].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].x));
+ outputColor += BlurWeights[0].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].y));
+ outputColor += BlurWeights[0].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].z));
+ outputColor += BlurWeights[0].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].w));
+ outputColor += BlurWeights[1].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].x));
+ outputColor += BlurWeights[1].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].y));
+ outputColor += BlurWeights[1].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].z));
+ outputColor += BlurWeights[1].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].w));
+ outputColor += BlurWeights[2].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[2].x));
+
+ return outputColor * mask.Sample(sMaskSampler, In.MaskTexCoord).a;
+};
+
+PS_TEXT_OUTPUT SampleTextTexturePS( VS_OUTPUT In) : SV_Target
+{
+ PS_TEXT_OUTPUT output;
+ output.color = float4(TextColor.r, TextColor.g, TextColor.b, 1.0);
+ output.alpha.rgba = tex.Sample(sSampler, In.TexCoord).bgrg * TextColor.a;
+ return output;
+};
+
+PS_TEXT_OUTPUT SampleTextTexturePSMasked( VS_OUTPUT In) : SV_Target
+{
+ PS_TEXT_OUTPUT output;
+
+ float maskValue = mask.Sample(sMaskSampler, In.MaskTexCoord).a;
+
+ output.color = float4(TextColor.r, TextColor.g, TextColor.b, 1.0);
+ output.alpha.rgba = tex.Sample(sSampler, In.TexCoord).bgrg * TextColor.a * maskValue;
+
+ return output;
+};
+
+technique10 SampleTexture
+{
+ pass P0
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTexturePS()));
+ }
+}
+
+technique10 SampleTextureForSeparableBlending_1
+{
+ pass P0
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleBlendTextureSeparablePS_1()));
+ }
+}
+
+technique10 SampleTextureForSeparableBlending_2
+{
+ pass P0
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleBlendTextureSeparablePS_2()));
+ }
+}
+
+technique10 SampleTextureForNonSeparableBlending
+{
+ pass P0
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleBlendTextureNonSeparablePS()));
+ }
+}
+
+technique10 SampleRadialGradient
+{
+ pass APos
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientPS( sSampler )));
+ }
+ pass A0
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientA0PS( sSampler )));
+ }
+ pass APosWrap
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientPS( sWrapSampler )));
+ }
+ pass A0Wrap
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientA0PS( sWrapSampler )));
+ }
+ pass APosMirror
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientPS( sMirrorSampler )));
+ }
+ pass A0Mirror
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientA0PS( sMirrorSampler )));
+ }
+}
+
+technique10 SampleMaskedTexture
+{
+ pass P0
+ {
+ SetRasterizerState(TextureRast);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleMaskTexturePS()));
+ }
+}
+
+technique10 SampleTextureWithShadow
+{
+ // Horizontal pass
+ pass P0
+ {
+ SetRasterizerState(TextureRast);
+ SetBlendState(ShadowBlendH, float4(1.0f, 1.0f, 1.0f, 1.0f), 0xffffffff);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleShadowHPS()));
+ }
+ // Vertical pass
+ pass P1
+ {
+ SetRasterizerState(TextureRast);
+ SetBlendState(ShadowBlendV, float4(1.0f, 1.0f, 1.0f, 1.0f), 0xffffffff);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleShadowVPS()));
+ }
+ // Vertical pass - used when using a mask
+ pass P2
+ {
+ SetRasterizerState(TextureRast);
+ SetBlendState(ShadowBlendV, float4(1.0f, 1.0f, 1.0f, 1.0f), 0xffffffff);
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleMaskShadowVPS()));
+ }
+}
+
+technique10 SampleTextTexture
+{
+ pass Unmasked
+ {
+ SetRasterizerState(TextureRast);
+ SetBlendState(bTextBlend, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTextTexturePS()));
+ }
+ pass Masked
+ {
+ SetRasterizerState(TextureRast);
+ SetBlendState(bTextBlend, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTextTexturePSMasked()));
+ }
+}
+
diff --git a/gfx/2d/ShadersD2D.h b/gfx/2d/ShadersD2D.h
new file mode 100644
index 000000000..9cc65a09b
--- /dev/null
+++ b/gfx/2d/ShadersD2D.h
@@ -0,0 +1,16012 @@
+#if 0
+//
+// FX Version: fx_4_0
+// Child effect (requires effect pool): false
+//
+// 4 local buffer(s)
+//
+cbuffer $Globals
+{
+ uint blendop; // Offset: 0, size: 4
+}
+
+cbuffer cb0
+{
+ float4 QuadDesc; // Offset: 0, size: 16
+ float4 TexCoords; // Offset: 16, size: 16
+ float4 MaskTexCoords; // Offset: 32, size: 16
+ float4 TextColor; // Offset: 48, size: 16
+}
+
+cbuffer cb1
+{
+ float4 BlurOffsetsH[3]; // Offset: 0, size: 48
+ float4 BlurOffsetsV[3]; // Offset: 48, size: 48
+ float4 BlurWeights[3]; // Offset: 96, size: 48
+ float4 ShadowColor; // Offset: 144, size: 16
+}
+
+cbuffer cb2
+{
+ float3x3 DeviceSpaceToUserSpace; // Offset: 0, size: 44
+ float2 dimensions; // Offset: 48, size: 8
+ float3 diff; // Offset: 64, size: 12
+ float2 center1; // Offset: 80, size: 8
+ float A; // Offset: 88, size: 4
+ float radius1; // Offset: 92, size: 4
+ float sq_radius1; // Offset: 96, size: 4
+}
+
+//
+// 13 local object(s)
+//
+Texture2D tex;
+Texture2D bcktex;
+Texture2D mask;
+SamplerState sSampler
+{
+ Filter = uint(MIN_MAG_MIP_LINEAR /* 21 */);
+ Texture = tex;
+ AddressU = uint(CLAMP /* 3 */);
+ AddressV = uint(CLAMP /* 3 */);
+};
+SamplerState sBckSampler
+{
+ Filter = uint(MIN_MAG_MIP_LINEAR /* 21 */);
+ Texture = bcktex;
+ AddressU = uint(CLAMP /* 3 */);
+ AddressV = uint(CLAMP /* 3 */);
+};
+SamplerState sWrapSampler
+{
+ Filter = uint(MIN_MAG_MIP_LINEAR /* 21 */);
+ Texture = tex;
+ AddressU = uint(WRAP /* 1 */);
+ AddressV = uint(WRAP /* 1 */);
+};
+SamplerState sMirrorSampler
+{
+ Filter = uint(MIN_MAG_MIP_LINEAR /* 21 */);
+ Texture = tex;
+ AddressU = uint(MIRROR /* 2 */);
+ AddressV = uint(MIRROR /* 2 */);
+};
+SamplerState sMaskSampler
+{
+ Filter = uint(MIN_MAG_MIP_LINEAR /* 21 */);
+ Texture = mask;
+ AddressU = uint(CLAMP /* 3 */);
+ AddressV = uint(CLAMP /* 3 */);
+};
+SamplerState sShadowSampler
+{
+ Filter = uint(MIN_MAG_MIP_LINEAR /* 21 */);
+ Texture = tex;
+ AddressU = uint(BORDER /* 4 */);
+ AddressV = uint(BORDER /* 4 */);
+ BorderColor = float4(0, 0, 0, 0);
+};
+RasterizerState TextureRast
+{
+ ScissorEnable = bool(TRUE /* 1 */);
+ CullMode = uint(NONE /* 1 */);
+};
+BlendState ShadowBlendH
+{
+ BlendEnable[0] = bool(FALSE /* 0 */);
+ RenderTargetWriteMask[0] = byte(0x0f);
+};
+BlendState ShadowBlendV
+{
+ BlendEnable[0] = bool(TRUE /* 1 */);
+ SrcBlend[0] = uint(ONE /* 2 */);
+ DestBlend[0] = uint(INV_SRC_ALPHA /* 6 */);
+ BlendOp[0] = uint(ADD /* 1 */);
+ SrcBlendAlpha[0] = uint(ONE /* 2 */);
+ DestBlendAlpha[0] = uint(INV_SRC_ALPHA /* 6 */);
+ BlendOpAlpha[0] = uint(ADD /* 1 */);
+ RenderTargetWriteMask[0] = byte(0x0f);
+};
+BlendState bTextBlend
+{
+ AlphaToCoverageEnable = bool(FALSE /* 0 */);
+ BlendEnable[0] = bool(TRUE /* 1 */);
+ SrcBlend[0] = uint(SRC1_COLOR /* 16 */);
+ DestBlend[0] = uint(INV_SRC1_COLOR /* 17 */);
+ BlendOp[0] = uint(ADD /* 1 */);
+ SrcBlendAlpha[0] = uint(SRC1_ALPHA /* 18 */);
+ DestBlendAlpha[0] = uint(INV_SRC1_ALPHA /* 19 */);
+ BlendOpAlpha[0] = uint(ADD /* 1 */);
+ RenderTargetWriteMask[0] = byte(0x0f);
+};
+
+//
+// 8 technique(s)
+//
+technique10 SampleTexture
+{
+ pass P0
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // tex texture float4 2d 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ dcl t0
+ dcl_2d s0
+ texld r0, t0, s0
+ mov oC0, r0
+
+ // approximately 2 instruction slots used (1 texture, 1 arithmetic)
+ ps_4_0
+ dcl_sampler s0, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ sample o0.xyzw, v1.xyxx, t0.xyzw, s0
+ ret
+ // Approximately 2 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleTextureForSeparableBlending_1
+{
+ pass P0
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer $Globals
+ // {
+ //
+ // uint blendop; // Offset: 0 Size: 4
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sBckSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // bcktex texture float4 2d 1 1
+ // $Globals cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 0 1 (UINT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c1, -1, -2, -3, -4
+ def c2, 1, 0, 0.5, -2
+ def c3, -5, 0, 0, 0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mov r0.w, c0.x
+ add r0.x, r0.w, c3.x
+ mul r0.x, r0.x, r0.x
+ 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
+ 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, r4.w, r0.w
+ cmp r2.xyz, -r0.w, r0, r2
+ mov oC0, r2
+
+ // approximately 56 instruction slots used (2 texture, 54 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[1], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_temps 7
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, v1.xyxx, t1.xyzw, s1
+ eq r2.x, r0.w, l(0.000000)
+ eq r2.y, r1.w, l(0.000000)
+ or r2.x, r2.y, r2.x
+ if_nz r2.x
+ mov o0.xyzw, r0.xyzw
+ ret
+ endif
+ div r0.xyz, r0.xyzx, r0.wwww
+ div r1.xyz, r1.xyzx, r1.wwww
+ ieq r2.x, cb0[0].x, l(1)
+ if_nz r2.x
+ mul r2.xyz, r0.xyzx, r1.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(2)
+ if_nz r2.w
+ add r3.xyz, r0.xyzx, r1.xyzx
+ mad r2.xyz, -r0.xyzx, r1.xyzx, r3.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(3)
+ if_nz r2.w
+ ge r3.xyz, l(0.500000, 0.500000, 0.500000, 0.000000), r1.xyzx
+ add r4.xyz, r1.xyzx, r1.xyzx
+ mul r4.xyz, r0.xyzx, r4.xyzx
+ mad r5.xyz, r1.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), l(-1.000000, -1.000000, -1.000000, 0.000000)
+ add r6.xyz, -r0.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ add r5.xyz, -r5.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ mad r5.xyz, -r6.xyzx, r5.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ movc r2.xyz, r3.xyzx, r4.xyzx, r5.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(4)
+ if_nz r2.w
+ min r2.xyz, r0.xyzx, r1.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(5)
+ 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
+ endif
+ add r1.x, -r1.w, l(1.000000)
+ mul r1.yzw, r1.wwww, r2.xxyz
+ mad r0.xyz, r1.xxxx, r0.xyzx, r1.yzwy
+ mul o0.xyz, r0.wwww, r0.xyzx
+ mov o0.w, r0.w
+ ret
+ // Approximately 57 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleTextureForSeparableBlending_2
+{
+ pass P0
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer $Globals
+ // {
+ //
+ // uint blendop; // Offset: 0 Size: 4
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sBckSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // bcktex texture float4 2d 1 1
+ // $Globals cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 0 1 (UINT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c1, -7, -8, -9, -10
+ 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
+ mov r0.w, c0.x
+ add r0, r0.w, c1
+ mul r0, r0, r0
+ texld r1, t0, s0
+ texld r2, t0, s1
+ rcp r3.w, r2.w
+ mad r3.xy, r2.yzzw, -r3.w, c2.w
+ mul r4.xyz, r2, r3.w
+ 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
+ cmp r4.w, r3.x, r5.y, r4.w
+ 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.y, c3.z
+ mad r4.w, r7.y, r4.w, r4.y
+ 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
+ rsq r4.w, r4.z
+ rcp r4.w, r4.w
+ cmp r4.w, r3.y, r5.z, r4.w
+ mad r4.w, r2.z, -r3.w, r4.w
+ mad r4.w, r7.z, r4.w, r4.z
+ cmp r11.z, r8.z, r10.z, r4.w
+ rsq r4.w, r4.x
+ 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 r3.yzw, -r7.xxyz, c2.x
+ mad r3.yzw, r9.xxyz, -r3, c2.x
+ cmp r11.x, r8.x, r10.x, r4.w
+ 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
+ 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
+ mul r0.xyz, r1.w, r3
+ mul r0.w, r1.w, r1.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
+
+ // approximately 78 instruction slots used (2 texture, 76 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[1], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_temps 7
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, v1.xyxx, t1.xyzw, s1
+ eq r2.x, r0.w, l(0.000000)
+ eq r2.y, r1.w, l(0.000000)
+ or r2.x, r2.y, r2.x
+ if_nz r2.x
+ mov o0.xyzw, r0.xyzw
+ ret
+ endif
+ div r0.xyz, r0.xyzx, r0.wwww
+ div r1.xyz, r1.xyzx, r1.wwww
+ ieq r2.x, cb0[0].x, l(7)
+ if_nz r2.x
+ 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
+ ge r3.xyz, l(0.500000, 0.500000, 0.500000, 0.000000), r0.xyzx
+ add r4.xyz, r0.xyzx, r0.xyzx
+ mul r4.xyz, r1.xyzx, r4.xyzx
+ mad r5.xyz, r0.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), l(-1.000000, -1.000000, -1.000000, 0.000000)
+ add r6.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ add r5.xyz, -r5.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ mad r5.xyz, -r6.xyzx, r5.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ movc r2.xyz, r3.xyzx, r4.xyzx, r5.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(9)
+ if_nz r2.w
+ ge r3.xyz, l(0.250000, 0.250000, 0.250000, 0.000000), r1.xyzx
+ mad r4.xyz, r1.xyzx, l(16.000000, 16.000000, 16.000000, 0.000000), l(-12.000000, -12.000000, -12.000000, 0.000000)
+ mad r4.xyz, r4.xyzx, r1.xyzx, l(4.000000, 4.000000, 4.000000, 0.000000)
+ mul r4.xyz, r1.xyzx, r4.xyzx
+ sqrt r5.xyz, r1.xyzx
+ movc r3.xyz, r3.xyzx, r4.xyzx, r5.xyzx
+ ge r4.xyz, l(0.500000, 0.500000, 0.500000, 0.000000), r0.xyzx
+ mad r5.xyz, -r0.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), l(1.000000, 1.000000, 1.000000, 0.000000)
+ mul r5.xyz, r1.xyzx, r5.xyzx
+ add r6.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ mad r5.xyz, -r5.xyzx, r6.xyzx, r1.xyzx
+ mad r6.xyz, r0.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), l(-1.000000, -1.000000, -1.000000, 0.000000)
+ add r3.xyz, -r1.xyzx, r3.xyzx
+ mad r3.xyz, r6.xyzx, r3.xyzx, r1.xyzx
+ movc r2.xyz, r4.xyzx, r5.xyzx, r3.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(10)
+ add r3.xyz, r0.xyzx, -r1.xyzx
+ add r4.xyz, r0.xyzx, r1.xyzx
+ mul r1.xyz, r0.xyzx, r1.xyzx
+ mad r1.xyz, -r1.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), r4.xyzx
+ movc r2.xyz, r2.wwww, |r3.xyzx|, r1.xyzx
+ endif
+ endif
+ endif
+ add r1.x, -r1.w, l(1.000000)
+ mul r1.yzw, r1.wwww, r2.xxyz
+ mad r0.xyz, r1.xxxx, r0.xyzx, r1.yzwy
+ mul o0.xyz, r0.wwww, r0.xyzx
+ mov o0.w, r0.w
+ ret
+ // Approximately 66 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleTextureForNonSeparableBlending
+{
+ pass P0
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer $Globals
+ // {
+ //
+ // uint blendop; // Offset: 0 Size: 4
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sBckSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // bcktex texture float4 2d 1 1
+ // $Globals cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 0 1 (UINT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c1, -12, -13, -14, 0
+ def c2, 1, 0, 0, 0
+ def c3, 0.300000012, 0.589999974, 0.109999999, 0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mov r0.y, c2.y
+ mov r1.y, c2.y
+ mov r2.z, c2.y
+ texld r3, t0, s1
+ texld r4, t0, s0
+ rcp r0.w, r4.w
+ mul r5.xyz, r0.w, r4
+ mad r6.xy, r4.yxzw, r0.w, -r5.zyzw
+ cmp r7.xy, r6.x, r5.yzzw, r5.zyzw
+ max r1.w, r5.x, r7.x
+ min r2.w, r7.y, r5.x
+ add r7.w, r1.w, -r2.w
+ rcp r1.w, r3.w
+ mul r8.xyz, r1.w, r3
+ mad r9.xy, r3.x, r1.w, -r8.zyzw
+ rcp r2.w, r9.y
+ mul r2.w, r2.w, r7.w
+ mad r10, r3.zyyz, r1.w, -r8.xxzy
+ mul r7.y, r2.w, r10.w
+ mov r9.zw, r10
+ cmp r1.xz, -r9.y, r9.yyww, r7.wyyw
+ rcp r2.w, r9.x
+ mul r2.w, r2.w, r7.w
+ mul r7.x, r2.w, r9.z
+ cmp r2.xy, -r9.x, r9.xzzw, r7.wxzw
+ cmp r1.xyz, r9.w, r1, r2
+ rcp r5.w, r9.w
+ mul r5.w, r5.w, r7.w
+ mul r7.z, r5.w, r9.y
+ cmp r0.xz, -r10.w, r9.yyww, r7.zyww
+ cmp r0.xyz, r10.x, r0, r1
+ mov r1.x, c2.y
+ mov r2.x, c2.y
+ mov r11.z, c2.y
+ rcp r2.w, r9.z
+ mul r2.w, r2.w, r7.w
+ mul r7.x, r2.w, r9.x
+ cmp r11.xy, -r10.z, r9.xzzw, r7.xwzw
+ rcp r2.w, r10.y
+ mul r2.w, r2.w, r7.w
+ mul r7.y, r2.w, r10.x
+ cmp r2.yz, -r10.y, r10.xyxw, r7.xwyw
+ cmp r2.xyz, r10.x, r2, r11
+ rcp r2.w, r10.x
+ mul r2.w, r2.w, r7.w
+ mul r7.z, r2.w, r10.y
+ cmp r1.yz, -r10.x, r10.xyxw, r7.xzww
+ cmp r1.xyz, r9.w, r1, r2
+ cmp r0.xyz, r10.y, r1, r0
+ cmp r1.xy, r9.z, r8.yzzw, r8.zyzw
+ dp3 r5.w, r0, c3
+ dp3 r1.z, r8, c3
+ add r5.w, -r5.w, r1.z
+ add r0.xyz, r0, r5.w
+ add r5.w, -r0.y, r0.x
+ cmp r2.xy, r5.w, r0.yxzw, r0
+ min r5.w, r0.z, r2.x
+ max r7.x, r2.y, r0.z
+ dp3 r2.x, r0, c3
+ add r2.y, -r5.w, r2.x
+ rcp r2.y, r2.y
+ add r7.yzw, r0.xxyz, -r2.x
+ mul r7.yzw, r2.x, r7
+ mad r2.yzw, r7, r2.y, r2.x
+ cmp r0.xyz, r5.w, r0, r2.yzww
+ add r2.yzw, -r2.x, r0.xxyz
+ add r5.w, -r2.x, c2.x
+ mul r2.yzw, r2, r5.w
+ add r5.w, -r2.x, r7.x
+ add r7.x, -r7.x, c2.x
+ rcp r5.w, r5.w
+ mad r2.xyz, r2.yzww, r5.w, r2.x
+ cmp r0.xyz, r7.x, r0, r2
+ dp3 r5.w, r5, c3
+ add r2.x, r1.z, -r5.w
+ add r5.w, -r1.z, r5.w
+ mad r2.yzw, r3.xxyz, r1.w, r5.w
+ mad r3.xyz, r4, r0.w, r2.x
+ mad r7, r4.zyzx, r0.w, -r5.xxyz
+ add r0.w, -r3.y, r3.x
+ cmp r8.yz, r0.w, r3.xyxw, r3.xxyw
+ min r0.w, r3.z, r8.y
+ max r1.w, r8.z, r3.z
+ dp3 r5.w, r3, c3
+ add r2.x, -r0.w, r5.w
+ rcp r2.x, r2.x
+ add r8.yzw, r3.xxyz, -r5.w
+ mul r8.yzw, r5.w, r8
+ mad r8.yzw, r8, r2.x, r5.w
+ cmp r3.xyz, r0.w, r3, r8.yzww
+ add r8.yzw, -r5.w, r3.xxyz
+ add r0.w, -r5.w, c2.x
+ mul r8.yzw, r0.w, r8
+ add r0.w, r1.w, -r5.w
+ add r1.w, -r1.w, c2.x
+ rcp r0.w, r0.w
+ mad r8.yzw, r8, r0.w, r5.w
+ cmp r3.xyz, r1.w, r3, r8.yzww
+ add r0.w, -r2.z, r2.y
+ cmp r8.yz, r0.w, r2.xzyw, r2
+ min r0.w, r2.w, r8.y
+ max r1.w, r8.z, r2.w
+ dp3 r5.w, r2.yzww, c3
+ add r2.x, -r0.w, r5.w
+ rcp r2.x, r2.x
+ add r8.yzw, r2, -r5.w
+ mul r8.yzw, r5.w, r8
+ mad r8.yzw, r8, r2.x, r5.w
+ cmp r2.xyz, r0.w, r2.yzww, r8.yzww
+ add r8.yzw, -r5.w, r2.xxyz
+ add r0.w, -r5.w, c2.x
+ mul r8.yzw, r0.w, r8
+ add r0.w, r1.w, -r5.w
+ add r1.w, -r1.w, c2.x
+ rcp r0.w, r0.w
+ mad r8.yzw, r8, r0.w, r5.w
+ cmp r2.xyz, r1.w, r2, r8.yzww
+ mov r0.w, c0.x
+ add r8.yzw, r0.w, c1.xxyz
+ mul r8.yzw, r8, r8
+ cmp r2.xyz, -r8.w, r3, r2
+ cmp r0.xyz, -r8.z, r0, r2
+ mov r2.y, c2.y
+ mov r3.y, c2.y
+ mov r9.z, c2.y
+ max r0.w, r8.x, r1.x
+ min r2.w, r1.y, r8.x
+ add r10.w, r0.w, -r2.w
+ rcp r0.w, r7.w
+ mul r0.w, r0.w, r10.w
+ mul r10.x, r0.w, r6.x
+ mov r6.zw, r7.xywz
+ cmp r9.xy, -r7.w, r6.zxzw, r10.wxzw
+ rcp r0.w, r6.y
+ mul r0.w, r0.w, r10.w
+ mul r10.y, r0.w, r7.z
+ cmp r3.xz, -r6.y, r6.yyww, r10.wyyw
+ cmp r1.xyw, r7.z, r3.xyzz, r9.xyzz
+ rcp r0.w, r7.z
+ mul r0.w, r0.w, r10.w
+ mul r10.z, r0.w, r6.y
+ cmp r2.xz, -r7.z, r6.yyww, r10.zyww
+ cmp r1.xyw, r7.x, r2.xyzz, r1
+ mov r2.x, c2.y
+ mov r3.z, c2.y
+ rcp r0.w, r6.x
+ mul r0.w, r0.w, r10.w
+ mul r10.x, r0.w, r7.w
+ cmp r3.xy, -r6.x, r6.zxzw, r10.xwzw
+ rcp r0.w, r7.y
+ mul r0.w, r0.w, r10.w
+ mul r10.y, r0.w, r7.x
+ cmp r2.yz, -r7.y, r7.xyxw, r10.xwyw
+ cmp r2.xyz, r7.x, r2, r3
+ mov r3.x, c2.y
+ rcp r0.w, r7.x
+ mul r0.w, r0.w, r10.w
+ mul r10.z, r0.w, r7.y
+ cmp r3.yz, -r7.x, r7.xyxw, r10.xzww
+ cmp r2.xyz, r7.z, r3, r2
+ cmp r1.xyw, r7.y, r2.xyzz, r1
+ dp3 r0.w, r1.xyww, c3
+ add r0.w, -r0.w, r1.z
+ add r1.xyz, r0.w, r1.xyww
+ add r0.w, -r1.y, r1.x
+ cmp r2.xy, r0.w, r1.yxzw, r1
+ min r0.w, r1.z, r2.x
+ max r5.w, r2.y, r1.z
+ dp3 r1.w, r1, c3
+ add r2.xyz, -r1.w, r1
+ mul r2.xyz, r1.w, r2
+ add r2.w, -r0.w, r1.w
+ rcp r2.w, r2.w
+ mad r2.xyz, r2, r2.w, r1.w
+ cmp r1.xyz, r0.w, r1, r2
+ add r2.xyz, -r1.w, r1
+ add r0.w, -r1.w, c2.x
+ mul r2.xyz, r0.w, r2
+ add r0.w, -r1.w, r5.w
+ add r2.w, -r5.w, c2.x
+ rcp r0.w, r0.w
+ mad r2.xyz, r2, r0.w, r1.w
+ cmp r1.xyz, r2.w, r1, r2
+ cmp r0.xyz, -r8.y, r1, r0
+ lrp r1.xyz, r3.w, r0, r5
+ mul r1.w, r3.w, r3.w
+ cmp r1.w, -r1.w, c2.x, c2.y
+ mul r0.xyz, r4.w, r1
+ mul r0.w, r4.w, r4.w
+ cmp r0.w, -r0.w, c2.x, c2.y
+ add r0.w, r1.w, r0.w
+ cmp r4.xyz, -r0.w, r0, r4
+ mov oC0, r4
+
+ // approximately 193 instruction slots used (2 texture, 191 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[1], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_temps 9
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, v1.xyxx, t1.xyzw, s1
+ eq r2.x, r0.w, l(0.000000)
+ eq r2.y, r1.w, l(0.000000)
+ or r2.x, r2.y, r2.x
+ if_nz r2.x
+ mov o0.xyzw, r0.xyzw
+ ret
+ endif
+ div r0.xyz, r0.xyzx, r0.wwww
+ div r1.xyz, r1.xyzx, r1.wwww
+ ieq r2.x, cb0[0].x, l(12)
+ if_nz r2.x
+ max r2.x, r1.z, r1.y
+ max r2.x, r1.x, r2.x
+ min r2.y, r1.z, r1.y
+ min r2.y, r1.x, r2.y
+ add r2.w, -r2.y, r2.x
+ ge r3.x, r0.y, r0.x
+ if_nz r3.x
+ add r3.xyzw, -r0.xxzz, r0.yzxy
+ lt r4.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r3.yxwy
+ div r5.xyz, r2.wwww, r3.yxwy
+ mul r2.xyz, r3.xyzx, r5.xyzx
+ movc r5.yz, r4.xxxx, r2.xxwx, r3.xxyx
+ ge r4.xw, r0.zzzz, r0.yyyx
+ movc r6.yz, r4.yyyy, r2.wwyw, r3.xxyx
+ movc r3.xy, r4.zzzz, r2.zwzz, r3.zwzz
+ mov r6.x, l(0)
+ mov r3.z, l(0)
+ movc r3.xyz, r4.wwww, r6.xyzx, r3.xyzx
+ mov r5.x, l(0)
+ movc r3.xyz, r4.xxxx, r5.xyzx, r3.xyzx
+ else
+ add r4.xyzw, -r0.yyzz, r0.xzyx
+ lt r5.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r4.yxwy
+ div r6.xyz, r2.wwww, r4.yxwy
+ mul r2.xyz, r4.xyzx, r6.xyzx
+ movc r6.xz, r5.xxxx, r2.xxwx, r4.xxyx
+ ge r5.xw, r0.zzzz, r0.xxxy
+ movc r7.xz, r5.yyyy, r2.wwyw, r4.xxyx
+ movc r2.xy, r5.zzzz, r2.wzww, r4.wzww
+ mov r7.y, l(0)
+ mov r2.z, l(0)
+ movc r2.xyz, r5.wwww, r7.xyzx, r2.xyzx
+ mov r6.y, l(0)
+ movc r3.xyz, r5.xxxx, r6.xyzx, r2.xyzx
+ endif
+ dp3 r2.x, r1.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ dp3 r2.y, r3.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ add r2.x, -r2.y, r2.x
+ add r2.xyz, r2.xxxx, r3.xyzx
+ dp3 r2.w, r2.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ min r3.x, r2.y, r2.x
+ min r3.x, r2.z, r3.x
+ max r3.y, r2.y, r2.x
+ max r3.y, r2.z, r3.y
+ lt r3.z, r3.x, l(0.000000)
+ add r4.xyz, -r2.wwww, r2.xyzx
+ mul r4.xyz, r2.wwww, r4.xyzx
+ add r3.x, r2.w, -r3.x
+ div r4.xyz, r4.xyzx, r3.xxxx
+ add r4.xyz, r2.wwww, r4.xyzx
+ movc r2.xyz, r3.zzzz, r4.xyzx, r2.xyzx
+ lt r3.x, l(1.000000), r3.y
+ add r4.xyz, -r2.wwww, r2.xyzx
+ add r3.z, -r2.w, l(1.000000)
+ mul r4.xyz, r3.zzzz, r4.xyzx
+ add r3.y, -r2.w, r3.y
+ div r3.yzw, r4.xxyz, r3.yyyy
+ add r3.yzw, r2.wwww, r3.yyzw
+ movc r2.xyz, r3.xxxx, r3.yzwy, r2.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(13)
+ if_nz r2.w
+ max r2.w, r0.z, r0.y
+ max r2.w, r0.x, r2.w
+ min r3.x, r0.z, r0.y
+ min r3.x, r0.x, r3.x
+ add r3.w, r2.w, -r3.x
+ ge r2.w, r1.y, r1.x
+ if_nz r2.w
+ add r4.xyzw, -r1.xxzz, r1.yzxy
+ lt r5.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r4.yxwy
+ div r6.xyz, r3.wwww, r4.yxwy
+ mul r3.xyz, r4.xyzx, r6.xyzx
+ movc r6.yz, r5.xxxx, r3.xxwx, r4.xxyx
+ ge r5.xw, r1.zzzz, r1.yyyx
+ movc r7.yz, r5.yyyy, r3.wwyw, r4.xxyx
+ movc r4.xy, r5.zzzz, r3.zwzz, r4.zwzz
+ mov r7.x, l(0)
+ mov r4.z, l(0)
+ movc r4.xyz, r5.wwww, r7.xyzx, r4.xyzx
+ mov r6.x, l(0)
+ movc r4.xyz, r5.xxxx, r6.xyzx, r4.xyzx
+ else
+ add r5.xyzw, -r1.yyzz, r1.xzyx
+ lt r6.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r5.yxwy
+ div r7.xyz, r3.wwww, r5.yxwy
+ mul r3.xyz, r5.xyzx, r7.xyzx
+ movc r7.xz, r6.xxxx, r3.xxwx, r5.xxyx
+ ge r6.xw, r1.zzzz, r1.xxxy
+ movc r8.xz, r6.yyyy, r3.wwyw, r5.xxyx
+ movc r3.xy, r6.zzzz, r3.wzww, r5.wzww
+ mov r8.y, l(0)
+ mov r3.z, l(0)
+ movc r3.xyz, r6.wwww, r8.xyzx, r3.xyzx
+ mov r7.y, l(0)
+ movc r4.xyz, r6.xxxx, r7.xyzx, r3.xyzx
+ endif
+ dp3 r2.w, r1.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ dp3 r3.x, r4.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ add r2.w, r2.w, -r3.x
+ add r3.xyz, r2.wwww, r4.xyzx
+ dp3 r2.w, r3.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ min r3.w, r3.y, r3.x
+ min r3.w, r3.z, r3.w
+ max r4.x, r3.y, r3.x
+ max r4.x, r3.z, r4.x
+ lt r4.y, r3.w, l(0.000000)
+ add r5.xyz, -r2.wwww, r3.xyzx
+ mul r5.xyz, r2.wwww, r5.xyzx
+ add r3.w, r2.w, -r3.w
+ div r5.xyz, r5.xyzx, r3.wwww
+ add r5.xyz, r2.wwww, r5.xyzx
+ movc r3.xyz, r4.yyyy, r5.xyzx, r3.xyzx
+ lt r3.w, l(1.000000), r4.x
+ add r4.yzw, -r2.wwww, r3.xxyz
+ add r5.x, -r2.w, l(1.000000)
+ mul r4.yzw, r4.yyzw, r5.xxxx
+ add r4.x, -r2.w, r4.x
+ div r4.xyz, r4.yzwy, r4.xxxx
+ add r4.xyz, r2.wwww, r4.xyzx
+ movc r2.xyz, r3.wwww, r4.xyzx, r3.xyzx
+ else
+ ieq r2.w, cb0[0].x, l(14)
+ if_nz r2.w
+ dp3 r2.w, r1.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ dp3 r3.x, r0.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ add r2.w, r2.w, -r3.x
+ add r3.xyz, r0.xyzx, r2.wwww
+ dp3 r2.w, r3.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ min r3.w, r3.y, r3.x
+ min r3.w, r3.z, r3.w
+ max r4.x, r3.y, r3.x
+ max r4.x, r3.z, r4.x
+ lt r4.y, r3.w, l(0.000000)
+ add r5.xyz, -r2.wwww, r3.xyzx
+ mul r5.xyz, r2.wwww, r5.xyzx
+ add r3.w, r2.w, -r3.w
+ div r5.xyz, r5.xyzx, r3.wwww
+ add r5.xyz, r2.wwww, r5.xyzx
+ movc r3.xyz, r4.yyyy, r5.xyzx, r3.xyzx
+ lt r3.w, l(1.000000), r4.x
+ add r4.yzw, -r2.wwww, r3.xxyz
+ add r5.x, -r2.w, l(1.000000)
+ mul r4.yzw, r4.yyzw, r5.xxxx
+ add r4.x, -r2.w, r4.x
+ div r4.xyz, r4.yzwy, r4.xxxx
+ add r4.xyz, r2.wwww, r4.xyzx
+ movc r2.xyz, r3.wwww, r4.xyzx, r3.xyzx
+ else
+ dp3 r2.w, r0.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ dp3 r3.x, r1.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ add r2.w, r2.w, -r3.x
+ add r1.xyz, r1.xyzx, r2.wwww
+ dp3 r2.w, r1.xyzx, l(0.300000, 0.590000, 0.110000, 0.000000)
+ min r3.x, r1.y, r1.x
+ min r3.x, r1.z, r3.x
+ max r3.y, r1.y, r1.x
+ max r3.y, r1.z, r3.y
+ lt r3.z, r3.x, l(0.000000)
+ add r4.xyz, r1.xyzx, -r2.wwww
+ mul r4.xyz, r2.wwww, r4.xyzx
+ add r3.x, r2.w, -r3.x
+ div r4.xyz, r4.xyzx, r3.xxxx
+ add r4.xyz, r2.wwww, r4.xyzx
+ movc r1.xyz, r3.zzzz, r4.xyzx, r1.xyzx
+ lt r3.x, l(1.000000), r3.y
+ add r4.xyz, -r2.wwww, r1.xyzx
+ add r3.z, -r2.w, l(1.000000)
+ mul r4.xyz, r3.zzzz, r4.xyzx
+ add r3.y, -r2.w, r3.y
+ div r3.yzw, r4.xxyz, r3.yyyy
+ add r3.yzw, r2.wwww, r3.yyzw
+ movc r2.xyz, r3.xxxx, r3.yzwy, r1.xyzx
+ endif
+ endif
+ endif
+ add r1.x, -r1.w, l(1.000000)
+ mul r1.yzw, r1.wwww, r2.xxyz
+ mad r0.xyz, r1.xxxx, r0.xyzx, r1.yzwy
+ mul o0.xyz, r0.wwww, r0.xyzx
+ mov o0.w, r0.w
+ ret
+ // Approximately 195 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleRadialGradient
+{
+ pass APos
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44
+ // float2 dimensions; // Offset: 48 Size: 8
+ // float3 diff; // Offset: 64 Size: 12 [unused]
+ // float2 center1; // Offset: 80 Size: 8 [unused]
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4 [unused]
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ // cb2 cbuffer NA NA 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 1 ( FLT, FLT, FLT, FLT)
+ // c2 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ // c3 cb1 0 2 ( FLT, FLT, FLT, FLT)
+ // c5 cb1 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c6, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add r0.z, r0.x, c6.x
+ mul r0.z, r0.z, c5.x
+ mul r1.x, r0.z, c6.y
+ add r0.z, -r0.y, c6.x
+ add oPos.xy, r0, c0
+ mul r0.x, r0.z, c5.y
+ mul r1.y, r0.x, c6.y
+ mov r1.z, c6.x
+ dp3 oT0.w, r1, c3
+ dp3 oT0.z, r1, c4
+ mov oPos.zw, c6.xyzx
+
+ // approximately 13 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_constantbuffer cb1[4], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ dcl_temps 2
+ mov o0.zw, l(0,0,0,1.000000)
+ mad r0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.xy, r0.xyxx
+ add r0.x, r0.x, l(1.000000)
+ add r0.y, -r0.y, l(1.000000)
+ mul r0.xy, r0.xyxx, cb1[3].xyxx
+ mul r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
+ mov r1.z, l(1.000000)
+ dp3 o1.z, r1.xyzx, cb1[0].xyzx
+ dp3 o1.w, r1.xyzx, cb1[1].xyzx
+ mad o1.xy, v0.xyxx, cb0[2].zwzz, cb0[2].xyxx
+ ret
+ // Approximately 12 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44 [unused]
+ // float2 dimensions; // Offset: 48 Size: 8 [unused]
+ // float3 diff; // Offset: 64 Size: 12
+ // float2 center1; // Offset: 80 Size: 8
+ // float A; // Offset: 88 Size: 4
+ // float radius1; // Offset: 92 Size: 4
+ // float sq_radius1; // Offset: 96 Size: 4
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb2 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 4 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c3, 0.5, 0, 0, 0
+ def c4, 1, -1, 0, -0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ add r0.xy, t0.wzzw, -c1
+ dp2add r0.w, r0, r0, -c2.x
+ mul r0.w, r0.w, c1.z
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ mad r0.y, r0.x, r0.x, -r0.w
+ abs r0.z, r0.y
+ rsq r0.z, r0.z
+ rcp r1.x, r0.z
+ mov r1.yz, -r1.x
+ add r0.xzw, r0.x, r1.xyyz
+ rcp r1.x, c1.z
+ 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 r0.xzw, r1.xyyz, c4.xyxy, c4.zyzw
+ mov r2.y, c3.x
+ texld r1, t0, s1
+ texld r2, r2, s0
+ mul r2.xyz, r2.w, r2
+ mul r1, r1.w, r2
+ add r0.w, r0.w, r0.x
+ cmp r0.x, r0.w, r0.x, r0.z
+ cmp r1, -r0.x, c4.z, r1
+ cmp r0, r0.y, r1, c4.z
+ mov oC0, r0
+
+ // approximately 28 instruction slots used (2 texture, 26 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[7], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 3
+ add r0.xy, v1.zwzz, -cb0[5].xyxx
+ mov r0.z, cb0[5].w
+ dp3 r0.z, r0.xyzx, cb0[4].xyzx
+ dp2 r0.x, r0.xyxx, r0.xyxx
+ add r0.x, r0.x, -cb0[6].x
+ mul r0.x, r0.x, cb0[5].z
+ mad r0.x, r0.z, r0.z, -r0.x
+ lt r0.y, r0.x, l(0.000000)
+ sqrt r1.x, |r0.x|
+ mov r1.y, -r1.x
+ add r0.xz, r0.zzzz, r1.xxyx
+ div r0.xz, r0.xxzx, cb0[5].zzzz
+ mul r1.xy, r0.xzxx, cb0[4].zzzz
+ ge r1.xy, r1.xyxx, -cb0[5].wwww
+ and r1.xy, r1.xyxx, l(0x3f800000, 0x3f800000, 0, 0)
+ add r0.x, -r0.z, r0.x
+ mad r2.x, r1.x, r0.x, r0.z
+ mov r2.y, l(0.500000)
+ sample r2.xyzw, r2.xyxx, t0.xyzw, s0
+ if_nz r0.y
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ max r0.x, r1.y, r1.x
+ ge r0.x, l(0.000000), r0.x
+ if_nz r0.x
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ mul r2.xyz, r2.wwww, r2.xyzx
+ sample r0.xyzw, v1.xyxx, t1.xyzw, s1
+ mul o0.xyzw, r0.wwww, r2.xyzw
+ ret
+ // Approximately 33 instruction slots used
+
+ };
+ }
+
+ pass A0
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44
+ // float2 dimensions; // Offset: 48 Size: 8
+ // float3 diff; // Offset: 64 Size: 12 [unused]
+ // float2 center1; // Offset: 80 Size: 8 [unused]
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4 [unused]
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ // cb2 cbuffer NA NA 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 1 ( FLT, FLT, FLT, FLT)
+ // c2 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ // c3 cb1 0 2 ( FLT, FLT, FLT, FLT)
+ // c5 cb1 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c6, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add r0.z, r0.x, c6.x
+ mul r0.z, r0.z, c5.x
+ mul r1.x, r0.z, c6.y
+ add r0.z, -r0.y, c6.x
+ add oPos.xy, r0, c0
+ mul r0.x, r0.z, c5.y
+ mul r1.y, r0.x, c6.y
+ mov r1.z, c6.x
+ dp3 oT0.w, r1, c3
+ dp3 oT0.z, r1, c4
+ mov oPos.zw, c6.xyzx
+
+ // approximately 13 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_constantbuffer cb1[4], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ dcl_temps 2
+ mov o0.zw, l(0,0,0,1.000000)
+ mad r0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.xy, r0.xyxx
+ add r0.x, r0.x, l(1.000000)
+ add r0.y, -r0.y, l(1.000000)
+ mul r0.xy, r0.xyxx, cb1[3].xyxx
+ mul r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
+ mov r1.z, l(1.000000)
+ dp3 o1.z, r1.xyzx, cb1[0].xyzx
+ dp3 o1.w, r1.xyzx, cb1[1].xyzx
+ mad o1.xy, v0.xyxx, cb0[2].zwzz, cb0[2].xyxx
+ ret
+ // Approximately 12 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44 [unused]
+ // float2 dimensions; // Offset: 48 Size: 8 [unused]
+ // float3 diff; // Offset: 64 Size: 12
+ // float2 center1; // Offset: 80 Size: 8
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb2 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 4 2 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c2, 0.5, 0, 0, 0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mul r0.w, c1.w, c1.w
+ add r0.xy, t0.wzzw, -c1
+ dp2add r0.w, r0, r0, -r0.w
+ mul r0.w, r0.w, c2.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, c2.x
+ texld r1, t0, s1
+ texld r2, r0, s0
+ mov r0.w, c1.w
+ mad r0.x, r0.x, -c0.z, -r0.w
+ mul r2.xyz, r2.w, r2
+ mul r1, r1.w, r2
+ cmp r0, r0.x, c2.y, r1
+ mov oC0, r0
+
+ // approximately 18 instruction slots used (2 texture, 16 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[6], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 2
+ add r0.xy, v1.zwzz, -cb0[5].xyxx
+ mov r0.z, cb0[5].w
+ dp3 r0.z, r0.xyzx, cb0[4].xyzx
+ dp2 r0.x, r0.xyxx, r0.xyxx
+ mad r0.x, -cb0[5].w, cb0[5].w, r0.x
+ mul r0.x, r0.x, l(0.500000)
+ div r0.x, r0.x, r0.z
+ mul r0.z, r0.x, cb0[4].z
+ ge r0.z, -cb0[5].w, r0.z
+ mov r0.y, l(0.500000)
+ sample r1.xyzw, r0.xyxx, t0.xyzw, s0
+ if_nz r0.z
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ mul r1.xyz, r1.wwww, r1.xyzx
+ sample r0.xyzw, v1.xyxx, t1.xyzw, s1
+ mul o0.xyzw, r0.wwww, r1.xyzw
+ ret
+ // Approximately 19 instruction slots used
+
+ };
+ }
+
+ pass APosWrap
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44
+ // float2 dimensions; // Offset: 48 Size: 8
+ // float3 diff; // Offset: 64 Size: 12 [unused]
+ // float2 center1; // Offset: 80 Size: 8 [unused]
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4 [unused]
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ // cb2 cbuffer NA NA 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 1 ( FLT, FLT, FLT, FLT)
+ // c2 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ // c3 cb1 0 2 ( FLT, FLT, FLT, FLT)
+ // c5 cb1 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c6, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add r0.z, r0.x, c6.x
+ mul r0.z, r0.z, c5.x
+ mul r1.x, r0.z, c6.y
+ add r0.z, -r0.y, c6.x
+ add oPos.xy, r0, c0
+ mul r0.x, r0.z, c5.y
+ mul r1.y, r0.x, c6.y
+ mov r1.z, c6.x
+ dp3 oT0.w, r1, c3
+ dp3 oT0.z, r1, c4
+ mov oPos.zw, c6.xyzx
+
+ // approximately 13 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_constantbuffer cb1[4], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ dcl_temps 2
+ mov o0.zw, l(0,0,0,1.000000)
+ mad r0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.xy, r0.xyxx
+ add r0.x, r0.x, l(1.000000)
+ add r0.y, -r0.y, l(1.000000)
+ mul r0.xy, r0.xyxx, cb1[3].xyxx
+ mul r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
+ mov r1.z, l(1.000000)
+ dp3 o1.z, r1.xyzx, cb1[0].xyzx
+ dp3 o1.w, r1.xyzx, cb1[1].xyzx
+ mad o1.xy, v0.xyxx, cb0[2].zwzz, cb0[2].xyxx
+ ret
+ // Approximately 12 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44 [unused]
+ // float2 dimensions; // Offset: 48 Size: 8 [unused]
+ // float3 diff; // Offset: 64 Size: 12
+ // float2 center1; // Offset: 80 Size: 8
+ // float A; // Offset: 88 Size: 4
+ // float radius1; // Offset: 92 Size: 4
+ // float sq_radius1; // Offset: 96 Size: 4
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sWrapSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb2 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 4 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c3, 0.5, 0, 0, 0
+ def c4, 1, -1, 0, -0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ add r0.xy, t0.wzzw, -c1
+ dp2add r0.w, r0, r0, -c2.x
+ mul r0.w, r0.w, c1.z
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ mad r0.y, r0.x, r0.x, -r0.w
+ abs r0.z, r0.y
+ rsq r0.z, r0.z
+ rcp r1.x, r0.z
+ mov r1.yz, -r1.x
+ add r0.xzw, r0.x, r1.xyyz
+ rcp r1.x, c1.z
+ 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 r0.xzw, r1.xyyz, c4.xyxy, c4.zyzw
+ mov r2.y, c3.x
+ texld r1, t0, s1
+ texld r2, r2, s0
+ mul r2.xyz, r2.w, r2
+ mul r1, r1.w, r2
+ add r0.w, r0.w, r0.x
+ cmp r0.x, r0.w, r0.x, r0.z
+ cmp r1, -r0.x, c4.z, r1
+ cmp r0, r0.y, r1, c4.z
+ mov oC0, r0
+
+ // approximately 28 instruction slots used (2 texture, 26 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[7], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 3
+ add r0.xy, v1.zwzz, -cb0[5].xyxx
+ mov r0.z, cb0[5].w
+ dp3 r0.z, r0.xyzx, cb0[4].xyzx
+ dp2 r0.x, r0.xyxx, r0.xyxx
+ add r0.x, r0.x, -cb0[6].x
+ mul r0.x, r0.x, cb0[5].z
+ mad r0.x, r0.z, r0.z, -r0.x
+ lt r0.y, r0.x, l(0.000000)
+ sqrt r1.x, |r0.x|
+ mov r1.y, -r1.x
+ add r0.xz, r0.zzzz, r1.xxyx
+ div r0.xz, r0.xxzx, cb0[5].zzzz
+ mul r1.xy, r0.xzxx, cb0[4].zzzz
+ ge r1.xy, r1.xyxx, -cb0[5].wwww
+ and r1.xy, r1.xyxx, l(0x3f800000, 0x3f800000, 0, 0)
+ add r0.x, -r0.z, r0.x
+ mad r2.x, r1.x, r0.x, r0.z
+ mov r2.y, l(0.500000)
+ sample r2.xyzw, r2.xyxx, t0.xyzw, s0
+ if_nz r0.y
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ max r0.x, r1.y, r1.x
+ ge r0.x, l(0.000000), r0.x
+ if_nz r0.x
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ mul r2.xyz, r2.wwww, r2.xyzx
+ sample r0.xyzw, v1.xyxx, t1.xyzw, s1
+ mul o0.xyzw, r0.wwww, r2.xyzw
+ ret
+ // Approximately 33 instruction slots used
+
+ };
+ }
+
+ pass A0Wrap
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44
+ // float2 dimensions; // Offset: 48 Size: 8
+ // float3 diff; // Offset: 64 Size: 12 [unused]
+ // float2 center1; // Offset: 80 Size: 8 [unused]
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4 [unused]
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ // cb2 cbuffer NA NA 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 1 ( FLT, FLT, FLT, FLT)
+ // c2 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ // c3 cb1 0 2 ( FLT, FLT, FLT, FLT)
+ // c5 cb1 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c6, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add r0.z, r0.x, c6.x
+ mul r0.z, r0.z, c5.x
+ mul r1.x, r0.z, c6.y
+ add r0.z, -r0.y, c6.x
+ add oPos.xy, r0, c0
+ mul r0.x, r0.z, c5.y
+ mul r1.y, r0.x, c6.y
+ mov r1.z, c6.x
+ dp3 oT0.w, r1, c3
+ dp3 oT0.z, r1, c4
+ mov oPos.zw, c6.xyzx
+
+ // approximately 13 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_constantbuffer cb1[4], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ dcl_temps 2
+ mov o0.zw, l(0,0,0,1.000000)
+ mad r0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.xy, r0.xyxx
+ add r0.x, r0.x, l(1.000000)
+ add r0.y, -r0.y, l(1.000000)
+ mul r0.xy, r0.xyxx, cb1[3].xyxx
+ mul r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
+ mov r1.z, l(1.000000)
+ dp3 o1.z, r1.xyzx, cb1[0].xyzx
+ dp3 o1.w, r1.xyzx, cb1[1].xyzx
+ mad o1.xy, v0.xyxx, cb0[2].zwzz, cb0[2].xyxx
+ ret
+ // Approximately 12 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44 [unused]
+ // float2 dimensions; // Offset: 48 Size: 8 [unused]
+ // float3 diff; // Offset: 64 Size: 12
+ // float2 center1; // Offset: 80 Size: 8
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sWrapSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb2 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 4 2 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c2, 0.5, 0, 0, 0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mul r0.w, c1.w, c1.w
+ add r0.xy, t0.wzzw, -c1
+ dp2add r0.w, r0, r0, -r0.w
+ mul r0.w, r0.w, c2.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, c2.x
+ texld r1, t0, s1
+ texld r2, r0, s0
+ mov r0.w, c1.w
+ mad r0.x, r0.x, -c0.z, -r0.w
+ mul r2.xyz, r2.w, r2
+ mul r1, r1.w, r2
+ cmp r0, r0.x, c2.y, r1
+ mov oC0, r0
+
+ // approximately 18 instruction slots used (2 texture, 16 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[6], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 2
+ add r0.xy, v1.zwzz, -cb0[5].xyxx
+ mov r0.z, cb0[5].w
+ dp3 r0.z, r0.xyzx, cb0[4].xyzx
+ dp2 r0.x, r0.xyxx, r0.xyxx
+ mad r0.x, -cb0[5].w, cb0[5].w, r0.x
+ mul r0.x, r0.x, l(0.500000)
+ div r0.x, r0.x, r0.z
+ mul r0.z, r0.x, cb0[4].z
+ ge r0.z, -cb0[5].w, r0.z
+ mov r0.y, l(0.500000)
+ sample r1.xyzw, r0.xyxx, t0.xyzw, s0
+ if_nz r0.z
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ mul r1.xyz, r1.wwww, r1.xyzx
+ sample r0.xyzw, v1.xyxx, t1.xyzw, s1
+ mul o0.xyzw, r0.wwww, r1.xyzw
+ ret
+ // Approximately 19 instruction slots used
+
+ };
+ }
+
+ pass APosMirror
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44
+ // float2 dimensions; // Offset: 48 Size: 8
+ // float3 diff; // Offset: 64 Size: 12 [unused]
+ // float2 center1; // Offset: 80 Size: 8 [unused]
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4 [unused]
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ // cb2 cbuffer NA NA 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 1 ( FLT, FLT, FLT, FLT)
+ // c2 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ // c3 cb1 0 2 ( FLT, FLT, FLT, FLT)
+ // c5 cb1 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c6, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add r0.z, r0.x, c6.x
+ mul r0.z, r0.z, c5.x
+ mul r1.x, r0.z, c6.y
+ add r0.z, -r0.y, c6.x
+ add oPos.xy, r0, c0
+ mul r0.x, r0.z, c5.y
+ mul r1.y, r0.x, c6.y
+ mov r1.z, c6.x
+ dp3 oT0.w, r1, c3
+ dp3 oT0.z, r1, c4
+ mov oPos.zw, c6.xyzx
+
+ // approximately 13 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_constantbuffer cb1[4], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ dcl_temps 2
+ mov o0.zw, l(0,0,0,1.000000)
+ mad r0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.xy, r0.xyxx
+ add r0.x, r0.x, l(1.000000)
+ add r0.y, -r0.y, l(1.000000)
+ mul r0.xy, r0.xyxx, cb1[3].xyxx
+ mul r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
+ mov r1.z, l(1.000000)
+ dp3 o1.z, r1.xyzx, cb1[0].xyzx
+ dp3 o1.w, r1.xyzx, cb1[1].xyzx
+ mad o1.xy, v0.xyxx, cb0[2].zwzz, cb0[2].xyxx
+ ret
+ // Approximately 12 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44 [unused]
+ // float2 dimensions; // Offset: 48 Size: 8 [unused]
+ // float3 diff; // Offset: 64 Size: 12
+ // float2 center1; // Offset: 80 Size: 8
+ // float A; // Offset: 88 Size: 4
+ // float radius1; // Offset: 92 Size: 4
+ // float sq_radius1; // Offset: 96 Size: 4
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sMirrorSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb2 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 4 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c3, 0.5, 0, 0, 0
+ def c4, 1, -1, 0, -0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ add r0.xy, t0.wzzw, -c1
+ dp2add r0.w, r0, r0, -c2.x
+ mul r0.w, r0.w, c1.z
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ mad r0.y, r0.x, r0.x, -r0.w
+ abs r0.z, r0.y
+ rsq r0.z, r0.z
+ rcp r1.x, r0.z
+ mov r1.yz, -r1.x
+ add r0.xzw, r0.x, r1.xyyz
+ rcp r1.x, c1.z
+ 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 r0.xzw, r1.xyyz, c4.xyxy, c4.zyzw
+ mov r2.y, c3.x
+ texld r1, t0, s1
+ texld r2, r2, s0
+ mul r2.xyz, r2.w, r2
+ mul r1, r1.w, r2
+ add r0.w, r0.w, r0.x
+ cmp r0.x, r0.w, r0.x, r0.z
+ cmp r1, -r0.x, c4.z, r1
+ cmp r0, r0.y, r1, c4.z
+ mov oC0, r0
+
+ // approximately 28 instruction slots used (2 texture, 26 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[7], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 3
+ add r0.xy, v1.zwzz, -cb0[5].xyxx
+ mov r0.z, cb0[5].w
+ dp3 r0.z, r0.xyzx, cb0[4].xyzx
+ dp2 r0.x, r0.xyxx, r0.xyxx
+ add r0.x, r0.x, -cb0[6].x
+ mul r0.x, r0.x, cb0[5].z
+ mad r0.x, r0.z, r0.z, -r0.x
+ lt r0.y, r0.x, l(0.000000)
+ sqrt r1.x, |r0.x|
+ mov r1.y, -r1.x
+ add r0.xz, r0.zzzz, r1.xxyx
+ div r0.xz, r0.xxzx, cb0[5].zzzz
+ mul r1.xy, r0.xzxx, cb0[4].zzzz
+ ge r1.xy, r1.xyxx, -cb0[5].wwww
+ and r1.xy, r1.xyxx, l(0x3f800000, 0x3f800000, 0, 0)
+ add r0.x, -r0.z, r0.x
+ mad r2.x, r1.x, r0.x, r0.z
+ mov r2.y, l(0.500000)
+ sample r2.xyzw, r2.xyxx, t0.xyzw, s0
+ if_nz r0.y
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ max r0.x, r1.y, r1.x
+ ge r0.x, l(0.000000), r0.x
+ if_nz r0.x
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ mul r2.xyz, r2.wwww, r2.xyzx
+ sample r0.xyzw, v1.xyxx, t1.xyzw, s1
+ mul o0.xyzw, r0.wwww, r2.xyzw
+ ret
+ // Approximately 33 instruction slots used
+
+ };
+ }
+
+ pass A0Mirror
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44
+ // float2 dimensions; // Offset: 48 Size: 8
+ // float3 diff; // Offset: 64 Size: 12 [unused]
+ // float2 center1; // Offset: 80 Size: 8 [unused]
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4 [unused]
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ // cb2 cbuffer NA NA 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 1 ( FLT, FLT, FLT, FLT)
+ // c2 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ // c3 cb1 0 2 ( FLT, FLT, FLT, FLT)
+ // c5 cb1 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c6, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add r0.z, r0.x, c6.x
+ mul r0.z, r0.z, c5.x
+ mul r1.x, r0.z, c6.y
+ add r0.z, -r0.y, c6.x
+ add oPos.xy, r0, c0
+ mul r0.x, r0.z, c5.y
+ mul r1.y, r0.x, c6.y
+ mov r1.z, c6.x
+ dp3 oT0.w, r1, c3
+ dp3 oT0.z, r1, c4
+ mov oPos.zw, c6.xyzx
+
+ // approximately 13 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_constantbuffer cb1[4], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ dcl_temps 2
+ mov o0.zw, l(0,0,0,1.000000)
+ mad r0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.xy, r0.xyxx
+ add r0.x, r0.x, l(1.000000)
+ add r0.y, -r0.y, l(1.000000)
+ mul r0.xy, r0.xyxx, cb1[3].xyxx
+ mul r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
+ mov r1.z, l(1.000000)
+ dp3 o1.z, r1.xyzx, cb1[0].xyzx
+ dp3 o1.w, r1.xyzx, cb1[1].xyzx
+ mad o1.xy, v0.xyxx, cb0[2].zwzz, cb0[2].xyxx
+ ret
+ // Approximately 12 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb2
+ // {
+ //
+ // float3x3 DeviceSpaceToUserSpace; // Offset: 0 Size: 44 [unused]
+ // float2 dimensions; // Offset: 48 Size: 8 [unused]
+ // float3 diff; // Offset: 64 Size: 12
+ // float2 center1; // Offset: 80 Size: 8
+ // float A; // Offset: 88 Size: 4 [unused]
+ // float radius1; // Offset: 92 Size: 4
+ // float sq_radius1; // Offset: 96 Size: 4 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sMirrorSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb2 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 4 2 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c2, 0.5, 0, 0, 0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mul r0.w, c1.w, c1.w
+ add r0.xy, t0.wzzw, -c1
+ dp2add r0.w, r0, r0, -r0.w
+ mul r0.w, r0.w, c2.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, c2.x
+ texld r1, t0, s1
+ texld r2, r0, s0
+ mov r0.w, c1.w
+ mad r0.x, r0.x, -c0.z, -r0.w
+ mul r2.xyz, r2.w, r2
+ mul r1, r1.w, r2
+ cmp r0, r0.x, c2.y, r1
+ mov oC0, r0
+
+ // approximately 18 instruction slots used (2 texture, 16 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[6], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 2
+ add r0.xy, v1.zwzz, -cb0[5].xyxx
+ mov r0.z, cb0[5].w
+ dp3 r0.z, r0.xyzx, cb0[4].xyzx
+ dp2 r0.x, r0.xyxx, r0.xyxx
+ mad r0.x, -cb0[5].w, cb0[5].w, r0.x
+ mul r0.x, r0.x, l(0.500000)
+ div r0.x, r0.x, r0.z
+ mul r0.z, r0.x, cb0[4].z
+ ge r0.z, -cb0[5].w, r0.z
+ mov r0.y, l(0.500000)
+ sample r1.xyzw, r0.xyxx, t0.xyzw, s0
+ if_nz r0.z
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+ endif
+ mul r1.xyz, r1.wwww, r1.xyzx
+ sample r0.xyzw, v1.xyxx, t1.xyzw, s1
+ mul o0.xyzw, r0.wwww, r1.xyzw
+ ret
+ // Approximately 19 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleMaskedTexture
+{
+ pass P0
+ {
+ RasterizerState = TextureRast;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mov r0.xy, t0.wzzw
+ texld r1, t0, s0
+ texld r0, r0, s1
+ mul r0, r0.w, r1
+ mov oC0, r0
+
+ // approximately 5 instruction slots used (2 texture, 3 arithmetic)
+ ps_4_0
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 2
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, v1.zwzz, t1.xyzw, s1
+ mul o0.xyzw, r0.xyzw, r1.wwww
+ ret
+ // Approximately 4 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleTextureWithShadow
+{
+ pass P0
+ {
+ RasterizerState = TextureRast;
+ AB_BlendFactor = float4(1, 1, 1, 1);
+ AB_SampleMask = uint(0xffffffff);
+ BlendState = ShadowBlendH;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb1
+ // {
+ //
+ // float4 BlurOffsetsH[3]; // Offset: 0 Size: 48
+ // float4 BlurOffsetsV[3]; // Offset: 48 Size: 48 [unused]
+ // float4 BlurWeights[3]; // Offset: 96 Size: 48
+ // float4 ShadowColor; // Offset: 144 Size: 16
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sShadowSampler sampler NA NA 0 1
+ // tex texture float4 2d 0 1
+ // cb1 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ // c3 cb0 6 4 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ dcl t0
+ dcl_2d s0
+ add r0.x, t0.x, c0.y
+ mov r0.y, t0.y
+ add r1.x, t0.x, c0.x
+ mov r1.y, t0.y
+ texld r0, r0, s0
+ texld r1, r1, s0
+ mul r0.x, r0.w, c3.y
+ mad r0.x, c3.x, r1.w, r0.x
+ add r1.x, t0.x, c0.z
+ mov r1.y, t0.y
+ add r2.x, t0.x, c0.w
+ mov r2.y, t0.y
+ texld r1, r1, s0
+ texld r2, r2, s0
+ mad r0.x, c3.z, r1.w, r0.x
+ mad r0.x, c3.w, r2.w, r0.x
+ add r1.x, t0.x, c1.x
+ mov r1.y, t0.y
+ add r2.x, t0.x, c1.y
+ mov r2.y, t0.y
+ texld r1, r1, s0
+ texld r2, r2, s0
+ mad r0.x, c4.x, r1.w, r0.x
+ mad r0.x, c4.y, r2.w, r0.x
+ add r1.x, t0.x, c1.z
+ mov r1.y, t0.y
+ add r2.x, t0.x, c1.w
+ mov r2.y, t0.y
+ texld r1, r1, s0
+ texld r2, r2, s0
+ mad r0.x, c4.z, r1.w, r0.x
+ mad r0.x, c4.w, r2.w, r0.x
+ add r1.x, t0.x, c2.x
+ mov r1.y, t0.y
+ texld r1, r1, s0
+ mad r0.x, c5.x, r1.w, r0.x
+ mul r0, r0.x, c6
+ mov oC0, r0
+
+ // approximately 38 instruction slots used (9 texture, 29 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[10], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_temps 4
+ add r0.xyzw, v1.xxxx, cb0[0].zxwy
+ mov r1.xz, r0.yywy
+ mov r1.yw, v1.yyyy
+ sample r2.xyzw, r1.zwzz, t0.xyzw, s0
+ sample r1.xyzw, r1.xyxx, t0.xyzw, s0
+ mul r1.x, r2.w, cb0[6].y
+ mad r1.x, cb0[6].x, r1.w, r1.x
+ mov r0.yw, v1.yyyy
+ sample r2.xyzw, r0.xyxx, t0.xyzw, s0
+ sample r0.xyzw, r0.zwzz, t0.xyzw, s0
+ mad r0.x, cb0[6].z, r2.w, r1.x
+ mad r0.x, cb0[6].w, r0.w, r0.x
+ add r1.xyzw, v1.xxxx, cb0[1].zxwy
+ mov r2.xz, r1.yywy
+ mov r2.yw, v1.yyyy
+ sample r3.xyzw, r2.xyxx, t0.xyzw, s0
+ sample r2.xyzw, r2.zwzz, t0.xyzw, s0
+ mad r0.x, cb0[7].x, r3.w, r0.x
+ mad r0.x, cb0[7].y, r2.w, r0.x
+ mov r1.yw, v1.yyyy
+ sample r2.xyzw, r1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, r1.zwzz, t0.xyzw, s0
+ mad r0.x, cb0[7].z, r2.w, r0.x
+ mad r0.x, cb0[7].w, r1.w, r0.x
+ add r1.x, v1.x, cb0[2].x
+ mov r1.y, v1.y
+ sample r1.xyzw, r1.xyxx, t0.xyzw, s0
+ mad r0.x, cb0[8].x, r1.w, r0.x
+ mul o0.xyzw, r0.xxxx, cb0[9].xyzw
+ ret
+ // Approximately 30 instruction slots used
+
+ };
+ }
+
+ pass P1
+ {
+ RasterizerState = TextureRast;
+ AB_BlendFactor = float4(1, 1, 1, 1);
+ AB_SampleMask = uint(0xffffffff);
+ BlendState = ShadowBlendV;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb1
+ // {
+ //
+ // float4 BlurOffsetsH[3]; // Offset: 0 Size: 48 [unused]
+ // float4 BlurOffsetsV[3]; // Offset: 48 Size: 48
+ // float4 BlurWeights[3]; // Offset: 96 Size: 48
+ // float4 ShadowColor; // Offset: 144 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sShadowSampler sampler NA NA 0 1
+ // tex texture float4 2d 0 1
+ // cb1 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 3 6 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ dcl t0
+ dcl_2d s0
+ add r0.y, t0.y, c0.y
+ mov r0.x, t0.x
+ add r1.y, t0.y, c0.x
+ mov r1.x, t0.x
+ texld r0, r0, s0
+ texld r1, r1, s0
+ mul r0, r0, c3.y
+ mad r0, c3.x, r1, r0
+ add r1.y, t0.y, c0.z
+ mov r1.x, t0.x
+ add r2.y, t0.y, c0.w
+ mov r2.x, t0.x
+ texld r1, r1, s0
+ texld r2, r2, s0
+ mad r0, c3.z, r1, r0
+ mad r0, c3.w, r2, r0
+ add r1.y, t0.y, c1.x
+ mov r1.x, t0.x
+ add r2.y, t0.y, c1.y
+ mov r2.x, t0.x
+ texld r1, r1, s0
+ texld r2, r2, s0
+ mad r0, c4.x, r1, r0
+ mad r0, c4.y, r2, r0
+ add r1.y, t0.y, c1.z
+ mov r1.x, t0.x
+ add r2.y, t0.y, c1.w
+ mov r2.x, t0.x
+ texld r1, r1, s0
+ texld r2, r2, s0
+ mad r0, c4.z, r1, r0
+ mad r0, c4.w, r2, r0
+ add r1.y, t0.y, c2.x
+ mov r1.x, t0.x
+ texld r1, r1, s0
+ mad r0, c5.x, r1, r0
+ mov oC0, r0
+
+ // approximately 37 instruction slots used (9 texture, 28 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[9], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_temps 4
+ mov r0.xz, v1.xxxx
+ add r1.xyzw, v1.yyyy, cb0[3].xzyw
+ mov r0.yw, r1.xxxz
+ sample r2.xyzw, r0.zwzz, t0.xyzw, s0
+ sample r0.xyzw, r0.xyxx, t0.xyzw, s0
+ mul r2.xyzw, r2.xyzw, cb0[6].yyyy
+ mad r0.xyzw, cb0[6].xxxx, r0.xyzw, r2.xyzw
+ mov r1.xz, v1.xxxx
+ sample r2.xyzw, r1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, r1.zwzz, t0.xyzw, s0
+ mad r0.xyzw, cb0[6].zzzz, r2.xyzw, r0.xyzw
+ mad r0.xyzw, cb0[6].wwww, r1.xyzw, r0.xyzw
+ mov r1.xz, v1.xxxx
+ add r2.xyzw, v1.yyyy, cb0[4].xzyw
+ mov r1.yw, r2.xxxz
+ sample r3.xyzw, r1.xyxx, t0.xyzw, s0
+ sample r1.xyzw, r1.zwzz, t0.xyzw, s0
+ mad r0.xyzw, cb0[7].xxxx, r3.xyzw, r0.xyzw
+ mad r0.xyzw, cb0[7].yyyy, r1.xyzw, r0.xyzw
+ mov r2.xz, v1.xxxx
+ sample r1.xyzw, r2.xyxx, t0.xyzw, s0
+ sample r2.xyzw, r2.zwzz, t0.xyzw, s0
+ mad r0.xyzw, cb0[7].zzzz, r1.xyzw, r0.xyzw
+ mad r0.xyzw, cb0[7].wwww, r2.xyzw, r0.xyzw
+ add r1.y, v1.y, cb0[5].x
+ mov r1.x, v1.x
+ sample r1.xyzw, r1.xyxx, t0.xyzw, s0
+ mad o0.xyzw, cb0[8].xxxx, r1.xyzw, r0.xyzw
+ ret
+ // Approximately 29 instruction slots used
+
+ };
+ }
+
+ pass P2
+ {
+ RasterizerState = TextureRast;
+ AB_BlendFactor = float4(1, 1, 1, 1);
+ AB_SampleMask = uint(0xffffffff);
+ BlendState = ShadowBlendV;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb1
+ // {
+ //
+ // float4 BlurOffsetsH[3]; // Offset: 0 Size: 48 [unused]
+ // float4 BlurOffsetsV[3]; // Offset: 48 Size: 48
+ // float4 BlurWeights[3]; // Offset: 96 Size: 48
+ // float4 ShadowColor; // Offset: 144 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sMaskSampler sampler NA NA 0 1
+ // sShadowSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb1 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 3 6 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t1
+ // s1 s1 t0
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ add r0.y, t0.y, c0.y
+ mov r0.x, t0.x
+ add r1.y, t0.y, c0.x
+ mov r1.x, t0.x
+ texld r0, r0, s1
+ texld r1, r1, s1
+ mul r0, r0, c3.y
+ mad r0, c3.x, r1, r0
+ add r1.y, t0.y, c0.z
+ mov r1.x, t0.x
+ add r2.y, t0.y, c0.w
+ mov r2.x, t0.x
+ texld r1, r1, s1
+ texld r2, r2, s1
+ mad r0, c3.z, r1, r0
+ mad r0, c3.w, r2, r0
+ add r1.y, t0.y, c1.x
+ mov r1.x, t0.x
+ add r2.y, t0.y, c1.y
+ mov r2.x, t0.x
+ texld r1, r1, s1
+ texld r2, r2, s1
+ mad r0, c4.x, r1, r0
+ mad r0, c4.y, r2, r0
+ add r1.y, t0.y, c1.z
+ mov r1.x, t0.x
+ add r2.y, t0.y, c1.w
+ mov r2.x, t0.x
+ texld r1, r1, s1
+ texld r2, r2, s1
+ mad r0, c4.z, r1, r0
+ mad r0, c4.w, r2, r0
+ add r1.y, t0.y, c2.x
+ mov r1.x, t0.x
+ mov r2.xy, t0.wzzw
+ texld r1, r1, s1
+ texld r2, r2, s0
+ mad r0, c5.x, r1, r0
+ mul r0, r2.w, r0
+ mov oC0, r0
+
+ // approximately 40 instruction slots used (10 texture, 30 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[9], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_temps 4
+ mov r0.xz, v1.xxxx
+ add r1.xyzw, v1.yyyy, cb0[3].xzyw
+ mov r0.yw, r1.xxxz
+ sample r2.xyzw, r0.zwzz, t0.xyzw, s1
+ sample r0.xyzw, r0.xyxx, t0.xyzw, s1
+ mul r2.xyzw, r2.xyzw, cb0[6].yyyy
+ mad r0.xyzw, cb0[6].xxxx, r0.xyzw, r2.xyzw
+ mov r1.xz, v1.xxxx
+ sample r2.xyzw, r1.xyxx, t0.xyzw, s1
+ sample r1.xyzw, r1.zwzz, t0.xyzw, s1
+ mad r0.xyzw, cb0[6].zzzz, r2.xyzw, r0.xyzw
+ mad r0.xyzw, cb0[6].wwww, r1.xyzw, r0.xyzw
+ mov r1.xz, v1.xxxx
+ add r2.xyzw, v1.yyyy, cb0[4].xzyw
+ mov r1.yw, r2.xxxz
+ sample r3.xyzw, r1.xyxx, t0.xyzw, s1
+ sample r1.xyzw, r1.zwzz, t0.xyzw, s1
+ mad r0.xyzw, cb0[7].xxxx, r3.xyzw, r0.xyzw
+ mad r0.xyzw, cb0[7].yyyy, r1.xyzw, r0.xyzw
+ mov r2.xz, v1.xxxx
+ sample r1.xyzw, r2.xyxx, t0.xyzw, s1
+ sample r2.xyzw, r2.zwzz, t0.xyzw, s1
+ mad r0.xyzw, cb0[7].zzzz, r1.xyzw, r0.xyzw
+ mad r0.xyzw, cb0[7].wwww, r2.xyzw, r0.xyzw
+ add r1.y, v1.y, cb0[5].x
+ mov r1.x, v1.x
+ sample r1.xyzw, r1.xyxx, t0.xyzw, s1
+ mad r0.xyzw, cb0[8].xxxx, r1.xyzw, r0.xyzw
+ sample r1.xyzw, v1.zwzz, t1.xyzw, s0
+ mul o0.xyzw, r0.xyzw, r1.wwww
+ ret
+ // Approximately 31 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleTextTexture
+{
+ pass Unmasked
+ {
+ RasterizerState = TextureRast;
+ AB_BlendFactor = float4(0, 0, 0, 0);
+ AB_SampleMask = uint(0xffffffff);
+ BlendState = bTextBlend;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16 [unused]
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16 [unused]
+ // float4 TextColor; // Offset: 48 Size: 16
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // tex texture float4 2d 0 1
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ // SV_Target 1 xyzw 1 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c1, 1, 0, 0, 0
+ dcl t0
+ dcl_2d s0
+ mov r0.xyz, c0
+ mad r0, r0.xyzx, c1.xxxy, c1.yyyx
+ mov oC0, r0
+ texld r0, t0, s0
+ mul r0, r0.zyxy, c0.w
+ mov oC1, r0
+
+ // approximately 6 instruction slots used (1 texture, 5 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[4], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_output o1.xyzw
+ dcl_temps 1
+ mov o0.xyz, cb0[3].xyzx
+ mov o0.w, l(1.000000)
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ mul o1.xyzw, r0.zyxy, cb0[3].wwww
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ }
+
+ pass Masked
+ {
+ RasterizerState = TextureRast;
+ AB_BlendFactor = float4(0, 0, 0, 0);
+ AB_SampleMask = uint(0xffffffff);
+ BlendState = bTextBlend;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 MaskTexCoords; // Offset: 32 Size: 16
+ // float4 TextColor; // Offset: 48 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 3 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c4, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad oT0.zw, v0.xyyx, c3.xywz, c3.xyyx
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c4.xyxy
+
+ // approximately 5 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ dcl_output o1.zw
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ mad o1.zw, v0.xxxy, cb0[2].zzzw, cb0[2].xxxy
+ ret
+ // Approximately 5 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16 [unused]
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 MaskTexCoords; // Offset: 32 Size: 16 [unused]
+ // float4 TextColor; // Offset: 48 Size: 16
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // sMaskSampler sampler NA NA 1 1
+ // tex texture float4 2d 0 1
+ // mask texture float4 2d 1 1
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ // TEXCOORD 1 zw 1 NONE float zw
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------- ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ // SV_Target 1 xyzw 1 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 3 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ // s1 s1 t1
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ def c1, 1, 0, 0, 0
+ dcl t0
+ dcl_2d s0
+ dcl_2d s1
+ mov r0.xyz, c0
+ mad r0, r0.xyzx, c1.xxxy, c1.yyyx
+ mov oC0, r0
+ mov r0.xy, t0.wzzw
+ texld r1, t0, s0
+ texld r0, r0, s1
+ mul r1, r1.zyxy, c0.w
+ mul r0, r0.w, r1
+ mov oC1, r0
+
+ // approximately 9 instruction slots used (2 texture, 7 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[4], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_sampler s1, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_resource_texture2d (float,float,float,float) t1
+ dcl_input_ps linear v1.xy
+ dcl_input_ps linear v1.zw
+ dcl_output o0.xyzw
+ dcl_output o1.xyzw
+ dcl_temps 2
+ mov o0.xyz, cb0[3].xyzx
+ mov o0.w, l(1.000000)
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ mul r0.xyzw, r0.zyxy, cb0[3].wwww
+ sample r1.xyzw, v1.zwzz, t1.xyzw, s1
+ mul o1.xyzw, r0.xyzw, r1.wwww
+ ret
+ // Approximately 7 instruction slots used
+
+ };
+ }
+
+}
+
+#endif
+
+const BYTE d2deffect[] =
+{
+ 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, 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, 62, 7,
+ 1, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 32, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 36, 71,
+ 108, 111, 98, 97, 108, 115,
+ 0, 117, 105, 110, 116, 0,
+ 13, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 25, 9, 0, 0, 98, 108,
+ 101, 110, 100, 111, 112, 0,
+ 99, 98, 48, 0, 102, 108,
+ 111, 97, 116, 52, 0, 58,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 16, 0, 0,
+ 0, 16, 0, 0, 0, 10,
+ 33, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 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, 99, 98,
+ 49, 0, 58, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 48, 0, 0, 0,
+ 16, 0, 0, 0, 48, 0,
+ 0, 0, 10, 33, 0, 0,
+ 66, 108, 117, 114, 79, 102,
+ 102, 115, 101, 116, 115, 72,
+ 0, 66, 108, 117, 114, 79,
+ 102, 102, 115, 101, 116, 115,
+ 86, 0, 66, 108, 117, 114,
+ 87, 101, 105, 103, 104, 116,
+ 115, 0, 83, 104, 97, 100,
+ 111, 119, 67, 111, 108, 111,
+ 114, 0, 99, 98, 50, 0,
+ 102, 108, 111, 97, 116, 51,
+ 120, 51, 0, 222, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 44, 0, 0,
+ 0, 48, 0, 0, 0, 36,
+ 0, 0, 0, 11, 91, 0,
+ 0, 68, 101, 118, 105, 99,
+ 101, 83, 112, 97, 99, 101,
+ 84, 111, 85, 115, 101, 114,
+ 83, 112, 97, 99, 101, 0,
+ 102, 108, 111, 97, 116, 50,
+ 0, 26, 1, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 0, 0, 0, 16,
+ 0, 0, 0, 8, 0, 0,
+ 0, 10, 17, 0, 0, 100,
+ 105, 109, 101, 110, 115, 105,
+ 111, 110, 115, 0, 102, 108,
+ 111, 97, 116, 51, 0, 72,
+ 1, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 0, 16, 0, 0,
+ 0, 12, 0, 0, 0, 10,
+ 25, 0, 0, 100, 105, 102,
+ 102, 0, 99, 101, 110, 116,
+ 101, 114, 49, 0, 102, 108,
+ 111, 97, 116, 0, 120, 1,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 16, 0, 0, 0,
+ 4, 0, 0, 0, 9, 9,
+ 0, 0, 65, 0, 114, 97,
+ 100, 105, 117, 115, 49, 0,
+ 115, 113, 95, 114, 97, 100,
+ 105, 117, 115, 49, 0, 84,
+ 101, 120, 116, 117, 114, 101,
+ 50, 68, 0, 175, 1, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 116, 101, 120, 0, 98,
+ 99, 107, 116, 101, 120, 0,
+ 109, 97, 115, 107, 0, 83,
+ 97, 109, 112, 108, 101, 114,
+ 83, 116, 97, 116, 101, 0,
+ 229, 1, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 21, 0, 0, 0, 115, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 21, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 3, 0, 0,
+ 0, 115, 66, 99, 107, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 21, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 3, 0, 0,
+ 0, 115, 87, 114, 97, 112,
+ 83, 97, 109, 112, 108, 101,
+ 114, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 21, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 115, 77, 105, 114,
+ 114, 111, 114, 83, 97, 109,
+ 112, 108, 101, 114, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 21, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 2, 0, 0, 0, 115,
+ 77, 97, 115, 107, 83, 97,
+ 109, 112, 108, 101, 114, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 21, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 115, 83, 104, 97, 100, 111,
+ 119, 83, 97, 109, 112, 108,
+ 101, 114, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 21,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 4,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 4,
+ 0, 0, 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, 0,
+ 0, 0, 0, 82, 97, 115,
+ 116, 101, 114, 105, 122, 101,
+ 114, 83, 116, 97, 116, 101,
+ 0, 87, 3, 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, 84,
+ 101, 120, 116, 117, 114, 101,
+ 82, 97, 115, 116, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 1, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 1, 0, 0, 0, 66,
+ 108, 101, 110, 100, 83, 116,
+ 97, 116, 101, 0, 167, 3,
+ 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, 83, 104, 97, 100,
+ 111, 119, 66, 108, 101, 110,
+ 100, 72, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 0, 0, 0, 15,
+ 0, 0, 0, 83, 104, 97,
+ 100, 111, 119, 66, 108, 101,
+ 110, 100, 86, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 6, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 6, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 0, 0, 0, 98, 84,
+ 101, 120, 116, 66, 108, 101,
+ 110, 100, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 16,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 17,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 18,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 19,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 0, 0, 0, 15,
+ 0, 0, 0, 83, 97, 109,
+ 112, 108, 101, 84, 101, 120,
+ 116, 117, 114, 101, 0, 80,
+ 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,
+ 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,
+ 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, 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, 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,
+ 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, 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, 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,
+ 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, 0, 0, 0, 0,
+ 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,
+ 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,
+ 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, 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,
+ 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, 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,
+ 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,
+ 8, 128, 4, 0, 255, 128,
+ 5, 0, 85, 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,
+ 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,
+ 8, 0, 228, 128, 7, 0,
+ 228, 128, 2, 0, 0, 3,
+ 7, 0, 15, 128, 0, 0,
+ 255, 128, 1, 0, 228, 160,
+ 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, 7, 0,
+ 255, 129, 8, 0, 228, 128,
+ 0, 0, 228, 128, 4, 0,
+ 0, 4, 8, 0, 7, 128,
+ 5, 0, 228, 128, 2, 0,
+ 255, 161, 2, 0, 0, 161,
+ 2, 0, 0, 3, 8, 0,
+ 7, 128, 8, 0, 228, 129,
+ 2, 0, 0, 160, 4, 0,
+ 0, 4, 4, 0, 7, 128,
+ 4, 0, 228, 128, 8, 0,
+ 228, 129, 2, 0, 0, 160,
+ 2, 0, 0, 3, 8, 0,
+ 7, 128, 5, 0, 228, 128,
+ 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, 7, 0,
+ 170, 129, 1, 0, 228, 128,
+ 0, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 7, 0, 85, 129, 6, 0,
+ 228, 128, 0, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 7, 0, 0, 129,
+ 5, 0, 228, 128, 0, 0,
+ 228, 128, 18, 0, 0, 4,
+ 4, 0, 7, 128, 1, 0,
+ 255, 128, 0, 0, 228, 128,
+ 3, 0, 228, 128, 5, 0,
+ 0, 3, 4, 0, 8, 128,
+ 1, 0, 255, 128, 1, 0,
+ 255, 128, 88, 0, 0, 4,
+ 4, 0, 8, 128, 4, 0,
+ 255, 129, 2, 0, 0, 160,
+ 2, 0, 85, 160, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 2, 0, 255, 128, 4, 0,
+ 228, 128, 5, 0, 0, 3,
+ 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, 4, 0,
+ 255, 128, 0, 0, 255, 128,
+ 88, 0, 0, 4, 2, 0,
+ 7, 128, 0, 0, 255, 129,
+ 0, 0, 228, 128, 2, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 2, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 8, 6,
+ 0, 0, 64, 0, 0, 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,
+ 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,
+ 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, 16, 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, 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, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 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,
+ 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, 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, 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,
+ 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, 1, 0,
+ 0, 0, 31, 0, 4, 3,
+ 10, 0, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 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, 2, 0, 0, 0,
+ 31, 0, 4, 3, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 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, 3, 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, 1, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 0, 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,
+ 1, 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, 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, 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, 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,
+ 4, 0, 0, 0, 31, 0,
+ 4, 3, 58, 0, 16, 0,
+ 2, 0, 0, 0, 51, 0,
+ 0, 7, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 1, 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, 5, 0,
+ 0, 0, 31, 0, 4, 3,
+ 58, 0, 16, 0, 2, 0,
+ 0, 0, 52, 0, 0, 7,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 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, 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, 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, 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, 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,
+ 0, 0, 70, 2, 16, 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,
+ 1, 0, 0, 0, 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, 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,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 0, 0, 0, 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,
+ 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, 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,
+ 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,
+ 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, 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, 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, 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, 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, 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,
+ 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,
+ 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, 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 170, 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, 4, 0,
+ 0, 4, 4, 0, 8, 128,
+ 2, 0, 170, 128, 3, 0,
+ 255, 129, 4, 0, 255, 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,
+ 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,
+ 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,
+ 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,
+ 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, 85, 128, 4, 0,
+ 0, 4, 4, 0, 8, 128,
+ 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, 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,
+ 4, 0, 8, 128, 9, 0,
+ 170, 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,
+ 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,
+ 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, 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,
+ 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, 16, 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, 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, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 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,
+ 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, 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, 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,
+ 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, 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,
+ 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, 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,
+ 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, 4, 0,
+ 0, 0, 70, 2, 16, 128,
+ 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, 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,
+ 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,
+ 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, 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, 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, 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, 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, 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, 0,
+ 0, 64, 0, 0, 0, 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,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 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,
+ 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, 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, 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,
+ 50, 0, 0, 9, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 6, 0,
+ 0, 0, 70, 2, 16, 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,
+ 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, 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,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 1, 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, 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, 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, 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, 0, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,
+ 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,
+ 0, 76, 35, 0, 0, 200,
+ 35, 0, 0, 52, 37, 0,
+ 0, 164, 37, 0, 0, 65,
+ 111, 110, 57, 8, 13, 0,
+ 0, 8, 13, 0, 0, 0,
+ 2, 255, 255, 208, 12, 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, 64,
+ 193, 0, 0, 80, 193, 0,
+ 0, 96, 193, 0, 0, 0,
+ 0, 81, 0, 0, 5, 2,
+ 0, 15, 160, 0, 0, 128,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 81, 0, 0, 5, 3,
+ 0, 15, 160, 154, 153, 153,
+ 62, 61, 10, 23, 63, 174,
+ 71, 225, 61, 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, 2, 128, 2, 0, 85,
+ 160, 1, 0, 0, 2, 1,
+ 0, 2, 128, 2, 0, 85,
+ 160, 1, 0, 0, 2, 2,
+ 0, 4, 128, 2, 0, 85,
+ 160, 66, 0, 0, 3, 3,
+ 0, 15, 128, 0, 0, 228,
+ 176, 1, 8, 228, 160, 66,
+ 0, 0, 3, 4, 0, 15,
+ 128, 0, 0, 228, 176, 0,
+ 8, 228, 160, 6, 0, 0,
+ 2, 0, 0, 8, 128, 4,
+ 0, 255, 128, 5, 0, 0,
+ 3, 5, 0, 7, 128, 0,
+ 0, 255, 128, 4, 0, 228,
+ 128, 4, 0, 0, 4, 6,
+ 0, 3, 128, 4, 0, 225,
+ 128, 0, 0, 255, 128, 5,
+ 0, 230, 129, 88, 0, 0,
+ 4, 7, 0, 3, 128, 6,
+ 0, 0, 128, 5, 0, 233,
+ 128, 5, 0, 230, 128, 11,
+ 0, 0, 3, 1, 0, 8,
+ 128, 5, 0, 0, 128, 7,
+ 0, 0, 128, 10, 0, 0,
+ 3, 2, 0, 8, 128, 7,
+ 0, 85, 128, 5, 0, 0,
+ 128, 2, 0, 0, 3, 7,
+ 0, 8, 128, 1, 0, 255,
+ 128, 2, 0, 255, 129, 6,
+ 0, 0, 2, 1, 0, 8,
+ 128, 3, 0, 255, 128, 5,
+ 0, 0, 3, 8, 0, 7,
+ 128, 1, 0, 255, 128, 3,
+ 0, 228, 128, 4, 0, 0,
+ 4, 9, 0, 3, 128, 3,
+ 0, 0, 128, 1, 0, 255,
+ 128, 8, 0, 230, 129, 6,
+ 0, 0, 2, 2, 0, 8,
+ 128, 9, 0, 85, 128, 5,
+ 0, 0, 3, 2, 0, 8,
+ 128, 2, 0, 255, 128, 7,
+ 0, 255, 128, 4, 0, 0,
+ 4, 10, 0, 15, 128, 3,
+ 0, 150, 128, 1, 0, 255,
+ 128, 8, 0, 96, 129, 5,
+ 0, 0, 3, 7, 0, 2,
+ 128, 2, 0, 255, 128, 10,
+ 0, 255, 128, 1, 0, 0,
+ 2, 9, 0, 12, 128, 10,
+ 0, 228, 128, 88, 0, 0,
+ 4, 1, 0, 5, 128, 9,
+ 0, 85, 129, 9, 0, 245,
+ 128, 7, 0, 215, 128, 6,
+ 0, 0, 2, 2, 0, 8,
+ 128, 9, 0, 0, 128, 5,
+ 0, 0, 3, 2, 0, 8,
+ 128, 2, 0, 255, 128, 7,
+ 0, 255, 128, 5, 0, 0,
+ 3, 7, 0, 1, 128, 2,
+ 0, 255, 128, 9, 0, 170,
+ 128, 88, 0, 0, 4, 2,
+ 0, 3, 128, 9, 0, 0,
+ 129, 9, 0, 232, 128, 7,
+ 0, 227, 128, 88, 0, 0,
+ 4, 1, 0, 7, 128, 9,
+ 0, 255, 128, 1, 0, 228,
+ 128, 2, 0, 228, 128, 6,
+ 0, 0, 2, 5, 0, 8,
+ 128, 9, 0, 255, 128, 5,
+ 0, 0, 3, 5, 0, 8,
+ 128, 5, 0, 255, 128, 7,
+ 0, 255, 128, 5, 0, 0,
+ 3, 7, 0, 4, 128, 5,
+ 0, 255, 128, 9, 0, 85,
+ 128, 88, 0, 0, 4, 0,
+ 0, 5, 128, 10, 0, 255,
+ 129, 9, 0, 245, 128, 7,
+ 0, 246, 128, 88, 0, 0,
+ 4, 0, 0, 7, 128, 10,
+ 0, 0, 128, 0, 0, 228,
+ 128, 1, 0, 228, 128, 1,
+ 0, 0, 2, 1, 0, 1,
+ 128, 2, 0, 85, 160, 1,
+ 0, 0, 2, 2, 0, 1,
+ 128, 2, 0, 85, 160, 1,
+ 0, 0, 2, 11, 0, 4,
+ 128, 2, 0, 85, 160, 6,
+ 0, 0, 2, 2, 0, 8,
+ 128, 9, 0, 170, 128, 5,
+ 0, 0, 3, 2, 0, 8,
+ 128, 2, 0, 255, 128, 7,
+ 0, 255, 128, 5, 0, 0,
+ 3, 7, 0, 1, 128, 2,
+ 0, 255, 128, 9, 0, 0,
+ 128, 88, 0, 0, 4, 11,
+ 0, 3, 128, 10, 0, 170,
+ 129, 9, 0, 232, 128, 7,
+ 0, 236, 128, 6, 0, 0,
+ 2, 2, 0, 8, 128, 10,
+ 0, 85, 128, 5, 0, 0,
+ 3, 2, 0, 8, 128, 2,
+ 0, 255, 128, 7, 0, 255,
+ 128, 5, 0, 0, 3, 7,
+ 0, 2, 128, 2, 0, 255,
+ 128, 10, 0, 0, 128, 88,
+ 0, 0, 4, 2, 0, 6,
+ 128, 10, 0, 85, 129, 10,
+ 0, 196, 128, 7, 0, 220,
+ 128, 88, 0, 0, 4, 2,
+ 0, 7, 128, 10, 0, 0,
+ 128, 2, 0, 228, 128, 11,
+ 0, 228, 128, 6, 0, 0,
+ 2, 2, 0, 8, 128, 10,
+ 0, 0, 128, 5, 0, 0,
+ 3, 2, 0, 8, 128, 2,
+ 0, 255, 128, 7, 0, 255,
+ 128, 5, 0, 0, 3, 7,
+ 0, 4, 128, 2, 0, 255,
+ 128, 10, 0, 85, 128, 88,
+ 0, 0, 4, 1, 0, 6,
+ 128, 10, 0, 0, 129, 10,
+ 0, 196, 128, 7, 0, 248,
+ 128, 88, 0, 0, 4, 1,
+ 0, 7, 128, 9, 0, 255,
+ 128, 1, 0, 228, 128, 2,
+ 0, 228, 128, 88, 0, 0,
+ 4, 0, 0, 7, 128, 10,
+ 0, 85, 128, 1, 0, 228,
+ 128, 0, 0, 228, 128, 88,
+ 0, 0, 4, 1, 0, 3,
+ 128, 9, 0, 170, 128, 8,
+ 0, 233, 128, 8, 0, 230,
+ 128, 8, 0, 0, 3, 5,
+ 0, 8, 128, 0, 0, 228,
+ 128, 3, 0, 228, 160, 8,
+ 0, 0, 3, 1, 0, 4,
+ 128, 8, 0, 228, 128, 3,
+ 0, 228, 160, 2, 0, 0,
+ 3, 5, 0, 8, 128, 5,
+ 0, 255, 129, 1, 0, 170,
+ 128, 2, 0, 0, 3, 0,
+ 0, 7, 128, 0, 0, 228,
+ 128, 5, 0, 255, 128, 2,
+ 0, 0, 3, 5, 0, 8,
+ 128, 0, 0, 85, 129, 0,
+ 0, 0, 128, 88, 0, 0,
+ 4, 2, 0, 3, 128, 5,
+ 0, 255, 128, 0, 0, 225,
+ 128, 0, 0, 228, 128, 10,
+ 0, 0, 3, 5, 0, 8,
+ 128, 0, 0, 170, 128, 2,
+ 0, 0, 128, 11, 0, 0,
+ 3, 7, 0, 1, 128, 2,
+ 0, 85, 128, 0, 0, 170,
+ 128, 8, 0, 0, 3, 2,
+ 0, 1, 128, 0, 0, 228,
+ 128, 3, 0, 228, 160, 2,
+ 0, 0, 3, 2, 0, 2,
+ 128, 5, 0, 255, 129, 2,
+ 0, 0, 128, 6, 0, 0,
+ 2, 2, 0, 2, 128, 2,
+ 0, 85, 128, 2, 0, 0,
+ 3, 7, 0, 14, 128, 0,
+ 0, 144, 128, 2, 0, 0,
+ 129, 5, 0, 0, 3, 7,
+ 0, 14, 128, 2, 0, 0,
+ 128, 7, 0, 228, 128, 4,
+ 0, 0, 4, 2, 0, 14,
+ 128, 7, 0, 228, 128, 2,
+ 0, 85, 128, 2, 0, 0,
+ 128, 88, 0, 0, 4, 0,
+ 0, 7, 128, 5, 0, 255,
+ 128, 0, 0, 228, 128, 2,
+ 0, 249, 128, 2, 0, 0,
+ 3, 2, 0, 14, 128, 2,
+ 0, 0, 129, 0, 0, 144,
+ 128, 2, 0, 0, 3, 5,
+ 0, 8, 128, 2, 0, 0,
+ 129, 2, 0, 0, 160, 5,
+ 0, 0, 3, 2, 0, 14,
+ 128, 2, 0, 228, 128, 5,
+ 0, 255, 128, 2, 0, 0,
+ 3, 5, 0, 8, 128, 2,
+ 0, 0, 129, 7, 0, 0,
+ 128, 2, 0, 0, 3, 7,
+ 0, 1, 128, 7, 0, 0,
+ 129, 2, 0, 0, 160, 6,
+ 0, 0, 2, 5, 0, 8,
+ 128, 5, 0, 255, 128, 4,
+ 0, 0, 4, 2, 0, 7,
+ 128, 2, 0, 249, 128, 5,
+ 0, 255, 128, 2, 0, 0,
+ 128, 88, 0, 0, 4, 0,
+ 0, 7, 128, 7, 0, 0,
+ 128, 0, 0, 228, 128, 2,
+ 0, 228, 128, 8, 0, 0,
+ 3, 5, 0, 8, 128, 5,
+ 0, 228, 128, 3, 0, 228,
+ 160, 2, 0, 0, 3, 2,
+ 0, 1, 128, 1, 0, 170,
+ 128, 5, 0, 255, 129, 2,
+ 0, 0, 3, 5, 0, 8,
+ 128, 1, 0, 170, 129, 5,
+ 0, 255, 128, 4, 0, 0,
+ 4, 2, 0, 14, 128, 3,
+ 0, 144, 128, 1, 0, 255,
+ 128, 5, 0, 255, 128, 4,
+ 0, 0, 4, 3, 0, 7,
+ 128, 4, 0, 228, 128, 0,
+ 0, 255, 128, 2, 0, 0,
+ 128, 4, 0, 0, 4, 7,
+ 0, 15, 128, 4, 0, 38,
+ 128, 0, 0, 255, 128, 5,
+ 0, 144, 129, 2, 0, 0,
+ 3, 0, 0, 8, 128, 3,
+ 0, 85, 129, 3, 0, 0,
+ 128, 88, 0, 0, 4, 8,
+ 0, 6, 128, 0, 0, 255,
+ 128, 3, 0, 196, 128, 3,
+ 0, 208, 128, 10, 0, 0,
+ 3, 0, 0, 8, 128, 3,
+ 0, 170, 128, 8, 0, 85,
+ 128, 11, 0, 0, 3, 1,
+ 0, 8, 128, 8, 0, 170,
+ 128, 3, 0, 170, 128, 8,
+ 0, 0, 3, 5, 0, 8,
+ 128, 3, 0, 228, 128, 3,
+ 0, 228, 160, 2, 0, 0,
+ 3, 2, 0, 1, 128, 0,
+ 0, 255, 129, 5, 0, 255,
+ 128, 6, 0, 0, 2, 2,
+ 0, 1, 128, 2, 0, 0,
+ 128, 2, 0, 0, 3, 8,
+ 0, 14, 128, 3, 0, 144,
+ 128, 5, 0, 255, 129, 5,
+ 0, 0, 3, 8, 0, 14,
+ 128, 5, 0, 255, 128, 8,
+ 0, 228, 128, 4, 0, 0,
+ 4, 8, 0, 14, 128, 8,
+ 0, 228, 128, 2, 0, 0,
+ 128, 5, 0, 255, 128, 88,
+ 0, 0, 4, 3, 0, 7,
+ 128, 0, 0, 255, 128, 3,
+ 0, 228, 128, 8, 0, 249,
+ 128, 2, 0, 0, 3, 8,
+ 0, 14, 128, 5, 0, 255,
+ 129, 3, 0, 144, 128, 2,
+ 0, 0, 3, 0, 0, 8,
+ 128, 5, 0, 255, 129, 2,
+ 0, 0, 160, 5, 0, 0,
+ 3, 8, 0, 14, 128, 0,
+ 0, 255, 128, 8, 0, 228,
+ 128, 2, 0, 0, 3, 0,
+ 0, 8, 128, 1, 0, 255,
+ 128, 5, 0, 255, 129, 2,
+ 0, 0, 3, 1, 0, 8,
+ 128, 1, 0, 255, 129, 2,
+ 0, 0, 160, 6, 0, 0,
+ 2, 0, 0, 8, 128, 0,
+ 0, 255, 128, 4, 0, 0,
+ 4, 8, 0, 14, 128, 8,
+ 0, 228, 128, 0, 0, 255,
+ 128, 5, 0, 255, 128, 88,
+ 0, 0, 4, 3, 0, 7,
+ 128, 1, 0, 255, 128, 3,
+ 0, 228, 128, 8, 0, 249,
+ 128, 2, 0, 0, 3, 0,
+ 0, 8, 128, 2, 0, 170,
+ 129, 2, 0, 85, 128, 88,
+ 0, 0, 4, 8, 0, 6,
+ 128, 0, 0, 255, 128, 2,
+ 0, 216, 128, 2, 0, 228,
+ 128, 10, 0, 0, 3, 0,
+ 0, 8, 128, 2, 0, 255,
+ 128, 8, 0, 85, 128, 11,
+ 0, 0, 3, 1, 0, 8,
+ 128, 8, 0, 170, 128, 2,
+ 0, 255, 128, 8, 0, 0,
+ 3, 5, 0, 8, 128, 2,
+ 0, 249, 128, 3, 0, 228,
+ 160, 2, 0, 0, 3, 2,
+ 0, 1, 128, 0, 0, 255,
+ 129, 5, 0, 255, 128, 6,
+ 0, 0, 2, 2, 0, 1,
+ 128, 2, 0, 0, 128, 2,
+ 0, 0, 3, 8, 0, 14,
+ 128, 2, 0, 228, 128, 5,
+ 0, 255, 129, 5, 0, 0,
+ 3, 8, 0, 14, 128, 5,
+ 0, 255, 128, 8, 0, 228,
+ 128, 4, 0, 0, 4, 8,
+ 0, 14, 128, 8, 0, 228,
+ 128, 2, 0, 0, 128, 5,
+ 0, 255, 128, 88, 0, 0,
+ 4, 2, 0, 7, 128, 0,
+ 0, 255, 128, 2, 0, 249,
+ 128, 8, 0, 249, 128, 2,
+ 0, 0, 3, 8, 0, 14,
+ 128, 5, 0, 255, 129, 2,
+ 0, 144, 128, 2, 0, 0,
+ 3, 0, 0, 8, 128, 5,
+ 0, 255, 129, 2, 0, 0,
+ 160, 5, 0, 0, 3, 8,
+ 0, 14, 128, 0, 0, 255,
+ 128, 8, 0, 228, 128, 2,
+ 0, 0, 3, 0, 0, 8,
+ 128, 1, 0, 255, 128, 5,
+ 0, 255, 129, 2, 0, 0,
+ 3, 1, 0, 8, 128, 1,
+ 0, 255, 129, 2, 0, 0,
+ 160, 6, 0, 0, 2, 0,
+ 0, 8, 128, 0, 0, 255,
+ 128, 4, 0, 0, 4, 8,
+ 0, 14, 128, 8, 0, 228,
+ 128, 0, 0, 255, 128, 5,
+ 0, 255, 128, 88, 0, 0,
+ 4, 2, 0, 7, 128, 1,
+ 0, 255, 128, 2, 0, 228,
+ 128, 8, 0, 249, 128, 1,
+ 0, 0, 2, 0, 0, 8,
+ 128, 0, 0, 0, 160, 2,
+ 0, 0, 3, 8, 0, 14,
+ 128, 0, 0, 255, 128, 1,
+ 0, 144, 160, 5, 0, 0,
+ 3, 8, 0, 14, 128, 8,
+ 0, 228, 128, 8, 0, 228,
+ 128, 88, 0, 0, 4, 2,
+ 0, 7, 128, 8, 0, 255,
+ 129, 3, 0, 228, 128, 2,
+ 0, 228, 128, 88, 0, 0,
+ 4, 0, 0, 7, 128, 8,
+ 0, 170, 129, 0, 0, 228,
+ 128, 2, 0, 228, 128, 1,
+ 0, 0, 2, 2, 0, 2,
+ 128, 2, 0, 85, 160, 1,
+ 0, 0, 2, 3, 0, 2,
+ 128, 2, 0, 85, 160, 1,
+ 0, 0, 2, 9, 0, 4,
+ 128, 2, 0, 85, 160, 11,
+ 0, 0, 3, 0, 0, 8,
+ 128, 8, 0, 0, 128, 1,
+ 0, 0, 128, 10, 0, 0,
+ 3, 2, 0, 8, 128, 1,
+ 0, 85, 128, 8, 0, 0,
+ 128, 2, 0, 0, 3, 10,
+ 0, 8, 128, 0, 0, 255,
+ 128, 2, 0, 255, 129, 6,
+ 0, 0, 2, 0, 0, 8,
+ 128, 7, 0, 255, 128, 5,
+ 0, 0, 3, 0, 0, 8,
+ 128, 0, 0, 255, 128, 10,
+ 0, 255, 128, 5, 0, 0,
+ 3, 10, 0, 1, 128, 0,
+ 0, 255, 128, 6, 0, 0,
+ 128, 1, 0, 0, 2, 6,
+ 0, 12, 128, 7, 0, 180,
+ 128, 88, 0, 0, 4, 9,
+ 0, 3, 128, 7, 0, 255,
+ 129, 6, 0, 226, 128, 10,
+ 0, 227, 128, 6, 0, 0,
+ 2, 0, 0, 8, 128, 6,
+ 0, 85, 128, 5, 0, 0,
+ 3, 0, 0, 8, 128, 0,
+ 0, 255, 128, 10, 0, 255,
+ 128, 5, 0, 0, 3, 10,
+ 0, 2, 128, 0, 0, 255,
+ 128, 7, 0, 170, 128, 88,
+ 0, 0, 4, 3, 0, 5,
+ 128, 6, 0, 85, 129, 6,
+ 0, 245, 128, 10, 0, 215,
+ 128, 88, 0, 0, 4, 1,
+ 0, 11, 128, 7, 0, 170,
+ 128, 3, 0, 164, 128, 9,
+ 0, 164, 128, 6, 0, 0,
+ 2, 0, 0, 8, 128, 7,
+ 0, 170, 128, 5, 0, 0,
+ 3, 0, 0, 8, 128, 0,
+ 0, 255, 128, 10, 0, 255,
+ 128, 5, 0, 0, 3, 10,
+ 0, 4, 128, 0, 0, 255,
+ 128, 6, 0, 85, 128, 88,
+ 0, 0, 4, 2, 0, 5,
+ 128, 7, 0, 170, 129, 6,
+ 0, 245, 128, 10, 0, 246,
+ 128, 88, 0, 0, 4, 1,
+ 0, 11, 128, 7, 0, 0,
+ 128, 2, 0, 164, 128, 1,
+ 0, 228, 128, 1, 0, 0,
+ 2, 2, 0, 1, 128, 2,
+ 0, 85, 160, 1, 0, 0,
+ 2, 3, 0, 4, 128, 2,
+ 0, 85, 160, 6, 0, 0,
+ 2, 0, 0, 8, 128, 6,
+ 0, 0, 128, 5, 0, 0,
+ 3, 0, 0, 8, 128, 0,
+ 0, 255, 128, 10, 0, 255,
+ 128, 5, 0, 0, 3, 10,
+ 0, 1, 128, 0, 0, 255,
+ 128, 7, 0, 255, 128, 88,
+ 0, 0, 4, 3, 0, 3,
+ 128, 6, 0, 0, 129, 6,
+ 0, 226, 128, 10, 0, 236,
+ 128, 6, 0, 0, 2, 0,
+ 0, 8, 128, 7, 0, 85,
+ 128, 5, 0, 0, 3, 0,
+ 0, 8, 128, 0, 0, 255,
+ 128, 10, 0, 255, 128, 5,
+ 0, 0, 3, 10, 0, 2,
+ 128, 0, 0, 255, 128, 7,
+ 0, 0, 128, 88, 0, 0,
+ 4, 2, 0, 6, 128, 7,
+ 0, 85, 129, 7, 0, 196,
+ 128, 10, 0, 220, 128, 88,
+ 0, 0, 4, 2, 0, 7,
+ 128, 7, 0, 0, 128, 2,
+ 0, 228, 128, 3, 0, 228,
+ 128, 1, 0, 0, 2, 3,
+ 0, 1, 128, 2, 0, 85,
+ 160, 6, 0, 0, 2, 0,
+ 0, 8, 128, 7, 0, 0,
+ 128, 5, 0, 0, 3, 0,
+ 0, 8, 128, 0, 0, 255,
+ 128, 10, 0, 255, 128, 5,
+ 0, 0, 3, 10, 0, 4,
+ 128, 0, 0, 255, 128, 7,
+ 0, 85, 128, 88, 0, 0,
+ 4, 3, 0, 6, 128, 7,
+ 0, 0, 129, 7, 0, 196,
+ 128, 10, 0, 248, 128, 88,
+ 0, 0, 4, 2, 0, 7,
+ 128, 7, 0, 170, 128, 3,
+ 0, 228, 128, 2, 0, 228,
+ 128, 88, 0, 0, 4, 1,
+ 0, 11, 128, 7, 0, 85,
+ 128, 2, 0, 164, 128, 1,
+ 0, 228, 128, 8, 0, 0,
+ 3, 0, 0, 8, 128, 1,
+ 0, 244, 128, 3, 0, 228,
+ 160, 2, 0, 0, 3, 0,
+ 0, 8, 128, 0, 0, 255,
+ 129, 1, 0, 170, 128, 2,
+ 0, 0, 3, 1, 0, 7,
+ 128, 0, 0, 255, 128, 1,
+ 0, 244, 128, 2, 0, 0,
+ 3, 0, 0, 8, 128, 1,
+ 0, 85, 129, 1, 0, 0,
+ 128, 88, 0, 0, 4, 2,
+ 0, 3, 128, 0, 0, 255,
+ 128, 1, 0, 225, 128, 1,
+ 0, 228, 128, 10, 0, 0,
+ 3, 0, 0, 8, 128, 1,
+ 0, 170, 128, 2, 0, 0,
+ 128, 11, 0, 0, 3, 5,
+ 0, 8, 128, 2, 0, 85,
+ 128, 1, 0, 170, 128, 8,
+ 0, 0, 3, 1, 0, 8,
+ 128, 1, 0, 228, 128, 3,
+ 0, 228, 160, 2, 0, 0,
+ 3, 2, 0, 7, 128, 1,
+ 0, 255, 129, 1, 0, 228,
+ 128, 5, 0, 0, 3, 2,
+ 0, 7, 128, 1, 0, 255,
+ 128, 2, 0, 228, 128, 2,
+ 0, 0, 3, 2, 0, 8,
+ 128, 0, 0, 255, 129, 1,
+ 0, 255, 128, 6, 0, 0,
+ 2, 2, 0, 8, 128, 2,
+ 0, 255, 128, 4, 0, 0,
+ 4, 2, 0, 7, 128, 2,
+ 0, 228, 128, 2, 0, 255,
+ 128, 1, 0, 255, 128, 88,
+ 0, 0, 4, 1, 0, 7,
+ 128, 0, 0, 255, 128, 1,
+ 0, 228, 128, 2, 0, 228,
+ 128, 2, 0, 0, 3, 2,
+ 0, 7, 128, 1, 0, 255,
+ 129, 1, 0, 228, 128, 2,
+ 0, 0, 3, 0, 0, 8,
+ 128, 1, 0, 255, 129, 2,
+ 0, 0, 160, 5, 0, 0,
+ 3, 2, 0, 7, 128, 0,
+ 0, 255, 128, 2, 0, 228,
+ 128, 2, 0, 0, 3, 0,
+ 0, 8, 128, 1, 0, 255,
+ 129, 5, 0, 255, 128, 2,
+ 0, 0, 3, 2, 0, 8,
+ 128, 5, 0, 255, 129, 2,
+ 0, 0, 160, 6, 0, 0,
+ 2, 0, 0, 8, 128, 0,
+ 0, 255, 128, 4, 0, 0,
+ 4, 2, 0, 7, 128, 2,
+ 0, 228, 128, 0, 0, 255,
+ 128, 1, 0, 255, 128, 88,
+ 0, 0, 4, 1, 0, 7,
+ 128, 2, 0, 255, 128, 1,
+ 0, 228, 128, 2, 0, 228,
+ 128, 88, 0, 0, 4, 0,
+ 0, 7, 128, 8, 0, 85,
+ 129, 1, 0, 228, 128, 0,
+ 0, 228, 128, 18, 0, 0,
+ 4, 1, 0, 7, 128, 3,
+ 0, 255, 128, 0, 0, 228,
+ 128, 5, 0, 228, 128, 5,
+ 0, 0, 3, 1, 0, 8,
+ 128, 3, 0, 255, 128, 3,
+ 0, 255, 128, 88, 0, 0,
+ 4, 1, 0, 8, 128, 1,
+ 0, 255, 129, 2, 0, 0,
+ 160, 2, 0, 85, 160, 5,
+ 0, 0, 3, 0, 0, 7,
+ 128, 4, 0, 255, 128, 1,
+ 0, 228, 128, 5, 0, 0,
+ 3, 0, 0, 8, 128, 4,
+ 0, 255, 128, 4, 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, 1,
+ 0, 255, 128, 0, 0, 255,
+ 128, 88, 0, 0, 4, 4,
+ 0, 7, 128, 0, 0, 255,
+ 129, 0, 0, 228, 128, 4,
+ 0, 228, 128, 1, 0, 0,
+ 2, 0, 8, 15, 128, 4,
+ 0, 228, 128, 255, 255, 0,
+ 0, 83, 72, 68, 82, 252,
+ 21, 0, 0, 64, 0, 0,
+ 0, 127, 5, 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, 9, 0, 0,
+ 0, 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, 69, 0, 0, 9, 242,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 16, 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, 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, 1, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 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, 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, 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, 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, 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, 12,
+ 0, 0, 0, 31, 0, 4,
+ 3, 10, 0, 16, 0, 2,
+ 0, 0, 0, 52, 0, 0,
+ 7, 18, 0, 16, 0, 2,
+ 0, 0, 0, 42, 0, 16,
+ 0, 1, 0, 0, 0, 26,
+ 0, 16, 0, 1, 0, 0,
+ 0, 52, 0, 0, 7, 18,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 0, 16, 0, 1,
+ 0, 0, 0, 10, 0, 16,
+ 0, 2, 0, 0, 0, 51,
+ 0, 0, 7, 34, 0, 16,
+ 0, 2, 0, 0, 0, 42,
+ 0, 16, 0, 1, 0, 0,
+ 0, 26, 0, 16, 0, 1,
+ 0, 0, 0, 51, 0, 0,
+ 7, 34, 0, 16, 0, 2,
+ 0, 0, 0, 10, 0, 16,
+ 0, 1, 0, 0, 0, 26,
+ 0, 16, 0, 2, 0, 0,
+ 0, 0, 0, 0, 8, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 26, 0, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 10, 0, 16, 0, 2,
+ 0, 0, 0, 29, 0, 0,
+ 7, 18, 0, 16, 0, 3,
+ 0, 0, 0, 26, 0, 16,
+ 0, 0, 0, 0, 0, 10,
+ 0, 16, 0, 0, 0, 0,
+ 0, 31, 0, 4, 3, 10,
+ 0, 16, 0, 3, 0, 0,
+ 0, 0, 0, 0, 8, 242,
+ 0, 16, 0, 3, 0, 0,
+ 0, 6, 10, 16, 128, 65,
+ 0, 0, 0, 0, 0, 0,
+ 0, 150, 4, 16, 0, 0,
+ 0, 0, 0, 49, 0, 0,
+ 10, 114, 0, 16, 0, 4,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 22,
+ 7, 16, 0, 3, 0, 0,
+ 0, 14, 0, 0, 7, 114,
+ 0, 16, 0, 5, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 22, 7, 16,
+ 0, 3, 0, 0, 0, 56,
+ 0, 0, 7, 114, 0, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 3, 0, 0,
+ 0, 70, 2, 16, 0, 5,
+ 0, 0, 0, 55, 0, 0,
+ 9, 98, 0, 16, 0, 5,
+ 0, 0, 0, 6, 0, 16,
+ 0, 4, 0, 0, 0, 6,
+ 3, 16, 0, 2, 0, 0,
+ 0, 6, 1, 16, 0, 3,
+ 0, 0, 0, 29, 0, 0,
+ 7, 146, 0, 16, 0, 4,
+ 0, 0, 0, 166, 10, 16,
+ 0, 0, 0, 0, 0, 86,
+ 1, 16, 0, 0, 0, 0,
+ 0, 55, 0, 0, 9, 98,
+ 0, 16, 0, 6, 0, 0,
+ 0, 86, 5, 16, 0, 4,
+ 0, 0, 0, 246, 13, 16,
+ 0, 2, 0, 0, 0, 6,
+ 1, 16, 0, 3, 0, 0,
+ 0, 55, 0, 0, 9, 50,
+ 0, 16, 0, 3, 0, 0,
+ 0, 166, 10, 16, 0, 4,
+ 0, 0, 0, 230, 10, 16,
+ 0, 2, 0, 0, 0, 230,
+ 10, 16, 0, 3, 0, 0,
+ 0, 54, 0, 0, 5, 18,
+ 0, 16, 0, 6, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 5, 66, 0, 16, 0, 3,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 55,
+ 0, 0, 9, 114, 0, 16,
+ 0, 3, 0, 0, 0, 246,
+ 15, 16, 0, 4, 0, 0,
+ 0, 70, 2, 16, 0, 6,
+ 0, 0, 0, 70, 2, 16,
+ 0, 3, 0, 0, 0, 54,
+ 0, 0, 5, 18, 0, 16,
+ 0, 5, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 55, 0, 0, 9, 114,
+ 0, 16, 0, 3, 0, 0,
+ 0, 6, 0, 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, 0,
+ 0, 0, 8, 242, 0, 16,
+ 0, 4, 0, 0, 0, 86,
+ 10, 16, 128, 65, 0, 0,
+ 0, 0, 0, 0, 0, 134,
+ 1, 16, 0, 0, 0, 0,
+ 0, 49, 0, 0, 10, 114,
+ 0, 16, 0, 5, 0, 0,
+ 0, 2, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 22, 7, 16,
+ 0, 4, 0, 0, 0, 14,
+ 0, 0, 7, 114, 0, 16,
+ 0, 6, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 22, 7, 16, 0, 4,
+ 0, 0, 0, 56, 0, 0,
+ 7, 114, 0, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 4, 0, 0, 0, 70,
+ 2, 16, 0, 6, 0, 0,
+ 0, 55, 0, 0, 9, 82,
+ 0, 16, 0, 6, 0, 0,
+ 0, 6, 0, 16, 0, 5,
+ 0, 0, 0, 6, 3, 16,
+ 0, 2, 0, 0, 0, 6,
+ 1, 16, 0, 4, 0, 0,
+ 0, 29, 0, 0, 7, 146,
+ 0, 16, 0, 5, 0, 0,
+ 0, 166, 10, 16, 0, 0,
+ 0, 0, 0, 6, 4, 16,
+ 0, 0, 0, 0, 0, 55,
+ 0, 0, 9, 82, 0, 16,
+ 0, 7, 0, 0, 0, 86,
+ 5, 16, 0, 5, 0, 0,
+ 0, 246, 13, 16, 0, 2,
+ 0, 0, 0, 6, 1, 16,
+ 0, 4, 0, 0, 0, 55,
+ 0, 0, 9, 50, 0, 16,
+ 0, 2, 0, 0, 0, 166,
+ 10, 16, 0, 5, 0, 0,
+ 0, 182, 15, 16, 0, 2,
+ 0, 0, 0, 182, 15, 16,
+ 0, 4, 0, 0, 0, 54,
+ 0, 0, 5, 34, 0, 16,
+ 0, 7, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 54, 0, 0, 5, 66,
+ 0, 16, 0, 2, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 55, 0, 0,
+ 9, 114, 0, 16, 0, 2,
+ 0, 0, 0, 246, 15, 16,
+ 0, 5, 0, 0, 0, 70,
+ 2, 16, 0, 7, 0, 0,
+ 0, 70, 2, 16, 0, 2,
+ 0, 0, 0, 54, 0, 0,
+ 5, 34, 0, 16, 0, 6,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 55,
+ 0, 0, 9, 114, 0, 16,
+ 0, 3, 0, 0, 0, 6,
+ 0, 16, 0, 5, 0, 0,
+ 0, 70, 2, 16, 0, 6,
+ 0, 0, 0, 70, 2, 16,
+ 0, 2, 0, 0, 0, 21,
+ 0, 0, 1, 16, 0, 0,
+ 10, 18, 0, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 2,
+ 64, 0, 0, 154, 153, 153,
+ 62, 61, 10, 23, 63, 174,
+ 71, 225, 61, 0, 0, 0,
+ 0, 16, 0, 0, 10, 34,
+ 0, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 3,
+ 0, 0, 0, 2, 64, 0,
+ 0, 154, 153, 153, 62, 61,
+ 10, 23, 63, 174, 71, 225,
+ 61, 0, 0, 0, 0, 0,
+ 0, 0, 8, 18, 0, 16,
+ 0, 2, 0, 0, 0, 26,
+ 0, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 0, 2, 0, 0,
+ 0, 0, 0, 0, 7, 114,
+ 0, 16, 0, 2, 0, 0,
+ 0, 6, 0, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 3, 0, 0, 0, 16,
+ 0, 0, 10, 130, 0, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 2, 0, 0,
+ 0, 2, 64, 0, 0, 154,
+ 153, 153, 62, 61, 10, 23,
+ 63, 174, 71, 225, 61, 0,
+ 0, 0, 0, 51, 0, 0,
+ 7, 18, 0, 16, 0, 3,
+ 0, 0, 0, 26, 0, 16,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 0, 2, 0, 0,
+ 0, 51, 0, 0, 7, 18,
+ 0, 16, 0, 3, 0, 0,
+ 0, 42, 0, 16, 0, 2,
+ 0, 0, 0, 10, 0, 16,
+ 0, 3, 0, 0, 0, 52,
+ 0, 0, 7, 34, 0, 16,
+ 0, 3, 0, 0, 0, 26,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 0, 16, 0, 2,
+ 0, 0, 0, 52, 0, 0,
+ 7, 34, 0, 16, 0, 3,
+ 0, 0, 0, 42, 0, 16,
+ 0, 2, 0, 0, 0, 26,
+ 0, 16, 0, 3, 0, 0,
+ 0, 49, 0, 0, 7, 66,
+ 0, 16, 0, 3, 0, 0,
+ 0, 10, 0, 16, 0, 3,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 8, 114, 0, 16,
+ 0, 4, 0, 0, 0, 246,
+ 15, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 2, 0, 0,
+ 0, 56, 0, 0, 7, 114,
+ 0, 16, 0, 4, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 8, 18, 0, 16,
+ 0, 3, 0, 0, 0, 58,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 0, 16, 128, 65,
+ 0, 0, 0, 3, 0, 0,
+ 0, 14, 0, 0, 7, 114,
+ 0, 16, 0, 4, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 6, 0, 16,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 7, 114, 0, 16,
+ 0, 4, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 55, 0, 0,
+ 9, 114, 0, 16, 0, 2,
+ 0, 0, 0, 166, 10, 16,
+ 0, 3, 0, 0, 0, 70,
+ 2, 16, 0, 4, 0, 0,
+ 0, 70, 2, 16, 0, 2,
+ 0, 0, 0, 49, 0, 0,
+ 7, 18, 0, 16, 0, 3,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 128, 63, 26,
+ 0, 16, 0, 3, 0, 0,
+ 0, 0, 0, 0, 8, 114,
+ 0, 16, 0, 4, 0, 0,
+ 0, 246, 15, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 8, 66, 0, 16, 0, 3,
+ 0, 0, 0, 58, 0, 16,
+ 128, 65, 0, 0, 0, 2,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 128, 63, 56,
+ 0, 0, 7, 114, 0, 16,
+ 0, 4, 0, 0, 0, 166,
+ 10, 16, 0, 3, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 8, 34, 0, 16, 0, 3,
+ 0, 0, 0, 58, 0, 16,
+ 128, 65, 0, 0, 0, 2,
+ 0, 0, 0, 26, 0, 16,
+ 0, 3, 0, 0, 0, 14,
+ 0, 0, 7, 226, 0, 16,
+ 0, 3, 0, 0, 0, 6,
+ 9, 16, 0, 4, 0, 0,
+ 0, 86, 5, 16, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 7, 226, 0, 16, 0, 3,
+ 0, 0, 0, 246, 15, 16,
+ 0, 2, 0, 0, 0, 86,
+ 14, 16, 0, 3, 0, 0,
+ 0, 55, 0, 0, 9, 114,
+ 0, 16, 0, 2, 0, 0,
+ 0, 6, 0, 16, 0, 3,
+ 0, 0, 0, 150, 7, 16,
+ 0, 3, 0, 0, 0, 70,
+ 2, 16, 0, 2, 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, 13, 0, 0,
+ 0, 31, 0, 4, 3, 58,
+ 0, 16, 0, 2, 0, 0,
+ 0, 52, 0, 0, 7, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 42, 0, 16, 0, 0,
+ 0, 0, 0, 26, 0, 16,
+ 0, 0, 0, 0, 0, 52,
+ 0, 0, 7, 130, 0, 16,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 0, 0, 0, 0,
+ 0, 58, 0, 16, 0, 2,
+ 0, 0, 0, 51, 0, 0,
+ 7, 18, 0, 16, 0, 3,
+ 0, 0, 0, 42, 0, 16,
+ 0, 0, 0, 0, 0, 26,
+ 0, 16, 0, 0, 0, 0,
+ 0, 51, 0, 0, 7, 18,
+ 0, 16, 0, 3, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 10, 0, 16,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 8, 130, 0, 16,
+ 0, 3, 0, 0, 0, 58,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 0, 16, 128, 65,
+ 0, 0, 0, 3, 0, 0,
+ 0, 29, 0, 0, 7, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 26, 0, 16, 0, 1,
+ 0, 0, 0, 10, 0, 16,
+ 0, 1, 0, 0, 0, 31,
+ 0, 4, 3, 58, 0, 16,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 8, 242, 0, 16,
+ 0, 4, 0, 0, 0, 6,
+ 10, 16, 128, 65, 0, 0,
+ 0, 1, 0, 0, 0, 150,
+ 4, 16, 0, 1, 0, 0,
+ 0, 49, 0, 0, 10, 114,
+ 0, 16, 0, 5, 0, 0,
+ 0, 2, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 22, 7, 16,
+ 0, 4, 0, 0, 0, 14,
+ 0, 0, 7, 114, 0, 16,
+ 0, 6, 0, 0, 0, 246,
+ 15, 16, 0, 3, 0, 0,
+ 0, 22, 7, 16, 0, 4,
+ 0, 0, 0, 56, 0, 0,
+ 7, 114, 0, 16, 0, 3,
+ 0, 0, 0, 70, 2, 16,
+ 0, 4, 0, 0, 0, 70,
+ 2, 16, 0, 6, 0, 0,
+ 0, 55, 0, 0, 9, 98,
+ 0, 16, 0, 6, 0, 0,
+ 0, 6, 0, 16, 0, 5,
+ 0, 0, 0, 6, 3, 16,
+ 0, 3, 0, 0, 0, 6,
+ 1, 16, 0, 4, 0, 0,
+ 0, 29, 0, 0, 7, 146,
+ 0, 16, 0, 5, 0, 0,
+ 0, 166, 10, 16, 0, 1,
+ 0, 0, 0, 86, 1, 16,
+ 0, 1, 0, 0, 0, 55,
+ 0, 0, 9, 98, 0, 16,
+ 0, 7, 0, 0, 0, 86,
+ 5, 16, 0, 5, 0, 0,
+ 0, 246, 13, 16, 0, 3,
+ 0, 0, 0, 6, 1, 16,
+ 0, 4, 0, 0, 0, 55,
+ 0, 0, 9, 50, 0, 16,
+ 0, 4, 0, 0, 0, 166,
+ 10, 16, 0, 5, 0, 0,
+ 0, 230, 10, 16, 0, 3,
+ 0, 0, 0, 230, 10, 16,
+ 0, 4, 0, 0, 0, 54,
+ 0, 0, 5, 18, 0, 16,
+ 0, 7, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 54, 0, 0, 5, 66,
+ 0, 16, 0, 4, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 55, 0, 0,
+ 9, 114, 0, 16, 0, 4,
+ 0, 0, 0, 246, 15, 16,
+ 0, 5, 0, 0, 0, 70,
+ 2, 16, 0, 7, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 54, 0, 0,
+ 5, 18, 0, 16, 0, 6,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 55,
+ 0, 0, 9, 114, 0, 16,
+ 0, 4, 0, 0, 0, 6,
+ 0, 16, 0, 5, 0, 0,
+ 0, 70, 2, 16, 0, 6,
+ 0, 0, 0, 70, 2, 16,
+ 0, 4, 0, 0, 0, 18,
+ 0, 0, 1, 0, 0, 0,
+ 8, 242, 0, 16, 0, 5,
+ 0, 0, 0, 86, 10, 16,
+ 128, 65, 0, 0, 0, 1,
+ 0, 0, 0, 134, 1, 16,
+ 0, 1, 0, 0, 0, 49,
+ 0, 0, 10, 114, 0, 16,
+ 0, 6, 0, 0, 0, 2,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 22, 7, 16, 0, 5,
+ 0, 0, 0, 14, 0, 0,
+ 7, 114, 0, 16, 0, 7,
+ 0, 0, 0, 246, 15, 16,
+ 0, 3, 0, 0, 0, 22,
+ 7, 16, 0, 5, 0, 0,
+ 0, 56, 0, 0, 7, 114,
+ 0, 16, 0, 3, 0, 0,
+ 0, 70, 2, 16, 0, 5,
+ 0, 0, 0, 70, 2, 16,
+ 0, 7, 0, 0, 0, 55,
+ 0, 0, 9, 82, 0, 16,
+ 0, 7, 0, 0, 0, 6,
+ 0, 16, 0, 6, 0, 0,
+ 0, 6, 3, 16, 0, 3,
+ 0, 0, 0, 6, 1, 16,
+ 0, 5, 0, 0, 0, 29,
+ 0, 0, 7, 146, 0, 16,
+ 0, 6, 0, 0, 0, 166,
+ 10, 16, 0, 1, 0, 0,
+ 0, 6, 4, 16, 0, 1,
+ 0, 0, 0, 55, 0, 0,
+ 9, 82, 0, 16, 0, 8,
+ 0, 0, 0, 86, 5, 16,
+ 0, 6, 0, 0, 0, 246,
+ 13, 16, 0, 3, 0, 0,
+ 0, 6, 1, 16, 0, 5,
+ 0, 0, 0, 55, 0, 0,
+ 9, 50, 0, 16, 0, 3,
+ 0, 0, 0, 166, 10, 16,
+ 0, 6, 0, 0, 0, 182,
+ 15, 16, 0, 3, 0, 0,
+ 0, 182, 15, 16, 0, 5,
+ 0, 0, 0, 54, 0, 0,
+ 5, 34, 0, 16, 0, 8,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 5, 66, 0, 16,
+ 0, 3, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 55, 0, 0, 9, 114,
+ 0, 16, 0, 3, 0, 0,
+ 0, 246, 15, 16, 0, 6,
+ 0, 0, 0, 70, 2, 16,
+ 0, 8, 0, 0, 0, 70,
+ 2, 16, 0, 3, 0, 0,
+ 0, 54, 0, 0, 5, 34,
+ 0, 16, 0, 7, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 55, 0, 0,
+ 9, 114, 0, 16, 0, 4,
+ 0, 0, 0, 6, 0, 16,
+ 0, 6, 0, 0, 0, 70,
+ 2, 16, 0, 7, 0, 0,
+ 0, 70, 2, 16, 0, 3,
+ 0, 0, 0, 21, 0, 0,
+ 1, 16, 0, 0, 10, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 1,
+ 0, 0, 0, 2, 64, 0,
+ 0, 154, 153, 153, 62, 61,
+ 10, 23, 63, 174, 71, 225,
+ 61, 0, 0, 0, 0, 16,
+ 0, 0, 10, 18, 0, 16,
+ 0, 3, 0, 0, 0, 70,
+ 2, 16, 0, 4, 0, 0,
+ 0, 2, 64, 0, 0, 154,
+ 153, 153, 62, 61, 10, 23,
+ 63, 174, 71, 225, 61, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 130, 0, 16, 0, 2,
+ 0, 0, 0, 58, 0, 16,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 128, 65, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 7, 114, 0, 16,
+ 0, 3, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 16, 0, 0,
+ 10, 130, 0, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 3, 0, 0, 0, 2,
+ 64, 0, 0, 154, 153, 153,
+ 62, 61, 10, 23, 63, 174,
+ 71, 225, 61, 0, 0, 0,
+ 0, 51, 0, 0, 7, 130,
+ 0, 16, 0, 3, 0, 0,
+ 0, 26, 0, 16, 0, 3,
+ 0, 0, 0, 10, 0, 16,
+ 0, 3, 0, 0, 0, 51,
+ 0, 0, 7, 130, 0, 16,
+ 0, 3, 0, 0, 0, 42,
+ 0, 16, 0, 3, 0, 0,
+ 0, 58, 0, 16, 0, 3,
+ 0, 0, 0, 52, 0, 0,
+ 7, 18, 0, 16, 0, 4,
+ 0, 0, 0, 26, 0, 16,
+ 0, 3, 0, 0, 0, 10,
+ 0, 16, 0, 3, 0, 0,
+ 0, 52, 0, 0, 7, 18,
+ 0, 16, 0, 4, 0, 0,
+ 0, 42, 0, 16, 0, 3,
+ 0, 0, 0, 10, 0, 16,
+ 0, 4, 0, 0, 0, 49,
+ 0, 0, 7, 34, 0, 16,
+ 0, 4, 0, 0, 0, 58,
+ 0, 16, 0, 3, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 114, 0, 16, 0, 5,
+ 0, 0, 0, 246, 15, 16,
+ 128, 65, 0, 0, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 3, 0, 0, 0, 56,
+ 0, 0, 7, 114, 0, 16,
+ 0, 5, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 5,
+ 0, 0, 0, 0, 0, 0,
+ 8, 130, 0, 16, 0, 3,
+ 0, 0, 0, 58, 0, 16,
+ 0, 2, 0, 0, 0, 58,
+ 0, 16, 128, 65, 0, 0,
+ 0, 3, 0, 0, 0, 14,
+ 0, 0, 7, 114, 0, 16,
+ 0, 5, 0, 0, 0, 70,
+ 2, 16, 0, 5, 0, 0,
+ 0, 246, 15, 16, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 7, 114, 0, 16, 0, 5,
+ 0, 0, 0, 246, 15, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 5, 0, 0,
+ 0, 55, 0, 0, 9, 114,
+ 0, 16, 0, 3, 0, 0,
+ 0, 86, 5, 16, 0, 4,
+ 0, 0, 0, 70, 2, 16,
+ 0, 5, 0, 0, 0, 70,
+ 2, 16, 0, 3, 0, 0,
+ 0, 49, 0, 0, 7, 130,
+ 0, 16, 0, 3, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 10, 0, 16,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 8, 226, 0, 16,
+ 0, 4, 0, 0, 0, 246,
+ 15, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 6,
+ 9, 16, 0, 3, 0, 0,
+ 0, 0, 0, 0, 8, 18,
+ 0, 16, 0, 5, 0, 0,
+ 0, 58, 0, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 56, 0, 0,
+ 7, 226, 0, 16, 0, 4,
+ 0, 0, 0, 86, 14, 16,
+ 0, 4, 0, 0, 0, 6,
+ 0, 16, 0, 5, 0, 0,
+ 0, 0, 0, 0, 8, 18,
+ 0, 16, 0, 4, 0, 0,
+ 0, 58, 0, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 10, 0, 16, 0, 4,
+ 0, 0, 0, 14, 0, 0,
+ 7, 114, 0, 16, 0, 4,
+ 0, 0, 0, 150, 7, 16,
+ 0, 4, 0, 0, 0, 6,
+ 0, 16, 0, 4, 0, 0,
+ 0, 0, 0, 0, 7, 114,
+ 0, 16, 0, 4, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 4, 0, 0, 0, 55,
+ 0, 0, 9, 114, 0, 16,
+ 0, 2, 0, 0, 0, 246,
+ 15, 16, 0, 3, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 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, 14, 0, 0, 0, 31,
+ 0, 4, 3, 58, 0, 16,
+ 0, 2, 0, 0, 0, 16,
+ 0, 0, 10, 130, 0, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 1, 0, 0,
+ 0, 2, 64, 0, 0, 154,
+ 153, 153, 62, 61, 10, 23,
+ 63, 174, 71, 225, 61, 0,
+ 0, 0, 0, 16, 0, 0,
+ 10, 18, 0, 16, 0, 3,
+ 0, 0, 0, 70, 2, 16,
+ 0, 0, 0, 0, 0, 2,
+ 64, 0, 0, 154, 153, 153,
+ 62, 61, 10, 23, 63, 174,
+ 71, 225, 61, 0, 0, 0,
+ 0, 0, 0, 0, 8, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 58, 0, 16, 0, 2,
+ 0, 0, 0, 10, 0, 16,
+ 128, 65, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 7, 114, 0, 16, 0, 3,
+ 0, 0, 0, 70, 2, 16,
+ 0, 0, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 16, 0, 0, 10, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 3,
+ 0, 0, 0, 2, 64, 0,
+ 0, 154, 153, 153, 62, 61,
+ 10, 23, 63, 174, 71, 225,
+ 61, 0, 0, 0, 0, 51,
+ 0, 0, 7, 130, 0, 16,
+ 0, 3, 0, 0, 0, 26,
+ 0, 16, 0, 3, 0, 0,
+ 0, 10, 0, 16, 0, 3,
+ 0, 0, 0, 51, 0, 0,
+ 7, 130, 0, 16, 0, 3,
+ 0, 0, 0, 42, 0, 16,
+ 0, 3, 0, 0, 0, 58,
+ 0, 16, 0, 3, 0, 0,
+ 0, 52, 0, 0, 7, 18,
+ 0, 16, 0, 4, 0, 0,
+ 0, 26, 0, 16, 0, 3,
+ 0, 0, 0, 10, 0, 16,
+ 0, 3, 0, 0, 0, 52,
+ 0, 0, 7, 18, 0, 16,
+ 0, 4, 0, 0, 0, 42,
+ 0, 16, 0, 3, 0, 0,
+ 0, 10, 0, 16, 0, 4,
+ 0, 0, 0, 49, 0, 0,
+ 7, 34, 0, 16, 0, 4,
+ 0, 0, 0, 58, 0, 16,
+ 0, 3, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 8, 114,
+ 0, 16, 0, 5, 0, 0,
+ 0, 246, 15, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 3,
+ 0, 0, 0, 56, 0, 0,
+ 7, 114, 0, 16, 0, 5,
+ 0, 0, 0, 246, 15, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 5, 0, 0,
+ 0, 0, 0, 0, 8, 130,
+ 0, 16, 0, 3, 0, 0,
+ 0, 58, 0, 16, 0, 2,
+ 0, 0, 0, 58, 0, 16,
+ 128, 65, 0, 0, 0, 3,
+ 0, 0, 0, 14, 0, 0,
+ 7, 114, 0, 16, 0, 5,
+ 0, 0, 0, 70, 2, 16,
+ 0, 5, 0, 0, 0, 246,
+ 15, 16, 0, 3, 0, 0,
+ 0, 0, 0, 0, 7, 114,
+ 0, 16, 0, 5, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 5, 0, 0, 0, 55,
+ 0, 0, 9, 114, 0, 16,
+ 0, 3, 0, 0, 0, 86,
+ 5, 16, 0, 4, 0, 0,
+ 0, 70, 2, 16, 0, 5,
+ 0, 0, 0, 70, 2, 16,
+ 0, 3, 0, 0, 0, 49,
+ 0, 0, 7, 130, 0, 16,
+ 0, 3, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 128,
+ 63, 10, 0, 16, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 8, 226, 0, 16, 0, 4,
+ 0, 0, 0, 246, 15, 16,
+ 128, 65, 0, 0, 0, 2,
+ 0, 0, 0, 6, 9, 16,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 8, 18, 0, 16,
+ 0, 5, 0, 0, 0, 58,
+ 0, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 128,
+ 63, 56, 0, 0, 7, 226,
+ 0, 16, 0, 4, 0, 0,
+ 0, 86, 14, 16, 0, 4,
+ 0, 0, 0, 6, 0, 16,
+ 0, 5, 0, 0, 0, 0,
+ 0, 0, 8, 18, 0, 16,
+ 0, 4, 0, 0, 0, 58,
+ 0, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 0, 4, 0, 0,
+ 0, 14, 0, 0, 7, 114,
+ 0, 16, 0, 4, 0, 0,
+ 0, 150, 7, 16, 0, 4,
+ 0, 0, 0, 6, 0, 16,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 7, 114, 0, 16,
+ 0, 4, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 55, 0, 0,
+ 9, 114, 0, 16, 0, 2,
+ 0, 0, 0, 246, 15, 16,
+ 0, 3, 0, 0, 0, 70,
+ 2, 16, 0, 4, 0, 0,
+ 0, 70, 2, 16, 0, 3,
+ 0, 0, 0, 18, 0, 0,
+ 1, 16, 0, 0, 10, 130,
+ 0, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 154, 153, 153, 62, 61,
+ 10, 23, 63, 174, 71, 225,
+ 61, 0, 0, 0, 0, 16,
+ 0, 0, 10, 18, 0, 16,
+ 0, 3, 0, 0, 0, 70,
+ 2, 16, 0, 1, 0, 0,
+ 0, 2, 64, 0, 0, 154,
+ 153, 153, 62, 61, 10, 23,
+ 63, 174, 71, 225, 61, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 130, 0, 16, 0, 2,
+ 0, 0, 0, 58, 0, 16,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 128, 65, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 7, 114, 0, 16,
+ 0, 1, 0, 0, 0, 70,
+ 2, 16, 0, 1, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 16, 0, 0,
+ 10, 130, 0, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 2,
+ 64, 0, 0, 154, 153, 153,
+ 62, 61, 10, 23, 63, 174,
+ 71, 225, 61, 0, 0, 0,
+ 0, 51, 0, 0, 7, 18,
+ 0, 16, 0, 3, 0, 0,
+ 0, 26, 0, 16, 0, 1,
+ 0, 0, 0, 10, 0, 16,
+ 0, 1, 0, 0, 0, 51,
+ 0, 0, 7, 18, 0, 16,
+ 0, 3, 0, 0, 0, 42,
+ 0, 16, 0, 1, 0, 0,
+ 0, 10, 0, 16, 0, 3,
+ 0, 0, 0, 52, 0, 0,
+ 7, 34, 0, 16, 0, 3,
+ 0, 0, 0, 26, 0, 16,
+ 0, 1, 0, 0, 0, 10,
+ 0, 16, 0, 1, 0, 0,
+ 0, 52, 0, 0, 7, 34,
+ 0, 16, 0, 3, 0, 0,
+ 0, 42, 0, 16, 0, 1,
+ 0, 0, 0, 26, 0, 16,
+ 0, 3, 0, 0, 0, 49,
+ 0, 0, 7, 66, 0, 16,
+ 0, 3, 0, 0, 0, 10,
+ 0, 16, 0, 3, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 114, 0, 16, 0, 4,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 246,
+ 15, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 56,
+ 0, 0, 7, 114, 0, 16,
+ 0, 4, 0, 0, 0, 246,
+ 15, 16, 0, 2, 0, 0,
+ 0, 70, 2, 16, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 8, 18, 0, 16, 0, 3,
+ 0, 0, 0, 58, 0, 16,
+ 0, 2, 0, 0, 0, 10,
+ 0, 16, 128, 65, 0, 0,
+ 0, 3, 0, 0, 0, 14,
+ 0, 0, 7, 114, 0, 16,
+ 0, 4, 0, 0, 0, 70,
+ 2, 16, 0, 4, 0, 0,
+ 0, 6, 0, 16, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 7, 114, 0, 16, 0, 4,
+ 0, 0, 0, 246, 15, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 4, 0, 0,
+ 0, 55, 0, 0, 9, 114,
+ 0, 16, 0, 1, 0, 0,
+ 0, 166, 10, 16, 0, 3,
+ 0, 0, 0, 70, 2, 16,
+ 0, 4, 0, 0, 0, 70,
+ 2, 16, 0, 1, 0, 0,
+ 0, 49, 0, 0, 7, 18,
+ 0, 16, 0, 3, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 26, 0, 16,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 8, 114, 0, 16,
+ 0, 4, 0, 0, 0, 246,
+ 15, 16, 128, 65, 0, 0,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 1, 0, 0,
+ 0, 0, 0, 0, 8, 66,
+ 0, 16, 0, 3, 0, 0,
+ 0, 58, 0, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 56, 0, 0,
+ 7, 114, 0, 16, 0, 4,
+ 0, 0, 0, 166, 10, 16,
+ 0, 3, 0, 0, 0, 70,
+ 2, 16, 0, 4, 0, 0,
+ 0, 0, 0, 0, 8, 34,
+ 0, 16, 0, 3, 0, 0,
+ 0, 58, 0, 16, 128, 65,
+ 0, 0, 0, 2, 0, 0,
+ 0, 26, 0, 16, 0, 3,
+ 0, 0, 0, 14, 0, 0,
+ 7, 226, 0, 16, 0, 3,
+ 0, 0, 0, 6, 9, 16,
+ 0, 4, 0, 0, 0, 86,
+ 5, 16, 0, 3, 0, 0,
+ 0, 0, 0, 0, 7, 226,
+ 0, 16, 0, 3, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 86, 14, 16,
+ 0, 3, 0, 0, 0, 55,
+ 0, 0, 9, 114, 0, 16,
+ 0, 2, 0, 0, 0, 6,
+ 0, 16, 0, 3, 0, 0,
+ 0, 150, 7, 16, 0, 3,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 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, 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, 56,
+ 0, 0, 7, 114, 32, 16,
+ 0, 0, 0, 0, 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, 195,
+ 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 0, 3, 0, 0, 0, 1,
+ 0, 0, 0, 7, 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, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 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,
+ 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, 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, 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, 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, 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,
+ 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, 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, 0,
+ 0, 0, 0, 15, 0, 0,
+ 0, 83, 86, 95, 84, 97,
+ 114, 103, 101, 116, 0, 171,
+ 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, 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, 0, 228,
+ 3, 0, 0, 136, 6, 0,
+ 0, 188, 6, 0, 0, 65,
+ 111, 110, 57, 84, 1, 0,
+ 0, 84, 1, 0, 0, 0,
+ 2, 254, 255, 252, 0, 0,
+ 0, 88, 0, 0, 0, 4,
+ 0, 36, 0, 0, 0, 84,
+ 0, 0, 0, 84, 0, 0,
+ 0, 36, 0, 1, 0, 84,
+ 0, 0, 0, 0, 0, 1,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 1,
+ 0, 2, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 3, 0, 0, 0, 0,
+ 0, 1, 0, 3, 0, 1,
+ 0, 5, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 254, 255, 81, 0, 0,
+ 5, 6, 0, 15, 160, 0,
+ 0, 128, 63, 0, 0, 0,
+ 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, 4, 128, 0, 0, 0,
+ 128, 6, 0, 0, 160, 5,
+ 0, 0, 3, 0, 0, 4,
+ 128, 0, 0, 170, 128, 5,
+ 0, 0, 160, 5, 0, 0,
+ 3, 1, 0, 1, 128, 0,
+ 0, 170, 128, 6, 0, 85,
+ 160, 2, 0, 0, 3, 0,
+ 0, 4, 128, 0, 0, 85,
+ 129, 6, 0, 0, 160, 2,
+ 0, 0, 3, 0, 0, 3,
+ 192, 0, 0, 228, 128, 0,
+ 0, 228, 160, 5, 0, 0,
+ 3, 0, 0, 1, 128, 0,
+ 0, 170, 128, 5, 0, 85,
+ 160, 5, 0, 0, 3, 1,
+ 0, 2, 128, 0, 0, 0,
+ 128, 6, 0, 85, 160, 1,
+ 0, 0, 2, 1, 0, 4,
+ 128, 6, 0, 0, 160, 8,
+ 0, 0, 3, 0, 0, 8,
+ 224, 1, 0, 228, 128, 3,
+ 0, 228, 160, 8, 0, 0,
+ 3, 0, 0, 4, 224, 1,
+ 0, 228, 128, 4, 0, 228,
+ 160, 1, 0, 0, 2, 0,
+ 0, 12, 192, 6, 0, 36,
+ 160, 255, 255, 0, 0, 83,
+ 72, 68, 82, 204, 1, 0,
+ 0, 64, 0, 1, 0, 115,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 89, 0, 0, 4, 70,
+ 142, 32, 0, 1, 0, 0,
+ 0, 4, 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, 104, 0, 0,
+ 2, 2, 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,
+ 0, 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,
+ 5, 50, 32, 16, 0, 0,
+ 0, 0, 0, 70, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 18, 0, 16,
+ 0, 0, 0, 0, 0, 10,
+ 0, 16, 0, 0, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 0, 0, 0,
+ 8, 34, 0, 16, 0, 0,
+ 0, 0, 0, 26, 0, 16,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 128, 63, 56,
+ 0, 0, 8, 50, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 128, 32, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 56, 0, 0, 10, 50,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 0, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 63, 0,
+ 0, 0, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 5, 66, 0, 16,
+ 0, 1, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 128,
+ 63, 16, 0, 0, 8, 66,
+ 32, 16, 0, 1, 0, 0,
+ 0, 70, 2, 16, 0, 1,
+ 0, 0, 0, 70, 130, 32,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 16, 0, 0,
+ 8, 130, 32, 16, 0, 1,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 70,
+ 130, 32, 0, 1, 0, 0,
+ 0, 1, 0, 0, 0, 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, 2, 0, 0,
+ 0, 70, 128, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 12, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 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, 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, 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, 0, 0, 0,
+ 0, 0, 0, 82, 68, 69,
+ 70, 156, 2, 0, 0, 2,
+ 0, 0, 0, 100, 0, 0,
+ 0, 2, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 254,
+ 255, 0, 1, 0, 0, 103,
+ 2, 0, 0, 92, 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, 96,
+ 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, 99, 98, 48, 0, 99,
+ 98, 50, 0, 92, 0, 0,
+ 0, 4, 0, 0, 0, 148,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 7, 0, 0, 0, 52,
+ 1, 0, 0, 112, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 244, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 16, 1, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 26, 1, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 40, 1, 0,
+ 0, 48, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 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, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 171,
+ 171, 220, 1, 0, 0, 0,
+ 0, 0, 0, 44, 0, 0,
+ 0, 2, 0, 0, 0, 244,
+ 1, 0, 0, 0, 0, 0,
+ 0, 4, 2, 0, 0, 48,
+ 0, 0, 0, 8, 0, 0,
+ 0, 2, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 32, 2, 0, 0, 64,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 40,
+ 2, 0, 0, 0, 0, 0,
+ 0, 56, 2, 0, 0, 80,
+ 0, 0, 0, 8, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 64, 2, 0, 0, 88,
+ 0, 0, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 68,
+ 2, 0, 0, 0, 0, 0,
+ 0, 84, 2, 0, 0, 92,
+ 0, 0, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 68,
+ 2, 0, 0, 0, 0, 0,
+ 0, 92, 2, 0, 0, 96,
+ 0, 0, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 68,
+ 2, 0, 0, 0, 0, 0,
+ 0, 68, 101, 118, 105, 99,
+ 101, 83, 112, 97, 99, 101,
+ 84, 111, 85, 115, 101, 114,
+ 83, 112, 97, 99, 101, 0,
+ 171, 3, 0, 3, 0, 3,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 100,
+ 105, 109, 101, 110, 115, 105,
+ 111, 110, 115, 0, 171, 1,
+ 0, 3, 0, 1, 0, 2,
+ 0, 0, 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, 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, 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, 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, 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, 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, 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,
+ 0, 88, 6, 0, 0, 212,
+ 6, 0, 0, 60, 9, 0,
+ 0, 172, 9, 0, 0, 65,
+ 111, 110, 57, 64, 2, 0,
+ 0, 64, 2, 0, 0, 0,
+ 2, 255, 255, 8, 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, 4,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 1, 2, 255,
+ 255, 81, 0, 0, 5, 3,
+ 0, 15, 160, 0, 0, 0,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 81, 0, 0, 5, 4,
+ 0, 15, 160, 0, 0, 128,
+ 63, 0, 0, 128, 191, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 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, 3, 128, 0, 0, 235,
+ 176, 1, 0, 228, 161, 90,
+ 0, 0, 4, 0, 0, 8,
+ 128, 0, 0, 228, 128, 0,
+ 0, 228, 128, 2, 0, 0,
+ 161, 5, 0, 0, 3, 0,
+ 0, 8, 128, 0, 0, 255,
+ 128, 1, 0, 170, 160, 1,
+ 0, 0, 2, 0, 0, 4,
+ 128, 1, 0, 255, 160, 8,
+ 0, 0, 3, 0, 0, 1,
+ 128, 0, 0, 228, 128, 0,
+ 0, 228, 160, 4, 0, 0,
+ 4, 0, 0, 2, 128, 0,
+ 0, 0, 128, 0, 0, 0,
+ 128, 0, 0, 255, 129, 35,
+ 0, 0, 2, 0, 0, 4,
+ 128, 0, 0, 85, 128, 7,
+ 0, 0, 2, 0, 0, 4,
+ 128, 0, 0, 170, 128, 6,
+ 0, 0, 2, 1, 0, 1,
+ 128, 0, 0, 170, 128, 1,
+ 0, 0, 2, 1, 0, 6,
+ 128, 1, 0, 0, 129, 2,
+ 0, 0, 3, 0, 0, 13,
+ 128, 0, 0, 0, 128, 1,
+ 0, 148, 128, 6, 0, 0,
+ 2, 1, 0, 1, 128, 1,
+ 0, 170, 160, 5, 0, 0,
+ 3, 0, 0, 13, 128, 0,
+ 0, 228, 128, 1, 0, 0,
+ 128, 1, 0, 0, 2, 1,
+ 0, 8, 128, 1, 0, 255,
+ 160, 4, 0, 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, 0, 0, 0,
+ 128, 0, 0, 255, 128, 88,
+ 0, 0, 4, 0, 0, 13,
+ 128, 1, 0, 148, 128, 4,
+ 0, 68, 160, 4, 0, 230,
+ 160, 1, 0, 0, 2, 2,
+ 0, 2, 128, 3, 0, 0,
+ 160, 66, 0, 0, 3, 1,
+ 0, 15, 128, 0, 0, 228,
+ 176, 1, 8, 228, 160, 66,
+ 0, 0, 3, 2, 0, 15,
+ 128, 2, 0, 228, 128, 0,
+ 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, 255,
+ 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, 0, 0, 170, 128, 88,
+ 0, 0, 4, 1, 0, 15,
+ 128, 0, 0, 0, 129, 4,
+ 0, 170, 160, 1, 0, 228,
+ 128, 88, 0, 0, 4, 0,
+ 0, 15, 128, 0, 0, 85,
+ 128, 1, 0, 228, 128, 4,
+ 0, 170, 160, 1, 0, 0,
+ 2, 0, 8, 15, 128, 0,
+ 0, 228, 128, 255, 255, 0,
+ 0, 83, 72, 68, 82, 208,
+ 3, 0, 0, 64, 0, 0,
+ 0, 244, 0, 0, 0, 89,
+ 0, 0, 4, 70, 142, 32,
+ 0, 0, 0, 0, 0, 7,
+ 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, 3, 0, 0,
+ 0, 0, 0, 0, 9, 50,
+ 0, 16, 0, 0, 0, 0,
+ 0, 230, 26, 16, 0, 1,
+ 0, 0, 0, 70, 128, 32,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 54, 0, 0, 6, 66,
+ 0, 16, 0, 0, 0, 0,
+ 0, 58, 128, 32, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 16, 0, 0, 8, 66,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 2, 16, 0, 0,
+ 0, 0, 0, 70, 130, 32,
+ 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 15, 0, 0,
+ 7, 18, 0, 16, 0, 0,
+ 0, 0, 0, 70, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 0, 9, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 10, 128, 32,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 6, 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, 5,
+ 0, 0, 0, 50, 0, 0,
+ 10, 18, 0, 16, 0, 0,
+ 0, 0, 0, 42, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 128, 65,
+ 0, 0, 0, 0, 0, 0,
+ 0, 49, 0, 0, 7, 34,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 75,
+ 0, 0, 6, 18, 0, 16,
+ 0, 1, 0, 0, 0, 10,
+ 0, 16, 128, 129, 0, 0,
+ 0, 0, 0, 0, 0, 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, 166,
+ 10, 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, 16,
+ 0, 0, 0, 0, 0, 166,
+ 138, 32, 0, 0, 0, 0,
+ 0, 5, 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, 4, 0, 0,
+ 0, 29, 0, 0, 9, 50,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 0, 16, 0, 1,
+ 0, 0, 0, 246, 143, 32,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 1, 0, 0, 10, 50,
+ 0, 16, 0, 1, 0, 0,
+ 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, 0,
+ 0, 0, 8, 18, 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, 50, 0, 0, 9, 18,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 0, 16, 0, 1,
+ 0, 0, 0, 10, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 0, 16, 0, 0, 0, 0,
+ 0, 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, 16, 0, 2,
+ 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, 31, 0, 4,
+ 3, 26, 0, 16, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 8, 242, 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, 0, 0, 62,
+ 0, 0, 1, 21, 0, 0,
+ 1, 52, 0, 0, 7, 18,
+ 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, 18, 0, 16,
+ 0, 0, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 31, 0, 4,
+ 3, 10, 0, 16, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 8, 242, 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, 0, 0, 62,
+ 0, 0, 1, 21, 0, 0,
+ 1, 56, 0, 0, 7, 114,
+ 0, 16, 0, 2, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 2, 0, 0, 0, 69,
+ 0, 0, 9, 242, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 16, 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, 246,
+ 15, 16, 0, 0, 0, 0,
+ 0, 70, 14, 16, 0, 2,
+ 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, 19, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 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, 2,
+ 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, 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, 96, 2, 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, 43, 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, 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, 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, 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, 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,
+ 50, 0, 171, 219, 0, 0,
+ 0, 7, 0, 0, 0, 248,
+ 0, 0, 0, 112, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 160, 1, 0,
+ 0, 0, 0, 0, 0, 44,
+ 0, 0, 0, 0, 0, 0,
+ 0, 184, 1, 0, 0, 0,
+ 0, 0, 0, 200, 1, 0,
+ 0, 48, 0, 0, 0, 8,
+ 0, 0, 0, 0, 0, 0,
+ 0, 212, 1, 0, 0, 0,
+ 0, 0, 0, 228, 1, 0,
+ 0, 64, 0, 0, 0, 12,
+ 0, 0, 0, 2, 0, 0,
+ 0, 236, 1, 0, 0, 0,
+ 0, 0, 0, 252, 1, 0,
+ 0, 80, 0, 0, 0, 8,
+ 0, 0, 0, 2, 0, 0,
+ 0, 212, 1, 0, 0, 0,
+ 0, 0, 0, 4, 2, 0,
+ 0, 88, 0, 0, 0, 4,
+ 0, 0, 0, 2, 0, 0,
+ 0, 8, 2, 0, 0, 0,
+ 0, 0, 0, 24, 2, 0,
+ 0, 92, 0, 0, 0, 4,
+ 0, 0, 0, 2, 0, 0,
+ 0, 8, 2, 0, 0, 0,
+ 0, 0, 0, 32, 2, 0,
+ 0, 96, 0, 0, 0, 4,
+ 0, 0, 0, 2, 0, 0,
+ 0, 8, 2, 0, 0, 0,
+ 0, 0, 0, 68, 101, 118,
+ 105, 99, 101, 83, 112, 97,
+ 99, 101, 84, 111, 85, 115,
+ 101, 114, 83, 112, 97, 99,
+ 101, 0, 171, 3, 0, 3,
+ 0, 3, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 100, 105, 109, 101, 110,
+ 115, 105, 111, 110, 115, 0,
+ 171, 1, 0, 3, 0, 1,
+ 0, 2, 0, 0, 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, 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, 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, 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, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 15, 0, 0, 0, 83,
+ 86, 95, 84, 97, 114, 103,
+ 101, 116, 0, 171, 171, 159,
+ 101, 0, 0, 0, 0, 0,
+ 0, 65, 48, 0, 44, 7,
+ 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, 0, 0, 228, 3,
+ 0, 0, 136, 6, 0, 0,
+ 188, 6, 0, 0, 65, 111,
+ 110, 57, 84, 1, 0, 0,
+ 84, 1, 0, 0, 0, 2,
+ 254, 255, 252, 0, 0, 0,
+ 88, 0, 0, 0, 4, 0,
+ 36, 0, 0, 0, 84, 0,
+ 0, 0, 84, 0, 0, 0,
+ 36, 0, 1, 0, 84, 0,
+ 0, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 1, 0,
+ 2, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 3, 0, 0, 0, 0, 0,
+ 1, 0, 3, 0, 1, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 254, 255, 81, 0, 0, 5,
+ 6, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 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,
+ 4, 128, 0, 0, 0, 128,
+ 6, 0, 0, 160, 5, 0,
+ 0, 3, 0, 0, 4, 128,
+ 0, 0, 170, 128, 5, 0,
+ 0, 160, 5, 0, 0, 3,
+ 1, 0, 1, 128, 0, 0,
+ 170, 128, 6, 0, 85, 160,
+ 2, 0, 0, 3, 0, 0,
+ 4, 128, 0, 0, 85, 129,
+ 6, 0, 0, 160, 2, 0,
+ 0, 3, 0, 0, 3, 192,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 170, 128, 5, 0, 85, 160,
+ 5, 0, 0, 3, 1, 0,
+ 2, 128, 0, 0, 0, 128,
+ 6, 0, 85, 160, 1, 0,
+ 0, 2, 1, 0, 4, 128,
+ 6, 0, 0, 160, 8, 0,
+ 0, 3, 0, 0, 8, 224,
+ 1, 0, 228, 128, 3, 0,
+ 228, 160, 8, 0, 0, 3,
+ 0, 0, 4, 224, 1, 0,
+ 228, 128, 4, 0, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 12, 192, 6, 0, 36, 160,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 204, 1, 0, 0,
+ 64, 0, 1, 0, 115, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 1, 0, 0, 0,
+ 4, 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, 104, 0, 0, 2,
+ 2, 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, 0,
+ 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, 5,
+ 50, 32, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 8,
+ 34, 0, 16, 0, 0, 0,
+ 0, 0, 26, 0, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 8, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 128, 32, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 10, 50, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 66, 0, 16, 0,
+ 1, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 16, 0, 0, 8, 66, 32,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 130, 32, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 8,
+ 130, 32, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 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, 2, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 12, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 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,
+ 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, 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, 0, 0, 0, 0,
+ 0, 0, 82, 68, 69, 70,
+ 156, 2, 0, 0, 2, 0,
+ 0, 0, 100, 0, 0, 0,
+ 2, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 254, 255,
+ 0, 1, 0, 0, 103, 2,
+ 0, 0, 92, 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, 96, 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,
+ 99, 98, 48, 0, 99, 98,
+ 50, 0, 92, 0, 0, 0,
+ 4, 0, 0, 0, 148, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 96, 0, 0, 0,
+ 7, 0, 0, 0, 52, 1,
+ 0, 0, 112, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 244, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 16, 1, 0, 0,
+ 16, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 26, 1, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 40, 1, 0, 0,
+ 48, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 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,
+ 84, 101, 120, 116, 67, 111,
+ 108, 111, 114, 0, 171, 171,
+ 220, 1, 0, 0, 0, 0,
+ 0, 0, 44, 0, 0, 0,
+ 2, 0, 0, 0, 244, 1,
+ 0, 0, 0, 0, 0, 0,
+ 4, 2, 0, 0, 48, 0,
+ 0, 0, 8, 0, 0, 0,
+ 2, 0, 0, 0, 16, 2,
+ 0, 0, 0, 0, 0, 0,
+ 32, 2, 0, 0, 64, 0,
+ 0, 0, 12, 0, 0, 0,
+ 0, 0, 0, 0, 40, 2,
+ 0, 0, 0, 0, 0, 0,
+ 56, 2, 0, 0, 80, 0,
+ 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 16, 2,
+ 0, 0, 0, 0, 0, 0,
+ 64, 2, 0, 0, 88, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 68, 2,
+ 0, 0, 0, 0, 0, 0,
+ 84, 2, 0, 0, 92, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 68, 2,
+ 0, 0, 0, 0, 0, 0,
+ 92, 2, 0, 0, 96, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 68, 2,
+ 0, 0, 0, 0, 0, 0,
+ 68, 101, 118, 105, 99, 101,
+ 83, 112, 97, 99, 101, 84,
+ 111, 85, 115, 101, 114, 83,
+ 112, 97, 99, 101, 0, 171,
+ 3, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 100, 105,
+ 109, 101, 110, 115, 105, 111,
+ 110, 115, 0, 171, 1, 0,
+ 3, 0, 1, 0, 2, 0,
+ 0, 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, 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, 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, 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, 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, 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,
+ 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,
+ 56, 4, 0, 0, 180, 4,
+ 0, 0, 28, 7, 0, 0,
+ 140, 7, 0, 0, 65, 111,
+ 110, 57, 132, 1, 0, 0,
+ 132, 1, 0, 0, 0, 2,
+ 255, 255, 76, 1, 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, 4, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 255, 255,
+ 81, 0, 0, 5, 2, 0,
+ 15, 160, 0, 0, 0, 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,
+ 5, 0, 0, 3, 0, 0,
+ 8, 128, 1, 0, 255, 160,
+ 1, 0, 255, 160, 2, 0,
+ 0, 3, 0, 0, 3, 128,
+ 0, 0, 235, 176, 1, 0,
+ 228, 161, 90, 0, 0, 4,
+ 0, 0, 8, 128, 0, 0,
+ 228, 128, 0, 0, 228, 128,
+ 0, 0, 255, 129, 5, 0,
+ 0, 3, 0, 0, 8, 128,
+ 0, 0, 255, 128, 2, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 0, 4, 128, 1, 0,
+ 255, 160, 8, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 228, 128, 0, 0, 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, 128,
+ 0, 0, 255, 128, 1, 0,
+ 0, 2, 0, 0, 2, 128,
+ 2, 0, 0, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 228, 176, 1, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 0, 0,
+ 228, 128, 0, 8, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 1, 0, 255, 160,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 0, 0, 170, 161, 0, 0,
+ 255, 129, 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, 255, 128,
+ 2, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 15, 128,
+ 0, 0, 0, 128, 2, 0,
+ 85, 160, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 108, 2, 0, 0,
+ 64, 0, 0, 0, 155, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 6, 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, 0, 0,
+ 0, 9, 50, 0, 16, 0,
+ 0, 0, 0, 0, 230, 26,
+ 16, 0, 1, 0, 0, 0,
+ 70, 128, 32, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 54, 0,
+ 0, 6, 66, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 16, 0,
+ 0, 8, 66, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 18, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 58, 128, 32, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 14, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 8, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 128, 32, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 29, 0, 0, 9,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 58, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 34, 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,
+ 16, 0, 0, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 31, 0,
+ 4, 3, 42, 0, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 242, 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, 0, 0,
+ 62, 0, 0, 1, 21, 0,
+ 0, 1, 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, 0, 0, 0, 0,
+ 70, 16, 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,
+ 246, 15, 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, 19, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 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, 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, 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,
+ 0, 0, 0, 0, 0, 0,
+ 82, 68, 69, 70, 96, 2,
+ 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, 43, 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, 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, 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, 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,
+ 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, 50, 0, 171, 219, 0,
+ 0, 0, 7, 0, 0, 0,
+ 248, 0, 0, 0, 112, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 160, 1,
+ 0, 0, 0, 0, 0, 0,
+ 44, 0, 0, 0, 0, 0,
+ 0, 0, 184, 1, 0, 0,
+ 0, 0, 0, 0, 200, 1,
+ 0, 0, 48, 0, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 212, 1, 0, 0,
+ 0, 0, 0, 0, 228, 1,
+ 0, 0, 64, 0, 0, 0,
+ 12, 0, 0, 0, 2, 0,
+ 0, 0, 236, 1, 0, 0,
+ 0, 0, 0, 0, 252, 1,
+ 0, 0, 80, 0, 0, 0,
+ 8, 0, 0, 0, 2, 0,
+ 0, 0, 212, 1, 0, 0,
+ 0, 0, 0, 0, 4, 2,
+ 0, 0, 88, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 8, 2, 0, 0,
+ 0, 0, 0, 0, 24, 2,
+ 0, 0, 92, 0, 0, 0,
+ 4, 0, 0, 0, 2, 0,
+ 0, 0, 8, 2, 0, 0,
+ 0, 0, 0, 0, 32, 2,
+ 0, 0, 96, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 8, 2, 0, 0,
+ 0, 0, 0, 0, 68, 101,
+ 118, 105, 99, 101, 83, 112,
+ 97, 99, 101, 84, 111, 85,
+ 115, 101, 114, 83, 112, 97,
+ 99, 101, 0, 171, 3, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 100, 105, 109, 101,
+ 110, 115, 105, 111, 110, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 2, 0, 0, 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, 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, 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, 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,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 83, 86, 95, 84, 97, 114,
+ 103, 101, 116, 0, 171, 171,
+ 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, 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, 0, 228,
+ 3, 0, 0, 136, 6, 0,
+ 0, 188, 6, 0, 0, 65,
+ 111, 110, 57, 84, 1, 0,
+ 0, 84, 1, 0, 0, 0,
+ 2, 254, 255, 252, 0, 0,
+ 0, 88, 0, 0, 0, 4,
+ 0, 36, 0, 0, 0, 84,
+ 0, 0, 0, 84, 0, 0,
+ 0, 36, 0, 1, 0, 84,
+ 0, 0, 0, 0, 0, 1,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 1,
+ 0, 2, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 2,
+ 0, 3, 0, 0, 0, 0,
+ 0, 1, 0, 3, 0, 1,
+ 0, 5, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 254, 255, 81, 0, 0,
+ 5, 6, 0, 15, 160, 0,
+ 0, 128, 63, 0, 0, 0,
+ 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, 4, 128, 0, 0, 0,
+ 128, 6, 0, 0, 160, 5,
+ 0, 0, 3, 0, 0, 4,
+ 128, 0, 0, 170, 128, 5,
+ 0, 0, 160, 5, 0, 0,
+ 3, 1, 0, 1, 128, 0,
+ 0, 170, 128, 6, 0, 85,
+ 160, 2, 0, 0, 3, 0,
+ 0, 4, 128, 0, 0, 85,
+ 129, 6, 0, 0, 160, 2,
+ 0, 0, 3, 0, 0, 3,
+ 192, 0, 0, 228, 128, 0,
+ 0, 228, 160, 5, 0, 0,
+ 3, 0, 0, 1, 128, 0,
+ 0, 170, 128, 5, 0, 85,
+ 160, 5, 0, 0, 3, 1,
+ 0, 2, 128, 0, 0, 0,
+ 128, 6, 0, 85, 160, 1,
+ 0, 0, 2, 1, 0, 4,
+ 128, 6, 0, 0, 160, 8,
+ 0, 0, 3, 0, 0, 8,
+ 224, 1, 0, 228, 128, 3,
+ 0, 228, 160, 8, 0, 0,
+ 3, 0, 0, 4, 224, 1,
+ 0, 228, 128, 4, 0, 228,
+ 160, 1, 0, 0, 2, 0,
+ 0, 12, 192, 6, 0, 36,
+ 160, 255, 255, 0, 0, 83,
+ 72, 68, 82, 204, 1, 0,
+ 0, 64, 0, 1, 0, 115,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 89, 0, 0, 4, 70,
+ 142, 32, 0, 1, 0, 0,
+ 0, 4, 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, 104, 0, 0,
+ 2, 2, 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,
+ 0, 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,
+ 5, 50, 32, 16, 0, 0,
+ 0, 0, 0, 70, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 18, 0, 16,
+ 0, 0, 0, 0, 0, 10,
+ 0, 16, 0, 0, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 0, 0, 0,
+ 8, 34, 0, 16, 0, 0,
+ 0, 0, 0, 26, 0, 16,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 128, 63, 56,
+ 0, 0, 8, 50, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 128, 32, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 56, 0, 0, 10, 50,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 0, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 63, 0,
+ 0, 0, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 5, 66, 0, 16,
+ 0, 1, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 128,
+ 63, 16, 0, 0, 8, 66,
+ 32, 16, 0, 1, 0, 0,
+ 0, 70, 2, 16, 0, 1,
+ 0, 0, 0, 70, 130, 32,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 16, 0, 0,
+ 8, 130, 32, 16, 0, 1,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 70,
+ 130, 32, 0, 1, 0, 0,
+ 0, 1, 0, 0, 0, 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, 2, 0, 0,
+ 0, 70, 128, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 12, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 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, 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, 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, 0, 0, 0,
+ 0, 0, 0, 82, 68, 69,
+ 70, 156, 2, 0, 0, 2,
+ 0, 0, 0, 100, 0, 0,
+ 0, 2, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 254,
+ 255, 0, 1, 0, 0, 103,
+ 2, 0, 0, 92, 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, 96,
+ 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, 99, 98, 48, 0, 99,
+ 98, 50, 0, 92, 0, 0,
+ 0, 4, 0, 0, 0, 148,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 7, 0, 0, 0, 52,
+ 1, 0, 0, 112, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 244, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 16, 1, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 26, 1, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 40, 1, 0,
+ 0, 48, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 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, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 171,
+ 171, 220, 1, 0, 0, 0,
+ 0, 0, 0, 44, 0, 0,
+ 0, 2, 0, 0, 0, 244,
+ 1, 0, 0, 0, 0, 0,
+ 0, 4, 2, 0, 0, 48,
+ 0, 0, 0, 8, 0, 0,
+ 0, 2, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 32, 2, 0, 0, 64,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 40,
+ 2, 0, 0, 0, 0, 0,
+ 0, 56, 2, 0, 0, 80,
+ 0, 0, 0, 8, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 64, 2, 0, 0, 88,
+ 0, 0, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 68,
+ 2, 0, 0, 0, 0, 0,
+ 0, 84, 2, 0, 0, 92,
+ 0, 0, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 68,
+ 2, 0, 0, 0, 0, 0,
+ 0, 92, 2, 0, 0, 96,
+ 0, 0, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 68,
+ 2, 0, 0, 0, 0, 0,
+ 0, 68, 101, 118, 105, 99,
+ 101, 83, 112, 97, 99, 101,
+ 84, 111, 85, 115, 101, 114,
+ 83, 112, 97, 99, 101, 0,
+ 171, 3, 0, 3, 0, 3,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 100,
+ 105, 109, 101, 110, 115, 105,
+ 111, 110, 115, 0, 171, 1,
+ 0, 3, 0, 1, 0, 2,
+ 0, 0, 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, 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, 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, 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, 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, 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, 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,
+ 0, 88, 6, 0, 0, 212,
+ 6, 0, 0, 64, 9, 0,
+ 0, 176, 9, 0, 0, 65,
+ 111, 110, 57, 64, 2, 0,
+ 0, 64, 2, 0, 0, 0,
+ 2, 255, 255, 8, 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, 4,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 1, 2, 255,
+ 255, 81, 0, 0, 5, 3,
+ 0, 15, 160, 0, 0, 0,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 81, 0, 0, 5, 4,
+ 0, 15, 160, 0, 0, 128,
+ 63, 0, 0, 128, 191, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 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, 3, 128, 0, 0, 235,
+ 176, 1, 0, 228, 161, 90,
+ 0, 0, 4, 0, 0, 8,
+ 128, 0, 0, 228, 128, 0,
+ 0, 228, 128, 2, 0, 0,
+ 161, 5, 0, 0, 3, 0,
+ 0, 8, 128, 0, 0, 255,
+ 128, 1, 0, 170, 160, 1,
+ 0, 0, 2, 0, 0, 4,
+ 128, 1, 0, 255, 160, 8,
+ 0, 0, 3, 0, 0, 1,
+ 128, 0, 0, 228, 128, 0,
+ 0, 228, 160, 4, 0, 0,
+ 4, 0, 0, 2, 128, 0,
+ 0, 0, 128, 0, 0, 0,
+ 128, 0, 0, 255, 129, 35,
+ 0, 0, 2, 0, 0, 4,
+ 128, 0, 0, 85, 128, 7,
+ 0, 0, 2, 0, 0, 4,
+ 128, 0, 0, 170, 128, 6,
+ 0, 0, 2, 1, 0, 1,
+ 128, 0, 0, 170, 128, 1,
+ 0, 0, 2, 1, 0, 6,
+ 128, 1, 0, 0, 129, 2,
+ 0, 0, 3, 0, 0, 13,
+ 128, 0, 0, 0, 128, 1,
+ 0, 148, 128, 6, 0, 0,
+ 2, 1, 0, 1, 128, 1,
+ 0, 170, 160, 5, 0, 0,
+ 3, 0, 0, 13, 128, 0,
+ 0, 228, 128, 1, 0, 0,
+ 128, 1, 0, 0, 2, 1,
+ 0, 8, 128, 1, 0, 255,
+ 160, 4, 0, 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, 0, 0, 0,
+ 128, 0, 0, 255, 128, 88,
+ 0, 0, 4, 0, 0, 13,
+ 128, 1, 0, 148, 128, 4,
+ 0, 68, 160, 4, 0, 230,
+ 160, 1, 0, 0, 2, 2,
+ 0, 2, 128, 3, 0, 0,
+ 160, 66, 0, 0, 3, 1,
+ 0, 15, 128, 0, 0, 228,
+ 176, 1, 8, 228, 160, 66,
+ 0, 0, 3, 2, 0, 15,
+ 128, 2, 0, 228, 128, 0,
+ 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, 255,
+ 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, 0, 0, 170, 128, 88,
+ 0, 0, 4, 1, 0, 15,
+ 128, 0, 0, 0, 129, 4,
+ 0, 170, 160, 1, 0, 228,
+ 128, 88, 0, 0, 4, 0,
+ 0, 15, 128, 0, 0, 85,
+ 128, 1, 0, 228, 128, 4,
+ 0, 170, 160, 1, 0, 0,
+ 2, 0, 8, 15, 128, 0,
+ 0, 228, 128, 255, 255, 0,
+ 0, 83, 72, 68, 82, 208,
+ 3, 0, 0, 64, 0, 0,
+ 0, 244, 0, 0, 0, 89,
+ 0, 0, 4, 70, 142, 32,
+ 0, 0, 0, 0, 0, 7,
+ 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, 3, 0, 0,
+ 0, 0, 0, 0, 9, 50,
+ 0, 16, 0, 0, 0, 0,
+ 0, 230, 26, 16, 0, 1,
+ 0, 0, 0, 70, 128, 32,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 54, 0, 0, 6, 66,
+ 0, 16, 0, 0, 0, 0,
+ 0, 58, 128, 32, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 16, 0, 0, 8, 66,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 2, 16, 0, 0,
+ 0, 0, 0, 70, 130, 32,
+ 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 15, 0, 0,
+ 7, 18, 0, 16, 0, 0,
+ 0, 0, 0, 70, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 0, 9, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 10, 128, 32,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 6, 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, 5,
+ 0, 0, 0, 50, 0, 0,
+ 10, 18, 0, 16, 0, 0,
+ 0, 0, 0, 42, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 128, 65,
+ 0, 0, 0, 0, 0, 0,
+ 0, 49, 0, 0, 7, 34,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 0, 0, 75,
+ 0, 0, 6, 18, 0, 16,
+ 0, 1, 0, 0, 0, 10,
+ 0, 16, 128, 129, 0, 0,
+ 0, 0, 0, 0, 0, 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, 166,
+ 10, 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, 16,
+ 0, 0, 0, 0, 0, 166,
+ 138, 32, 0, 0, 0, 0,
+ 0, 5, 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, 4, 0, 0,
+ 0, 29, 0, 0, 9, 50,
+ 0, 16, 0, 1, 0, 0,
+ 0, 70, 0, 16, 0, 1,
+ 0, 0, 0, 246, 143, 32,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 5, 0, 0,
+ 0, 1, 0, 0, 10, 50,
+ 0, 16, 0, 1, 0, 0,
+ 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, 0,
+ 0, 0, 8, 18, 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, 50, 0, 0, 9, 18,
+ 0, 16, 0, 2, 0, 0,
+ 0, 10, 0, 16, 0, 1,
+ 0, 0, 0, 10, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 0, 16, 0, 0, 0, 0,
+ 0, 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, 16, 0, 2,
+ 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, 31, 0, 4,
+ 3, 26, 0, 16, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 8, 242, 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, 0, 0, 62,
+ 0, 0, 1, 21, 0, 0,
+ 1, 52, 0, 0, 7, 18,
+ 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, 18, 0, 16,
+ 0, 0, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 31, 0, 4,
+ 3, 10, 0, 16, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 8, 242, 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, 0, 0, 62,
+ 0, 0, 1, 21, 0, 0,
+ 1, 56, 0, 0, 7, 114,
+ 0, 16, 0, 2, 0, 0,
+ 0, 246, 15, 16, 0, 2,
+ 0, 0, 0, 70, 2, 16,
+ 0, 2, 0, 0, 0, 69,
+ 0, 0, 9, 242, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 16, 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, 246,
+ 15, 16, 0, 0, 0, 0,
+ 0, 70, 14, 16, 0, 2,
+ 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, 19, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 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, 2,
+ 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, 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, 100, 2, 0,
+ 0, 1, 0, 0, 0, 228,
+ 0, 0, 0, 5, 0, 0,
+ 0, 28, 0, 0, 0, 0,
+ 4, 255, 255, 0, 1, 0,
+ 0, 47, 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, 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, 0,
+ 0, 0, 0, 214, 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, 218,
+ 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, 223, 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, 115, 87, 114,
+ 97, 112, 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, 50, 0,
+ 171, 223, 0, 0, 0, 7,
+ 0, 0, 0, 252, 0, 0,
+ 0, 112, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 164, 1, 0, 0, 0,
+ 0, 0, 0, 44, 0, 0,
+ 0, 0, 0, 0, 0, 188,
+ 1, 0, 0, 0, 0, 0,
+ 0, 204, 1, 0, 0, 48,
+ 0, 0, 0, 8, 0, 0,
+ 0, 0, 0, 0, 0, 216,
+ 1, 0, 0, 0, 0, 0,
+ 0, 232, 1, 0, 0, 64,
+ 0, 0, 0, 12, 0, 0,
+ 0, 2, 0, 0, 0, 240,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 80,
+ 0, 0, 0, 8, 0, 0,
+ 0, 2, 0, 0, 0, 216,
+ 1, 0, 0, 0, 0, 0,
+ 0, 8, 2, 0, 0, 88,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 12,
+ 2, 0, 0, 0, 0, 0,
+ 0, 28, 2, 0, 0, 92,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 12,
+ 2, 0, 0, 0, 0, 0,
+ 0, 36, 2, 0, 0, 96,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 12,
+ 2, 0, 0, 0, 0, 0,
+ 0, 68, 101, 118, 105, 99,
+ 101, 83, 112, 97, 99, 101,
+ 84, 111, 85, 115, 101, 114,
+ 83, 112, 97, 99, 101, 0,
+ 171, 3, 0, 3, 0, 3,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 100,
+ 105, 109, 101, 110, 115, 105,
+ 111, 110, 115, 0, 171, 1,
+ 0, 3, 0, 1, 0, 2,
+ 0, 0, 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, 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, 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, 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, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 83, 86, 95,
+ 84, 97, 114, 103, 101, 116,
+ 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, 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, 0,
+ 228, 3, 0, 0, 136, 6,
+ 0, 0, 188, 6, 0, 0,
+ 65, 111, 110, 57, 84, 1,
+ 0, 0, 84, 1, 0, 0,
+ 0, 2, 254, 255, 252, 0,
+ 0, 0, 88, 0, 0, 0,
+ 4, 0, 36, 0, 0, 0,
+ 84, 0, 0, 0, 84, 0,
+ 0, 0, 36, 0, 1, 0,
+ 84, 0, 0, 0, 0, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 1, 0, 2, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 3, 0, 0, 0,
+ 0, 0, 1, 0, 3, 0,
+ 1, 0, 5, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 254, 255, 81, 0,
+ 0, 5, 6, 0, 15, 160,
+ 0, 0, 128, 63, 0, 0,
+ 0, 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, 4, 128, 0, 0,
+ 0, 128, 6, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 4, 128, 0, 0, 170, 128,
+ 5, 0, 0, 160, 5, 0,
+ 0, 3, 1, 0, 1, 128,
+ 0, 0, 170, 128, 6, 0,
+ 85, 160, 2, 0, 0, 3,
+ 0, 0, 4, 128, 0, 0,
+ 85, 129, 6, 0, 0, 160,
+ 2, 0, 0, 3, 0, 0,
+ 3, 192, 0, 0, 228, 128,
+ 0, 0, 228, 160, 5, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 170, 128, 5, 0,
+ 85, 160, 5, 0, 0, 3,
+ 1, 0, 2, 128, 0, 0,
+ 0, 128, 6, 0, 85, 160,
+ 1, 0, 0, 2, 1, 0,
+ 4, 128, 6, 0, 0, 160,
+ 8, 0, 0, 3, 0, 0,
+ 8, 224, 1, 0, 228, 128,
+ 3, 0, 228, 160, 8, 0,
+ 0, 3, 0, 0, 4, 224,
+ 1, 0, 228, 128, 4, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 12, 192, 6, 0,
+ 36, 160, 255, 255, 0, 0,
+ 83, 72, 68, 82, 204, 1,
+ 0, 0, 64, 0, 1, 0,
+ 115, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 1, 0,
+ 0, 0, 4, 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, 104, 0,
+ 0, 2, 2, 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, 0, 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, 5, 50, 32, 16, 0,
+ 0, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 0, 8, 34, 0, 16, 0,
+ 0, 0, 0, 0, 26, 0,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 56, 0, 0, 8, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 128, 32, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 50, 0, 16, 0, 1, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 16, 0, 0, 8,
+ 66, 32, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 8, 130, 32, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 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, 2, 0,
+ 0, 0, 70, 128, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 12, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 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, 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,
+ 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, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 156, 2, 0, 0,
+ 2, 0, 0, 0, 100, 0,
+ 0, 0, 2, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 103, 2, 0, 0, 92, 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,
+ 96, 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, 99, 98, 48, 0,
+ 99, 98, 50, 0, 92, 0,
+ 0, 0, 4, 0, 0, 0,
+ 148, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 96, 0,
+ 0, 0, 7, 0, 0, 0,
+ 52, 1, 0, 0, 112, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 244, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 26, 1,
+ 0, 0, 32, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 40, 1,
+ 0, 0, 48, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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, 84, 101, 120, 116,
+ 67, 111, 108, 111, 114, 0,
+ 171, 171, 220, 1, 0, 0,
+ 0, 0, 0, 0, 44, 0,
+ 0, 0, 2, 0, 0, 0,
+ 244, 1, 0, 0, 0, 0,
+ 0, 0, 4, 2, 0, 0,
+ 48, 0, 0, 0, 8, 0,
+ 0, 0, 2, 0, 0, 0,
+ 16, 2, 0, 0, 0, 0,
+ 0, 0, 32, 2, 0, 0,
+ 64, 0, 0, 0, 12, 0,
+ 0, 0, 0, 0, 0, 0,
+ 40, 2, 0, 0, 0, 0,
+ 0, 0, 56, 2, 0, 0,
+ 80, 0, 0, 0, 8, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 2, 0, 0, 0, 0,
+ 0, 0, 64, 2, 0, 0,
+ 88, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 68, 2, 0, 0, 0, 0,
+ 0, 0, 84, 2, 0, 0,
+ 92, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 68, 2, 0, 0, 0, 0,
+ 0, 0, 92, 2, 0, 0,
+ 96, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 68, 2, 0, 0, 0, 0,
+ 0, 0, 68, 101, 118, 105,
+ 99, 101, 83, 112, 97, 99,
+ 101, 84, 111, 85, 115, 101,
+ 114, 83, 112, 97, 99, 101,
+ 0, 171, 3, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 105, 109, 101, 110, 115,
+ 105, 111, 110, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 2, 0, 0, 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,
+ 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, 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,
+ 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, 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, 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, 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,
+ 0, 0, 56, 4, 0, 0,
+ 180, 4, 0, 0, 32, 7,
+ 0, 0, 144, 7, 0, 0,
+ 65, 111, 110, 57, 132, 1,
+ 0, 0, 132, 1, 0, 0,
+ 0, 2, 255, 255, 76, 1,
+ 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,
+ 4, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 2, 0, 15, 160, 0, 0,
+ 0, 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, 5, 0, 0, 3,
+ 0, 0, 8, 128, 1, 0,
+ 255, 160, 1, 0, 255, 160,
+ 2, 0, 0, 3, 0, 0,
+ 3, 128, 0, 0, 235, 176,
+ 1, 0, 228, 161, 90, 0,
+ 0, 4, 0, 0, 8, 128,
+ 0, 0, 228, 128, 0, 0,
+ 228, 128, 0, 0, 255, 129,
+ 5, 0, 0, 3, 0, 0,
+ 8, 128, 0, 0, 255, 128,
+ 2, 0, 0, 160, 1, 0,
+ 0, 2, 0, 0, 4, 128,
+ 1, 0, 255, 160, 8, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 228, 128, 0, 0,
+ 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, 128, 0, 0, 255, 128,
+ 1, 0, 0, 2, 0, 0,
+ 2, 128, 2, 0, 0, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 0, 0, 228, 128, 0, 8,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 8, 128, 1, 0,
+ 255, 160, 4, 0, 0, 4,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 0, 0, 170, 161,
+ 0, 0, 255, 129, 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,
+ 255, 128, 2, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 15, 128, 0, 0, 0, 128,
+ 2, 0, 85, 160, 1, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 108, 2,
+ 0, 0, 64, 0, 0, 0,
+ 155, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 6, 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,
+ 0, 0, 0, 9, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 230, 26, 16, 0, 1, 0,
+ 0, 0, 70, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 54, 0, 0, 6, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 128, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 16, 0, 0, 8, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 15, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 12, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 128, 32, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 58, 128,
+ 32, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 63, 14, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 29, 0,
+ 0, 9, 66, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 34, 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, 16, 0, 0, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 31, 0, 4, 3, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 242, 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,
+ 0, 0, 62, 0, 0, 1,
+ 21, 0, 0, 1, 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, 0, 0,
+ 0, 0, 70, 16, 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, 246, 15, 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,
+ 19, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 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,
+ 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, 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, 0, 0, 0, 0,
+ 0, 0, 82, 68, 69, 70,
+ 100, 2, 0, 0, 1, 0,
+ 0, 0, 228, 0, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 47, 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,
+ 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, 0, 0, 0, 0,
+ 214, 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, 218, 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, 223, 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,
+ 115, 87, 114, 97, 112, 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, 50, 0, 171, 223, 0,
+ 0, 0, 7, 0, 0, 0,
+ 252, 0, 0, 0, 112, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 164, 1,
+ 0, 0, 0, 0, 0, 0,
+ 44, 0, 0, 0, 0, 0,
+ 0, 0, 188, 1, 0, 0,
+ 0, 0, 0, 0, 204, 1,
+ 0, 0, 48, 0, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 216, 1, 0, 0,
+ 0, 0, 0, 0, 232, 1,
+ 0, 0, 64, 0, 0, 0,
+ 12, 0, 0, 0, 2, 0,
+ 0, 0, 240, 1, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 80, 0, 0, 0,
+ 8, 0, 0, 0, 2, 0,
+ 0, 0, 216, 1, 0, 0,
+ 0, 0, 0, 0, 8, 2,
+ 0, 0, 88, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 12, 2, 0, 0,
+ 0, 0, 0, 0, 28, 2,
+ 0, 0, 92, 0, 0, 0,
+ 4, 0, 0, 0, 2, 0,
+ 0, 0, 12, 2, 0, 0,
+ 0, 0, 0, 0, 36, 2,
+ 0, 0, 96, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 12, 2, 0, 0,
+ 0, 0, 0, 0, 68, 101,
+ 118, 105, 99, 101, 83, 112,
+ 97, 99, 101, 84, 111, 85,
+ 115, 101, 114, 83, 112, 97,
+ 99, 101, 0, 171, 3, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 100, 105, 109, 101,
+ 110, 115, 105, 111, 110, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 2, 0, 0, 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, 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, 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, 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,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 83, 86, 95, 84, 97, 114,
+ 103, 101, 116, 0, 171, 171,
+ 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, 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,
+ 0, 228, 3, 0, 0, 136,
+ 6, 0, 0, 188, 6, 0,
+ 0, 65, 111, 110, 57, 84,
+ 1, 0, 0, 84, 1, 0,
+ 0, 0, 2, 254, 255, 252,
+ 0, 0, 0, 88, 0, 0,
+ 0, 4, 0, 36, 0, 0,
+ 0, 84, 0, 0, 0, 84,
+ 0, 0, 0, 36, 0, 1,
+ 0, 84, 0, 0, 0, 0,
+ 0, 1, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 1, 0, 2, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 0, 3, 0, 0,
+ 0, 0, 0, 1, 0, 3,
+ 0, 1, 0, 5, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 254, 255, 81,
+ 0, 0, 5, 6, 0, 15,
+ 160, 0, 0, 128, 63, 0,
+ 0, 0, 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, 4, 128, 0,
+ 0, 0, 128, 6, 0, 0,
+ 160, 5, 0, 0, 3, 0,
+ 0, 4, 128, 0, 0, 170,
+ 128, 5, 0, 0, 160, 5,
+ 0, 0, 3, 1, 0, 1,
+ 128, 0, 0, 170, 128, 6,
+ 0, 85, 160, 2, 0, 0,
+ 3, 0, 0, 4, 128, 0,
+ 0, 85, 129, 6, 0, 0,
+ 160, 2, 0, 0, 3, 0,
+ 0, 3, 192, 0, 0, 228,
+ 128, 0, 0, 228, 160, 5,
+ 0, 0, 3, 0, 0, 1,
+ 128, 0, 0, 170, 128, 5,
+ 0, 85, 160, 5, 0, 0,
+ 3, 1, 0, 2, 128, 0,
+ 0, 0, 128, 6, 0, 85,
+ 160, 1, 0, 0, 2, 1,
+ 0, 4, 128, 6, 0, 0,
+ 160, 8, 0, 0, 3, 0,
+ 0, 8, 224, 1, 0, 228,
+ 128, 3, 0, 228, 160, 8,
+ 0, 0, 3, 0, 0, 4,
+ 224, 1, 0, 228, 128, 4,
+ 0, 228, 160, 1, 0, 0,
+ 2, 0, 0, 12, 192, 6,
+ 0, 36, 160, 255, 255, 0,
+ 0, 83, 72, 68, 82, 204,
+ 1, 0, 0, 64, 0, 1,
+ 0, 115, 0, 0, 0, 89,
+ 0, 0, 4, 70, 142, 32,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 1,
+ 0, 0, 0, 4, 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, 104,
+ 0, 0, 2, 2, 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, 0, 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, 5, 50, 32, 16,
+ 0, 0, 0, 0, 0, 70,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 10, 0, 16, 0, 0,
+ 0, 0, 0, 1, 64, 0,
+ 0, 0, 0, 128, 63, 0,
+ 0, 0, 8, 34, 0, 16,
+ 0, 0, 0, 0, 0, 26,
+ 0, 16, 128, 65, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 128,
+ 63, 56, 0, 0, 8, 50,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 0, 16, 0, 0,
+ 0, 0, 0, 70, 128, 32,
+ 0, 1, 0, 0, 0, 3,
+ 0, 0, 0, 56, 0, 0,
+ 10, 50, 0, 16, 0, 1,
+ 0, 0, 0, 70, 0, 16,
+ 0, 0, 0, 0, 0, 2,
+ 64, 0, 0, 0, 0, 0,
+ 63, 0, 0, 0, 63, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 54, 0, 0, 5, 66,
+ 0, 16, 0, 1, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 16, 0, 0,
+ 8, 66, 32, 16, 0, 1,
+ 0, 0, 0, 70, 2, 16,
+ 0, 1, 0, 0, 0, 70,
+ 130, 32, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 8, 130, 32, 16,
+ 0, 1, 0, 0, 0, 70,
+ 2, 16, 0, 1, 0, 0,
+ 0, 70, 130, 32, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 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, 2,
+ 0, 0, 0, 70, 128, 32,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 62, 0, 0,
+ 1, 83, 84, 65, 84, 116,
+ 0, 0, 0, 12, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 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, 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, 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, 0,
+ 0, 0, 0, 0, 0, 82,
+ 68, 69, 70, 156, 2, 0,
+ 0, 2, 0, 0, 0, 100,
+ 0, 0, 0, 2, 0, 0,
+ 0, 28, 0, 0, 0, 0,
+ 4, 254, 255, 0, 1, 0,
+ 0, 103, 2, 0, 0, 92,
+ 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, 96, 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, 99, 98, 48,
+ 0, 99, 98, 50, 0, 92,
+ 0, 0, 0, 4, 0, 0,
+ 0, 148, 0, 0, 0, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 0, 7, 0, 0,
+ 0, 52, 1, 0, 0, 112,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 244,
+ 0, 0, 0, 0, 0, 0,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 16,
+ 1, 0, 0, 16, 0, 0,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 26,
+ 1, 0, 0, 32, 0, 0,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 40,
+ 1, 0, 0, 48, 0, 0,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 0, 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, 84, 101, 120,
+ 116, 67, 111, 108, 111, 114,
+ 0, 171, 171, 220, 1, 0,
+ 0, 0, 0, 0, 0, 44,
+ 0, 0, 0, 2, 0, 0,
+ 0, 244, 1, 0, 0, 0,
+ 0, 0, 0, 4, 2, 0,
+ 0, 48, 0, 0, 0, 8,
+ 0, 0, 0, 2, 0, 0,
+ 0, 16, 2, 0, 0, 0,
+ 0, 0, 0, 32, 2, 0,
+ 0, 64, 0, 0, 0, 12,
+ 0, 0, 0, 0, 0, 0,
+ 0, 40, 2, 0, 0, 0,
+ 0, 0, 0, 56, 2, 0,
+ 0, 80, 0, 0, 0, 8,
+ 0, 0, 0, 0, 0, 0,
+ 0, 16, 2, 0, 0, 0,
+ 0, 0, 0, 64, 2, 0,
+ 0, 88, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 0, 68, 2, 0, 0, 0,
+ 0, 0, 0, 84, 2, 0,
+ 0, 92, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 0, 68, 2, 0, 0, 0,
+ 0, 0, 0, 92, 2, 0,
+ 0, 96, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 0, 68, 2, 0, 0, 0,
+ 0, 0, 0, 68, 101, 118,
+ 105, 99, 101, 83, 112, 97,
+ 99, 101, 84, 111, 85, 115,
+ 101, 114, 83, 112, 97, 99,
+ 101, 0, 171, 3, 0, 3,
+ 0, 3, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 100, 105, 109, 101, 110,
+ 115, 105, 111, 110, 115, 0,
+ 171, 1, 0, 3, 0, 1,
+ 0, 2, 0, 0, 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, 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, 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, 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, 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, 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, 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,
+ 0, 212, 6, 0, 0, 68,
+ 9, 0, 0, 180, 9, 0,
+ 0, 65, 111, 110, 57, 64,
+ 2, 0, 0, 64, 2, 0,
+ 0, 0, 2, 255, 255, 8,
+ 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, 4, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 255, 255, 81, 0, 0,
+ 5, 3, 0, 15, 160, 0,
+ 0, 0, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 81, 0, 0,
+ 5, 4, 0, 15, 160, 0,
+ 0, 128, 63, 0, 0, 128,
+ 191, 0, 0, 0, 0, 0,
+ 0, 0, 128, 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, 3, 128, 0,
+ 0, 235, 176, 1, 0, 228,
+ 161, 90, 0, 0, 4, 0,
+ 0, 8, 128, 0, 0, 228,
+ 128, 0, 0, 228, 128, 2,
+ 0, 0, 161, 5, 0, 0,
+ 3, 0, 0, 8, 128, 0,
+ 0, 255, 128, 1, 0, 170,
+ 160, 1, 0, 0, 2, 0,
+ 0, 4, 128, 1, 0, 255,
+ 160, 8, 0, 0, 3, 0,
+ 0, 1, 128, 0, 0, 228,
+ 128, 0, 0, 228, 160, 4,
+ 0, 0, 4, 0, 0, 2,
+ 128, 0, 0, 0, 128, 0,
+ 0, 0, 128, 0, 0, 255,
+ 129, 35, 0, 0, 2, 0,
+ 0, 4, 128, 0, 0, 85,
+ 128, 7, 0, 0, 2, 0,
+ 0, 4, 128, 0, 0, 170,
+ 128, 6, 0, 0, 2, 1,
+ 0, 1, 128, 0, 0, 170,
+ 128, 1, 0, 0, 2, 1,
+ 0, 6, 128, 1, 0, 0,
+ 129, 2, 0, 0, 3, 0,
+ 0, 13, 128, 0, 0, 0,
+ 128, 1, 0, 148, 128, 6,
+ 0, 0, 2, 1, 0, 1,
+ 128, 1, 0, 170, 160, 5,
+ 0, 0, 3, 0, 0, 13,
+ 128, 0, 0, 228, 128, 1,
+ 0, 0, 128, 1, 0, 0,
+ 2, 1, 0, 8, 128, 1,
+ 0, 255, 160, 4, 0, 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, 0,
+ 0, 0, 128, 0, 0, 255,
+ 128, 88, 0, 0, 4, 0,
+ 0, 13, 128, 1, 0, 148,
+ 128, 4, 0, 68, 160, 4,
+ 0, 230, 160, 1, 0, 0,
+ 2, 2, 0, 2, 128, 3,
+ 0, 0, 160, 66, 0, 0,
+ 3, 1, 0, 15, 128, 0,
+ 0, 228, 176, 1, 8, 228,
+ 160, 66, 0, 0, 3, 2,
+ 0, 15, 128, 2, 0, 228,
+ 128, 0, 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, 255, 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, 0, 0, 170,
+ 128, 88, 0, 0, 4, 1,
+ 0, 15, 128, 0, 0, 0,
+ 129, 4, 0, 170, 160, 1,
+ 0, 228, 128, 88, 0, 0,
+ 4, 0, 0, 15, 128, 0,
+ 0, 85, 128, 1, 0, 228,
+ 128, 4, 0, 170, 160, 1,
+ 0, 0, 2, 0, 8, 15,
+ 128, 0, 0, 228, 128, 255,
+ 255, 0, 0, 83, 72, 68,
+ 82, 208, 3, 0, 0, 64,
+ 0, 0, 0, 244, 0, 0,
+ 0, 89, 0, 0, 4, 70,
+ 142, 32, 0, 0, 0, 0,
+ 0, 7, 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, 3,
+ 0, 0, 0, 0, 0, 0,
+ 9, 50, 0, 16, 0, 0,
+ 0, 0, 0, 230, 26, 16,
+ 0, 1, 0, 0, 0, 70,
+ 128, 32, 128, 65, 0, 0,
+ 0, 0, 0, 0, 0, 5,
+ 0, 0, 0, 54, 0, 0,
+ 6, 66, 0, 16, 0, 0,
+ 0, 0, 0, 58, 128, 32,
+ 0, 0, 0, 0, 0, 5,
+ 0, 0, 0, 16, 0, 0,
+ 8, 66, 0, 16, 0, 0,
+ 0, 0, 0, 70, 2, 16,
+ 0, 0, 0, 0, 0, 70,
+ 130, 32, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 15,
+ 0, 0, 7, 18, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 0, 16, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 9, 18, 0, 16, 0, 0,
+ 0, 0, 0, 10, 0, 16,
+ 0, 0, 0, 0, 0, 10,
+ 128, 32, 128, 65, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 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, 5, 0, 0, 0, 50,
+ 0, 0, 10, 18, 0, 16,
+ 0, 0, 0, 0, 0, 42,
+ 0, 16, 0, 0, 0, 0,
+ 0, 42, 0, 16, 0, 0,
+ 0, 0, 0, 10, 0, 16,
+ 128, 65, 0, 0, 0, 0,
+ 0, 0, 0, 49, 0, 0,
+ 7, 34, 0, 16, 0, 0,
+ 0, 0, 0, 10, 0, 16,
+ 0, 0, 0, 0, 0, 1,
+ 64, 0, 0, 0, 0, 0,
+ 0, 75, 0, 0, 6, 18,
+ 0, 16, 0, 1, 0, 0,
+ 0, 10, 0, 16, 128, 129,
+ 0, 0, 0, 0, 0, 0,
+ 0, 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, 166, 10, 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, 16, 0, 0, 0, 0,
+ 0, 166, 138, 32, 0, 0,
+ 0, 0, 0, 5, 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, 4,
+ 0, 0, 0, 29, 0, 0,
+ 9, 50, 0, 16, 0, 1,
+ 0, 0, 0, 70, 0, 16,
+ 0, 1, 0, 0, 0, 246,
+ 143, 32, 128, 65, 0, 0,
+ 0, 0, 0, 0, 0, 5,
+ 0, 0, 0, 1, 0, 0,
+ 10, 50, 0, 16, 0, 1,
+ 0, 0, 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, 0, 0, 0, 8, 18,
+ 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, 50, 0, 0,
+ 9, 18, 0, 16, 0, 2,
+ 0, 0, 0, 10, 0, 16,
+ 0, 1, 0, 0, 0, 10,
+ 0, 16, 0, 0, 0, 0,
+ 0, 42, 0, 16, 0, 0,
+ 0, 0, 0, 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, 16,
+ 0, 2, 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, 31,
+ 0, 4, 3, 26, 0, 16,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 8, 242, 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, 0,
+ 0, 62, 0, 0, 1, 21,
+ 0, 0, 1, 52, 0, 0,
+ 7, 18, 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, 18,
+ 0, 16, 0, 0, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 0, 0, 10, 0, 16,
+ 0, 0, 0, 0, 0, 31,
+ 0, 4, 3, 10, 0, 16,
+ 0, 0, 0, 0, 0, 54,
+ 0, 0, 8, 242, 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, 0,
+ 0, 62, 0, 0, 1, 21,
+ 0, 0, 1, 56, 0, 0,
+ 7, 114, 0, 16, 0, 2,
+ 0, 0, 0, 246, 15, 16,
+ 0, 2, 0, 0, 0, 70,
+ 2, 16, 0, 2, 0, 0,
+ 0, 69, 0, 0, 9, 242,
+ 0, 16, 0, 0, 0, 0,
+ 0, 70, 16, 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, 246, 15, 16, 0, 0,
+ 0, 0, 0, 70, 14, 16,
+ 0, 2, 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, 19, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 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, 2, 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, 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, 104,
+ 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, 51, 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, 0,
+ 0, 0, 0, 203, 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, 216,
+ 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, 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, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 115,
+ 77, 105, 114, 114, 111, 114,
+ 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, 50, 0, 171, 171,
+ 171, 225, 0, 0, 0, 7,
+ 0, 0, 0, 0, 1, 0,
+ 0, 112, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 168, 1, 0, 0, 0,
+ 0, 0, 0, 44, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 1, 0, 0, 0, 0, 0,
+ 0, 208, 1, 0, 0, 48,
+ 0, 0, 0, 8, 0, 0,
+ 0, 0, 0, 0, 0, 220,
+ 1, 0, 0, 0, 0, 0,
+ 0, 236, 1, 0, 0, 64,
+ 0, 0, 0, 12, 0, 0,
+ 0, 2, 0, 0, 0, 244,
+ 1, 0, 0, 0, 0, 0,
+ 0, 4, 2, 0, 0, 80,
+ 0, 0, 0, 8, 0, 0,
+ 0, 2, 0, 0, 0, 220,
+ 1, 0, 0, 0, 0, 0,
+ 0, 12, 2, 0, 0, 88,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 32, 2, 0, 0, 92,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 40, 2, 0, 0, 96,
+ 0, 0, 0, 4, 0, 0,
+ 0, 2, 0, 0, 0, 16,
+ 2, 0, 0, 0, 0, 0,
+ 0, 68, 101, 118, 105, 99,
+ 101, 83, 112, 97, 99, 101,
+ 84, 111, 85, 115, 101, 114,
+ 83, 112, 97, 99, 101, 0,
+ 171, 3, 0, 3, 0, 3,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 100,
+ 105, 109, 101, 110, 115, 105,
+ 111, 110, 115, 0, 171, 1,
+ 0, 3, 0, 1, 0, 2,
+ 0, 0, 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, 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, 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, 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, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 83, 86, 95,
+ 84, 97, 114, 103, 101, 116,
+ 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, 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, 0, 228, 3, 0, 0,
+ 136, 6, 0, 0, 188, 6,
+ 0, 0, 65, 111, 110, 57,
+ 84, 1, 0, 0, 84, 1,
+ 0, 0, 0, 2, 254, 255,
+ 252, 0, 0, 0, 88, 0,
+ 0, 0, 4, 0, 36, 0,
+ 0, 0, 84, 0, 0, 0,
+ 84, 0, 0, 0, 36, 0,
+ 1, 0, 84, 0, 0, 0,
+ 0, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 1, 0, 2, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 3, 0,
+ 0, 0, 0, 0, 1, 0,
+ 3, 0, 1, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 254, 255,
+ 81, 0, 0, 5, 6, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 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, 4, 128,
+ 0, 0, 0, 128, 6, 0,
+ 0, 160, 5, 0, 0, 3,
+ 0, 0, 4, 128, 0, 0,
+ 170, 128, 5, 0, 0, 160,
+ 5, 0, 0, 3, 1, 0,
+ 1, 128, 0, 0, 170, 128,
+ 6, 0, 85, 160, 2, 0,
+ 0, 3, 0, 0, 4, 128,
+ 0, 0, 85, 129, 6, 0,
+ 0, 160, 2, 0, 0, 3,
+ 0, 0, 3, 192, 0, 0,
+ 228, 128, 0, 0, 228, 160,
+ 5, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 170, 128,
+ 5, 0, 85, 160, 5, 0,
+ 0, 3, 1, 0, 2, 128,
+ 0, 0, 0, 128, 6, 0,
+ 85, 160, 1, 0, 0, 2,
+ 1, 0, 4, 128, 6, 0,
+ 0, 160, 8, 0, 0, 3,
+ 0, 0, 8, 224, 1, 0,
+ 228, 128, 3, 0, 228, 160,
+ 8, 0, 0, 3, 0, 0,
+ 4, 224, 1, 0, 228, 128,
+ 4, 0, 228, 160, 1, 0,
+ 0, 2, 0, 0, 12, 192,
+ 6, 0, 36, 160, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 204, 1, 0, 0, 64, 0,
+ 1, 0, 115, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 1, 0, 0, 0, 4, 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,
+ 104, 0, 0, 2, 2, 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, 0, 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, 5, 50, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 0, 8, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 0, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 56, 0, 0, 8,
+ 50, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 70, 128,
+ 32, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 56, 0,
+ 0, 10, 50, 0, 16, 0,
+ 1, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 63, 0, 0, 0, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 66, 0, 16, 0, 1, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 16, 0,
+ 0, 8, 66, 32, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 8, 130, 32,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 130, 32, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 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,
+ 2, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 12, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 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, 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, 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,
+ 0, 0, 0, 0, 0, 0,
+ 82, 68, 69, 70, 156, 2,
+ 0, 0, 2, 0, 0, 0,
+ 100, 0, 0, 0, 2, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 254, 255, 0, 1,
+ 0, 0, 103, 2, 0, 0,
+ 92, 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, 96, 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, 99, 98,
+ 48, 0, 99, 98, 50, 0,
+ 92, 0, 0, 0, 4, 0,
+ 0, 0, 148, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 7, 0,
+ 0, 0, 52, 1, 0, 0,
+ 112, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 244, 0, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 16, 1, 0, 0, 16, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 26, 1, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 40, 1, 0, 0, 48, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 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, 84, 101,
+ 120, 116, 67, 111, 108, 111,
+ 114, 0, 171, 171, 220, 1,
+ 0, 0, 0, 0, 0, 0,
+ 44, 0, 0, 0, 2, 0,
+ 0, 0, 244, 1, 0, 0,
+ 0, 0, 0, 0, 4, 2,
+ 0, 0, 48, 0, 0, 0,
+ 8, 0, 0, 0, 2, 0,
+ 0, 0, 16, 2, 0, 0,
+ 0, 0, 0, 0, 32, 2,
+ 0, 0, 64, 0, 0, 0,
+ 12, 0, 0, 0, 0, 0,
+ 0, 0, 40, 2, 0, 0,
+ 0, 0, 0, 0, 56, 2,
+ 0, 0, 80, 0, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 16, 2, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 88, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 68, 2, 0, 0,
+ 0, 0, 0, 0, 84, 2,
+ 0, 0, 92, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 68, 2, 0, 0,
+ 0, 0, 0, 0, 92, 2,
+ 0, 0, 96, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 68, 2, 0, 0,
+ 0, 0, 0, 0, 68, 101,
+ 118, 105, 99, 101, 83, 112,
+ 97, 99, 101, 84, 111, 85,
+ 115, 101, 114, 83, 112, 97,
+ 99, 101, 0, 171, 3, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 100, 105, 109, 101,
+ 110, 115, 105, 111, 110, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 2, 0, 0, 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, 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, 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, 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,
+ 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,
+ 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, 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,
+ 0, 0, 180, 4, 0, 0,
+ 36, 7, 0, 0, 148, 7,
+ 0, 0, 65, 111, 110, 57,
+ 132, 1, 0, 0, 132, 1,
+ 0, 0, 0, 2, 255, 255,
+ 76, 1, 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, 4, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 81, 0,
+ 0, 5, 2, 0, 15, 160,
+ 0, 0, 0, 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, 5, 0,
+ 0, 3, 0, 0, 8, 128,
+ 1, 0, 255, 160, 1, 0,
+ 255, 160, 2, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 235, 176, 1, 0, 228, 161,
+ 90, 0, 0, 4, 0, 0,
+ 8, 128, 0, 0, 228, 128,
+ 0, 0, 228, 128, 0, 0,
+ 255, 129, 5, 0, 0, 3,
+ 0, 0, 8, 128, 0, 0,
+ 255, 128, 2, 0, 0, 160,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 1, 0, 255, 160,
+ 8, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 228, 128,
+ 0, 0, 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, 128, 0, 0,
+ 255, 128, 1, 0, 0, 2,
+ 0, 0, 2, 128, 2, 0,
+ 0, 160, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 1, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 0, 0, 228, 128,
+ 0, 8, 228, 160, 1, 0,
+ 0, 2, 0, 0, 8, 128,
+ 1, 0, 255, 160, 4, 0,
+ 0, 4, 0, 0, 1, 128,
+ 0, 0, 0, 128, 0, 0,
+ 170, 161, 0, 0, 255, 129,
+ 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, 255, 128, 2, 0,
+ 228, 128, 88, 0, 0, 4,
+ 0, 0, 15, 128, 0, 0,
+ 0, 128, 2, 0, 85, 160,
+ 1, 0, 228, 128, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 108, 2, 0, 0, 64, 0,
+ 0, 0, 155, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 6, 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, 0, 0, 0, 9,
+ 50, 0, 16, 0, 0, 0,
+ 0, 0, 230, 26, 16, 0,
+ 1, 0, 0, 0, 70, 128,
+ 32, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 54, 0, 0, 6,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 58, 128, 32, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 16, 0, 0, 8,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 130,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 15, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 58, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 58, 128, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 63,
+ 14, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 66, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 29, 0, 0, 9, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 128, 32, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 34, 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, 16, 0,
+ 0, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 31, 0, 4, 3,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 242, 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, 0, 0, 62, 0,
+ 0, 1, 21, 0, 0, 1,
+ 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,
+ 0, 0, 0, 0, 70, 16,
+ 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, 246, 15,
+ 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, 19, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 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, 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,
+ 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, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 104, 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,
+ 51, 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, 0, 0, 0, 0,
+ 203, 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, 216, 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, 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, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 77, 105, 114,
+ 114, 111, 114, 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, 50,
+ 0, 171, 171, 171, 225, 0,
+ 0, 0, 7, 0, 0, 0,
+ 0, 1, 0, 0, 112, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 44, 0, 0, 0, 0, 0,
+ 0, 0, 192, 1, 0, 0,
+ 0, 0, 0, 0, 208, 1,
+ 0, 0, 48, 0, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 220, 1, 0, 0,
+ 0, 0, 0, 0, 236, 1,
+ 0, 0, 64, 0, 0, 0,
+ 12, 0, 0, 0, 2, 0,
+ 0, 0, 244, 1, 0, 0,
+ 0, 0, 0, 0, 4, 2,
+ 0, 0, 80, 0, 0, 0,
+ 8, 0, 0, 0, 2, 0,
+ 0, 0, 220, 1, 0, 0,
+ 0, 0, 0, 0, 12, 2,
+ 0, 0, 88, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 16, 2, 0, 0,
+ 0, 0, 0, 0, 32, 2,
+ 0, 0, 92, 0, 0, 0,
+ 4, 0, 0, 0, 2, 0,
+ 0, 0, 16, 2, 0, 0,
+ 0, 0, 0, 0, 40, 2,
+ 0, 0, 96, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 16, 2, 0, 0,
+ 0, 0, 0, 0, 68, 101,
+ 118, 105, 99, 101, 83, 112,
+ 97, 99, 101, 84, 111, 85,
+ 115, 101, 114, 83, 112, 97,
+ 99, 101, 0, 171, 3, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 100, 105, 109, 101,
+ 110, 115, 105, 111, 110, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 2, 0, 0, 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, 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, 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, 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,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 83, 86, 95, 84, 97, 114,
+ 103, 101, 116, 0, 171, 171,
+ 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, 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,
+ 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, 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 230, 26, 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, 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,
+ 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,
+ 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, 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, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 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, 1, 0,
+ 0, 0, 12, 0, 0, 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,
+ 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, 3, 0,
+ 0, 0, 0, 0, 0, 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, 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,
+ 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,
+ 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, 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,
+ 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,
+ 255, 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, 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, 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,
+ 0, 160, 1, 0, 255, 128,
+ 0, 0, 0, 128, 4, 0,
+ 0, 4, 0, 0, 1, 128,
+ 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, 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,
+ 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, 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,
+ 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,
+ 0, 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, 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, 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,
+ 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, 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, 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, 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, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 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,
+ 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, 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,
+ 7, 0, 0, 0, 58, 0,
+ 16, 0, 1, 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, 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,
+ 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,
+ 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, 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,
+ 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, 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, 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,
+ 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, 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,
+ 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, 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, 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, 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, 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, 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, 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,
+ 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,
+ 0, 72, 7, 0, 0, 8,
+ 9, 0, 0, 120, 9, 0,
+ 0, 65, 111, 110, 57, 156,
+ 2, 0, 0, 156, 2, 0,
+ 0, 0, 2, 255, 255, 104,
+ 2, 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, 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, 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, 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, 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, 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, 15, 128, 3, 0, 170,
+ 160, 1, 0, 228, 128, 0,
+ 0, 228, 128, 4, 0, 0,
+ 4, 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, 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, 85,
+ 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, 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, 15, 128, 4,
+ 0, 0, 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, 228,
+ 128, 2, 0, 0, 3, 1,
+ 0, 2, 128, 0, 0, 85,
+ 176, 1, 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, 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, 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, 15,
+ 128, 4, 0, 170, 160, 1,
+ 0, 228, 128, 0, 0, 228,
+ 128, 4, 0, 0, 4, 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, 66, 0, 0, 3, 1,
+ 0, 15, 128, 1, 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, 1, 0, 0, 2, 0,
+ 8, 15, 128, 0, 0, 228,
+ 128, 255, 255, 0, 0, 83,
+ 72, 68, 82, 232, 3, 0,
+ 0, 64, 0, 0, 0, 250,
+ 0, 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, 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, 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, 0, 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, 0, 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, 70,
+ 14, 16, 0, 2, 0, 0,
+ 0, 54, 0, 0, 5, 82,
+ 0, 16, 0, 1, 0, 0,
+ 0, 6, 16, 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, 242, 0, 16, 0, 0,
+ 0, 0, 0, 166, 138, 32,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 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, 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, 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, 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, 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, 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, 0,
+ 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, 0,
+ 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, 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, 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, 50, 0, 0,
+ 10, 242, 32, 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, 0, 70,
+ 14, 16, 0, 0, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 29, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 12,
+ 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, 9, 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, 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, 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, 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, 0,
+ 0, 0, 0, 0, 0, 48,
+ 0, 0, 0, 0, 0, 0,
+ 0, 28, 1, 0, 0, 0,
+ 0, 0, 0, 44, 1, 0,
+ 0, 48, 0, 0, 0, 48,
+ 0, 0, 0, 2, 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, 0, 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, 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, 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, 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, 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, 0, 0, 0, 0, 15,
+ 0, 0, 0, 83, 86, 95,
+ 84, 97, 114, 103, 101, 116,
+ 0, 171, 171, 209, 218, 0,
+ 0, 0, 0, 0, 0, 80,
+ 50, 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, 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, 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, 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,
+ 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,
+ 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, 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, 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, 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, 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, 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, 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,
+ 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,
+ 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, 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, 4, 0, 0, 4,
+ 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,
+ 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, 85, 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,
+ 0, 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, 228, 128,
+ 2, 0, 0, 3, 1, 0,
+ 2, 128, 0, 0, 85, 176,
+ 1, 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,
+ 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, 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, 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, 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, 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, 1, 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, 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, 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, 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, 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, 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,
+ 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,
+ 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,
+ 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, 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, 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,
+ 1, 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, 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, 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, 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, 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, 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, 1, 0,
+ 0, 0, 12, 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,
+ 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,
+ 96, 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, 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, 0, 0, 0, 0,
+ 0, 0, 66, 108, 117, 114,
+ 79, 102, 102, 115, 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, 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, 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,
+ 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,
+ 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 68, 0, 0,
+ 0, 2, 0, 0, 0, 8,
+ 0, 0, 0, 56, 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, 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, 111, 248, 0,
+ 0, 0, 0, 0, 0, 77,
+ 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,
+ 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, 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,
+ 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, 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,
+ 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,
+ 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, 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,
+ 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, 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, 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, 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, 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,
+ 0, 0, 88, 1, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 116, 1, 0, 0,
+ 16, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 126, 1, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 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,
+ 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, 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, 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, 68, 0, 0, 0,
+ 2, 0, 0, 0, 8, 0,
+ 0, 0, 56, 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, 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, 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, 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, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 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, 112, 0,
+ 0, 0, 65, 0, 0, 0,
+ 0, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 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,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 168, 0, 0, 0, 140, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 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, 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, 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,
+ 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, 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,
+ 0, 0, 0, 0, 0, 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, 154, 1, 0, 0,
+ 126, 1, 0, 0, 0, 0,
+ 0, 0, 88, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 156, 1, 0, 0, 126, 1,
+ 0, 0, 0, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 164, 1,
+ 0, 0, 126, 1, 0, 0,
+ 0, 0, 0, 0, 96, 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, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 224, 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, 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, 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, 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, 0, 0,
+ 55, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 217, 1, 0, 0, 46, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 83, 2,
+ 0, 0, 47, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 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,
+ 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, 132, 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,
+ 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, 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, 47, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 195, 2,
+ 0, 0, 0, 0, 0, 0,
+ 207, 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, 0, 0,
+ 55, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 224, 1, 0, 0, 46, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 232, 2,
+ 0, 0, 47, 0, 0, 0,
+ 0, 0, 0, 0, 1, 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,
+ 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, 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, 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, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 4, 0, 0, 38, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 12, 4,
+ 0, 0, 39, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 24, 4, 0, 0,
+ 40, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 36, 4, 0, 0, 41, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 48, 4,
+ 0, 0, 42, 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, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 107, 4, 0, 0,
+ 37, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 119, 4, 0, 0, 38, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 131, 4,
+ 0, 0, 39, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 143, 4, 0, 0,
+ 40, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 155, 4, 0, 0, 41, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 167, 4,
+ 0, 0, 42, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 179, 4, 0, 0,
+ 43, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 191, 4, 0, 0, 44, 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, 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, 48, 9,
+ 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 56, 9, 0, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 28, 12, 0, 0, 36, 12,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 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, 144, 16, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 152, 16, 0, 0, 7, 0,
+ 0, 0, 0, 0, 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,
+ 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,
+ 100, 34, 0, 0, 8, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 108, 34,
+ 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 212, 51, 0, 0,
+ 220, 51, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 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, 73, 56,
+ 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 81, 56, 0, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 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, 139, 101, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 147, 101, 0, 0, 7, 0,
+ 0, 0, 0, 0, 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,
+ 190, 118, 0, 0, 8, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 198, 118,
+ 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 7, 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, 215, 133,
+ 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 223, 133, 0, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 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, 18, 151, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 151, 0, 0, 7, 0,
+ 0, 0, 0, 0, 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,
+ 49, 166, 0, 0, 8, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 57, 166,
+ 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 7, 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, 114, 183,
+ 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 122, 183, 0, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 82, 191, 0, 0, 90, 191,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 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, 182, 195, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 190, 195, 0, 0, 7, 0,
+ 0, 0, 0, 0, 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,
+ 194, 199, 0, 0, 11, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 230, 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,
+ 58, 204, 0, 0, 8, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 66, 204,
+ 0, 0, 7, 0, 0, 0,
+ 0, 0, 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, 69, 214,
+ 0, 0, 11, 0, 0, 0,
+ 0, 0, 0, 0, 1, 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, 189, 218,
+ 0, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 197, 218, 0, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 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, 140, 228, 0, 0,
+ 11, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 176, 228, 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, 4, 233, 0, 0,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 233, 0, 0, 7, 0,
+ 0, 0, 0, 0, 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,
+ 227, 243, 0, 0, 11, 0,
+ 0, 0, 0, 0, 0, 0,
+ 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,
+ 91, 248, 0, 0, 8, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 99, 248,
+ 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 7, 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
new file mode 100644
index 000000000..2d004a000
--- /dev/null
+++ b/gfx/2d/ShadersD2D1.h
@@ -0,0 +1,1419 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+//
+//
+// Buffer Definitions:
+//
+// cbuffer constants
+// {
+//
+// float3 diff; // Offset: 0 Size: 12
+// float2 center1; // Offset: 16 Size: 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
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// InputSampler sampler NA NA 0 1
+// GradientSampler sampler NA NA 1 1
+// InputTexture texture float4 2d 0 1
+// GradientTexture texture float4 2d 1 1
+// constants cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_POSITION 0 xyzw 0 POS float
+// SCENE_POSITION 0 xyzw 1 NONE float xy
+// TEXCOORD 0 xyzw 2 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 0 5 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s1 t1
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c5, 0.5, 1, 0, 0
+ def c6, 1, -1, 0, -0
+ dcl t0
+ dcl t1
+ dcl_2d s0
+ dcl_2d s1
+ dp2add r0.x, t0, c3, c3.z
+ dp2add r0.y, t0, c4, c4.z
+ add r0.xy, r0, -c1
+ dp2add r0.w, r0, r0, -c2.x
+ mul r0.w, r0.w, c1.z
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ mad r0.y, r0.x, r0.x, -r0.w
+ abs r0.z, r0.y
+ cmp r0.y, r0.y, c5.y, c5.z
+ rsq r0.z, r0.z
+ rcp r1.x, r0.z
+ mov r1.yz, -r1.x
+ add r0.xzw, r0.x, r1.xyyz
+ rcp r1.x, c1.z
+ mul r0.xzw, r0, r1.x
+ mov r1.w, c1.w
+ mad r1.xyz, r0.xzww, c0.z, r1.w
+ cmp r1.w, r1.x, r0.x, r0.w
+ cmp r0.xzw, r1.xyyz, c6.xyxy, c6.zyzw
+ 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, r3, s1
+ mul r2.xyz, r2.w, r2
+ mul r1, r1, r2
+ add r0.w, r0.w, r0.x
+ cmp r0.x, r0.w, r0.x, r0.z
+ mul r1, r0.x, r1
+ mul r0, r0.y, r1
+ mov oC0, r0
+
+// approximately 46 instruction slots used (2 texture, 44 arithmetic)
+ps_4_0
+dcl_constantbuffer cb0[5], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_sampler s1, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t1
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v2.xy
+dcl_output o0.xyzw
+dcl_temps 3
+dp2 r0.x, v1.xyxx, cb0[3].xyxx
+add r0.x, r0.x, cb0[3].z
+dp2 r0.z, v1.xyxx, cb0[4].xyxx
+add r0.y, r0.z, cb0[4].z
+add r0.xy, r0.xyxx, -cb0[1].xyxx
+dp2 r0.w, r0.xyxx, r0.xyxx
+add r0.w, r0.w, -cb0[2].x
+mul r0.w, r0.w, cb0[1].z
+mov r0.z, cb0[1].w
+dp3 r0.x, r0.xyzx, cb0[0].xyzx
+mad r0.y, r0.x, r0.x, -r0.w
+sqrt r1.x, |r0.y|
+ge r0.y, r0.y, l(0.000000)
+and r0.y, r0.y, l(0x3f800000)
+mov r1.y, -r1.x
+add r0.xz, r0.xxxx, r1.xxyx
+div r0.xz, r0.xxzx, cb0[1].zzzz
+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 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.zzzz, r1.xyzw
+mul o0.xyzw, r0.yyyy, r1.xyzw
+ret
+// Approximately 49 instruction slots used
+#endif
+
+const BYTE SampleRadialGradientPS[] =
+{
+ 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,
+ 136, 3, 0, 0, 232, 9,
+ 0, 0, 100, 10, 0, 0,
+ 4, 13, 0, 0, 128, 13,
+ 0, 0, 65, 111, 110, 57,
+ 72, 3, 0, 0, 72, 3,
+ 0, 0, 0, 2, 255, 255,
+ 16, 3, 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, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 81, 0,
+ 0, 5, 5, 0, 15, 160,
+ 0, 0, 0, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 81, 0,
+ 0, 5, 6, 0, 15, 160,
+ 0, 0, 128, 63, 0, 0,
+ 128, 191, 0, 0, 0, 0,
+ 0, 0, 0, 128, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 176, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 1, 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, 90, 0,
+ 0, 4, 0, 0, 1, 128,
+ 0, 0, 228, 176, 3, 0,
+ 228, 160, 3, 0, 170, 160,
+ 90, 0, 0, 4, 0, 0,
+ 2, 128, 0, 0, 228, 176,
+ 4, 0, 228, 160, 4, 0,
+ 170, 160, 2, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 228, 128, 1, 0, 228, 161,
+ 90, 0, 0, 4, 0, 0,
+ 8, 128, 0, 0, 228, 128,
+ 0, 0, 228, 128, 2, 0,
+ 0, 161, 5, 0, 0, 3,
+ 0, 0, 8, 128, 0, 0,
+ 255, 128, 1, 0, 170, 160,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 1, 0, 255, 160,
+ 8, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 228, 128,
+ 0, 0, 228, 160, 4, 0,
+ 0, 4, 0, 0, 2, 128,
+ 0, 0, 0, 128, 0, 0,
+ 0, 128, 0, 0, 255, 129,
+ 35, 0, 0, 2, 0, 0,
+ 4, 128, 0, 0, 85, 128,
+ 88, 0, 0, 4, 0, 0,
+ 2, 128, 0, 0, 85, 128,
+ 5, 0, 85, 160, 5, 0,
+ 170, 160, 7, 0, 0, 2,
+ 0, 0, 4, 128, 0, 0,
+ 170, 128, 6, 0, 0, 2,
+ 1, 0, 1, 128, 0, 0,
+ 170, 128, 1, 0, 0, 2,
+ 1, 0, 6, 128, 1, 0,
+ 0, 129, 2, 0, 0, 3,
+ 0, 0, 13, 128, 0, 0,
+ 0, 128, 1, 0, 148, 128,
+ 6, 0, 0, 2, 1, 0,
+ 1, 128, 1, 0, 170, 160,
+ 5, 0, 0, 3, 0, 0,
+ 13, 128, 0, 0, 228, 128,
+ 1, 0, 0, 128, 1, 0,
+ 0, 2, 1, 0, 8, 128,
+ 1, 0, 255, 160, 4, 0,
+ 0, 4, 1, 0, 7, 128,
+ 0, 0, 248, 128, 0, 0,
+ 170, 160, 1, 0, 255, 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, 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,
+ 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,
+ 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,
+ 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, 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, 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, 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, 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, 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,
+ 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,
+ 14, 0, 0, 8, 82, 0,
+ 16, 0, 0, 0, 0, 0,
+ 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, 0, 0, 0, 0,
+ 29, 0, 0, 9, 50, 0,
+ 16, 0, 1, 0, 0, 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,
+ 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, 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,
+ 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,
+ 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,
+ 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, 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,
+ 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,
+ 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, 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, 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, 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,
+ 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,
+ 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, 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,
+ 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, 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, 1, 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,
+ 8, 0, 0, 0, 24, 1,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 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, 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, 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, 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
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
+//
+//
+// Buffer Definitions:
+//
+// cbuffer constants
+// {
+//
+// float3 diff; // Offset: 0 Size: 12
+// float2 center1; // Offset: 16 Size: 8
+// 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
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// InputSampler sampler NA NA 0 1
+// GradientSampler sampler NA NA 1 1
+// InputTexture texture float4 2d 0 1
+// GradientTexture texture float4 2d 1 1
+// constants cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_POSITION 0 xyzw 0 POS float
+// SCENE_POSITION 0 xyzw 1 NONE float xy
+// TEXCOORD 0 xyzw 2 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 0 5 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s1 t1
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c5, 0.5, -0, 1, 0
+ dcl t0
+ dcl t1
+ dcl_2d s0
+ dcl_2d s1
+ 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, c5.x
+ mov r0.z, c1.w
+ dp3 r0.x, r0, c0
+ rcp r0.x, r0.x
+ 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.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 36 instruction slots used (2 texture, 34 arithmetic)
+ps_4_0
+dcl_constantbuffer cb0[5], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_sampler s1, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t1
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v2.xy
+dcl_output o0.xyzw
+dcl_temps 3
+dp2 r0.x, v1.xyxx, cb0[3].xyxx
+add r0.x, r0.x, cb0[3].z
+dp2 r0.z, v1.xyxx, cb0[4].xyxx
+add r0.y, r0.z, cb0[4].z
+add r0.xy, r0.xyxx, -cb0[1].xyxx
+dp2 r0.w, r0.xyxx, r0.xyxx
+mad r0.w, -cb0[1].w, cb0[1].w, r0.w
+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
+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 36 instruction slots used
+#endif
+
+const BYTE SampleRadialGradientA0PS[] =
+{
+ 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,
+ 212, 2, 0, 0, 172, 7,
+ 0, 0, 40, 8, 0, 0,
+ 200, 10, 0, 0, 68, 11,
+ 0, 0, 65, 111, 110, 57,
+ 148, 2, 0, 0, 148, 2,
+ 0, 0, 0, 2, 255, 255,
+ 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, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 81, 0,
+ 0, 5, 5, 0, 15, 160,
+ 0, 0, 0, 63, 0, 0,
+ 0, 128, 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, 128,
+ 1, 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, 90, 0,
+ 0, 4, 0, 0, 1, 128,
+ 0, 0, 228, 176, 3, 0,
+ 228, 160, 3, 0, 170, 160,
+ 90, 0, 0, 4, 0, 0,
+ 2, 128, 0, 0, 228, 176,
+ 4, 0, 228, 160, 4, 0,
+ 170, 160, 2, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 228, 128, 1, 0, 228, 161,
+ 5, 0, 0, 3, 0, 0,
+ 8, 128, 1, 0, 255, 160,
+ 1, 0, 255, 160, 90, 0,
+ 0, 4, 0, 0, 8, 128,
+ 0, 0, 228, 128, 0, 0,
+ 228, 128, 0, 0, 255, 129,
+ 5, 0, 0, 3, 0, 0,
+ 8, 128, 0, 0, 255, 128,
+ 5, 0, 0, 160, 1, 0,
+ 0, 2, 0, 0, 4, 128,
+ 1, 0, 255, 160, 8, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 6, 0, 0, 2,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 5, 0, 0, 3,
+ 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,
+ 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, 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,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 2, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 0, 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, 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,
+ 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,
+ 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,
+ 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, 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, 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, 70, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 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, 14, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 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, 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,
+ 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,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 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,
+ 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, 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, 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, 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,
+ 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, 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, 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, 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,
+ 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, 1, 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, 8, 0,
+ 0, 0, 24, 1, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 0, 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,
+ 0, 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,
+ 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,
+ 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,
+ 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
new file mode 100644
index 000000000..42337afc2
--- /dev/null
+++ b/gfx/2d/ShadersD2D1.hlsl
@@ -0,0 +1,117 @@
+/* -*- 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/. */
+
+Texture2D InputTexture : register(t0);
+SamplerState InputSampler : register(s0);
+Texture2D GradientTexture : register(t1);
+SamplerState GradientSampler : register(s1);
+
+cbuffer constants : register(b0)
+{
+ // Precalculate as much as we can!
+ float3 diff : packoffset(c0.x);
+ float2 center1 : packoffset(c1.x);
+ 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);
+}
+
+float4 SampleRadialGradientPS(
+ float4 clipSpaceOutput : SV_POSITION,
+ float4 sceneSpaceOutput : SCENE_POSITION,
+ float4 texelSpaceInput0 : TEXCOORD0
+ ) : SV_Target
+{
+ // Radial gradient painting is defined as the set of circles whose centers
+ // are described by C(t) = (C2 - C1) * t + C1; with radii
+ // R(t) = (R2 - R1) * t + R1; for R(t) > 0. This shader solves the
+ // quadratic equation that arises when calculating t for pixel (x, y).
+ //
+ // A more extensive derrivation can be found in the pixman radial gradient
+ // code.
+
+ float2 p = float2(sceneSpaceOutput.x * transform._11 + sceneSpaceOutput.y * transform._21 + transform._31,
+ sceneSpaceOutput.x * transform._12 + sceneSpaceOutput.y * transform._22 + transform._32);
+ float3 dp = float3(p - center1, radius1);
+
+ // dpx * dcx + dpy * dcy + r * dr
+ float B = dot(dp, diff);
+
+ float C = pow(dp.x, 2) + pow(dp.y, 2) - sq_radius1;
+
+ float det = pow(B, 2) - A * C;
+
+ float sqrt_det = sqrt(abs(det));
+
+ float2 t = (B + float2(sqrt_det, -sqrt_det)) / A;
+
+ float2 isValid = step(float2(-radius1, -radius1), t * diff.z);
+
+ float upper_t = lerp(t.y, t.x, isValid.x);
+
+ // 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.
+ output *= InputTexture.Sample(InputSampler, texelSpaceInput0.xy);
+
+ // In order to compile for PS_4_0_level_9_3 we need to be branchless.
+ // This is essentially returning nothing, i.e. bailing early if:
+ // det < 0 || max(isValid.x, isValid.y) <= 0
+ return output * abs(step(max(isValid.x, isValid.y), 0) - 1.0f) * step(0, det);
+};
+
+float4 SampleRadialGradientA0PS(
+ float4 clipSpaceOutput : SV_POSITION,
+ float4 sceneSpaceOutput : SCENE_POSITION,
+ float4 texelSpaceInput0 : TEXCOORD0
+ ) : SV_Target
+{
+ // This simpler shader is used for the degenerate case where A is 0,
+ // i.e. we're actually solving a linear equation.
+
+ float2 p = float2(sceneSpaceOutput.x * transform._11 + sceneSpaceOutput.y * transform._21 + transform._31,
+ sceneSpaceOutput.x * transform._12 + sceneSpaceOutput.y * transform._22 + transform._32);
+ float3 dp = float3(p - center1, radius1);
+
+ // dpx * dcx + dpy * dcy + r * dr
+ float B = dot(dp, diff);
+
+ float C = pow(dp.x, 2) + pow(dp.y, 2) - pow(radius1, 2);
+
+ float t = 0.5 * C / B;
+
+ // 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.
+ output *= InputTexture.Sample(InputSampler, texelSpaceInput0.xy);
+
+ // In order to compile for PS_4_0_level_9_3 we need to be branchless.
+ // This is essentially returning nothing, i.e. bailing early if:
+ // -radius1 >= t * diff.z
+ return output * abs(step(t * diff.z, -radius1) - 1.0f);
+};
+
diff --git a/gfx/2d/SourceSurfaceCairo.cpp b/gfx/2d/SourceSurfaceCairo.cpp
new file mode 100644
index 000000000..ba8498b84
--- /dev/null
+++ b/gfx/2d/SourceSurfaceCairo.cpp
@@ -0,0 +1,164 @@
+/* -*- 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 "SourceSurfaceCairo.h"
+#include "DrawTargetCairo.h"
+#include "HelpersCairo.h"
+#include "DataSourceSurfaceWrapper.h"
+
+#include "cairo.h"
+
+namespace mozilla {
+namespace gfx {
+
+static SurfaceFormat
+CairoFormatToSurfaceFormat(cairo_format_t format)
+{
+ switch (format)
+ {
+ case CAIRO_FORMAT_ARGB32:
+ return SurfaceFormat::B8G8R8A8;
+ case CAIRO_FORMAT_RGB24:
+ return SurfaceFormat::B8G8R8X8;
+ case CAIRO_FORMAT_RGB16_565:
+ return SurfaceFormat::R5G6B5_UINT16;
+ case CAIRO_FORMAT_A8:
+ return SurfaceFormat::A8;
+ default:
+ return SurfaceFormat::B8G8R8A8;
+ }
+}
+
+SourceSurfaceCairo::SourceSurfaceCairo(cairo_surface_t* aSurface,
+ const IntSize& aSize,
+ const SurfaceFormat& aFormat,
+ DrawTargetCairo* aDrawTarget /* = nullptr */)
+ : mSize(aSize)
+ , mFormat(aFormat)
+ , mSurface(aSurface)
+ , mDrawTarget(aDrawTarget)
+{
+ cairo_surface_reference(mSurface);
+}
+
+SourceSurfaceCairo::~SourceSurfaceCairo()
+{
+ cairo_surface_destroy(mSurface);
+}
+
+IntSize
+SourceSurfaceCairo::GetSize() const
+{
+ return mSize;
+}
+
+SurfaceFormat
+SourceSurfaceCairo::GetFormat() const
+{
+ return mFormat;
+}
+
+already_AddRefed<DataSourceSurface>
+SourceSurfaceCairo::GetDataSurface()
+{
+ RefPtr<DataSourceSurface> dataSurf;
+
+ if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
+ dataSurf = new DataSourceSurfaceCairo(mSurface);
+ } else {
+ cairo_surface_t* imageSurf = cairo_image_surface_create(GfxFormatToCairoFormat(mFormat),
+ mSize.width, mSize.height);
+
+ // Fill the new image surface with the contents of our surface.
+ cairo_t* ctx = cairo_create(imageSurf);
+ cairo_set_source_surface(ctx, mSurface, 0, 0);
+ cairo_paint(ctx);
+ cairo_destroy(ctx);
+
+ dataSurf = new DataSourceSurfaceCairo(imageSurf);
+ cairo_surface_destroy(imageSurf);
+ }
+
+ // We also need to make sure that the returned surface has
+ // surface->GetType() == SurfaceType::DATA.
+ return MakeAndAddRef<DataSourceSurfaceWrapper>(dataSurf);
+}
+
+cairo_surface_t*
+SourceSurfaceCairo::GetSurface() const
+{
+ return mSurface;
+}
+
+void
+SourceSurfaceCairo::DrawTargetWillChange()
+{
+ if (mDrawTarget) {
+ mDrawTarget = nullptr;
+
+ // We're about to lose our version of the surface, so make a copy of it.
+ cairo_surface_t* surface = cairo_surface_create_similar(mSurface,
+ GfxFormatToCairoContent(mFormat),
+ mSize.width, mSize.height);
+ cairo_t* ctx = cairo_create(surface);
+ cairo_pattern_t* pat = cairo_pattern_create_for_surface(mSurface);
+ cairo_set_source(ctx, pat);
+ cairo_paint(ctx);
+ cairo_destroy(ctx);
+ cairo_pattern_destroy(pat);
+
+ // Swap in this new surface.
+ cairo_surface_destroy(mSurface);
+ mSurface = surface;
+ }
+}
+
+DataSourceSurfaceCairo::DataSourceSurfaceCairo(cairo_surface_t* imageSurf)
+ : mImageSurface(imageSurf)
+{
+ cairo_surface_reference(mImageSurface);
+}
+
+DataSourceSurfaceCairo::~DataSourceSurfaceCairo()
+{
+ cairo_surface_destroy(mImageSurface);
+}
+
+unsigned char *
+DataSourceSurfaceCairo::GetData()
+{
+ return cairo_image_surface_get_data(mImageSurface);
+}
+
+int32_t
+DataSourceSurfaceCairo::Stride()
+{
+ return cairo_image_surface_get_stride(mImageSurface);
+}
+
+IntSize
+DataSourceSurfaceCairo::GetSize() const
+{
+ IntSize size;
+ size.width = cairo_image_surface_get_width(mImageSurface);
+ size.height = cairo_image_surface_get_height(mImageSurface);
+
+ return size;
+}
+
+SurfaceFormat
+DataSourceSurfaceCairo::GetFormat() const
+{
+ return CairoFormatToSurfaceFormat(cairo_image_surface_get_format(mImageSurface));
+}
+
+cairo_surface_t*
+DataSourceSurfaceCairo::GetSurface() const
+{
+ return mImageSurface;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/SourceSurfaceCairo.h b/gfx/2d/SourceSurfaceCairo.h
new file mode 100644
index 000000000..428c75957
--- /dev/null
+++ b/gfx/2d/SourceSurfaceCairo.h
@@ -0,0 +1,70 @@
+/* -*- 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_OP_SOURCESURFACE_CAIRO_H
+#define _MOZILLA_GFX_OP_SOURCESURFACE_CAIRO_H
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+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
+ // surface, and we'll call DrawTargetCairo::RemoveSnapshot(this) on it when
+ // we're destroyed.
+ SourceSurfaceCairo(cairo_surface_t* aSurface, const IntSize& aSize,
+ const SurfaceFormat& aFormat,
+ DrawTargetCairo* aDrawTarget = nullptr);
+ virtual ~SourceSurfaceCairo();
+
+ virtual SurfaceType GetType() const { return SurfaceType::CAIRO; }
+ virtual IntSize GetSize() const;
+ virtual SurfaceFormat GetFormat() const;
+ virtual already_AddRefed<DataSourceSurface> GetDataSurface();
+
+ cairo_surface_t* GetSurface() const;
+
+private: // methods
+ friend class DrawTargetCairo;
+ void DrawTargetWillChange();
+
+private: // data
+ IntSize mSize;
+ SurfaceFormat mFormat;
+ cairo_surface_t* mSurface;
+ DrawTargetCairo* mDrawTarget;
+};
+
+class DataSourceSurfaceCairo : public DataSourceSurface
+{
+public:
+ 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 SurfaceType::CAIRO_IMAGE; }
+ virtual IntSize GetSize() const;
+ virtual SurfaceFormat GetFormat() const;
+
+ cairo_surface_t* GetSurface() const;
+
+private:
+ cairo_surface_t* mImageSurface;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _MOZILLA_GFX_OP_SOURCESURFACE_CAIRO_H
diff --git a/gfx/2d/SourceSurfaceD2D1.cpp b/gfx/2d/SourceSurfaceD2D1.cpp
new file mode 100644
index 000000000..92b2e4d1d
--- /dev/null
+++ b/gfx/2d/SourceSurfaceD2D1.cpp
@@ -0,0 +1,241 @@
+/* -*- 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)
+ , mDevice(Factory::GetD2D1Device())
+ , mDrawTarget(aDT)
+{
+ aImage->QueryInterface((ID2D1Bitmap1**)getter_AddRefs(mRealizedBitmap));
+
+ mFormat = aFormat;
+ mSize = aSize;
+}
+
+SourceSurfaceD2D1::~SourceSurfaceD2D1()
+{
+}
+
+bool
+SourceSurfaceD2D1::IsValid() const
+{
+ return mDevice == Factory::GetD2D1Device();
+}
+
+already_AddRefed<DataSourceSurface>
+SourceSurfaceD2D1::GetDataSurface()
+{
+ HRESULT hr;
+
+ if (!EnsureRealizedBitmap()) {
+ gfxCriticalError() << "Failed to realize a bitmap, device " << hexa(mDevice);
+ return nullptr;
+ }
+
+ 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**)getter_AddRefs(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 MakeAndAddRef<DataSourceSurfaceD2D1>(softwareBitmap, mFormat);
+}
+
+bool
+SourceSurfaceD2D1::EnsureRealizedBitmap()
+{
+ if (mRealizedBitmap) {
+ return true;
+ }
+
+ // Why aren't we using mDevice here or anywhere else?
+ ID2D1Device* device = Factory::GetD2D1Device();
+ if (!device) {
+ return false;
+ }
+
+ RefPtr<ID2D1DeviceContext> dc;
+ device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, getter_AddRefs(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**)getter_AddRefs(mRealizedBitmap));
+
+ dc->SetTarget(mRealizedBitmap);
+
+ dc->BeginDraw();
+ dc->DrawImage(mImage);
+ dc->EndDraw();
+
+ return true;
+}
+
+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**)getter_AddRefs(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 {
+ gfxWarning() << "Attempt to map D2D1 DrawTarget for writing.";
+ return false;
+ }
+
+ D2D1_MAPPED_RECT map;
+ if (FAILED(mBitmap->Map(D2D1_MAP_OPTIONS_READ, &map))) {
+ gfxCriticalError() << "Failed to map bitmap (M).";
+ 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 (EM).";
+ return;
+ }
+ mMapped = true;
+}
+
+}
+}
diff --git a/gfx/2d/SourceSurfaceD2D1.h b/gfx/2d/SourceSurfaceD2D1.h
new file mode 100644
index 000000000..5b7cedf4c
--- /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 already_AddRefed<DataSourceSurface> GetDataSurface();
+
+ ID2D1Image *GetImage() { return mImage; }
+
+ void EnsureIndependent() { if (!mDrawTarget) return; DrawTargetWillChange(); }
+
+private:
+ friend class DrawTargetD2D1;
+
+ bool 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/SourceSurfaceDual.h b/gfx/2d/SourceSurfaceDual.h
new file mode 100644
index 000000000..df81b87f2
--- /dev/null
+++ b/gfx/2d/SourceSurfaceDual.h
@@ -0,0 +1,47 @@
+/* -*- 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_SOURCESURFACEDUAL_H_
+#define MOZILLA_GFX_SOURCESURFACEDUAL_H_
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DualSurface;
+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 SurfaceType::DUAL_DT; }
+ virtual IntSize GetSize() const { return mA->GetSize(); }
+ virtual SurfaceFormat GetFormat() const { return mA->GetFormat(); }
+
+ // This is implemented for debugging purposes only (used by dumping
+ // client-side textures for paint dumps), for which we don't care about
+ // component alpha, so we just use the first of the two surfaces.
+ virtual already_AddRefed<DataSourceSurface> GetDataSurface() {
+ return mA->GetDataSurface();
+ }
+private:
+ friend class DualSurface;
+ friend class DualPattern;
+
+ RefPtr<SourceSurface> mA;
+ RefPtr<SourceSurface> mB;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SOURCESURFACEDUAL_H_ */
diff --git a/gfx/2d/SourceSurfaceRawData.cpp b/gfx/2d/SourceSurfaceRawData.cpp
new file mode 100644
index 000000000..9d95e9900
--- /dev/null
+++ b/gfx/2d/SourceSurfaceRawData.cpp
@@ -0,0 +1,81 @@
+/* -*- 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 "SourceSurfaceRawData.h"
+
+#include "DataSurfaceHelpers.h"
+#include "Logging.h"
+#include "mozilla/Types.h" // for decltype
+
+namespace mozilla {
+namespace gfx {
+
+void
+SourceSurfaceRawData::InitWrappingData(uint8_t *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat,
+ Factory::SourceSurfaceDeallocator aDeallocator,
+ void* aClosure)
+{
+ mRawData = aData;
+ mSize = aSize;
+ mStride = aStride;
+ mFormat = aFormat;
+
+ if (aDeallocator) {
+ mOwnData = true;
+ }
+ mDeallocator = aDeallocator;
+ mClosure = aClosure;
+}
+
+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 aClearMem,
+ uint8_t aClearValue,
+ int32_t aStride)
+{
+ mFormat = aFormat;
+ mStride = aStride ? aStride : GetAlignedStride<16>(aSize.width, BytesPerPixel(aFormat));
+
+ size_t bufLen = BufferSizeFromStrideAndHeight(mStride, aSize.height);
+ if (bufLen > 0) {
+ bool zeroMem = aClearMem && !aClearValue;
+ 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");
+
+ // AlignedArray uses cmalloc to zero mem for a fast path.
+ mArray.Realloc(/* actually an object count */ bufLen, zeroMem);
+ mSize = aSize;
+
+ if (mArray && aClearMem && aClearValue) {
+ memset(mArray, aClearValue, mStride * aSize.height);
+ }
+ } else {
+ mArray.Dealloc();
+ mSize.SizeTo(0, 0);
+ }
+
+ return mArray != nullptr;
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/SourceSurfaceRawData.h b/gfx/2d/SourceSurfaceRawData.h
new file mode 100644
index 000000000..f4c707198
--- /dev/null
+++ b/gfx/2d/SourceSurfaceRawData.h
@@ -0,0 +1,160 @@
+/* -*- 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_SOURCESURFACERAWDATA_H_
+#define MOZILLA_GFX_SOURCESURFACERAWDATA_H_
+
+#include "2D.h"
+#include "Tools.h"
+#include "mozilla/Atomics.h"
+
+namespace mozilla {
+namespace gfx {
+
+class SourceSurfaceRawData : public DataSourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceRawData, override)
+
+ SourceSurfaceRawData()
+ : mRawData(0)
+ , mStride(0)
+ , mFormat(SurfaceFormat::UNKNOWN)
+ , mMapCount(0)
+ , mOwnData(false)
+ , mDeallocator(nullptr)
+ , mClosure(nullptr)
+ {
+ }
+
+ virtual ~SourceSurfaceRawData()
+ {
+ if (mDeallocator) {
+ mDeallocator(mClosure);
+ } else if (mOwnData) {
+ // The buffer is created from GuaranteePersistance().
+ delete [] mRawData;
+ }
+
+ MOZ_ASSERT(mMapCount == 0);
+ }
+
+ virtual uint8_t *GetData() override { return mRawData; }
+ virtual int32_t Stride() override { return mStride; }
+
+ virtual SurfaceType GetType() const override { return SurfaceType::DATA; }
+ virtual IntSize GetSize() const override { return mSize; }
+ virtual SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual void GuaranteePersistance() override;
+
+ // Althought Map (and Moz2D in general) isn't normally threadsafe,
+ // we want to allow it for SourceSurfaceRawData since it should
+ // always be fine (for reading at least).
+ //
+ // This is the same as the base class implementation except using
+ // mMapCount instead of mIsMapped since that breaks for multithread.
+ //
+ // Once mfbt supports Monitors we should implement proper read/write
+ // locking to prevent write races.
+ virtual bool Map(MapType, MappedSurface *aMappedSurface) override
+ {
+ aMappedSurface->mData = GetData();
+ aMappedSurface->mStride = Stride();
+ bool success = !!aMappedSurface->mData;
+ if (success) {
+ mMapCount++;
+ }
+ return success;
+ }
+
+ virtual void Unmap() override
+ {
+ mMapCount--;
+ MOZ_ASSERT(mMapCount >= 0);
+ }
+
+private:
+ friend class Factory;
+
+ // If we have a custom deallocator, the |aData| will be released using the
+ // custom deallocator and |aClosure| in dtor. The assumption is that the
+ // caller will check for valid size and stride before making this call.
+ void InitWrappingData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat,
+ Factory::SourceSurfaceDeallocator aDeallocator,
+ void* aClosure);
+
+ uint8_t *mRawData;
+ int32_t mStride;
+ SurfaceFormat mFormat;
+ IntSize mSize;
+ Atomic<int32_t> mMapCount;
+
+ bool mOwnData;
+ Factory::SourceSurfaceDeallocator mDeallocator;
+ void* mClosure;
+};
+
+class SourceSurfaceAlignedRawData : public DataSourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceAlignedRawData, override)
+ SourceSurfaceAlignedRawData()
+ : mStride(0)
+ , mFormat(SurfaceFormat::UNKNOWN)
+ , mMapCount(0)
+ {}
+ ~SourceSurfaceAlignedRawData()
+ {
+ MOZ_ASSERT(mMapCount == 0);
+ }
+
+ virtual uint8_t* GetData() override { return mArray; }
+ virtual int32_t Stride() override { return mStride; }
+
+ virtual SurfaceType GetType() const override { return SurfaceType::DATA; }
+ virtual IntSize GetSize() const override { return mSize; }
+ virtual SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual bool Map(MapType, MappedSurface *aMappedSurface) override
+ {
+ aMappedSurface->mData = GetData();
+ aMappedSurface->mStride = Stride();
+ bool success = !!aMappedSurface->mData;
+ if (success) {
+ mMapCount++;
+ }
+ return success;
+ }
+
+ virtual void Unmap() override
+ {
+ mMapCount--;
+ MOZ_ASSERT(mMapCount >= 0);
+ }
+
+private:
+ friend class Factory;
+
+ bool Init(const IntSize &aSize,
+ SurfaceFormat aFormat,
+ bool aClearMem,
+ uint8_t aClearValue,
+ int32_t aStride = 0);
+
+ AlignedArray<uint8_t> mArray;
+ int32_t mStride;
+ SurfaceFormat mFormat;
+ IntSize mSize;
+ Atomic<int32_t> mMapCount;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SOURCESURFACERAWDATA_H_ */
diff --git a/gfx/2d/SourceSurfaceSkia.cpp b/gfx/2d/SourceSurfaceSkia.cpp
new file mode 100644
index 000000000..14cbf6a84
--- /dev/null
+++ b/gfx/2d/SourceSurfaceSkia.cpp
@@ -0,0 +1,172 @@
+/* -*- 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 "Logging.h"
+#include "SourceSurfaceSkia.h"
+#include "HelpersSkia.h"
+#include "DrawTargetSkia.h"
+#include "DataSurfaceHelpers.h"
+#include "skia/include/core/SkData.h"
+#include "mozilla/CheckedInt.h"
+
+namespace mozilla {
+namespace gfx {
+
+SourceSurfaceSkia::SourceSurfaceSkia()
+ : mDrawTarget(nullptr)
+{
+}
+
+SourceSurfaceSkia::~SourceSurfaceSkia()
+{
+ if (mDrawTarget) {
+ mDrawTarget->SnapshotDestroyed();
+ mDrawTarget = nullptr;
+ }
+}
+
+IntSize
+SourceSurfaceSkia::GetSize() const
+{
+ return mSize;
+}
+
+SurfaceFormat
+SourceSurfaceSkia::GetFormat() const
+{
+ return mFormat;
+}
+
+static sk_sp<SkData>
+MakeSkData(unsigned char* aData, const IntSize& aSize, int32_t aStride)
+{
+ CheckedInt<size_t> size = aStride;
+ size *= aSize.height;
+ if (size.isValid()) {
+ void* mem = sk_malloc_flags(size.value(), 0);
+ if (mem) {
+ if (aData) {
+ memcpy(mem, aData, size.value());
+ }
+ return SkData::MakeFromMalloc(mem, size.value());
+ }
+ }
+ return nullptr;
+}
+
+bool
+SourceSurfaceSkia::InitFromData(unsigned char* aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat)
+{
+ sk_sp<SkData> data = MakeSkData(aData, aSize, aStride);
+ if (!data) {
+ return false;
+ }
+
+ SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
+ mImage = SkImage::MakeRasterData(info, data, aStride);
+ if (!mImage) {
+ return false;
+ }
+
+ mSize = aSize;
+ mFormat = aFormat;
+ mStride = aStride;
+ return true;
+}
+
+bool
+SourceSurfaceSkia::InitFromImage(sk_sp<SkImage> aImage,
+ SurfaceFormat aFormat,
+ DrawTargetSkia* aOwner)
+{
+ if (!aImage) {
+ return false;
+ }
+
+ mSize = IntSize(aImage->width(), aImage->height());
+
+ // For the raster image case, we want to use the format and stride
+ // information that the underlying raster image is using, which is
+ // reliable.
+ // For the GPU case (for which peekPixels is false), we can't easily
+ // figure this information out. It is better to report the originally
+ // intended format and stride that we will convert to if this GPU
+ // image is ever read back into a raster image.
+ SkPixmap pixmap;
+ if (aImage->peekPixels(&pixmap)) {
+ mFormat =
+ aFormat != SurfaceFormat::UNKNOWN ?
+ aFormat :
+ SkiaColorTypeToGfxFormat(pixmap.colorType(), pixmap.alphaType());
+ mStride = pixmap.rowBytes();
+ } else if (aFormat != SurfaceFormat::UNKNOWN) {
+ mFormat = aFormat;
+ SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat);
+ mStride = SkAlign4(info.minRowBytes());
+ } else {
+ return false;
+ }
+
+ mImage = aImage;
+
+ if (aOwner) {
+ mDrawTarget = aOwner;
+ }
+
+ return true;
+}
+
+uint8_t*
+SourceSurfaceSkia::GetData()
+{
+#ifdef USE_SKIA_GPU
+ if (mImage->isTextureBacked()) {
+ sk_sp<SkImage> raster;
+ if (sk_sp<SkData> data = MakeSkData(nullptr, mSize, mStride)) {
+ SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat);
+ if (mImage->readPixels(info, data->writable_data(), mStride, 0, 0, SkImage::kDisallow_CachingHint)) {
+ raster = SkImage::MakeRasterData(info, data, mStride);
+ }
+ }
+ if (raster) {
+ mImage = raster;
+ } else {
+ gfxCriticalError() << "Failed making Skia raster image for GPU surface";
+ }
+ }
+#endif
+ SkPixmap pixmap;
+ if (!mImage->peekPixels(&pixmap)) {
+ gfxCriticalError() << "Failed accessing pixels for Skia raster image";
+ }
+ return reinterpret_cast<uint8_t*>(pixmap.writable_addr());
+}
+
+void
+SourceSurfaceSkia::DrawTargetWillChange()
+{
+ if (mDrawTarget) {
+ // Raster snapshots do not use Skia's internal copy-on-write mechanism,
+ // so we need to do an explicit copy here.
+ // GPU snapshots, for which peekPixels is false, will already be dealt
+ // with automatically via the internal copy-on-write mechanism, so we
+ // don't need to do anything for them here.
+ SkPixmap pixmap;
+ if (mImage->peekPixels(&pixmap)) {
+ mImage = SkImage::MakeRasterCopy(pixmap);
+ if (!mImage) {
+ gfxCriticalError() << "Failed copying Skia raster snapshot";
+ }
+ }
+ mDrawTarget = nullptr;
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/2d/SourceSurfaceSkia.h b/gfx/2d/SourceSurfaceSkia.h
new file mode 100644
index 000000000..a91ae93ed
--- /dev/null
+++ b/gfx/2d/SourceSurfaceSkia.h
@@ -0,0 +1,61 @@
+/* -*- 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_SOURCESURFACESKIA_H_
+#define MOZILLA_GFX_SOURCESURFACESKIA_H_
+
+#include "2D.h"
+#include <vector>
+#include "skia/include/core/SkCanvas.h"
+#include "skia/include/core/SkImage.h"
+
+namespace mozilla {
+
+namespace gfx {
+
+class DrawTargetSkia;
+
+class SourceSurfaceSkia : public DataSourceSurface
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceSkia)
+ SourceSurfaceSkia();
+ ~SourceSurfaceSkia();
+
+ virtual SurfaceType GetType() const { return SurfaceType::SKIA; }
+ virtual IntSize GetSize() const;
+ virtual SurfaceFormat GetFormat() const;
+
+ sk_sp<SkImage>& GetImage() { return mImage; }
+
+ bool InitFromData(unsigned char* aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat);
+
+ bool InitFromImage(sk_sp<SkImage> aImage,
+ SurfaceFormat aFormat = SurfaceFormat::UNKNOWN,
+ DrawTargetSkia* aOwner = nullptr);
+
+ virtual uint8_t* GetData();
+
+ virtual int32_t Stride() { return mStride; }
+
+private:
+ friend class DrawTargetSkia;
+
+ void DrawTargetWillChange();
+
+ sk_sp<SkImage> mImage;
+ SurfaceFormat mFormat;
+ IntSize mSize;
+ int32_t mStride;
+ RefPtr<DrawTargetSkia> mDrawTarget;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SOURCESURFACESKIA_H_ */
diff --git a/gfx/2d/StackArray.h b/gfx/2d/StackArray.h
new file mode 100644
index 000000000..a2451f930
--- /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 AutoTArray */
+
+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
new file mode 100644
index 000000000..585cbbb2c
--- /dev/null
+++ b/gfx/2d/Tools.h
@@ -0,0 +1,246 @@
+/* -*- 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_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>
+
+namespace mozilla {
+namespace gfx {
+
+static inline bool
+IsOperatorBoundByMask(CompositionOp aOp) {
+ switch (aOp) {
+ 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;
+ }
+}
+
+template <class T>
+struct ClassStorage
+{
+ char bytes[sizeof(T)];
+
+ const T *addr() const { return (const T *)bytes; }
+ T *addr() { return (T *)(void *)bytes; }
+};
+
+static inline bool
+FuzzyEqual(Float aA, Float aB, Float aErr)
+{
+ if ((aA + aErr >= aB) && (aA - aErr <= aB)) {
+ return true;
+ }
+ return false;
+}
+
+static inline void
+NudgeToInteger(float *aVal)
+{
+ float r = floorf(*aVal + 0.5f);
+ // The error threshold should be proportional to the rounded value. This
+ // bounds the relative error introduced by the nudge operation. However,
+ // when the rounded value is 0, the error threshold can't be proportional
+ // to the rounded value (we'd never round), so we just choose the same
+ // threshold as for a rounded value of 1.
+ if (FuzzyEqual(r, *aVal, r == 0.0f ? 1e-6f : fabs(r*1e-6f))) {
+ *aVal = r;
+ }
+}
+
+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)
+{
+ return hypotf(aB.x - aA.x, aB.y - aA.y);
+}
+
+static inline int
+BytesPerPixel(SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::A8:
+ return 1;
+ case SurfaceFormat::R5G6B5_UINT16:
+ return 2;
+ case SurfaceFormat::R8G8B8:
+ case SurfaceFormat::B8G8R8:
+ return 3;
+ case SurfaceFormat::HSV:
+ case SurfaceFormat::Lab:
+ return 3 * sizeof(float);
+ case SurfaceFormat::Depth:
+ return sizeof(uint16_t);
+ default:
+ return 4;
+ }
+}
+
+static inline bool
+IsOpaqueFormat(SurfaceFormat aFormat) {
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8X8:
+ case SurfaceFormat::R8G8B8X8:
+ case SurfaceFormat::X8R8G8B8:
+ case SurfaceFormat::YUV:
+ case SurfaceFormat::NV12:
+ case SurfaceFormat::YUV422:
+ case SurfaceFormat::R5G6B5_UINT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+template<typename T, int alignment = 16>
+struct AlignedArray
+{
+ typedef T value_type;
+
+ AlignedArray()
+ : mPtr(nullptr)
+ , mStorage(nullptr)
+ {
+ }
+
+ explicit MOZ_ALWAYS_INLINE AlignedArray(size_t aCount, bool aZero = false)
+ : mStorage(nullptr)
+ , mCount(0)
+ {
+ Realloc(aCount, aZero);
+ }
+
+ MOZ_ALWAYS_INLINE ~AlignedArray()
+ {
+ Dealloc();
+ }
+
+ void Dealloc()
+ {
+ // 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
+
+ free(mStorage);
+ mStorage = nullptr;
+ mPtr = nullptr;
+ }
+
+ MOZ_ALWAYS_INLINE void Realloc(size_t aCount, bool aZero = false)
+ {
+ 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 *>(calloc(1, storageByteCount.value()));
+ } else {
+ mStorage = static_cast<uint8_t *>(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 mPtr does!
+ mPtr = (T*)(uintptr_t(mStorage) + alignment - (uintptr_t(mStorage) % alignment));
+ } else {
+ 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*()
+ {
+ return mPtr;
+ }
+
+ T *mPtr;
+
+private:
+ uint8_t *mStorage;
+ size_t mCount;
+};
+
+/**
+ * Returns aWidth * aBytesPerPixel 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 aWidth, int32_t aBytesPerPixel)
+{
+ static_assert(alignment > 0 && (alignment & (alignment-1)) == 0,
+ "This implementation currently require power-of-two alignment");
+ const int32_t mask = alignment - 1;
+ CheckedInt32 stride = CheckedInt32(aWidth) * CheckedInt32(aBytesPerPixel) + CheckedInt32(mask);
+ if (stride.isValid()) {
+ return stride.value() & ~mask;
+ }
+ return 0;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TOOLS_H_ */
diff --git a/gfx/2d/Triangle.h b/gfx/2d/Triangle.h
new file mode 100644
index 000000000..c667abe47
--- /dev/null
+++ b/gfx/2d/Triangle.h
@@ -0,0 +1,66 @@
+/* -*- 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_TRIANGLE_H
+#define MOZILLA_GFX_TRIANGLE_H
+
+#include <algorithm>
+
+#include "mozilla/Move.h"
+#include "Point.h"
+#include "Rect.h"
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * A simple triangle data structure.
+ */
+template<class Units, class F = Float>
+struct TriangleTyped
+{
+ PointTyped<Units, F> p1, p2, p3;
+ F width, height;
+
+ TriangleTyped()
+ : p1(), p2(), p3() {}
+
+ TriangleTyped(PointTyped<Units, F> aP1,
+ PointTyped<Units, F> aP2,
+ PointTyped<Units, F> aP3)
+ : p1(aP1), p2(aP2), p3(aP3) {}
+
+ RectTyped<Units, F> BoundingBox() const
+ {
+ F minX = std::min(std::min(p1.x, p2.x), p3.x);
+ F maxX = std::max(std::max(p1.x, p2.x), p3.x);
+
+ F minY = std::min(std::min(p1.y, p2.y), p3.y);
+ F maxY = std::max(std::max(p1.y, p2.y), p3.y);
+
+ return RectTyped<Units, F>(minX, minY, maxX - minX, maxY - minY);
+ }
+};
+
+typedef TriangleTyped<UnknownUnits, Float> Triangle;
+
+template<class Units, class F = Float>
+struct TexturedTriangleTyped : public TriangleTyped<Units, F>
+{
+ explicit TexturedTriangleTyped(const TriangleTyped<Units, F>& aTriangle)
+ : TriangleTyped<Units, F>(aTriangle) {}
+
+ explicit TexturedTriangleTyped(TriangleTyped<Units, F>&& aTriangle)
+ : TriangleTyped<Units, F>(Move(aTriangle)) {}
+
+ TriangleTyped<Units, F> textureCoords;
+};
+
+typedef TexturedTriangleTyped<UnknownUnits, Float> TexturedTriangle;
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TRIANGLE_H */
diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h
new file mode 100644
index 000000000..7b1676ab2
--- /dev/null
+++ b/gfx/2d/Types.h
@@ -0,0 +1,407 @@
+/* -*- 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_TYPES_H_
+#define MOZILLA_GFX_TYPES_H_
+
+#include "mozilla/EndianUtils.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace mozilla {
+namespace gfx {
+
+typedef float Float;
+
+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 class SurfaceFormat : int8_t {
+ // The following values are named to reflect layout of colors in memory, from
+ // lowest byte to highest byte. The 32-bit value layout depends on machine
+ // endianness.
+ // in-memory 32-bit LE value 32-bit BE value
+ B8G8R8A8, // [BB, GG, RR, AA] 0xAARRGGBB 0xBBGGRRAA
+ B8G8R8X8, // [BB, GG, RR, 00] 0x00RRGGBB 0xBBGGRR00
+ R8G8B8A8, // [RR, GG, BB, AA] 0xAABBGGRR 0xRRGGBBAA
+ R8G8B8X8, // [RR, GG, BB, 00] 0x00BBGGRR 0xRRGGBB00
+ A8R8G8B8, // [AA, RR, GG, BB] 0xBBGGRRAA 0xAARRGGBB
+ X8R8G8B8, // [00, RR, GG, BB] 0xBBGGRR00 0x00RRGGBB
+
+ R8G8B8,
+ B8G8R8,
+
+ // The _UINT16 suffix here indicates that the name reflects the layout when
+ // viewed as a uint16_t value. In memory these values are stored using native
+ // endianness.
+ R5G6B5_UINT16, // 0bRRRRRGGGGGGBBBBB
+
+ // This one is a single-byte, so endianness isn't an issue.
+ A8,
+
+ // These ones are their own special cases.
+ YUV,
+ NV12,
+ YUV422,
+ HSV,
+ Lab,
+ Depth,
+
+ // This represents the unknown format.
+ UNKNOWN,
+
+ // The following values are endian-independent synonyms. The _UINT32 suffix
+ // indicates that the name reflects the layout when viewed as a uint32_t
+ // value.
+#if MOZ_LITTLE_ENDIAN
+ A8R8G8B8_UINT32 = B8G8R8A8, // 0xAARRGGBB
+ X8R8G8B8_UINT32 = B8G8R8X8 // 0x00RRGGBB
+#elif MOZ_BIG_ENDIAN
+ A8R8G8B8_UINT32 = A8R8G8B8, // 0xAARRGGBB
+ X8R8G8B8_UINT32 = X8R8G8B8 // 0x00RRGGBB
+#else
+# error "bad endianness"
+#endif
+};
+
+inline bool IsOpaque(SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8X8:
+ case SurfaceFormat::R8G8B8X8:
+ case SurfaceFormat::R5G6B5_UINT16:
+ case SurfaceFormat::YUV:
+ case SurfaceFormat::NV12:
+ case SurfaceFormat::YUV422:
+ 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 class DrawTargetType : int8_t {
+ SOFTWARE_RASTER = 0,
+ HARDWARE_RASTER,
+ VECTOR
+};
+
+enum class BackendType : int8_t {
+ NONE = 0,
+ DIRECT2D, // Used for version independent D2D objects.
+ CAIRO,
+ SKIA,
+ RECORDING,
+ DIRECT2D1_1,
+
+ // Add new entries above this line.
+ BACKEND_LAST
+};
+
+enum class FontType : int8_t {
+ DWRITE,
+ GDI,
+ MAC,
+ SKIA,
+ CAIRO,
+ COREGRAPHICS,
+ FONTCONFIG
+};
+
+enum class NativeSurfaceType : int8_t {
+ D3D10_TEXTURE,
+ 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 Axis : int8_t {
+ X_AXIS,
+ Y_AXIS,
+ BOTH
+};
+
+enum class ExtendMode : int8_t {
+ CLAMP, // Do not repeat
+ REPEAT, // Repeat in both axis
+ REPEAT_X, // Only X axis
+ REPEAT_Y, // Only Y axis
+ REFLECT // Mirror the image
+};
+
+enum class FillRule : int8_t {
+ FILL_WINDING,
+ FILL_EVEN_ODD
+};
+
+enum class AntialiasMode : int8_t {
+ NONE,
+ GRAY,
+ SUBPIXEL,
+ DEFAULT
+};
+
+// See https://en.wikipedia.org/wiki/Texture_filtering
+enum class SamplingFilter : int8_t {
+ GOOD,
+ LINEAR,
+ POINT,
+ SENTINEL // one past the last valid value
+};
+
+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
+{
+public:
+ Color()
+ : r(0.0f), g(0.0f), b(0.0f), a(0.0f)
+ {}
+ Color(Float aR, Float aG, Float aB, Float aA)
+ : r(aR), g(aG), b(aB), a(aA)
+ {}
+ Color(Float aR, Float aG, Float aB)
+ : r(aR), g(aG), b(aB), a(1.0f)
+ {}
+
+ static Color FromABGR(uint32_t aColor)
+ {
+ Color newColor(((aColor >> 0) & 0xff) * (1.0f / 255.0f),
+ ((aColor >> 8) & 0xff) * (1.0f / 255.0f),
+ ((aColor >> 16) & 0xff) * (1.0f / 255.0f),
+ ((aColor >> 24) & 0xff) * (1.0f / 255.0f));
+
+ return newColor;
+ }
+
+ // The "Unusual" prefix is to avoid unintentionally using this function when
+ // FromABGR(), which is much more common, is needed.
+ static Color UnusualFromARGB(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;
+ }
+
+ // The "Unusual" prefix is to avoid unintentionally using this function when
+ // ToABGR(), which is much more common, is needed.
+ uint32_t UnusualToARGB() 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;
+ }
+
+ bool operator==(const Color& aColor) const {
+ return r == aColor.r && g == aColor.g && b == aColor.b && a == aColor.a;
+ }
+
+ bool operator!=(const Color& aColor) const {
+ return !(*this == aColor);
+ }
+
+ Float r, g, b, a;
+};
+
+struct GradientStop
+{
+ bool operator<(const GradientStop& aOther) const {
+ return offset < aOther.offset;
+ }
+
+ Float offset;
+ Color color;
+};
+
+enum class JobStatus {
+ Complete,
+ Wait,
+ Yield,
+ Error
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+// XXX: temporary
+typedef mozilla::gfx::SurfaceFormat gfxImageFormat;
+
+#if defined(XP_WIN) && defined(MOZ_GFX)
+#ifdef GFX2D_INTERNAL
+#define GFX2D_API __declspec(dllexport)
+#else
+#define GFX2D_API __declspec(dllimport)
+#endif
+#else
+#define GFX2D_API
+#endif
+
+namespace mozilla {
+
+// 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
new file mode 100644
index 000000000..a10dbc8b8
--- /dev/null
+++ b/gfx/2d/UserData.h
@@ -0,0 +1,128 @@
+/* -*- 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_USERDATA_H_
+#define MOZILLA_GFX_USERDATA_H_
+
+#include <stdlib.h>
+#include "Types.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace gfx {
+
+struct UserDataKey {
+ int unused;
+};
+
+/* this class is basically a clone of the user data concept from cairo */
+class UserData
+{
+ typedef void (*destroyFunc)(void *data);
+public:
+ UserData() : count(0), entries(nullptr) {}
+
+ /* Attaches untyped userData associated with key. destroy is called on destruction */
+ void Add(UserDataKey *key, void *userData, destroyFunc destroy)
+ {
+ for (int i=0; i<count; i++) {
+ if (key == entries[i].key) {
+ if (entries[i].destroy) {
+ entries[i].destroy(entries[i].userData);
+ }
+ entries[i].userData = userData;
+ entries[i].destroy = destroy;
+ return;
+ }
+ }
+
+ // We could keep entries in a std::vector instead of managing it by hand
+ // but that would propagate an stl dependency out which we'd rather not
+ // do (see bug 666609). Plus, the entries array is expect to stay small
+ // so doing a realloc everytime we add a new entry shouldn't be too costly
+ entries = static_cast<Entry*>(realloc(entries, sizeof(Entry)*(count+1)));
+
+ if (!entries) {
+ MOZ_CRASH("GFX: UserData::Add");
+ }
+
+ entries[count].key = key;
+ entries[count].userData = userData;
+ entries[count].destroy = destroy;
+
+ count++;
+ }
+
+ /* Remove and return user data associated with key, without destroying it */
+ void* Remove(UserDataKey *key)
+ {
+ for (int i=0; i<count; i++) {
+ if (key == entries[i].key) {
+ void *userData = entries[i].userData;
+ // decrement before looping so entries[i+1] doesn't read past the end:
+ --count;
+ for (;i<count; i++) {
+ entries[i] = entries[i+1];
+ }
+ return userData;
+ }
+ }
+ return nullptr;
+ }
+
+ /* Retrives the userData for the associated key */
+ void *Get(UserDataKey *key) const
+ {
+ for (int i=0; i<count; i++) {
+ if (key == entries[i].key) {
+ return entries[i].userData;
+ }
+ }
+ return nullptr;
+ }
+
+ bool Has(UserDataKey *key)
+ {
+ for (int i=0; i<count; i++) {
+ if (key == entries[i].key) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void Destroy()
+ {
+ for (int i=0; i<count; i++) {
+ if (entries[i].destroy) {
+ entries[i].destroy(entries[i].userData);
+ }
+ }
+ free(entries);
+ entries = nullptr;
+ count = 0;
+ }
+
+ ~UserData()
+ {
+ Destroy();
+ }
+
+private:
+ struct Entry {
+ const UserDataKey *key;
+ void *userData;
+ destroyFunc destroy;
+ };
+
+ int count;
+ Entry *entries;
+
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_USERDATA_H_ */
diff --git a/gfx/2d/convolver.cpp b/gfx/2d/convolver.cpp
new file mode 100644
index 000000000..0221f1563
--- /dev/null
+++ b/gfx/2d/convolver.cpp
@@ -0,0 +1,562 @@
+// 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 "2D.h"
+#include "convolver.h"
+
+#include <algorithm>
+
+#include "skia/include/core/SkTypes.h"
+
+
+#if defined(USE_SSE2)
+#include "convolverSSE2.h"
+#endif
+
+#if defined(_MIPS_ARCH_LOONGSON3A)
+#include "convolverLS3.h"
+#endif
+
+using mozilla::gfx::Factory;
+
+#if defined(SK_CPU_LENDIAN)
+#define R_OFFSET_IDX 0
+#define G_OFFSET_IDX 1
+#define B_OFFSET_IDX 2
+#define A_OFFSET_IDX 3
+#else
+#define R_OFFSET_IDX 3
+#define G_OFFSET_IDX 2
+#define B_OFFSET_IDX 1
+#define A_OFFSET_IDX 0
+#endif
+
+#if defined(USE_SSE2)
+#define ConvolveHorizontally4_SIMD ConvolveHorizontally4_SSE2
+#define ConvolveHorizontally_SIMD ConvolveHorizontally_SSE2
+#define ConvolveVertically_SIMD ConvolveVertically_SSE2
+#endif
+
+#if defined(_MIPS_ARCH_LOONGSON3A)
+#define ConvolveHorizontally4_SIMD ConvolveHorizontally4_LS3
+#define ConvolveHorizontally_SIMD ConvolveHorizontally_LS3
+#define ConvolveVertically_SIMD ConvolveVertically_LS3
+#endif
+
+namespace skia {
+
+namespace {
+
+// Converts the argument to an 8-bit unsigned value by clamping to the range
+// 0-255.
+inline unsigned char ClampTo8(int a) {
+ if (static_cast<unsigned>(a) < 256)
+ return a; // Avoid the extra check in the common case.
+ if (a < 0)
+ return 0;
+ return 255;
+}
+
+// Stores a list of rows in a circular buffer. The usage is you write into it
+// by calling AdvanceRow. It will keep track of which row in the buffer it
+// should use next, and the total number of rows added.
+class CircularRowBuffer {
+ public:
+ // The number of pixels in each row is given in |source_row_pixel_width|.
+ // The maximum number of rows needed in the buffer is |max_y_filter_size|
+ // (we only need to store enough rows for the biggest filter).
+ //
+ // We use the |first_input_row| to compute the coordinates of all of the
+ // following rows returned by Advance().
+ CircularRowBuffer(int dest_row_pixel_width, int max_y_filter_size,
+ int first_input_row)
+ : row_byte_width_(dest_row_pixel_width * 4),
+ num_rows_(max_y_filter_size),
+ next_row_(0),
+ next_row_coordinate_(first_input_row) {
+ buffer_.resize(row_byte_width_ * max_y_filter_size);
+ row_addresses_.resize(num_rows_);
+ }
+
+ // Moves to the next row in the buffer, returning a pointer to the beginning
+ // of it.
+ unsigned char* AdvanceRow() {
+ unsigned char* row = &buffer_[next_row_ * row_byte_width_];
+ next_row_coordinate_++;
+
+ // Set the pointer to the next row to use, wrapping around if necessary.
+ next_row_++;
+ if (next_row_ == num_rows_)
+ next_row_ = 0;
+ return row;
+ }
+
+ // Returns a pointer to an "unrolled" array of rows. These rows will start
+ // at the y coordinate placed into |*first_row_index| and will continue in
+ // order for the maximum number of rows in this circular buffer.
+ //
+ // The |first_row_index_| may be negative. This means the circular buffer
+ // starts before the top of the image (it hasn't been filled yet).
+ unsigned char* const* GetRowAddresses(int* first_row_index) {
+ // Example for a 4-element circular buffer holding coords 6-9.
+ // Row 0 Coord 8
+ // Row 1 Coord 9
+ // Row 2 Coord 6 <- next_row_ = 2, next_row_coordinate_ = 10.
+ // Row 3 Coord 7
+ //
+ // The "next" row is also the first (lowest) coordinate. This computation
+ // may yield a negative value, but that's OK, the math will work out
+ // since the user of this buffer will compute the offset relative
+ // to the first_row_index and the negative rows will never be used.
+ *first_row_index = next_row_coordinate_ - num_rows_;
+
+ int cur_row = next_row_;
+ for (int i = 0; i < num_rows_; i++) {
+ row_addresses_[i] = &buffer_[cur_row * row_byte_width_];
+
+ // Advance to the next row, wrapping if necessary.
+ cur_row++;
+ if (cur_row == num_rows_)
+ cur_row = 0;
+ }
+ return &row_addresses_[0];
+ }
+
+ private:
+ // The buffer storing the rows. They are packed, each one row_byte_width_.
+ std::vector<unsigned char> buffer_;
+
+ // Number of bytes per row in the |buffer_|.
+ int row_byte_width_;
+
+ // The number of rows available in the buffer.
+ int num_rows_;
+
+ // The next row index we should write into. This wraps around as the
+ // circular buffer is used.
+ int next_row_;
+
+ // The y coordinate of the |next_row_|. This is incremented each time a
+ // new row is appended and does not wrap.
+ int next_row_coordinate_;
+
+ // Buffer used by GetRowAddresses().
+ 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 [begin, end) of the filter.
+template<bool has_alpha>
+void ConvolveHorizontally(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row) {
+ int num_values = filter.num_values();
+ // Loop over each pixel on this row in the output image.
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ // Get the filter that determines the current output pixel.
+ int filter_offset, filter_length;
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+ // Compute the first pixel in this row that the filter affects. It will
+ // touch |filter_length| pixels (4 bytes each) after this.
+ const unsigned char* row_to_filter = &src_data[filter_offset * 4];
+
+ // Apply the filter to the row to get the destination pixel in |accum|.
+ int accum[4] = {0};
+ for (int filter_x = 0; filter_x < filter_length; filter_x++) {
+ ConvolutionFilter1D::Fixed cur_filter = filter_values[filter_x];
+ accum[0] += cur_filter * row_to_filter[filter_x * 4 + R_OFFSET_IDX];
+ accum[1] += cur_filter * row_to_filter[filter_x * 4 + G_OFFSET_IDX];
+ accum[2] += cur_filter * row_to_filter[filter_x * 4 + B_OFFSET_IDX];
+ if (has_alpha)
+ accum[3] += cur_filter * row_to_filter[filter_x * 4 + A_OFFSET_IDX];
+ }
+
+ // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with kShiftBits bits of fractional part.
+ accum[0] >>= ConvolutionFilter1D::kShiftBits;
+ accum[1] >>= ConvolutionFilter1D::kShiftBits;
+ accum[2] >>= ConvolutionFilter1D::kShiftBits;
+ if (has_alpha)
+ accum[3] >>= ConvolutionFilter1D::kShiftBits;
+
+ // Store the new pixel.
+ out_row[out_x * 4 + R_OFFSET_IDX] = ClampTo8(accum[0]);
+ out_row[out_x * 4 + G_OFFSET_IDX] = ClampTo8(accum[1]);
+ out_row[out_x * 4 + B_OFFSET_IDX] = ClampTo8(accum[2]);
+ if (has_alpha)
+ out_row[out_x * 4 + A_OFFSET_IDX] = ClampTo8(accum[3]);
+ }
+}
+
+// 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(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ 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++) {
+ // 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;
+
+ // Apply the filter to one column of pixels.
+ int accum[4] = {0};
+ for (int filter_y = 0; filter_y < filter_length; filter_y++) {
+ ConvolutionFilter1D::Fixed cur_filter = filter_values[filter_y];
+ accum[0] += cur_filter
+ * source_data_rows[filter_y][byte_offset + R_OFFSET_IDX];
+ accum[1] += cur_filter
+ * source_data_rows[filter_y][byte_offset + G_OFFSET_IDX];
+ accum[2] += cur_filter
+ * source_data_rows[filter_y][byte_offset + B_OFFSET_IDX];
+ if (has_alpha)
+ accum[3] += cur_filter
+ * source_data_rows[filter_y][byte_offset + A_OFFSET_IDX];
+ }
+
+ // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with kShiftBits bits of precision.
+ accum[0] >>= ConvolutionFilter1D::kShiftBits;
+ accum[1] >>= ConvolutionFilter1D::kShiftBits;
+ accum[2] >>= ConvolutionFilter1D::kShiftBits;
+ if (has_alpha)
+ accum[3] >>= ConvolutionFilter1D::kShiftBits;
+
+ // Store the new pixel.
+ out_row[byte_offset + R_OFFSET_IDX] = ClampTo8(accum[0]);
+ out_row[byte_offset + G_OFFSET_IDX] = ClampTo8(accum[1]);
+ out_row[byte_offset + B_OFFSET_IDX] = ClampTo8(accum[2]);
+ if (has_alpha) {
+ unsigned char alpha = ClampTo8(accum[3]);
+
+ // Make sure the alpha channel doesn't come out smaller than any of the
+ // color channels. We use premultipled alpha channels, so this should
+ // never happen, but rounding errors will cause this from time to time.
+ // These "impossible" colors will cause overflows (and hence random pixel
+ // values) when the resulting bitmap is drawn to the screen.
+ //
+ // We only need to do this when generating the final output row (here).
+ int max_color_channel = std::max(out_row[byte_offset + R_OFFSET_IDX],
+ std::max(out_row[byte_offset + G_OFFSET_IDX], out_row[byte_offset + B_OFFSET_IDX]));
+ if (alpha < max_color_channel)
+ out_row[byte_offset + A_OFFSET_IDX] = max_color_channel;
+ else
+ out_row[byte_offset + A_OFFSET_IDX] = alpha;
+ } else {
+ // No alpha channel, the image is opaque.
+ out_row[byte_offset + A_OFFSET_IDX] = 0xff;
+ }
+ }
+}
+
+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_simd) {
+
+#if defined(USE_SSE2) || defined(_MIPS_ARCH_LOONGSON3A)
+ // If the binary was not built with SSE2 support, we had to fallback to C version.
+ if (use_simd) {
+ ConvolveVertically_SIMD(filter_values, filter_length,
+ source_data_rows,
+ pixel_width,
+ out_row, has_alpha);
+ } else
+#endif
+ {
+ if (has_alpha) {
+ ConvolveVertically<true>(filter_values, filter_length,
+ source_data_rows,
+ pixel_width,
+ out_row);
+ } else {
+ ConvolveVertically<false>(filter_values, filter_length,
+ source_data_rows,
+ pixel_width,
+ out_row);
+ }
+ }
+}
+
+void ConvolveHorizontally(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row,
+ bool has_alpha, bool use_simd) {
+ int width = filter.num_values();
+ int processed = 0;
+#if defined(USE_SSE2) || defined(_MIPS_ARCH_LOONGSON3A)
+ int simd_width = width & ~3;
+ if (use_simd && 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_SIMD(src_data, filter, out_row);
+ processed = simd_width;
+ }
+#endif
+
+ if (width > processed) {
+#if defined(_MIPS_ARCH_LOONGSON3A)
+ ConvolveHorizontally1_LS3(src_data, filter, out_row);
+#else
+ if (has_alpha) {
+ ConvolveHorizontally<true>(src_data, filter, out_row);
+ } else {
+ ConvolveHorizontally<false>(src_data, filter, out_row);
+ }
+#endif
+ }
+}
+
+// ConvolutionFilter1D ---------------------------------------------------------
+
+ConvolutionFilter1D::ConvolutionFilter1D()
+ : max_filter_(0) {
+}
+
+ConvolutionFilter1D::~ConvolutionFilter1D() {
+}
+
+void ConvolutionFilter1D::AddFilter(int filter_offset,
+ const float* filter_values,
+ int filter_length) {
+ SkASSERT(filter_length > 0);
+
+ std::vector<Fixed> fixed_values;
+ fixed_values.reserve(filter_length);
+
+ for (int i = 0; i < filter_length; ++i)
+ fixed_values.push_back(FloatToFixed(filter_values[i]));
+
+ AddFilter(filter_offset, &fixed_values[0], filter_length);
+}
+
+void ConvolutionFilter1D::AddFilter(int filter_offset,
+ const Fixed* filter_values,
+ int filter_length) {
+ // It is common for leading/trailing filter values to be zeros. In such
+ // cases it is beneficial to only store the central factors.
+ // For a scaling to 1/4th in each dimension using a Lanczos-2 filter on
+ // a 1080p image this optimization gives a ~10% speed improvement.
+ int first_non_zero = 0;
+ while (first_non_zero < filter_length && filter_values[first_non_zero] == 0)
+ first_non_zero++;
+
+ if (first_non_zero < filter_length) {
+ // Here we have at least one non-zero factor.
+ int last_non_zero = filter_length - 1;
+ while (last_non_zero >= 0 && filter_values[last_non_zero] == 0)
+ last_non_zero--;
+
+ filter_offset += first_non_zero;
+ filter_length = last_non_zero + 1 - first_non_zero;
+ SkASSERT(filter_length > 0);
+
+ for (int i = first_non_zero; i <= last_non_zero; i++)
+ filter_values_.push_back(filter_values[i]);
+ } else {
+ // Here all the factors were zeroes.
+ filter_length = 0;
+ }
+
+ FilterInstance instance;
+
+ // We pushed filter_length elements onto filter_values_
+ instance.data_location = (static_cast<int>(filter_values_.size()) -
+ filter_length);
+ instance.offset = filter_offset;
+ instance.length = filter_length;
+ filters_.push_back(instance);
+
+ max_filter_ = std::max(max_filter_, filter_length);
+}
+
+void BGRAConvolve2D(const unsigned char* source_data,
+ int source_byte_row_stride,
+ bool source_has_alpha,
+ const ConvolutionFilter1D& filter_x,
+ const ConvolutionFilter1D& filter_y,
+ int output_byte_row_stride,
+ unsigned char* output) {
+ bool use_simd = 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_simd = false;
+#endif
+
+#if defined(_MIPS_ARCH_LOONGSON3A)
+ use_simd = true;
+#endif
+
+
+ int max_y_filter_size = filter_y.max_filter();
+
+ // The next row in the input that we will generate a horizontally
+ // convolved row for. If the filter doesn't start at the beginning of the
+ // image (this is the case when we are only resizing a subset), then we
+ // don't want to generate any output rows before that. Compute the starting
+ // row for convolution as the first pixel for the first vertical filter.
+ int filter_offset, filter_length;
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter_y.FilterForValue(0, &filter_offset, &filter_length);
+ int next_x_row = filter_offset;
+
+ // We loop over each row in the input doing a horizontal convolution. This
+ // will result in a horizontally convolved image. We write the results into
+ // a circular buffer of convolved rows and do vertical convolution as rows
+ // are available. This prevents us from having to store the entire
+ // intermediate image and helps cache coherency.
+ // We will need four extra rows to allow horizontal convolution could be done
+ // simultaneously. We also padding each row in row buffer to be aligned-up to
+ // 16 bytes.
+ // TODO(jiesun): We do not use aligned load from row buffer in vertical
+ // convolution pass yet. Somehow Windows does not like it.
+ int row_buffer_width = (filter_x.num_values() + 15) & ~0xF;
+ int row_buffer_height = max_y_filter_size + (use_simd ? 4 : 0);
+ CircularRowBuffer row_buffer(row_buffer_width,
+ row_buffer_height,
+ filter_offset);
+
+ // Loop over every possible output row, processing just enough horizontal
+ // 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.
+ int last_filter_offset, last_filter_length;
+ // SSE2 can access up to 3 extra pixels past the end of the
+ // buffer. At the bottom of the image, we have to be careful
+ // not to access data past the end of the buffer. Normally
+ // we fall back to the C++ implementation for the last row.
+ // If the last row is less than 3 pixels wide, we may have to fall
+ // back to the C++ version for more rows. Compute how many
+ // rows we need to avoid the SSE implementation for here.
+ filter_x.FilterForValue(filter_x.num_values() - 1, &last_filter_offset,
+ &last_filter_length);
+#if defined(USE_SSE2) || defined(_MIPS_ARCH_LOONGSON3A)
+ int avoid_simd_rows = 1 + 3 /
+ (last_filter_offset + last_filter_length);
+#endif
+ filter_y.FilterForValue(num_output_rows - 1, &last_filter_offset,
+ &last_filter_length);
+
+ for (int out_y = 0; out_y < num_output_rows; out_y++) {
+ filter_values = filter_y.FilterForValue(out_y,
+ &filter_offset, &filter_length);
+
+ // Generate output rows until we have enough to run the current filter.
+ if (use_simd) {
+#if defined(USE_SSE2) || defined(_MIPS_ARCH_LOONGSON3A)
+ // 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 -
+ avoid_simd_rows) {
+ 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_SIMD(src, filter_x, out_row);
+ next_x_row += 4;
+ } else {
+ // Check if we need to avoid SSE2 for this row.
+ if (next_x_row < last_filter_offset + last_filter_length -
+ avoid_simd_rows) {
+ ConvolveHorizontally_SIMD(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ } else {
+ if (source_has_alpha) {
+ ConvolveHorizontally<true>(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ } else {
+ ConvolveHorizontally<false>(
+ &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());
+ } else {
+ ConvolveHorizontally<false>(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ }
+ next_x_row++;
+ }
+ }
+
+ // Compute where in the output image this row of final data will go.
+ unsigned char* cur_output_row = &output[out_y * output_byte_row_stride];
+
+ // Get the list of rows that the circular buffer has, in order.
+ int first_row_in_circular_buffer;
+ unsigned char* const* rows_to_convolve =
+ row_buffer.GetRowAddresses(&first_row_in_circular_buffer);
+
+ // Now compute the start of the subset of those rows that the filter
+ // needs.
+ unsigned char* const* first_row_for_filter =
+ &rows_to_convolve[filter_offset - first_row_in_circular_buffer];
+
+ ConvolveVertically(filter_values, filter_length,
+ first_row_for_filter, pixel_width,
+ cur_output_row, source_has_alpha, use_simd);
+ }
+}
+
+} // namespace skia
diff --git a/gfx/2d/convolver.h b/gfx/2d/convolver.h
new file mode 100644
index 000000000..0c4591ac8
--- /dev/null
+++ b/gfx/2d/convolver.h
@@ -0,0 +1,201 @@
+// Copyright (c) 2006-2012 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_H_
+#define SKIA_EXT_CONVOLVER_H_
+
+#include <cmath>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "mozilla/Assertions.h"
+#include "skia/include/core/SkTypes.h"
+
+// avoid confusion with Mac OS X's math library (Carbon)
+#if defined(__APPLE__)
+#undef FloatToFixed
+#undef FixedToFloat
+#endif
+
+namespace skia {
+
+// Represents a filter in one dimension. Each output pixel has one entry in this
+// object for the filter values contributing to it. You build up the filter
+// list by calling AddFilter for each output pixel (in order).
+//
+// We do 2-dimensional convolution by first convolving each row by one
+// ConvolutionFilter1D, then convolving each column by another one.
+//
+// Entries are stored in fixed point, shifted left by kShiftBits.
+class ConvolutionFilter1D {
+ public:
+ typedef short Fixed;
+
+ // The number of bits that fixed point values are shifted by.
+ enum { kShiftBits = 14 };
+
+ ConvolutionFilter1D();
+ ~ConvolutionFilter1D();
+
+ // Convert between floating point and our fixed point representation.
+ static Fixed FloatToFixed(float f) {
+ return static_cast<Fixed>(f * (1 << kShiftBits));
+ }
+ static unsigned char FixedToChar(Fixed x) {
+ return static_cast<unsigned char>(x >> kShiftBits);
+ }
+ static float FixedToFloat(Fixed x) {
+ // 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.
+ static_assert(sizeof(Fixed) == 2,
+ "fixed type should fit in float mantissa");
+ float raw = static_cast<float>(x);
+ return ldexpf(raw, -kShiftBits);
+ }
+
+ // Returns the maximum pixel span of a filter.
+ int max_filter() const { return max_filter_; }
+
+ // Returns the number of filters in this filter. This is the dimension of the
+ // output image.
+ int num_values() const { return static_cast<int>(filters_.size()); }
+
+ // Appends the given list of scaling values for generating a given output
+ // pixel. |filter_offset| is the distance from the edge of the image to where
+ // the scaling factors start. The scaling factors apply to the source pixels
+ // starting from this position, and going for the next |filter_length| pixels.
+ //
+ // You will probably want to make sure your input is normalized (that is,
+ // all entries in |filter_values| sub to one) to prevent affecting the overall
+ // brighness of the image.
+ //
+ // The filter_length must be > 0.
+ //
+ // This version will automatically convert your input to fixed point.
+ void AddFilter(int filter_offset,
+ const float* filter_values,
+ int filter_length);
+
+ // Same as the above version, but the input is already fixed point.
+ void AddFilter(int filter_offset,
+ const Fixed* filter_values,
+ int filter_length);
+
+ // Retrieves a filter for the given |value_offset|, a position in the output
+ // image in the direction we're convolving. The offset and length of the
+ // filter values are put into the corresponding out arguments (see AddFilter
+ // above for what these mean), and a pointer to the first scaling factor is
+ // returned. There will be |filter_length| values in this array.
+ inline const Fixed* FilterForValue(int value_offset,
+ int* filter_offset,
+ int* filter_length) const {
+ const FilterInstance& filter = filters_[value_offset];
+ *filter_offset = filter.offset;
+ *filter_length = filter.length;
+ if (filter.length == 0) {
+ return NULL;
+ }
+ return &filter_values_[filter.data_location];
+ }
+
+
+ inline void PaddingForSIMD(int padding_count) {
+ // Padding |padding_count| of more dummy coefficients after the coefficients
+ // of last filter to prevent SIMD instructions which load 8 or 16 bytes
+ // together to access invalid memory areas. We are not trying to align the
+ // coefficients right now due to the opaqueness of <vector> implementation.
+ // This has to be done after all |AddFilter| calls.
+ for (int i = 0; i < padding_count; ++i)
+ filter_values_.push_back(static_cast<Fixed>(0));
+ }
+
+ private:
+ struct FilterInstance {
+ // Offset within filter_values for this instance of the filter.
+ int data_location;
+
+ // Distance from the left of the filter to the center. IN PIXELS
+ int offset;
+
+ // Number of values in this filter instance.
+ int length;
+ };
+
+ // Stores the information for each filter added to this class.
+ std::vector<FilterInstance> filters_;
+
+ // We store all the filter values in this flat list, indexed by
+ // |FilterInstance.data_location| to avoid the mallocs required for storing
+ // each one separately.
+ std::vector<Fixed> filter_values_;
+
+ // The maximum size of any filter we've added.
+ int max_filter_;
+};
+
+// Does a two-dimensional convolution on the given source image.
+//
+// It is assumed the source pixel offsets referenced in the input filters
+// reference only valid pixels, so the source image size is not required. Each
+// row of the source image starts |source_byte_row_stride| after the previous
+// one (this allows you to have rows with some padding at the end).
+//
+// The result will be put into the given output buffer. The destination image
+// size will be xfilter.num_values() * yfilter.num_values() pixels. It will be
+// in rows of exactly xfilter.num_values() * 4 bytes.
+//
+// |source_has_alpha| is a hint that allows us to avoid doing computations on
+// the alpha channel if the image is opaque. If you don't know, set this to
+// true and it will work properly, but setting this to false will be a few
+// percent faster if you know the image is opaque.
+//
+// The layout in memory is assumed to be 4-bytes per pixel in B-G-R-A order
+// (this is ARGB when loaded into 32-bit words on a little-endian machine).
+void BGRAConvolve2D(const unsigned char* source_data,
+ int source_byte_row_stride,
+ bool source_has_alpha,
+ const ConvolutionFilter1D& xfilter,
+ const ConvolutionFilter1D& yfilter,
+ int output_byte_row_stride,
+ 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/convolverLS3.cpp b/gfx/2d/convolverLS3.cpp
new file mode 100644
index 000000000..a1a41c98b
--- /dev/null
+++ b/gfx/2d/convolverLS3.cpp
@@ -0,0 +1,927 @@
+// Copyright (c) 2014-2015 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/include/core/SkTypes.h"
+
+#if defined(_MIPS_ARCH_LOONGSON3A)
+
+#include "MMIHelpers.h"
+
+namespace skia {
+
+// 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_LS3(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row) {
+ int num_values = filter.num_values();
+ int tmp, filter_offset, filter_length;
+ double zero, mask[4], shuf_50, shuf_fa;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "xor %[zero], %[zero], %[zero] \n\t"
+ // |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.
+ "li %[tmp], 1 \n\t"
+ "dsll32 %[tmp], 0x10 \n\t"
+ "daddiu %[tmp], -1 \n\t"
+ "dmtc1 %[tmp], %[mask3] \n\t"
+ "dsrl %[tmp], 0x10 \n\t"
+ "mtc1 %[tmp], %[mask2] \n\t"
+ "dsrl %[tmp], 0x10 \n\t"
+ "mtc1 %[tmp], %[mask1] \n\t"
+ "ori %[tmp], $0, 0x50 \n\t"
+ "mtc1 %[tmp], %[shuf_50] \n\t"
+ "ori %[tmp], $0, 0xfa \n\t"
+ "mtc1 %[tmp], %[shuf_fa] \n\t"
+ ".set pop \n\t"
+ :[zero]"=f"(zero), [mask1]"=f"(mask[1]),
+ [mask2]"=f"(mask[2]), [mask3]"=f"(mask[3]),
+ [shuf_50]"=f"(shuf_50), [shuf_fa]"=f"(shuf_fa),
+ [tmp]"=&r"(tmp)
+ );
+
+ // 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);
+ double accumh, accuml;
+ // Compute the first pixel in this row that the filter affects. It will
+ // touch |filter_length| pixels (4 bytes each) after this.
+ const void *row_to_filter =
+ reinterpret_cast<const void*>(&src_data[filter_offset << 2]);
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_xor(accum, accum, accum)
+ ".set pop \n\t"
+ :[accumh]"=f"(accumh), [accuml]"=f"(accuml)
+ );
+
+ // We will load and accumulate with four coefficients per iteration.
+ for (int filter_x = 0; filter_x < filter_length >> 2; filter_x++) {
+ double src16h, src16l, mul_hih, mul_hil, mul_loh, mul_lol;
+ double coeffh, coeffl, src8h, src8l, th, tl, coeff16h, coeff16l;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // Load 4 coefficients => duplicate 1st and 2nd of them for all channels.
+ // [16] xx xx xx xx c3 c2 c1 c0
+ "gsldlc1 %[coeffl], 7(%[fval]) \n\t"
+ "gsldrc1 %[coeffl], (%[fval]) \n\t"
+ "xor %[coeffh], %[coeffh], %[coeffh] \n\t"
+ // [16] xx xx xx xx c1 c1 c0 c0
+ _mm_pshuflh(coeff16, coeff, shuf_50)
+ // [16] c1 c1 c1 c1 c0 c0 c0 c0
+ _mm_punpcklhw(coeff16, 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
+ "gsldlc1 %[src8h], 0xf(%[rtf]) \n\t"
+ "gsldrc1 %[src8h], 0x8(%[rtf]) \n\t"
+ "gsldlc1 %[src8l], 0x7(%[rtf]) \n\t"
+ "gsldrc1 %[src8l], 0x0(%[rtf]) \n\t"
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ _mm_punpcklbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ // [32] a0*c0 b0*c0 g0*c0 r0*c0
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ // [32] a1*c1 b1*c1 g1*c1 r1*c1
+ _mm_punpckhhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, 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
+ _mm_pshuflh(coeff16, coeff, shuf_fa)
+ // [16] c3 c3 c3 c3 c2 c2 c2 c2
+ _mm_punpcklhw(coeff16, coeff16, coeff16)
+ // [16] a3 g3 b3 r3 a2 g2 b2 r2
+ _mm_punpckhbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ // [32] a2*c2 b2*c2 g2*c2 r2*c2
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ // [32] a3*c3 b3*c3 g3*c3 r3*c3
+ _mm_punpckhhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ ".set pop \n\t"
+ :[th]"=&f"(th), [tl]"=&f"(tl),
+ [src8h]"=&f"(src8h), [src8l]"=&f"(src8l),
+ [accumh]"+f"(accumh), [accuml]"+f"(accuml),
+ [src16h]"=&f"(src16h), [src16l]"=&f"(src16l),
+ [coeffh]"=&f"(coeffh), [coeffl]"=&f"(coeffl),
+ [coeff16h]"=&f"(coeff16h), [coeff16l]"=&f"(coeff16l),
+ [mul_hih]"=&f"(mul_hih), [mul_hil]"=&f"(mul_hil),
+ [mul_loh]"=&f"(mul_loh), [mul_lol]"=&f"(mul_lol)
+ :[zeroh]"f"(zero), [zerol]"f"(zero),
+ [shuf_50]"f"(shuf_50), [shuf_fa]"f"(shuf_fa),
+ [fval]"r"(filter_values), [rtf]"r"(row_to_filter)
+ );
+
+ // Advance the pixel and coefficients pointers.
+ row_to_filter += 16;
+ 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) {
+ double coeffh, coeffl, th, tl, coeff16h, coeff16l;
+ double src8h, src8l, src16h, src16l, mul_hih, mul_hil, mul_loh, mul_lol;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gsldlc1 %[coeffl], 7(%[fval]) \n\t"
+ "gsldrc1 %[coeffl], (%[fval]) \n\t"
+ "xor %[coeffh], %[coeffh], %[coeffh] \n\t"
+ // Mask out extra filter taps.
+ "and %[coeffl], %[coeffl], %[mask] \n\t"
+ _mm_pshuflh(coeff16, coeff, shuf_50)
+ _mm_punpcklhw(coeff16, coeff16, coeff16)
+ "gsldlc1 %[src8h], 0xf(%[rtf]) \n\t"
+ "gsldrc1 %[src8h], 0x8(%[rtf]) \n\t"
+ "gsldlc1 %[src8l], 0x7(%[rtf]) \n\t"
+ "gsldrc1 %[src8l], 0x0(%[rtf]) \n\t"
+ _mm_punpcklbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ _mm_punpckhhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ _mm_punpckhbh(src16, src8, zero)
+ _mm_pshuflh(coeff16, coeff, shuf_fa)
+ _mm_punpcklhw(coeff16, coeff16, coeff16)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum, accum, t)
+ ".set pop \n\t"
+ :[th]"=&f"(th), [tl]"=&f"(tl),
+ [src8h]"=&f"(src8h), [src8l]"=&f"(src8l),
+ [accumh]"+f"(accumh), [accuml]"+f"(accuml),
+ [src16h]"=&f"(src16h), [src16l]"=&f"(src16l),
+ [coeffh]"=&f"(coeffh), [coeffl]"=&f"(coeffl),
+ [coeff16h]"=&f"(coeff16h), [coeff16l]"=&f"(coeff16l),
+ [mul_hih]"=&f"(mul_hih), [mul_hil]"=&f"(mul_hil),
+ [mul_loh]"=&f"(mul_loh), [mul_lol]"=&f"(mul_lol)
+ :[fval]"r"(filter_values), [rtf]"r"(row_to_filter),
+ [zeroh]"f"(zero), [zerol]"f"(zero), [mask]"f"(mask[r]),
+ [shuf_50]"f"(shuf_50), [shuf_fa]"f"(shuf_fa)
+ );
+ }
+
+ double t, sra;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "ori %[tmp], $0, %[sk_sra] \n\t"
+ "mtc1 %[tmp], %[sra] \n\t"
+ // Shift right for fixed point implementation.
+ _mm_psraw(accum, accum, sra)
+ // Packing 32 bits |accum| to 16 bits per channel (signed saturation).
+ _mm_packsswh(accum, accum, zero, t)
+ // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation).
+ _mm_packushb(accum, accum, zero, t)
+ // Store the pixel value of 32 bits.
+ "swc1 %[accuml], (%[out_row]) \n\t"
+ ".set pop \n\t"
+ :[sra]"=&f"(sra), [t]"=&f"(t), [tmp]"=&r"(tmp),
+ [accumh]"+f"(accumh), [accuml]"+f"(accuml)
+ :[sk_sra]"i"(ConvolutionFilter1D::kShiftBits),
+ [out_row]"r"(out_row), [zeroh]"f"(zero), [zerol]"f"(zero)
+ :"memory"
+ );
+
+ out_row += 4;
+ }
+}
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+// Process one pixel at a time.
+void ConvolveHorizontally1_LS3(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row) {
+ int num_values = filter.num_values();
+ double zero;
+ double sra;
+
+ asm volatile (
+ ".set push \n"
+ ".set arch=loongson3a \n"
+ "xor %[zero], %[zero], %[zero] \n"
+ "mtc1 %[sk_sra], %[sra] \n"
+ ".set pop \n"
+ :[zero]"=&f"(zero), [sra]"=&f"(sra)
+ :[sk_sra]"r"(ConvolutionFilter1D::kShiftBits)
+ );
+ // Loop over each pixel on this row in the output image.
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ // Get the filter that determines the current output pixel.
+ int filter_offset;
+ int filter_length;
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+ // Compute the first pixel in this row that the filter affects. It will
+ // touch |filter_length| pixels (4 bytes each) after this.
+ const unsigned char* row_to_filter = &src_data[filter_offset * 4];
+
+ // Apply the filter to the row to get the destination pixel in |accum|.
+ double accuml;
+ double accumh;
+ asm volatile (
+ ".set push \n"
+ ".set arch=loongson3a \n"
+ "xor %[accuml], %[accuml], %[accuml] \n"
+ "xor %[accumh], %[accumh], %[accumh] \n"
+ ".set pop \n"
+ :[accuml]"=&f"(accuml), [accumh]"=&f"(accumh)
+ );
+ for (int filter_x = 0; filter_x < filter_length; filter_x++) {
+ double src8;
+ double src16;
+ double coeff;
+ double coeff16;
+ asm volatile (
+ ".set push \n"
+ ".set arch=loongson3a \n"
+ "lwc1 %[src8], %[rtf] \n"
+ "mtc1 %[fv], %[coeff] \n"
+ "pshufh %[coeff16], %[coeff], %[zero] \n"
+ "punpcklbh %[src16], %[src8], %[zero] \n"
+ "pmullh %[src8], %[src16], %[coeff16] \n"
+ "pmulhh %[coeff], %[src16], %[coeff16] \n"
+ "punpcklhw %[src16], %[src8], %[coeff] \n"
+ "punpckhhw %[coeff16], %[src8], %[coeff] \n"
+ "paddw %[accuml], %[accuml], %[src16] \n"
+ "paddw %[accumh], %[accumh], %[coeff16] \n"
+ ".set pop \n"
+ :[accuml]"+f"(accuml), [accumh]"+f"(accumh),
+ [src8]"=&f"(src8), [src16]"=&f"(src16),
+ [coeff]"=&f"(coeff), [coeff16]"=&f"(coeff16)
+ :[rtf]"m"(row_to_filter[filter_x * 4]),
+ [fv]"r"(filter_values[filter_x]), [zero]"f"(zero)
+ );
+ }
+
+ asm volatile (
+ ".set push \n"
+ ".set arch=loongson3a \n"
+ // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with kShiftBits bits of fractional part.
+ "psraw %[accuml], %[accuml], %[sra] \n"
+ "psraw %[accumh], %[accumh], %[sra] \n"
+ // Store the new pixel.
+ "packsswh %[accuml], %[accuml], %[accumh] \n"
+ "packushb %[accuml], %[accuml], %[zero] \n"
+ "swc1 %[accuml], %[out_row] \n"
+ ".set pop \n"
+ :[accuml]"+f"(accuml), [accumh]"+f"(accumh)
+ :[sra]"f"(sra), [zero]"f"(zero), [out_row]"m"(out_row[out_x * 4])
+ :"memory"
+ );
+ }
+}
+
+// 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_LS3|. Please
+// refer to that function for detailed comments.
+void ConvolveHorizontally4_LS3(const unsigned char* src_data[4],
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row[4]) {
+ int num_values = filter.num_values();
+ int tmp, filter_offset, filter_length;
+ double zero, mask[4], shuf_50, shuf_fa;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "xor %[zero], %[zero], %[zero] \n\t"
+ // |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.
+ "li %[tmp], 1 \n\t"
+ "dsll32 %[tmp], 0x10 \n\t"
+ "daddiu %[tmp], -1 \n\t"
+ "dmtc1 %[tmp], %[mask3] \n\t"
+ "dsrl %[tmp], 0x10 \n\t"
+ "mtc1 %[tmp], %[mask2] \n\t"
+ "dsrl %[tmp], 0x10 \n\t"
+ "mtc1 %[tmp], %[mask1] \n\t"
+ "ori %[tmp], $0, 0x50 \n\t"
+ "mtc1 %[tmp], %[shuf_50] \n\t"
+ "ori %[tmp], $0, 0xfa \n\t"
+ "mtc1 %[tmp], %[shuf_fa] \n\t"
+ ".set pop \n\t"
+ :[zero]"=f"(zero), [mask1]"=f"(mask[1]),
+ [mask2]"=f"(mask[2]), [mask3]"=f"(mask[3]),
+ [shuf_50]"=f"(shuf_50), [shuf_fa]"=f"(shuf_fa),
+ [tmp]"=&r"(tmp)
+ );
+
+ // 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);
+ double accum0h, accum0l, accum1h, accum1l;
+ double accum2h, accum2l, accum3h, accum3l;
+
+ // four pixels in a column per iteration.
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_xor(accum0, accum0, accum0)
+ _mm_xor(accum1, accum1, accum1)
+ _mm_xor(accum2, accum2, accum2)
+ _mm_xor(accum3, accum3, accum3)
+ ".set pop \n\t"
+ :[accum0h]"=f"(accum0h), [accum0l]"=f"(accum0l),
+ [accum1h]"=f"(accum1h), [accum1l]"=f"(accum1l),
+ [accum2h]"=f"(accum2h), [accum2l]"=f"(accum2l),
+ [accum3h]"=f"(accum3h), [accum3l]"=f"(accum3l)
+ );
+
+ 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++) {
+ double src8h, src8l, src16h, src16l;
+ double mul_hih, mul_hil, mul_loh, mul_lol, th, tl;
+ double coeffh, coeffl, coeff16loh, coeff16lol, coeff16hih, coeff16hil;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // [16] xx xx xx xx c3 c2 c1 c0
+ "gsldlc1 %[coeffl], 7(%[fval]) \n\t"
+ "gsldrc1 %[coeffl], (%[fval]) \n\t"
+ "xor %[coeffh], %[coeffh], %[coeffh] \n\t"
+ // [16] xx xx xx xx c1 c1 c0 c0
+ _mm_pshuflh(coeff16lo, coeff, shuf_50)
+ // [16] c1 c1 c1 c1 c0 c0 c0 c0
+ _mm_punpcklhw(coeff16lo, coeff16lo, coeff16lo)
+ // [16] xx xx xx xx c3 c3 c2 c2
+ _mm_pshuflh(coeff16hi, coeff, shuf_fa)
+ // [16] c3 c3 c3 c3 c2 c2 c2 c2
+ _mm_punpcklhw(coeff16hi, coeff16hi, coeff16hi)
+ ".set pop \n\t"
+ :[coeffh]"=&f"(coeffh), [coeffl]"=&f"(coeffl),
+ [coeff16loh]"=&f"(coeff16loh), [coeff16lol]"=&f"(coeff16lol),
+ [coeff16hih]"=&f"(coeff16hih), [coeff16hil]"=&f"(coeff16hil)
+ :[fval]"r"(filter_values), [shuf_50]"f"(shuf_50), [shuf_fa]"f"(shuf_fa)
+ );
+
+#define ITERATION(_src, _accumh, _accuml) \
+ asm volatile ( \
+ ".set push \n\t" \
+ ".set arch=loongson3a \n\t" \
+ "gsldlc1 %[src8h], 0xf(%[src]) \n\t" \
+ "gsldrc1 %[src8h], 0x8(%[src]) \n\t" \
+ "gsldlc1 %[src8l], 0x7(%[src]) \n\t" \
+ "gsldrc1 %[src8l], 0x0(%[src]) \n\t" \
+ _mm_punpcklbh(src16, src8, zero) \
+ _mm_pmulhh(mul_hi, src16, coeff16lo) \
+ _mm_pmullh(mul_lo, src16, coeff16lo) \
+ _mm_punpcklhw(t, mul_lo, mul_hi) \
+ _mm_paddw(accum, accum, t) \
+ _mm_punpckhhw(t, mul_lo, mul_hi) \
+ _mm_paddw(accum, accum, t) \
+ _mm_punpckhbh(src16, src8, zero) \
+ _mm_pmulhh(mul_hi, src16, coeff16hi) \
+ _mm_pmullh(mul_lo, src16, coeff16hi) \
+ _mm_punpcklhw(t, mul_lo, mul_hi) \
+ _mm_paddw(accum, accum, t) \
+ _mm_punpckhhw(t, mul_lo, mul_hi) \
+ _mm_paddw(accum, accum, t) \
+ ".set pop \n\t" \
+ :[th]"=&f"(th), [tl]"=&f"(tl), \
+ [src8h]"=&f"(src8h), [src8l]"=&f"(src8l), \
+ [src16h]"=&f"(src16h), [src16l]"=&f"(src16l), \
+ [mul_hih]"=&f"(mul_hih), [mul_hil]"=&f"(mul_hil), \
+ [mul_loh]"=&f"(mul_loh), [mul_lol]"=&f"(mul_lol), \
+ [accumh]"+f"(_accumh), [accuml]"+f"(_accuml) \
+ :[zeroh]"f"(zero), [zerol]"f"(zero), [src]"r"(_src), \
+ [coeff16loh]"f"(coeff16loh), [coeff16lol]"f"(coeff16lol), \
+ [coeff16hih]"f"(coeff16hih), [coeff16hil]"f"(coeff16hil) \
+ );
+
+ ITERATION(src_data[0] + start, accum0h, accum0l);
+ ITERATION(src_data[1] + start, accum1h, accum1l);
+ ITERATION(src_data[2] + start, accum2h, accum2l);
+ ITERATION(src_data[3] + start, accum3h, accum3l);
+
+ start += 16;
+ filter_values += 4;
+ }
+
+ int r = filter_length & 3;
+ if (r) {
+ double src8h, src8l, src16h, src16l;
+ double mul_hih, mul_hil, mul_loh, mul_lol, th, tl;
+ double coeffh, coeffl, coeff16loh, coeff16lol, coeff16hih, coeff16hil;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gsldlc1 %[coeffl], 7(%[fval]) \n\t"
+ "gsldrc1 %[coeffl], (%[fval]) \n\t"
+ "xor %[coeffh], %[coeffh], %[coeffh] \n\t"
+ // Mask out extra filter taps.
+ "and %[coeffl], %[coeffl], %[mask] \n\t"
+ _mm_pshuflh(coeff16lo, coeff, shuf_50)
+ /* c1 c1 c1 c1 c0 c0 c0 c0 */
+ _mm_punpcklhw(coeff16lo, coeff16lo, coeff16lo)
+ _mm_pshuflh(coeff16hi, coeff, shuf_fa)
+ _mm_punpcklhw(coeff16hi, coeff16hi, coeff16hi)
+ ".set pop \n\t"
+ :[coeffh]"=&f"(coeffh), [coeffl]"=&f"(coeffl),
+ [coeff16loh]"=&f"(coeff16loh), [coeff16lol]"=&f"(coeff16lol),
+ [coeff16hih]"=&f"(coeff16hih), [coeff16hil]"=&f"(coeff16hil)
+ :[fval]"r"(filter_values), [mask]"f"(mask[r]),
+ [shuf_50]"f"(shuf_50), [shuf_fa]"f"(shuf_fa)
+ );
+
+ ITERATION(src_data[0] + start, accum0h, accum0l);
+ ITERATION(src_data[1] + start, accum1h, accum1l);
+ ITERATION(src_data[2] + start, accum2h, accum2l);
+ ITERATION(src_data[3] + start, accum3h, accum3l);
+ }
+
+ double t, sra;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "ori %[tmp], $0, %[sk_sra] \n\t"
+ "mtc1 %[tmp], %[sra] \n\t"
+ _mm_psraw(accum0, accum0, sra)
+ _mm_packsswh(accum0, accum0, zero, t)
+ _mm_packushb(accum0, accum0, zero, t)
+ _mm_psraw(accum1, accum1, sra)
+ _mm_packsswh(accum1, accum1, zero, t)
+ _mm_packushb(accum1, accum1, zero, t)
+ _mm_psraw(accum2, accum2, sra)
+ _mm_packsswh(accum2, accum2, zero, t)
+ _mm_packushb(accum2, accum2, zero, t)
+ _mm_psraw(accum3, accum3, sra)
+ _mm_packsswh(accum3, accum3, zero, t)
+ _mm_packushb(accum3, accum3, zero, t)
+ "swc1 %[accum0l], (%[out_row0]) \n\t"
+ "swc1 %[accum1l], (%[out_row1]) \n\t"
+ "swc1 %[accum2l], (%[out_row2]) \n\t"
+ "swc1 %[accum3l], (%[out_row3]) \n\t"
+ ".set pop \n\t"
+ :[accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [accum1h]"+f"(accum1h), [accum1l]"+f"(accum1l),
+ [accum2h]"+f"(accum2h), [accum2l]"+f"(accum2l),
+ [accum3h]"+f"(accum3h), [accum3l]"+f"(accum3l),
+ [sra]"=&f"(sra), [t]"=&f"(t), [tmp]"=&r"(tmp)
+ :[zeroh]"f"(zero), [zerol]"f"(zero),
+ [out_row0]"r"(out_row[0]), [out_row1]"r"(out_row[1]),
+ [out_row2]"r"(out_row[2]), [out_row3]"r"(out_row[3]),
+ [sk_sra]"i"(ConvolutionFilter1D::kShiftBits)
+ :"memory"
+ );
+
+ 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 |pixel_width| wide.
+//
+// The output must have room for |pixel_width * 4| bytes.
+template<bool has_alpha>
+void ConvolveVertically_LS3_impl(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row) {
+ uint64_t tmp;
+ int width = pixel_width & ~3;
+ double zero, sra, coeff16h, coeff16l;
+ double accum0h, accum0l, accum1h, accum1l;
+ double accum2h, accum2l, accum3h, accum3l;
+ const void *src;
+ int out_x;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "xor %[zero], %[zero], %[zero] \n\t"
+ "ori %[tmp], $0, %[sk_sra] \n\t"
+ "mtc1 %[tmp], %[sra] \n\t"
+ ".set pop \n\t"
+ :[zero]"=f"(zero), [sra]"=f"(sra), [tmp]"=&r"(tmp)
+ :[sk_sra]"i"(ConvolutionFilter1D::kShiftBits)
+ );
+
+ // Output four pixels per iteration (16 bytes).
+ for (out_x = 0; out_x < width; out_x += 4) {
+ // Accumulated result for each pixel. 32 bits per RGBA channel.
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_xor(accum0, accum0, accum0)
+ _mm_xor(accum1, accum1, accum1)
+ _mm_xor(accum2, accum2, accum2)
+ _mm_xor(accum3, accum3, accum3)
+ ".set pop \n\t"
+ :[accum0h]"=f"(accum0h), [accum0l]"=f"(accum0l),
+ [accum1h]"=f"(accum1h), [accum1l]"=f"(accum1l),
+ [accum2h]"=f"(accum2h), [accum2l]"=f"(accum2l),
+ [accum3h]"=f"(accum3h), [accum3l]"=f"(accum3l)
+ );
+
+ // Convolve with one filter coefficient per iteration.
+ for (int filter_y = 0; filter_y < filter_length; filter_y++) {
+ double src8h, src8l, src16h, src16l;
+ double mul_hih, mul_hil, mul_loh, mul_lol, th, tl;
+
+ src = reinterpret_cast<const void*>(
+ &source_data_rows[filter_y][out_x << 2]);
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // Duplicate the filter coefficient 8 times.
+ // [16] cj cj cj cj cj cj cj cj
+ "gsldlc1 %[coeff16l], 7+%[fval] \n\t"
+ "gsldrc1 %[coeff16l], %[fval] \n\t"
+ "pshufh %[coeff16l], %[coeff16l], %[zerol] \n\t"
+ "mov.d %[coeff16h], %[coeff16l] \n\t"
+ // Load four pixels (16 bytes) together.
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ "gsldlc1 %[src8h], 0xf(%[src]) \n\t"
+ "gsldrc1 %[src8h], 0x8(%[src]) \n\t"
+ "gsldlc1 %[src8l], 0x7(%[src]) \n\t"
+ "gsldrc1 %[src8l], 0x0(%[src]) \n\t"
+ // 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
+ _mm_punpcklbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ // [32] a0 b0 g0 r0
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum0, accum0, t)
+ // [32] a1 b1 g1 r1
+ _mm_punpckhhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum1, 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
+ _mm_punpckhbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ ".set pop \n\t"
+ :[th]"=&f"(th), [tl]"=&f"(tl),
+ [src8h]"=&f"(src8h), [src8l]"=&f"(src8l),
+ [src16h]"=&f"(src16h), [src16l]"=&f"(src16l),
+ [mul_hih]"=&f"(mul_hih), [mul_hil]"=&f"(mul_hil),
+ [mul_loh]"=&f"(mul_loh), [mul_lol]"=&f"(mul_lol),
+ [accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [accum1h]"+f"(accum1h), [accum1l]"+f"(accum1l),
+ [coeff16h]"=&f"(coeff16h), [coeff16l]"=&f"(coeff16l)
+ :[zeroh]"f"(zero), [zerol]"f"(zero),
+ [fval]"m"(filter_values[filter_y]),
+ [src]"r"(src)
+ );
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // [32] a2 b2 g2 r2
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum2, accum2, t)
+ // [32] a3 b3 g3 r3
+ _mm_punpckhhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum3, accum3, t)
+ ".set pop \n\t"
+ :[th]"=&f"(th), [tl]"=&f"(tl),
+ [mul_hih]"+f"(mul_hih), [mul_hil]"+f"(mul_hil),
+ [mul_loh]"+f"(mul_loh), [mul_lol]"+f"(mul_lol),
+ [accum2h]"+f"(accum2h), [accum2l]"+f"(accum2l),
+ [accum3h]"+f"(accum3h), [accum3l]"+f"(accum3l)
+ );
+ }
+
+ double t;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // Shift right for fixed point implementation.
+ _mm_psraw(accum0, accum0, sra)
+ _mm_psraw(accum1, accum1, sra)
+ _mm_psraw(accum2, accum2, sra)
+ _mm_psraw(accum3, accum3, sra)
+ // Packing 32 bits |accum| to 16 bits per channel (signed saturation).
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ _mm_packsswh(accum0, accum0, accum1, t)
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ _mm_packsswh(accum2, accum2, accum3, t)
+ // 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
+ _mm_packushb(accum0, accum0, accum2, t)
+ ".set pop \n\t"
+ :[accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [accum1h]"+f"(accum1h), [accum1l]"+f"(accum1l),
+ [accum2h]"+f"(accum2h), [accum2l]"+f"(accum2l),
+ [accum3h]"+f"(accum3h), [accum3l]"+f"(accum3l),
+ [t]"=&f"(t)
+ :[sra]"f"(sra)
+ );
+
+ if (has_alpha) {
+ double ah, al, bh, bl, srl8, srl16, sll24;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "li %[tmp], 8 \n\t"
+ "mtc1 %[tmp], %[srl8] \n\t"
+ "li %[tmp], 16 \n\t"
+ "mtc1 %[tmp], %[srl16] \n\t"
+ "li %[tmp], 24 \n\t"
+ "mtc1 %[tmp], %[sll24] \n\t"
+ // 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
+ _mm_psraw(a, accum0, srl8)
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ _mm_pmaxub(b, a, accum0) // Max of r and g.
+ // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0
+ _mm_psrlw(a, accum0, srl16)
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ _mm_pmaxub(b, 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
+ _mm_psllw(b, b, sll24)
+ // Make sure the value of alpha channel is always larger than maximum
+ // value of color channels.
+ _mm_pmaxub(accum0, b, accum0)
+ ".set pop \n\t"
+ :[accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [tmp]"=&r"(tmp), [ah]"=&f"(ah), [al]"=&f"(al),
+ [bh]"=&f"(bh), [bl]"=&f"(bl), [srl8]"=&f"(srl8),
+ [srl16]"=&f"(srl16), [sll24]"=&f"(sll24)
+ );
+ } else {
+ double maskh, maskl;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // Set value of alpha channels to 0xFF.
+ "li %[tmp], 0xff000000 \n\t"
+ "mtc1 %[tmp], %[maskl] \n\t"
+ "punpcklwd %[maskl], %[maskl], %[maskl] \n\t"
+ "mov.d %[maskh], %[maskl] \n\t"
+ _mm_or(accum0, accum0, mask)
+ ".set pop \n\t"
+ :[maskh]"=&f"(maskh), [maskl]"=&f"(maskl),
+ [accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [tmp]"=&r"(tmp)
+ );
+ }
+
+ // Store the convolution result (16 bytes) and advance the pixel pointers.
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gssdlc1 %[accum0h], 0xf(%[out_row]) \n\t"
+ "gssdrc1 %[accum0h], 0x8(%[out_row]) \n\t"
+ "gssdlc1 %[accum0l], 0x7(%[out_row]) \n\t"
+ "gssdrc1 %[accum0l], 0x0(%[out_row]) \n\t"
+ ".set pop \n\t"
+ ::[accum0h]"f"(accum0h), [accum0l]"f"(accum0l),
+ [out_row]"r"(out_row)
+ :"memory"
+ );
+ 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) {
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_xor(accum0, accum0, accum0)
+ _mm_xor(accum1, accum1, accum1)
+ _mm_xor(accum2, accum2, accum2)
+ ".set pop \n\t"
+ :[accum0h]"=&f"(accum0h), [accum0l]"=&f"(accum0l),
+ [accum1h]"=&f"(accum1h), [accum1l]"=&f"(accum1l),
+ [accum2h]"=&f"(accum2h), [accum2l]"=&f"(accum2l)
+ );
+ for (int filter_y = 0; filter_y < filter_length; ++filter_y) {
+ double src8h, src8l, src16h, src16l;
+ double th, tl, mul_hih, mul_hil, mul_loh, mul_lol;
+ src = reinterpret_cast<const void*>(
+ &source_data_rows[filter_y][out_x<<2]);
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "gsldlc1 %[coeff16l], 7+%[fval] \n\t"
+ "gsldrc1 %[coeff16l], %[fval] \n\t"
+ "pshufh %[coeff16l], %[coeff16l], %[zerol] \n\t"
+ "mov.d %[coeff16h], %[coeff16l] \n\t"
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ "gsldlc1 %[src8h], 0xf(%[src]) \n\t"
+ "gsldrc1 %[src8h], 0x8(%[src]) \n\t"
+ "gsldlc1 %[src8l], 0x7(%[src]) \n\t"
+ "gsldrc1 %[src8l], 0x0(%[src]) \n\t"
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ _mm_punpcklbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ // [32] a0 b0 g0 r0
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum0, accum0, t)
+ // [32] a1 b1 g1 r1
+ _mm_punpckhhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum1, accum1, t)
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ _mm_punpckhbh(src16, src8, zero)
+ _mm_pmulhh(mul_hi, src16, coeff16)
+ _mm_pmullh(mul_lo, src16, coeff16)
+ // [32] a2 b2 g2 r2
+ _mm_punpcklhw(t, mul_lo, mul_hi)
+ _mm_paddw(accum2, accum2, t)
+ ".set pop \n\t"
+ :[th]"=&f"(th), [tl]"=&f"(tl),
+ [src8h]"=&f"(src8h), [src8l]"=&f"(src8l),
+ [src16h]"=&f"(src16h), [src16l]"=&f"(src16l),
+ [mul_hih]"=&f"(mul_hih), [mul_hil]"=&f"(mul_hil),
+ [mul_loh]"=&f"(mul_loh), [mul_lol]"=&f"(mul_lol),
+ [accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [accum1h]"+f"(accum1h), [accum1l]"+f"(accum1l),
+ [accum2h]"+f"(accum2h), [accum2l]"+f"(accum2l),
+ [coeff16h]"=&f"(coeff16h), [coeff16l]"=&f"(coeff16l)
+ :[zeroh]"f"(zero), [zerol]"f"(zero),
+ [fval]"m"(filter_values[filter_y]),
+ [src]"r"(src)
+ );
+ }
+
+ double t;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ _mm_psraw(accum0, accum0, sra)
+ _mm_psraw(accum1, accum1, sra)
+ _mm_psraw(accum2, accum2, sra)
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ _mm_packsswh(accum0, accum0, accum1, t)
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ _mm_packsswh(accum2, accum2, zero, t)
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ _mm_packushb(accum0, accum0, accum2, t)
+ ".set pop \n\t"
+ :[accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [accum1h]"+f"(accum1h), [accum1l]"+f"(accum1l),
+ [accum2h]"+f"(accum2h), [accum2l]"+f"(accum2l),
+ [t]"=&f"(t)
+ :[zeroh]"f"(zero), [zerol]"f"(zero), [sra]"f"(sra)
+ );
+ if (has_alpha) {
+ double ah, al, bh, bl, srl8, srl16, sll24;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "li %[tmp], 8 \n\t"
+ "mtc1 %[tmp], %[srl8] \n\t"
+ "li %[tmp], 16 \n\t"
+ "mtc1 %[tmp], %[srl16] \n\t"
+ "li %[tmp], 24 \n\t"
+ "mtc1 %[tmp], %[sll24] \n\t"
+ // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0
+ _mm_psrlw(a, accum0, srl8)
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ _mm_pmaxub(b, a, accum0) // Max of r and g.
+ // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0
+ _mm_psrlw(a, accum0, srl16)
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ _mm_pmaxub(b, 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
+ _mm_psllw(b, b, sll24)
+ _mm_pmaxub(accum0, b, accum0)
+ ".set pop \n\t"
+ :[ah]"=&f"(ah), [al]"=&f"(al), [bh]"=&f"(bh), [bl]"=&f"(bl),
+ [accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l), [tmp]"=&r"(tmp),
+ [srl8]"=&f"(srl8), [srl16]"=&f"(srl16), [sll24]"=&f"(sll24)
+ );
+ } else {
+ double maskh, maskl;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ // Set value of alpha channels to 0xFF.
+ "li %[tmp], 0xff000000 \n\t"
+ "mtc1 %[tmp], %[maskl] \n\t"
+ "punpcklwd %[maskl], %[maskl], %[maskl] \n\t"
+ "mov.d %[maskh], %[maskl] \n\t"
+ _mm_or(accum0, accum0, mask)
+ ".set pop \n\t"
+ :[maskh]"=&f"(maskh), [maskl]"=&f"(maskl),
+ [accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l),
+ [tmp]"=&r"(tmp)
+ );
+ }
+
+ double s4, s64;
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "li %[tmp], 4 \n\t"
+ "mtc1 %[tmp], %[s4] \n\t"
+ "li %[tmp], 64 \n\t"
+ "mtc1 %[tmp], %[s64] \n\t"
+ ".set pop \n\t"
+ :[s4]"=f"(s4), [s64]"=f"(s64),
+ [tmp]"=&r"(tmp)
+ );
+ for (int out_x = width; out_x < pixel_width; out_x++) {
+ double t;
+
+ asm volatile (
+ ".set push \n\t"
+ ".set arch=loongson3a \n\t"
+ "swc1 %[accum0l], (%[out_row]) \n\t"
+ _mm_psrlq(accum0, accum0, s4, s64, t)
+ ".set pop \n\t"
+ :[t]"=&f"(t),
+ [accum0h]"+f"(accum0h), [accum0l]"+f"(accum0l)
+ :[out_row]"r"(out_row), [s4]"f"(s4), [s64]"f"(s64)
+ :"memory"
+ );
+ out_row += 4;
+ }
+ }
+}
+
+void ConvolveVertically_LS3(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row, bool has_alpha) {
+ if (has_alpha) {
+ ConvolveVertically_LS3_impl<true>(filter_values, filter_length,
+ source_data_rows, pixel_width, out_row);
+ } else {
+ ConvolveVertically_LS3_impl<false>(filter_values, filter_length,
+ source_data_rows, pixel_width, out_row);
+ }
+}
+
+} // namespace skia
+
+#endif /* _MIPS_ARCH_LOONGSON3A */
diff --git a/gfx/2d/convolverLS3.h b/gfx/2d/convolverLS3.h
new file mode 100644
index 000000000..af531c927
--- /dev/null
+++ b/gfx/2d/convolverLS3.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2014-2015 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_LS3_H_
+#define SKIA_EXT_CONVOLVER_LS3_H_
+
+#include "convolver.h"
+
+#include <algorithm>
+
+#include "skia/include/core/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_LS3(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row);
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the [begin, end) of the filter.
+// Process one pixel at a time.
+void ConvolveHorizontally1_LS3(const unsigned char* src_data,
+ 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_LS3|. Please
+// refer to that function for detailed comments.
+void ConvolveHorizontally4_LS3(const unsigned char* src_data[4],
+ 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_LS3(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row, bool has_alpha);
+
+} // namespace skia
+
+#endif // SKIA_EXT_CONVOLVER_LS3_H_
diff --git a/gfx/2d/convolverSSE2.cpp b/gfx/2d/convolverSSE2.cpp
new file mode 100644
index 000000000..20ddd45f1
--- /dev/null
+++ b/gfx/2d/convolverSSE2.cpp
@@ -0,0 +1,471 @@
+// 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/include/core/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 num_values() of the filter.
+void ConvolveHorizontally_SSE2(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row) {
+ 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;
+ }
+}
+
+// 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]) {
+ 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;
+ }
+}
+
+// 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_impl(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row) {
+ 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 (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.
+ 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);
+ }
+
+ 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 (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;
+ }
+ }
+}
+
+void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row, bool has_alpha) {
+ if (has_alpha) {
+ ConvolveVertically_SSE2_impl<true>(filter_values, filter_length,
+ source_data_rows, pixel_width, out_row);
+ } else {
+ ConvolveVertically_SSE2_impl<false>(filter_values, filter_length,
+ source_data_rows, pixel_width, out_row);
+ }
+}
+
+} // namespace skia
diff --git a/gfx/2d/convolverSSE2.h b/gfx/2d/convolverSSE2.h
new file mode 100644
index 000000000..a54ce676b
--- /dev/null
+++ b/gfx/2d/convolverSSE2.h
@@ -0,0 +1,68 @@
+// 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/include/core/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,
+ 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],
+ 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 pixel_width,
+ 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
new file mode 100644
index 000000000..74b1a3d7b
--- /dev/null
+++ b/gfx/2d/genshaders.sh
@@ -0,0 +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/.
+
+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
diff --git a/gfx/2d/gfx2d.sln b/gfx/2d/gfx2d.sln
new file mode 100644
index 000000000..40a137a1c
--- /dev/null
+++ b/gfx/2d/gfx2d.sln
@@ -0,0 +1,29 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gfx2d", "gfx2d.vcxproj", "{49E973D7-53C9-3D66-BE58-52125FAE193D}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittest", "unittest\unittest.vcxproj", "{CCF4BC8B-0CED-47CA-B621-ABF1832527D9}"
+ ProjectSection(ProjectDependencies) = postProject
+ {49E973D7-53C9-3D66-BE58-52125FAE193D} = {49E973D7-53C9-3D66-BE58-52125FAE193D}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {49E973D7-53C9-3D66-BE58-52125FAE193D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {49E973D7-53C9-3D66-BE58-52125FAE193D}.Debug|Win32.Build.0 = Debug|Win32
+ {49E973D7-53C9-3D66-BE58-52125FAE193D}.Release|Win32.ActiveCfg = Release|Win32
+ {49E973D7-53C9-3D66-BE58-52125FAE193D}.Release|Win32.Build.0 = Release|Win32
+ {CCF4BC8B-0CED-47CA-B621-ABF1832527D9}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CCF4BC8B-0CED-47CA-B621-ABF1832527D9}.Debug|Win32.Build.0 = Debug|Win32
+ {CCF4BC8B-0CED-47CA-B621-ABF1832527D9}.Release|Win32.ActiveCfg = Release|Win32
+ {CCF4BC8B-0CED-47CA-B621-ABF1832527D9}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/gfx/2d/gfx2d.vcxproj b/gfx/2d/gfx2d.vcxproj
new file mode 100644
index 000000000..8b4607899
--- /dev/null
+++ b/gfx/2d/gfx2d.vcxproj
@@ -0,0 +1,139 @@
+<?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">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <Keyword>Win32Proj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <ExecutablePath>$(DXSDK_DIR)\Utilities\bin\x86;$(ExecutablePath)</ExecutablePath>
+ <IncludePath>$(ProjectDir);$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>USE_SSE2;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);MFBT_STAND_ALONE;XP_WIN</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <Optimization>Disabled</Optimization>
+ </ClCompile>
+ <Link>
+ <TargetMachine>MachineX86</TargetMachine>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <EntryPointSymbol>
+ </EntryPointSymbol>
+ </Link>
+ <PreBuildEvent>
+ <Command>xcopy $(ProjectDir)..\..\mfbt\*.h mozilla\ /Y</Command>
+ <Message>Copying MFBT files</Message>
+ </PreBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>USE_SSE2;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <AdditionalIncludeDirectories>./</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <TargetMachine>MachineX86</TargetMachine>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="2D.h" />
+ <ClInclude Include="BaseMargin.h" />
+ <ClInclude Include="BasePoint.h" />
+ <ClInclude Include="BaseRect.h" />
+ <ClInclude Include="BaseSize.h" />
+ <ClInclude Include="DrawEventRecorder.h" />
+ <ClInclude Include="DrawTargetD2D.h" />
+ <ClInclude Include="DrawTargetDual.h" />
+ <ClInclude Include="DrawTargetRecording.h" />
+ <ClInclude Include="GradientStopsD2D.h" />
+ <ClInclude Include="HelpersD2D.h" />
+ <ClInclude Include="ImageScaling.h" />
+ <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" />
+ <ClInclude Include="RecordingTypes.h" />
+ <ClInclude Include="Rect.h" />
+ <ClInclude Include="ScaledFontBase.h" />
+ <ClInclude Include="ScaledFontDWrite.h" />
+ <ClInclude Include="SourceSurfaceD2D.h" />
+ <ClInclude Include="SourceSurfaceD2DTarget.h" />
+ <ClInclude Include="SourceSurfaceRawData.h" />
+ <ClInclude Include="Tools.h" />
+ <ClInclude Include="Types.h" />
+ <ClInclude Include="UserData.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="DrawEventRecorder.cpp" />
+ <ClCompile Include="DrawTargetD2D.cpp" />
+ <ClCompile Include="DrawTargetDual.cpp" />
+ <ClCompile Include="DrawTargetRecording.cpp" />
+ <ClCompile Include="Factory.cpp" />
+ <ClCompile Include="ImageScaling.cpp" />
+ <ClCompile Include="ImageScalingSSE2.cpp" />
+ <ClCompile Include="Matrix.cpp" />
+ <ClCompile Include="PathD2D.cpp" />
+ <ClCompile Include="PathRecording.cpp" />
+ <ClCompile Include="RecordedEvent.cpp" />
+ <ClCompile Include="ScaledFontBase.cpp" />
+ <ClCompile Include="ScaledFontDWrite.cpp" />
+ <ClCompile Include="SourceSurfaceD2D.cpp" />
+ <ClCompile Include="SourceSurfaceD2DTarget.cpp" />
+ <ClCompile Include="SourceSurfaceRawData.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Makefile.in" />
+ <CustomBuild Include="ShadersD2D.fx">
+ <FileType>Document</FileType>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">fxc /Tfx_4_0 /FhShadersD2D.h ShadersD2D.fx /Vn d2deffect</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">ShadersD2D.h</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/gfx/2d/image_operations.cpp b/gfx/2d/image_operations.cpp
new file mode 100644
index 000000000..62215f007
--- /dev/null
+++ b/gfx/2d/image_operations.cpp
@@ -0,0 +1,390 @@
+// Copyright (c) 2006-2012 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 "base/basictypes.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include "image_operations.h"
+
+#include "base/stack_container.h"
+#include "convolver.h"
+#include "skia/include/core/SkColorPriv.h"
+#include "skia/include/core/SkBitmap.h"
+#include "skia/include/core/SkRect.h"
+#include "skia/include/core/SkFontLCDConfig.h"
+
+namespace skia {
+
+namespace resize {
+
+// TODO(egouriou): Take advantage of periods in the convolution.
+// Practical resizing filters are periodic outside of the border area.
+// For Lanczos, a scaling by a (reduced) factor of p/q (q pixels in the
+// source become p pixels in the destination) will have a period of p.
+// A nice consequence is a period of 1 when downscaling by an integral
+// factor. Downscaling from typical display resolutions is also bound
+// to produce interesting periods as those are chosen to have multiple
+// small factors.
+// 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 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
+ // means the destination pixels are much smaller than the source pixels, and
+ // that the range covered by the filter won't necessarily cover any source
+ // pixel boundaries. Therefore, we use these clamped values (max of 1) for
+ // 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;
+
+ StackVector<float, 64> filter_values;
+ StackVector<int16_t, 64> fixed_filter_values;
+
+ // Loop over all pixels in the output range. We will generate one set of
+ // filter values for each one. Those values will tell us how to blend the
+ // source pixels to compute the destination pixel.
+ for (int dest_subset_i = dest_subset_lo; dest_subset_i < dest_subset_hi;
+ dest_subset_i++) {
+ // Reset the arrays. We don't declare them inside so they can re-use the
+ // same malloc-ed buffer.
+ filter_values->clear();
+ fixed_filter_values->clear();
+
+ // This is the pixel in the source directly under the pixel in the dest.
+ // Note that we base computations on the "center" of the pixels. To see
+ // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x
+ // downscale should "cover" the pixels around the pixel with *its center*
+ // at coordinates (2.5, 2.5) in the source, not those around (0, 0).
+ // Hence we need to scale coordinates (0.5, 0.5), not (0, 0).
+ float src_pixel = (static_cast<float>(dest_subset_i) + 0.5f) * inv_scale;
+
+ // Compute the (inclusive) range of source pixels the filter covers.
+ int src_begin = std::max(0, FloorInt(src_pixel - src_support));
+ int src_end = std::min(src_size - 1, CeilInt(src_pixel + src_support));
+
+ // Compute the unnormalized filter value at each location of the source
+ // it covers.
+ float filter_sum = 0.0f; // Sub of the filter values for normalizing.
+ for (int cur_filter_pixel = src_begin; cur_filter_pixel <= src_end;
+ cur_filter_pixel++) {
+ // Distance from the center of the filter, this is the filter coordinate
+ // in source space. We also need to consider the center of the pixel
+ // when comparing distance against 'src_pixel'. In the 5x downscale
+ // example used above the distance from the center of the filter to
+ // the pixel with coordinates (2, 2) should be 0, because its center
+ // is at (2.5, 2.5).
+ float src_filter_dist =
+ ((static_cast<float>(cur_filter_pixel) + 0.5f) - src_pixel);
+
+ // Since the filter really exists in dest space, map it there.
+ float dest_filter_dist = src_filter_dist * clamped_scale;
+
+ // Compute the filter value at that location.
+ float filter_value = ComputeFilter(method, dest_filter_dist);
+ filter_values->push_back(filter_value);
+
+ filter_sum += filter_value;
+ }
+
+ // The filter must be normalized so that we don't affect the brightness of
+ // the image. Convert to normalized fixed point.
+ int16_t fixed_sum = 0;
+ for (size_t i = 0; i < filter_values->size(); i++) {
+ int16_t cur_fixed = output->FloatToFixed(filter_values[i] / filter_sum);
+ fixed_sum += cur_fixed;
+ fixed_filter_values->push_back(cur_fixed);
+ }
+
+ // The conversion to fixed point will leave some rounding errors, which
+ // we add back in to avoid affecting the brightness of the image. We
+ // arbitrarily add this to the center of the filter array (this won't always
+ // be the center of the filter function since it could get clipped on the
+ // edges, but it doesn't matter enough to worry about that case).
+ int16_t leftovers = output->FloatToFixed(1.0f) - fixed_sum;
+ fixed_filter_values[fixed_filter_values->size() / 2] += leftovers;
+
+ // Now it's ready to go.
+ output->AddFilter(src_begin, &fixed_filter_values[0],
+ static_cast<int>(fixed_filter_values->size()));
+ }
+
+ output->PaddingForSIMD(8);
+}
+
+} // namespace resize
+
+ImageOperations::ResizeMethod ResizeMethodToAlgorithmMethod(
+ ImageOperations::ResizeMethod method) {
+ // Convert any "Quality Method" into an "Algorithm Method"
+ if (method >= ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD &&
+ method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD) {
+ return method;
+ }
+ // The call to ImageOperationsGtv::Resize() above took care of
+ // 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:
+ // 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 acceptable trade-off between quality and speed.
+ case ImageOperations::RESIZE_BETTER:
+ return ImageOperations::RESIZE_HAMMING1;
+ default:
+ return ImageOperations::RESIZE_LANCZOS3;
+ }
+}
+
+// Resize ----------------------------------------------------------------------
+
+// static
+SkBitmap ImageOperations::Resize(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ void* dest_pixels /* = nullptr */) {
+ if (method == ImageOperations::RESIZE_SUBPIXEL)
+ return ResizeSubpixel(source, dest_width, dest_height, dest_subset);
+ else
+ return ResizeBasic(source, method, dest_width, dest_height, dest_subset,
+ dest_pixels);
+}
+
+// static
+SkBitmap ImageOperations::ResizeSubpixel(const SkBitmap& source,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset) {
+ // Currently only works on Linux/BSD because these are the only platforms
+ // where SkFontLCDConfig::GetSubpixelOrder is defined.
+#if defined(XP_UNIX)
+ // Understand the display.
+ const SkFontLCDConfig::LCDOrder order = SkFontLCDConfig::GetSubpixelOrder();
+ const SkFontLCDConfig::LCDOrientation orientation =
+ SkFontLCDConfig::GetSubpixelOrientation();
+
+ // Decide on which dimension, if any, to deploy subpixel rendering.
+ int w = 1;
+ int h = 1;
+ switch (orientation) {
+ case SkFontLCDConfig::kHorizontal_LCDOrientation:
+ w = dest_width < source.width() ? 3 : 1;
+ break;
+ case SkFontLCDConfig::kVertical_LCDOrientation:
+ h = dest_height < source.height() ? 3 : 1;
+ break;
+ }
+
+ // Resize the image.
+ const int width = dest_width * w;
+ const int height = dest_height * h;
+ SkIRect subset = { dest_subset.fLeft, dest_subset.fTop,
+ dest_subset.fLeft + dest_subset.width() * w,
+ dest_subset.fTop + dest_subset.height() * h };
+ SkBitmap img = ResizeBasic(source, ImageOperations::RESIZE_LANCZOS3, width,
+ height, subset);
+ const int row_words = img.rowBytes() / 4;
+ if (w == 1 && h == 1)
+ return img;
+
+ // Render into subpixels.
+ SkBitmap result;
+ SkImageInfo info = SkImageInfo::Make(dest_subset.width(),
+ dest_subset.height(),
+ kBGRA_8888_SkColorType,
+ kPremul_SkAlphaType);
+
+
+ result.allocPixels(info);
+ if (!result.readyToDraw())
+ return img;
+
+ SkAutoLockPixels locker(img);
+ if (!img.readyToDraw())
+ return img;
+
+ uint32_t* src_row = img.getAddr32(0, 0);
+ uint32_t* dst_row = result.getAddr32(0, 0);
+ for (int y = 0; y < dest_subset.height(); y++) {
+ uint32_t* src = src_row;
+ uint32_t* dst = dst_row;
+ for (int x = 0; x < dest_subset.width(); x++, src += w, dst++) {
+ uint8_t r = 0, g = 0, b = 0, a = 0;
+ switch (order) {
+ case SkFontLCDConfig::kRGB_LCDOrder:
+ switch (orientation) {
+ case SkFontLCDConfig::kHorizontal_LCDOrientation:
+ r = SkGetPackedR32(src[0]);
+ g = SkGetPackedG32(src[1]);
+ b = SkGetPackedB32(src[2]);
+ a = SkGetPackedA32(src[1]);
+ break;
+ case SkFontLCDConfig::kVertical_LCDOrientation:
+ r = SkGetPackedR32(src[0 * row_words]);
+ g = SkGetPackedG32(src[1 * row_words]);
+ b = SkGetPackedB32(src[2 * row_words]);
+ a = SkGetPackedA32(src[1 * row_words]);
+ break;
+ }
+ break;
+ case SkFontLCDConfig::kBGR_LCDOrder:
+ switch (orientation) {
+ case SkFontLCDConfig::kHorizontal_LCDOrientation:
+ b = SkGetPackedB32(src[0]);
+ g = SkGetPackedG32(src[1]);
+ r = SkGetPackedR32(src[2]);
+ a = SkGetPackedA32(src[1]);
+ break;
+ case SkFontLCDConfig::kVertical_LCDOrientation:
+ b = SkGetPackedB32(src[0 * row_words]);
+ g = SkGetPackedG32(src[1 * row_words]);
+ r = SkGetPackedR32(src[2 * row_words]);
+ a = SkGetPackedA32(src[1 * row_words]);
+ break;
+ }
+ break;
+ case SkFontLCDConfig::kNONE_LCDOrder:
+ break;
+ }
+ // Premultiplied alpha is very fragile.
+ a = a > r ? a : r;
+ a = a > g ? a : g;
+ a = a > b ? a : b;
+ *dst = SkPackARGB32(a, r, g, b);
+ }
+ src_row += h * row_words;
+ dst_row += result.rowBytes() / 4;
+ }
+ result.setAlphaType(img.alphaType());
+ return result;
+#else
+ return SkBitmap();
+#endif // OS_POSIX && !OS_MACOSX && !defined(OS_ANDROID)
+}
+
+// static
+SkBitmap ImageOperations::ResizeBasic(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ void* dest_pixels /* = nullptr */) {
+ // Ensure that the ResizeMethod enumeration is sound.
+ SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) &&
+ (method <= RESIZE_LAST_QUALITY_METHOD)) ||
+ ((RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
+ (method <= RESIZE_LAST_ALGORITHM_METHOD)));
+
+ // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just
+ // return empty.
+ if (source.width() < 1 || source.height() < 1 ||
+ dest_width < 1 || dest_height < 1)
+ return SkBitmap();
+
+ method = ResizeMethodToAlgorithmMethod(method);
+ // Check that we deal with an "algorithm methods" from this point onward.
+ SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
+ (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
+
+ SkAutoLockPixels locker(source);
+ if (!source.readyToDraw())
+ return SkBitmap();
+
+ 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
+ // referring to the old data.
+ const uint8_t* source_subset =
+ reinterpret_cast<const uint8_t*>(source.getPixels());
+
+ // Convolve into the result.
+ SkBitmap result;
+ SkImageInfo info = SkImageInfo::Make(dest_subset.width(),
+ dest_subset.height(),
+ kBGRA_8888_SkColorType,
+ kPremul_SkAlphaType);
+
+ if (dest_pixels) {
+ result.installPixels(info, dest_pixels, info.minRowBytes());
+ } else {
+ result.allocPixels(info);
+ }
+
+ if (!result.readyToDraw())
+ return SkBitmap();
+
+ BGRAConvolve2D(source_subset, static_cast<int>(source.rowBytes()),
+ !source.isOpaque(), x_filter, y_filter,
+ static_cast<int>(result.rowBytes()),
+ static_cast<unsigned char*>(result.getPixels()));
+
+ // Preserve the "opaque" flag for use as an optimization later.
+ result.setAlphaType(source.alphaType());
+
+ return result;
+}
+
+// static
+SkBitmap ImageOperations::Resize(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ void* dest_pixels /* = nullptr */) {
+ SkIRect dest_subset = { 0, 0, dest_width, dest_height };
+ return Resize(source, method, dest_width, dest_height, dest_subset,
+ dest_pixels);
+}
+
+} // namespace skia
diff --git a/gfx/2d/image_operations.h b/gfx/2d/image_operations.h
new file mode 100644
index 000000000..8e3191363
--- /dev/null
+++ b/gfx/2d/image_operations.h
@@ -0,0 +1,285 @@
+// 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_IMAGE_OPERATIONS_H_
+#define SKIA_EXT_IMAGE_OPERATIONS_H_
+
+#include "skia/include/core/SkTypes.h"
+#include "Types.h"
+#include "convolver.h"
+#include "skia/include/core/SkRect.h"
+
+class SkBitmap;
+struct SkIRect;
+
+namespace skia {
+
+class ImageOperations {
+ public:
+ enum ResizeMethod {
+ //
+ // Quality Methods
+ //
+ // Those enumeration values express a desired quality/speed tradeoff.
+ // They are translated into an algorithm-specific method that depends
+ // on the capabilities (CPU, GPU) of the underlying platform.
+ // It is possible for all three methods to be mapped to the same
+ // algorithm on a given platform.
+
+ // Good quality resizing. Fastest resizing with acceptable visual quality.
+ // This is typically intended for use during interactive layouts
+ // where slower platforms may want to trade image quality for large
+ // increase in resizing performance.
+ //
+ // For example the resizing implementation may devolve to linear
+ // filtering if this enables GPU acceleration to be used.
+ //
+ // Note that the underlying resizing method may be determined
+ // on the fly based on the parameters for a given resize call.
+ // For example an implementation using a GPU-based linear filter
+ // in the common case may still use a higher-quality software-based
+ // filter in cases where using the GPU would actually be slower - due
+ // to too much latency - or impossible - due to image format or size
+ // constraints.
+ RESIZE_GOOD,
+
+ // Medium quality resizing. Close to high quality resizing (better
+ // than linear interpolation) with potentially some quality being
+ // traded-off for additional speed compared to RESIZE_BEST.
+ //
+ // This is intended, for example, for generation of large thumbnails
+ // (hundreds of pixels in each dimension) from large sources, where
+ // a linear filter would produce too many artifacts but where
+ // a RESIZE_HIGH might be too costly time-wise.
+ RESIZE_BETTER,
+
+ // High quality resizing. The algorithm is picked to favor image quality.
+ RESIZE_BEST,
+
+ //
+ // Algorithm-specific enumerations
+ //
+
+ // Box filter. This is a weighted average of all of the pixels touching
+ // the destination pixel. For enlargement, this is nearest neighbor.
+ //
+ // You probably don't want this, it is here for testing since it is easy to
+ // compute. Use RESIZE_LANCZOS3 instead.
+ RESIZE_BOX,
+
+ // 1-cycle Hamming filter. This is tall is the middle and falls off towards
+ // the window edges but without going to 0. This is about 40% faster than
+ // a 2-cycle Lanczos.
+ RESIZE_HAMMING1,
+
+ // 2-cycle Lanczos filter. This is tall in the middle, goes negative on
+ // each side, then returns to zero. Does not provide as good a frequency
+ // response as a 3-cycle Lanczos but is roughly 30% faster.
+ RESIZE_LANCZOS2,
+
+ // 3-cycle Lanczos filter. This is tall in the middle, goes negative on
+ // each side, then oscillates 2 more times. It gives nice sharp edges.
+ RESIZE_LANCZOS3,
+
+ // Lanczos filter + subpixel interpolation. If subpixel rendering is not
+ // appropriate we automatically fall back to Lanczos.
+ RESIZE_SUBPIXEL,
+
+ // enum aliases for first and last methods by algorithm or by quality.
+ RESIZE_FIRST_QUALITY_METHOD = RESIZE_GOOD,
+ RESIZE_LAST_QUALITY_METHOD = RESIZE_BEST,
+ RESIZE_FIRST_ALGORITHM_METHOD = RESIZE_BOX,
+ RESIZE_LAST_ALGORITHM_METHOD = RESIZE_SUBPIXEL,
+ };
+
+ // Resizes the given source bitmap using the specified resize method, so that
+ // the entire image is (dest_size) big. The dest_subset is the rectangle in
+ // this destination image that should actually be returned.
+ //
+ // The output image will be (dest_subset.width(), dest_subset.height()). This
+ // will save work if you do not need the entire bitmap.
+ //
+ // The destination subset must be smaller than the destination image.
+ static SkBitmap Resize(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ void* dest_pixels = nullptr);
+
+ // Alternate version for resizing and returning the entire bitmap rather than
+ // a subset.
+ static SkBitmap Resize(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ void* dest_pixels = nullptr);
+
+ private:
+ ImageOperations(); // Class for scoping only.
+
+ // Supports all methods except RESIZE_SUBPIXEL.
+ static SkBitmap ResizeBasic(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ void* dest_pixels = nullptr);
+
+ // Subpixel renderer.
+ static SkBitmap ResizeSubpixel(const SkBitmap& source,
+ int dest_width, int dest_height,
+ 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 (sinf(xpi) / xpi) * // sinc(x)
+ sinf(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 ((sinf(xpi) / xpi) * // sinc(x)
+ (0.54f + 0.46f * cosf(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
new file mode 100644
index 000000000..ad095503d
--- /dev/null
+++ b/gfx/2d/moz.build
@@ -0,0 +1,230 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla += [
+ 'GenericRefCounted.h',
+]
+
+EXPORTS.mozilla.gfx += [
+ '2D.h',
+ 'BaseCoord.h',
+ 'BaseMargin.h',
+ 'BasePoint.h',
+ 'BasePoint3D.h',
+ 'BasePoint4D.h',
+ 'BaseRect.h',
+ 'BaseSize.h',
+ 'BezierUtils.h',
+ 'Blur.h',
+ 'BorrowedContext.h',
+ 'Coord.h',
+ 'CriticalSection.h',
+ 'DataSurfaceHelpers.h',
+ 'DrawEventRecorder.h',
+ 'DrawTargetTiled.h',
+ 'Filters.h',
+ 'Helpers.h',
+ 'HelpersCairo.h',
+ 'IterableArena.h',
+ 'JobScheduler.h',
+ 'JobScheduler_posix.h',
+ 'JobScheduler_win32.h',
+ 'Logging.h',
+ 'LoggingConstants.h',
+ 'Matrix.h',
+ 'MatrixFwd.h',
+ 'NumericTools.h',
+ 'PathHelpers.h',
+ 'PatternHelpers.h',
+ 'Point.h',
+ 'Polygon.h',
+ 'Quaternion.h',
+ 'RecordedEvent.h',
+ 'RecordingTypes.h',
+ 'Rect.h',
+ 'Scale.h',
+ 'ScaleFactor.h',
+ 'ScaleFactors2D.h',
+ 'SourceSurfaceCairo.h',
+ 'SourceSurfaceRawData.h',
+ 'StackArray.h',
+ 'Tools.h',
+ 'Triangle.h',
+ 'Types.h',
+ 'UserData.h',
+]
+
+EXPORTS.mozilla.gfx += ['ssse3-scaler.h']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'uikit'):
+ EXPORTS.mozilla.gfx += [
+ 'MacIOSurface.h',
+ ]
+ UNIFIED_SOURCES += [
+ 'NativeFontResourceMac.cpp',
+ 'PathCG.cpp',
+ 'ScaledFontMac.cpp',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ SOURCES += [
+ 'DrawTargetD2D1.cpp',
+ 'ExtendInputEffectD2D1.cpp',
+ 'FilterNodeD2D1.cpp',
+ 'JobScheduler_win32.cpp',
+ 'NativeFontResourceDWrite.cpp',
+ 'NativeFontResourceGDI.cpp',
+ 'PathD2D.cpp',
+ 'RadialGradientEffectD2D1.cpp',
+ 'ScaledFontDWrite.cpp',
+ 'ScaledFontWin.cpp',
+ 'SourceSurfaceD2D1.cpp',
+ ]
+ DEFINES['WIN32'] = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'windows':
+ SOURCES += [
+ 'JobScheduler_posix.cpp',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
+ SOURCES += [
+ 'ScaledFontFontconfig.cpp',
+ ]
+
+if CONFIG['MOZ_ENABLE_SKIA']:
+ UNIFIED_SOURCES += [
+ 'convolver.cpp',
+ ]
+ SOURCES += [
+ 'DrawTargetSkia.cpp',
+ 'image_operations.cpp', # Uses _USE_MATH_DEFINES
+ 'PathSkia.cpp',
+ 'SourceSurfaceSkia.cpp',
+ ]
+ if CONFIG['CLANG_CXX']:
+ # Suppress warnings from Skia header files.
+ SOURCES['DrawTargetSkia.cpp'].flags += ['-Wno-implicit-fallthrough']
+ SOURCES['PathSkia.cpp'].flags += ['-Wno-implicit-fallthrough']
+ SOURCES['SourceSurfaceSkia.cpp'].flags += ['-Wno-implicit-fallthrough']
+ EXPORTS.mozilla.gfx += [
+ 'HelpersSkia.h',
+ ]
+
+# Are we targeting x86 or x64? If so, build SSE2 files.
+if CONFIG['INTEL_ARCHITECTURE']:
+ SOURCES += [
+ 'BlurSSE2.cpp',
+ 'FilterProcessingSSE2.cpp',
+ 'ImageScalingSSE2.cpp',
+ 'ssse3-scaler.c',
+ ]
+ 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']
+ SOURCES['ssse3-scaler.c'].flags += CONFIG['SSSE3_FLAGS']
+ if CONFIG['MOZ_ENABLE_SKIA']:
+ SOURCES['convolverSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+elif CONFIG['CPU_ARCH'].startswith('mips'):
+ SOURCES += [
+ 'BlurLS3.cpp',
+ ]
+ if CONFIG['MOZ_ENABLE_SKIA']:
+ SOURCES += [
+ 'convolverLS3.cpp',
+ ]
+
+UNIFIED_SOURCES += [
+ 'BezierUtils.cpp',
+ 'Blur.cpp',
+ 'DataSourceSurface.cpp',
+ 'DataSurfaceHelpers.cpp',
+ 'DrawEventRecorder.cpp',
+ 'DrawingJob.cpp',
+ 'DrawTarget.cpp',
+ 'DrawTargetCairo.cpp',
+ 'DrawTargetCapture.cpp',
+ 'DrawTargetDual.cpp',
+ 'DrawTargetRecording.cpp',
+ 'DrawTargetTiled.cpp',
+ 'FilterNodeSoftware.cpp',
+ 'FilterProcessing.cpp',
+ 'FilterProcessingScalar.cpp',
+ 'ImageScaling.cpp',
+ 'JobScheduler.cpp',
+ 'Matrix.cpp',
+ 'Path.cpp',
+ 'PathCairo.cpp',
+ 'PathHelpers.cpp',
+ 'PathRecording.cpp',
+ 'Quaternion.cpp',
+ 'RecordedEvent.cpp',
+ 'Scale.cpp',
+ 'ScaledFontBase.cpp',
+ 'ScaledFontCairo.cpp',
+ 'SFNTData.cpp',
+ 'SFNTNameTable.cpp',
+ 'SourceSurfaceCairo.cpp',
+ 'SourceSurfaceRawData.cpp',
+]
+
+SOURCES += [
+ 'Factory.cpp', # Need to suppress warnings in Skia header files.
+]
+
+if CONFIG['CLANG_CXX']:
+ SOURCES['Factory.cpp'].flags += ['-Wno-implicit-fallthrough']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ EXPORTS.mozilla.gfx += [
+ 'QuartzSupport.h',
+ ]
+ SOURCES += [
+ 'MacIOSurface.cpp',
+ 'QuartzSupport.mm',
+ ]
+
+if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
+ SOURCES += ['BlurNEON.cpp']
+ SOURCES['BlurNEON.cpp'].flags += CONFIG['NEON_FLAGS']
+
+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'):
+ DEFINES['MOZ_ENABLE_FREETYPE'] = True
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3'):
+ CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
+
+LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES']
+
+if CONFIG['MOZ_ENABLE_SKIA']:
+ LOCAL_INCLUDES += [
+ '/gfx/skia/skia/include/private',
+ '/gfx/skia/skia/src/core',
+ '/gfx/skia/skia/src/image',
+ ]
+if CONFIG['MOZ_ENABLE_SKIA_GPU']:
+ LOCAL_INCLUDES += [
+ '/gfx/skia/skia/src/gpu',
+ ]
+
diff --git a/gfx/2d/ssse3-scaler.c b/gfx/2d/ssse3-scaler.c
new file mode 100644
index 000000000..345844b84
--- /dev/null
+++ b/gfx/2d/ssse3-scaler.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright © 2013 Soren Sandmann Pedersen
+ * Copyright © 2013 Red Hat, Inc.
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Soren Sandmann (soren.sandmann@gmail.com)
+ * Jeff Muizelaar (jmuizelaar@mozilla.com)
+ */
+
+/* This has been adapted from the ssse3 code from pixman. It's currently
+ * a mess as I want to try it out in practice before finalizing the details.
+ */
+
+#include <stdlib.h>
+#include <mmintrin.h>
+#include <xmmintrin.h>
+#include <emmintrin.h>
+#include <tmmintrin.h>
+#include <stdint.h>
+#include <assert.h>
+
+typedef int32_t pixman_fixed_16_16_t;
+typedef pixman_fixed_16_16_t pixman_fixed_t;
+#define pixman_fixed_1 (pixman_int_to_fixed(1))
+#define pixman_fixed_to_int(f) ((int) ((f) >> 16))
+#define pixman_int_to_fixed(i) ((pixman_fixed_t) ((i) << 16))
+#define pixman_double_to_fixed(d) ((pixman_fixed_t) ((d) * 65536.0))
+typedef struct pixman_vector pixman_vector_t;
+
+typedef int pixman_bool_t;
+typedef int64_t pixman_fixed_32_32_t;
+typedef pixman_fixed_32_32_t pixman_fixed_48_16_t;
+typedef struct { pixman_fixed_48_16_t v[3]; } pixman_vector_48_16_t;
+
+struct pixman_vector
+{
+ pixman_fixed_t vector[3];
+};
+typedef struct pixman_transform pixman_transform_t;
+
+struct pixman_transform
+{
+ pixman_fixed_t matrix[3][3];
+};
+
+#ifdef _MSC_VER
+#define force_inline __forceinline
+#else
+#define force_inline __inline__ __attribute__((always_inline))
+#endif
+
+#define BILINEAR_INTERPOLATION_BITS 6
+
+static force_inline int
+pixman_fixed_to_bilinear_weight (pixman_fixed_t x)
+{
+ return (x >> (16 - BILINEAR_INTERPOLATION_BITS)) &
+ ((1 << BILINEAR_INTERPOLATION_BITS) - 1);
+}
+
+static void
+pixman_transform_point_31_16_3d (const pixman_transform_t *t,
+ const pixman_vector_48_16_t *v,
+ pixman_vector_48_16_t *result)
+{
+ int i;
+ int64_t tmp[3][2];
+
+ /* input vector values must have no more than 31 bits (including sign)
+ * in the integer part */
+ assert (v->v[0] < ((pixman_fixed_48_16_t)1 << (30 + 16)));
+ assert (v->v[0] >= -((pixman_fixed_48_16_t)1 << (30 + 16)));
+ assert (v->v[1] < ((pixman_fixed_48_16_t)1 << (30 + 16)));
+ assert (v->v[1] >= -((pixman_fixed_48_16_t)1 << (30 + 16)));
+ assert (v->v[2] < ((pixman_fixed_48_16_t)1 << (30 + 16)));
+ assert (v->v[2] >= -((pixman_fixed_48_16_t)1 << (30 + 16)));
+
+ for (i = 0; i < 3; i++)
+ {
+ tmp[i][0] = (int64_t)t->matrix[i][0] * (v->v[0] >> 16);
+ tmp[i][1] = (int64_t)t->matrix[i][0] * (v->v[0] & 0xFFFF);
+ tmp[i][0] += (int64_t)t->matrix[i][1] * (v->v[1] >> 16);
+ tmp[i][1] += (int64_t)t->matrix[i][1] * (v->v[1] & 0xFFFF);
+ tmp[i][0] += (int64_t)t->matrix[i][2] * (v->v[2] >> 16);
+ tmp[i][1] += (int64_t)t->matrix[i][2] * (v->v[2] & 0xFFFF);
+ }
+
+ result->v[0] = tmp[0][0] + ((tmp[0][1] + 0x8000) >> 16);
+ result->v[1] = tmp[1][0] + ((tmp[1][1] + 0x8000) >> 16);
+ result->v[2] = tmp[2][0] + ((tmp[2][1] + 0x8000) >> 16);
+}
+
+static pixman_bool_t
+pixman_transform_point_3d (const struct pixman_transform *transform,
+ struct pixman_vector * vector)
+{
+ pixman_vector_48_16_t tmp;
+ tmp.v[0] = vector->vector[0];
+ tmp.v[1] = vector->vector[1];
+ tmp.v[2] = vector->vector[2];
+
+ pixman_transform_point_31_16_3d (transform, &tmp, &tmp);
+
+ vector->vector[0] = tmp.v[0];
+ vector->vector[1] = tmp.v[1];
+ vector->vector[2] = tmp.v[2];
+
+ return vector->vector[0] == tmp.v[0] &&
+ vector->vector[1] == tmp.v[1] &&
+ vector->vector[2] == tmp.v[2];
+}
+
+
+struct bits_image_t
+{
+ uint32_t * bits;
+ int rowstride;
+ pixman_transform_t *transform;
+};
+
+typedef struct bits_image_t bits_image_t;
+typedef struct {
+ int unused;
+} pixman_iter_info_t;
+
+typedef struct pixman_iter_t pixman_iter_t;
+typedef void (* pixman_iter_fini_t) (pixman_iter_t *iter);
+
+struct pixman_iter_t
+{
+ int x, y;
+ pixman_iter_fini_t fini;
+ bits_image_t *image;
+ uint32_t * buffer;
+ int width;
+ int height;
+ void * data;
+};
+
+typedef struct
+{
+ int y;
+ uint64_t * buffer;
+} line_t;
+
+typedef struct
+{
+ line_t lines[2];
+ pixman_fixed_t y;
+ pixman_fixed_t x;
+ uint64_t data[1];
+} bilinear_info_t;
+
+static void
+ssse3_fetch_horizontal (bits_image_t *image, line_t *line,
+ int y, pixman_fixed_t x, pixman_fixed_t ux, int n)
+{
+ uint32_t *bits = image->bits + y * image->rowstride;
+ __m128i vx = _mm_set_epi16 (
+ - (x + 1), x, - (x + 1), x,
+ - (x + ux + 1), x + ux, - (x + ux + 1), x + ux);
+ __m128i vux = _mm_set_epi16 (
+ - 2 * ux, 2 * ux, - 2 * ux, 2 * ux,
+ - 2 * ux, 2 * ux, - 2 * ux, 2 * ux);
+ __m128i vaddc = _mm_set_epi16 (1, 0, 1, 0, 1, 0, 1, 0);
+ __m128i *b = (__m128i *)line->buffer;
+ __m128i vrl0, vrl1;
+
+ while ((n -= 2) >= 0)
+ {
+ __m128i vw, vr, s;
+#ifdef HACKY_PADDING
+ if (pixman_fixed_to_int(x + ux) >= image->rowstride) {
+ vrl1 = _mm_setzero_si128();
+ printf("overread 2loop\n");
+ } else {
+ if (pixman_fixed_to_int(x + ux) < 0)
+ printf("underflow\n");
+ vrl1 = _mm_loadl_epi64(
+ (__m128i *)(bits + (pixman_fixed_to_int(x + ux) < 0 ? 0 : pixman_fixed_to_int(x + ux))));
+ }
+#else
+ vrl1 = _mm_loadl_epi64(
+ (__m128i *)(bits + pixman_fixed_to_int(x + ux)));
+#endif
+ /* vrl1: R1, L1 */
+
+ final_pixel:
+#ifdef HACKY_PADDING
+ vrl0 = _mm_loadl_epi64 (
+ (__m128i *)(bits + (pixman_fixed_to_int (x) < 0 ? 0 : pixman_fixed_to_int (x))));
+#else
+ vrl0 = _mm_loadl_epi64 (
+ (__m128i *)(bits + pixman_fixed_to_int (x)));
+#endif
+ /* vrl0: R0, L0 */
+
+ /* The weights are based on vx which is a vector of
+ *
+ * - (x + 1), x, - (x + 1), x,
+ * - (x + ux + 1), x + ux, - (x + ux + 1), x + ux
+ *
+ * so the 16 bit weights end up like this:
+ *
+ * iw0, w0, iw0, w0, iw1, w1, iw1, w1
+ *
+ * and after shifting and packing, we get these bytes:
+ *
+ * iw0, w0, iw0, w0, iw1, w1, iw1, w1,
+ * iw0, w0, iw0, w0, iw1, w1, iw1, w1,
+ *
+ * which means the first and the second input pixel
+ * have to be interleaved like this:
+ *
+ * la0, ra0, lr0, rr0, la1, ra1, lr1, rr1,
+ * lg0, rg0, lb0, rb0, lg1, rg1, lb1, rb1
+ *
+ * before maddubsw can be used.
+ */
+
+ vw = _mm_add_epi16 (
+ vaddc, _mm_srli_epi16 (vx, 16 - BILINEAR_INTERPOLATION_BITS));
+ /* vw: iw0, w0, iw0, w0, iw1, w1, iw1, w1
+ */
+
+ vw = _mm_packus_epi16 (vw, vw);
+ /* vw: iw0, w0, iw0, w0, iw1, w1, iw1, w1,
+ * iw0, w0, iw0, w0, iw1, w1, iw1, w1
+ */
+ vx = _mm_add_epi16 (vx, vux);
+
+ x += 2 * ux;
+
+ vr = _mm_unpacklo_epi16 (vrl1, vrl0);
+ /* vr: rar0, rar1, rgb0, rgb1, lar0, lar1, lgb0, lgb1 */
+
+ s = _mm_shuffle_epi32 (vr, _MM_SHUFFLE (1, 0, 3, 2));
+ /* s: lar0, lar1, lgb0, lgb1, rar0, rar1, rgb0, rgb1 */
+
+ vr = _mm_unpackhi_epi8 (vr, s);
+ /* vr: la0, ra0, lr0, rr0, la1, ra1, lr1, rr1,
+ * lg0, rg0, lb0, rb0, lg1, rg1, lb1, rb1
+ */
+
+ vr = _mm_maddubs_epi16 (vr, vw);
+
+ /* When the weight is 0, the inverse weight is
+ * 128 which can't be represented in a signed byte.
+ * As a result maddubsw computes the following:
+ *
+ * r = l * -128 + r * 0
+ *
+ * rather than the desired
+ *
+ * r = l * 128 + r * 0
+ *
+ * We fix this by taking the absolute value of the
+ * result.
+ */
+ // we can drop this if we use lower precision
+
+ vr = _mm_shuffle_epi32 (vr, _MM_SHUFFLE (2, 0, 3, 1));
+ /* vr: A0, R0, A1, R1, G0, B0, G1, B1 */
+ _mm_store_si128 (b++, vr);
+ }
+
+ if (n == -1)
+ {
+ vrl1 = _mm_setzero_si128();
+ goto final_pixel;
+ }
+
+ line->y = y;
+}
+
+// scale a line of destination pixels
+static uint32_t *
+ssse3_fetch_bilinear_cover (pixman_iter_t *iter, const uint32_t *mask)
+{
+ pixman_fixed_t fx, ux;
+ bilinear_info_t *info = iter->data;
+ line_t *line0, *line1;
+ int y0, y1;
+ int32_t dist_y;
+ __m128i vw, uvw;
+ int i;
+
+ fx = info->x;
+ ux = iter->image->transform->matrix[0][0];
+
+ y0 = pixman_fixed_to_int (info->y);
+ if (y0 < 0)
+ *(volatile char*)0 = 9;
+ y1 = y0 + 1;
+
+ // clamping in y direction
+ if (y1 >= iter->height) {
+ y1 = iter->height - 1;
+ }
+
+ line0 = &info->lines[y0 & 0x01];
+ line1 = &info->lines[y1 & 0x01];
+
+ if (line0->y != y0)
+ {
+ ssse3_fetch_horizontal (
+ iter->image, line0, y0, fx, ux, iter->width);
+ }
+
+ if (line1->y != y1)
+ {
+ ssse3_fetch_horizontal (
+ iter->image, line1, y1, fx, ux, iter->width);
+ }
+
+#ifdef PIXMAN_STYLE_INTERPOLATION
+ dist_y = pixman_fixed_to_bilinear_weight (info->y);
+ dist_y <<= (16 - BILINEAR_INTERPOLATION_BITS);
+
+ vw = _mm_set_epi16 (
+ dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y);
+
+#else
+ // setup the weights for the top (vw) and bottom (uvw) lines
+ dist_y = pixman_fixed_to_bilinear_weight (info->y);
+ // we use 15 instead of 16 because we need an extra bit to handle when the weights are 0 and 1
+ dist_y <<= (15 - BILINEAR_INTERPOLATION_BITS);
+
+ vw = _mm_set_epi16 (
+ dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y);
+
+
+ dist_y = (1 << BILINEAR_INTERPOLATION_BITS) - pixman_fixed_to_bilinear_weight (info->y);
+ dist_y <<= (15 - BILINEAR_INTERPOLATION_BITS);
+ uvw = _mm_set_epi16 (
+ dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y);
+#endif
+
+ for (i = 0; i + 3 < iter->width; i += 4)
+ {
+ __m128i top0 = _mm_load_si128 ((__m128i *)(line0->buffer + i));
+ __m128i bot0 = _mm_load_si128 ((__m128i *)(line1->buffer + i));
+ __m128i top1 = _mm_load_si128 ((__m128i *)(line0->buffer + i + 2));
+ __m128i bot1 = _mm_load_si128 ((__m128i *)(line1->buffer + i + 2));
+#ifdef PIXMAN_STYLE_INTERPOLATION
+ __m128i r0, r1, tmp, p;
+
+ r0 = _mm_mulhi_epu16 (
+ _mm_sub_epi16 (bot0, top0), vw);
+ tmp = _mm_cmplt_epi16 (bot0, top0);
+ tmp = _mm_and_si128 (tmp, vw);
+ r0 = _mm_sub_epi16 (r0, tmp);
+ r0 = _mm_add_epi16 (r0, top0);
+ r0 = _mm_srli_epi16 (r0, BILINEAR_INTERPOLATION_BITS);
+ /* r0: A0 R0 A1 R1 G0 B0 G1 B1 */
+ //r0 = _mm_shuffle_epi32 (r0, _MM_SHUFFLE (2, 0, 3, 1));
+ /* r0: A1 R1 G1 B1 A0 R0 G0 B0 */
+
+ // tmp = bot1 < top1 ? vw : 0;
+ // r1 = (bot1 - top1)*vw + top1 - tmp
+ // r1 = bot1*vw - vw*top1 + top1 - tmp
+ // r1 = bot1*vw + top1 - vw*top1 - tmp
+ // r1 = bot1*vw + top1*(1 - vw) - tmp
+ r1 = _mm_mulhi_epu16 (
+ _mm_sub_epi16 (bot1, top1), vw);
+ tmp = _mm_cmplt_epi16 (bot1, top1);
+ tmp = _mm_and_si128 (tmp, vw);
+ r1 = _mm_sub_epi16 (r1, tmp);
+ r1 = _mm_add_epi16 (r1, top1);
+ r1 = _mm_srli_epi16 (r1, BILINEAR_INTERPOLATION_BITS);
+ //r1 = _mm_shuffle_epi32 (r1, _MM_SHUFFLE (2, 0, 3, 1));
+ /* r1: A3 R3 G3 B3 A2 R2 G2 B2 */
+#else
+ __m128i r0, r1, p;
+ top0 = _mm_mulhi_epu16 (top0, uvw);
+ bot0 = _mm_mulhi_epu16 (bot0, vw);
+ r0 = _mm_add_epi16(top0, bot0);
+ r0 = _mm_srli_epi16(r0, BILINEAR_INTERPOLATION_BITS-1);
+
+ top1 = _mm_mulhi_epu16 (top1, uvw);
+ bot1 = _mm_mulhi_epu16 (bot1, vw);
+ r1 = _mm_add_epi16(top1, bot1);
+ r1 = _mm_srli_epi16(r1, BILINEAR_INTERPOLATION_BITS-1);
+#endif
+
+ p = _mm_packus_epi16 (r0, r1);
+ _mm_storeu_si128 ((__m128i *)(iter->buffer + i), p);
+ }
+
+ while (i < iter->width)
+ {
+ __m128i top0 = _mm_load_si128 ((__m128i *)(line0->buffer + i));
+ __m128i bot0 = _mm_load_si128 ((__m128i *)(line1->buffer + i));
+
+#ifdef PIXMAN_STYLE_INTERPOLATION
+ __m128i r0, tmp, p;
+ r0 = _mm_mulhi_epu16 (
+ _mm_sub_epi16 (bot0, top0), vw);
+ tmp = _mm_cmplt_epi16 (bot0, top0);
+ tmp = _mm_and_si128 (tmp, vw);
+ r0 = _mm_sub_epi16 (r0, tmp);
+ r0 = _mm_add_epi16 (r0, top0);
+ r0 = _mm_srli_epi16 (r0, BILINEAR_INTERPOLATION_BITS);
+ /* r0: A0 R0 A1 R1 G0 B0 G1 B1 */
+ r0 = _mm_shuffle_epi32 (r0, _MM_SHUFFLE (2, 0, 3, 1));
+ /* r0: A1 R1 G1 B1 A0 R0 G0 B0 */
+#else
+ __m128i r0, p;
+ top0 = _mm_mulhi_epu16 (top0, uvw);
+ bot0 = _mm_mulhi_epu16 (bot0, vw);
+ r0 = _mm_add_epi16(top0, bot0);
+ r0 = _mm_srli_epi16(r0, BILINEAR_INTERPOLATION_BITS-1);
+#endif
+
+ p = _mm_packus_epi16 (r0, r0);
+
+ if (iter->width - i == 1)
+ {
+ *(uint32_t *)(iter->buffer + i) = _mm_cvtsi128_si32 (p);
+ i++;
+ }
+ else
+ {
+ _mm_storel_epi64 ((__m128i *)(iter->buffer + i), p);
+ i += 2;
+ }
+ }
+
+ info->y += iter->image->transform->matrix[1][1];
+
+ return iter->buffer;
+}
+
+static void
+ssse3_bilinear_cover_iter_fini (pixman_iter_t *iter)
+{
+ free (iter->data);
+}
+
+static void
+ssse3_bilinear_cover_iter_init (pixman_iter_t *iter)
+{
+ int width = iter->width;
+ bilinear_info_t *info;
+ pixman_vector_t v;
+
+ /* Reference point is the center of the pixel */
+ v.vector[0] = pixman_int_to_fixed (iter->x) + pixman_fixed_1 / 2;
+ v.vector[1] = pixman_int_to_fixed (iter->y) + pixman_fixed_1 / 2;
+ v.vector[2] = pixman_fixed_1;
+
+ if (!pixman_transform_point_3d (iter->image->transform, &v))
+ goto fail;
+
+ info = malloc (sizeof (*info) + (2 * width - 1) * sizeof (uint64_t) + 64);
+ if (!info)
+ goto fail;
+
+ info->x = v.vector[0] - pixman_fixed_1 / 2;
+ info->y = v.vector[1] - pixman_fixed_1 / 2;
+
+#define ALIGN(addr) \
+ ((void *)((((uintptr_t)(addr)) + 15) & (~15)))
+
+ /* It is safe to set the y coordinates to -1 initially
+ * because COVER_CLIP_BILINEAR ensures that we will only
+ * be asked to fetch lines in the [0, height) interval
+ */
+ info->lines[0].y = -1;
+ info->lines[0].buffer = ALIGN (&(info->data[0]));
+ info->lines[1].y = -1;
+ info->lines[1].buffer = ALIGN (info->lines[0].buffer + width);
+
+ iter->fini = ssse3_bilinear_cover_iter_fini;
+
+ iter->data = info;
+ return;
+
+fail:
+ /* Something went wrong, either a bad matrix or OOM; in such cases,
+ * we don't guarantee any particular rendering.
+ */
+ iter->fini = NULL;
+}
+
+/* scale the src from src_width/height to dest_width/height drawn
+ * into the rectangle x,y width,height
+ * src_stride and dst_stride are 4 byte units */
+void ssse3_scale_data(uint32_t *src, int src_width, int src_height, int src_stride,
+ uint32_t *dest, int dest_width, int dest_height,
+ int dest_stride,
+ int x, int y,
+ int width, int height)
+{
+ //XXX: assert(src_width > 1)
+ pixman_transform_t transform = {
+ { { pixman_fixed_1, 0, 0 },
+ { 0, pixman_fixed_1, 0 },
+ { 0, 0, pixman_fixed_1 } }
+ };
+ double width_scale = ((double)src_width)/dest_width;
+ double height_scale = ((double)src_height)/dest_height;
+#define AVOID_PADDING
+#ifdef AVOID_PADDING
+ // scale up by enough that we don't read outside of the bounds of the source surface
+ // currently this is required to avoid reading out of bounds.
+ if (width_scale < 1) {
+ width_scale = (double)(src_width-1)/dest_width;
+ transform.matrix[0][2] = pixman_fixed_1/2;
+ }
+ if (height_scale < 1) {
+ height_scale = (double)(src_height-1)/dest_height;
+ transform.matrix[1][2] = pixman_fixed_1/2;
+ }
+#endif
+ transform.matrix[0][0] = pixman_double_to_fixed(width_scale);
+ transform.matrix[1][1] = pixman_double_to_fixed(height_scale);
+ transform.matrix[2][2] = pixman_fixed_1;
+
+ bits_image_t image;
+ image.bits = src;
+ image.transform = &transform;
+ image.rowstride = src_stride;
+
+ pixman_iter_t iter;
+ iter.image = &image;
+ iter.x = x;
+ iter.y = y;
+ iter.width = width;
+ iter.height = src_height;
+ iter.buffer = dest;
+ iter.data = NULL;
+
+ ssse3_bilinear_cover_iter_init(&iter);
+ if (iter.data) {
+ for (int iy = 0; iy < height; iy++) {
+ ssse3_fetch_bilinear_cover(&iter, NULL);
+ iter.buffer += dest_stride;
+ }
+ ssse3_bilinear_cover_iter_fini(&iter);
+ }
+}
diff --git a/gfx/2d/ssse3-scaler.h b/gfx/2d/ssse3-scaler.h
new file mode 100644
index 000000000..b3b53ed64
--- /dev/null
+++ b/gfx/2d/ssse3-scaler.h
@@ -0,0 +1,22 @@
+#/* -*- 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_SSSE3_SCALER_H_
+#define MOZILLA_GFX_2D_SSSE3_SCALER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void ssse3_scale_data(uint32_t *src, int src_width, int src_height,
+ int src_stride,
+ uint32_t *dest, int dest_width, int dest_height,
+ int dest_rowstride,
+ int x, int y,
+ int width, int height);
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MOZILLA_GFX_2D_SSS3_SCALER_H_
diff --git a/gfx/2d/u16string.h b/gfx/2d/u16string.h
new file mode 100644
index 000000000..61380ee6b
--- /dev/null
+++ b/gfx/2d/u16string.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_u16string_h
+#define mozilla_gfx_u16string_h
+
+#include <string>
+
+#include "mozilla/Char16.h"
+
+namespace mozilla {
+
+#if defined(_MSC_VER)
+typedef std::u16string u16string;
+#else
+typedef std::basic_string<char16_t> u16string;
+#endif
+
+} // mozilla
+
+#endif // mozilla_gfx_u16string_h
diff --git a/gfx/2d/unittest/Main.cpp b/gfx/2d/unittest/Main.cpp
new file mode 100644
index 000000000..46a1af5b6
--- /dev/null
+++ b/gfx/2d/unittest/Main.cpp
@@ -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/. */
+
+#include "SanityChecks.h"
+#include "TestPoint.h"
+#include "TestScaling.h"
+#include "TestBugs.h"
+#ifdef WIN32
+#include "TestDrawTargetD2D.h"
+#endif
+
+#include <string>
+#include <sstream>
+
+struct TestObject {
+ TestBase *test;
+ std::string name;
+};
+
+
+using namespace std;
+
+int
+main()
+{
+ TestObject tests[] =
+ {
+ { new SanityChecks(), "Sanity Checks" },
+ #ifdef WIN32
+ { new TestDrawTargetD2D(), "DrawTarget (D2D)" },
+ #endif
+ { new TestPoint(), "Point Tests" },
+ { new TestScaling(), "Scaling Tests" }
+ { new TestBugs(), "Bug Tests" }
+ };
+
+ int totalFailures = 0;
+ int totalTests = 0;
+ stringstream message;
+ printf("------ STARTING RUNNING TESTS ------\n");
+ for (int i = 0; i < sizeof(tests) / sizeof(TestObject); i++) {
+ message << "--- RUNNING TESTS: " << tests[i].name << " ---\n";
+ printf(message.str().c_str());
+ message.str("");
+ int failures = 0;
+ totalTests += tests[i].test->RunTests(&failures);
+ totalFailures += failures;
+ // Done with this test!
+ delete tests[i].test;
+ }
+ message << "------ FINISHED RUNNING TESTS ------\nTests run: " << totalTests << " - Passes: " << totalTests - totalFailures << " - Failures: " << totalFailures << "\n";
+ printf(message.str().c_str());
+ return totalFailures;
+}
diff --git a/gfx/2d/unittest/SanityChecks.cpp b/gfx/2d/unittest/SanityChecks.cpp
new file mode 100644
index 000000000..7f109facd
--- /dev/null
+++ b/gfx/2d/unittest/SanityChecks.cpp
@@ -0,0 +1,19 @@
+/* -*- 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 "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
new file mode 100644
index 000000000..f2a4a0b59
--- /dev/null
+++ b/gfx/2d/unittest/SanityChecks.h
@@ -0,0 +1,16 @@
+/* -*- 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 SanityChecks : public TestBase
+{
+public:
+ SanityChecks();
+
+ void AlwaysPasses();
+};
diff --git a/gfx/2d/unittest/TestBase.cpp b/gfx/2d/unittest/TestBase.cpp
new file mode 100644
index 000000000..5818a7299
--- /dev/null
+++ b/gfx/2d/unittest/TestBase.cpp
@@ -0,0 +1,48 @@
+/* -*- 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 "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
new file mode 100644
index 000000000..a57d6a730
--- /dev/null
+++ b/gfx/2d/unittest/TestBase.h
@@ -0,0 +1,54 @@
+/* -*- 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 <string>
+#include <vector>
+
+#ifdef _MSC_VER
+// On MSVC otherwise our generic member pointer trick doesn't work.
+#pragma pointers_to_members(full_generality, single_inheritance)
+#endif
+
+#define VERIFY(arg) if (!(arg)) { \
+ LogMessage("VERIFY FAILED: "#arg"\n"); \
+ mTestFailed = true; \
+ }
+
+#define REGISTER_TEST(className, testName) \
+ mTests.push_back(Test(static_cast<TestCall>(&className::testName), #testName, this))
+
+class TestBase
+{
+public:
+ TestBase() {}
+
+ typedef void (TestBase::*TestCall)();
+
+ int RunTests(int *aFailures);
+
+protected:
+ static void LogMessage(std::string aMessage);
+
+ struct Test {
+ Test(TestCall aCall, std::string aName, void *aImplPointer)
+ : funcCall(aCall)
+ , name(aName)
+ , implPointer(aImplPointer)
+ {
+ }
+ TestCall funcCall;
+ std::string name;
+ void *implPointer;
+ };
+ std::vector<Test> mTests;
+
+ bool mTestFailed;
+
+private:
+ // This doesn't really work with our generic member pointer trick.
+ TestBase(const TestBase &aOther);
+};
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/TestCairo.cpp b/gfx/2d/unittest/TestCairo.cpp
new file mode 100644
index 000000000..7df604695
--- /dev/null
+++ b/gfx/2d/unittest/TestCairo.cpp
@@ -0,0 +1,96 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "cairo.h"
+
+#include "gtest/gtest.h"
+
+namespace mozilla {
+namespace layers {
+
+void TryCircle(double centerX, double centerY, double radius) {
+ printf("TestCairo:TryArcs centerY %f, radius %f\n",centerY,radius);
+
+ cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,8,21);
+ ASSERT_TRUE(surf != nullptr);
+
+ cairo_t *cairo = cairo_create(surf);
+ ASSERT_TRUE(cairo != nullptr);
+
+ cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
+ cairo_arc(cairo, 0.0, centerY, radius, 0.0, 6.2831853071795862);
+ cairo_fill_preserve(cairo);
+
+ cairo_surface_destroy(surf);
+ cairo_destroy(cairo);
+}
+
+TEST(Cairo, Simple) {
+ TryCircle(0.0, 0.0, 14.0);
+ TryCircle(0.0, 1.0, 22.4);
+ TryCircle(1.0, 0.0, 1422.4);
+ TryCircle(1.0, 1.0, 3422.4);
+ TryCircle(-10.0, 1.0, -2);
+}
+
+TEST(Cairo, Bug825721) {
+ // OK:
+ TryCircle(0.0, 0.0, 8761126469220696064.0);
+ TryCircle(0.0, 1.0, 8761126469220696064.0);
+
+ // OK:
+ TryCircle(1.0, 0.0, 5761126469220696064.0);
+
+ // This was the crash in 825721. Note that centerY has to be non-zero,
+ // and radius has to be not only large, but in particular range.
+ // 825721 has a band-aid fix, where the crash is inevitable, but does
+ // not fix the cause. The same code crashes in cairo standalone.
+ TryCircle(0.0, 1.0, 5761126469220696064.0);
+}
+
+TEST(Cairo, Bug1063486) {
+
+ double x1, y1, x2, y2;
+ const double epsilon = .01;
+
+ cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
+ ASSERT_TRUE(surf != nullptr);
+
+ cairo_t *cairo = cairo_create(surf);
+ ASSERT_TRUE(cairo != nullptr);
+
+ printf("Path 1\n");
+ cairo_move_to(cairo, -20, -10);
+ cairo_line_to(cairo, 20, -10);
+ cairo_line_to(cairo, 20, 10);
+ cairo_curve_to(cairo, 10,10, -10,10, -20,10);
+ cairo_curve_to(cairo, -30,10, -30,-10, -20,-10);
+
+ cairo_path_extents(cairo, &x1, &y1, &x2, &y2);
+
+ ASSERT_LT(std::abs(-27.5 - x1), epsilon); // the failing coordinate
+ ASSERT_LT(std::abs(-10 - y1), epsilon);
+ ASSERT_LT(std::abs(20 - x2), epsilon);
+ ASSERT_LT(std::abs(10 - y2), epsilon);
+
+ printf("Path 2\n");
+ cairo_new_path(cairo);
+ cairo_move_to(cairo, 10, 30);
+ cairo_line_to(cairo, 90, 30);
+ cairo_curve_to(cairo, 30,30, 30,30, 10,30);
+ cairo_curve_to(cairo, 0,30, 0,0, 30,5);
+
+ cairo_path_extents(cairo, &x1, &y1, &x2, &y2);
+
+ ASSERT_LT(std::abs(4.019531 - x1), epsilon); // the failing coordinate
+ ASSERT_LT(std::abs(4.437500 - y1), epsilon);
+ ASSERT_LT(std::abs(90. - x2), epsilon);
+ ASSERT_LT(std::abs(30. - y2), epsilon);
+
+ cairo_surface_destroy(surf);
+ cairo_destroy(cairo);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/2d/unittest/TestDrawTargetBase.cpp b/gfx/2d/unittest/TestDrawTargetBase.cpp
new file mode 100644
index 000000000..2a0d95ed6
--- /dev/null
+++ b/gfx/2d/unittest/TestDrawTargetBase.cpp
@@ -0,0 +1,109 @@
+/* -*- 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 "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
new file mode 100644
index 000000000..06a62413f
--- /dev/null
+++ b/gfx/2d/unittest/TestDrawTargetBase.h
@@ -0,0 +1,38 @@
+/* -*- 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 "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);
+
+ RefPtr<mozilla::gfx::DrawTarget> mDT;
+ RefPtr<mozilla::gfx::DataSourceSurface> mDataSnapshot;
+};
diff --git a/gfx/2d/unittest/TestDrawTargetD2D.cpp b/gfx/2d/unittest/TestDrawTargetD2D.cpp
new file mode 100644
index 000000000..0715f93d8
--- /dev/null
+++ b/gfx/2d/unittest/TestDrawTargetD2D.cpp
@@ -0,0 +1,23 @@
+/* -*- 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 "TestDrawTargetD2D.h"
+
+using namespace mozilla::gfx;
+TestDrawTargetD2D::TestDrawTargetD2D()
+{
+ ::D3D10CreateDevice1(nullptr,
+ D3D10_DRIVER_TYPE_HARDWARE,
+ nullptr,
+ D3D10_CREATE_DEVICE_BGRA_SUPPORT |
+ D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+ D3D10_FEATURE_LEVEL_10_0,
+ D3D10_1_SDK_VERSION,
+ getter_AddRefs(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
new file mode 100644
index 000000000..97bb88cf2
--- /dev/null
+++ b/gfx/2d/unittest/TestDrawTargetD2D.h
@@ -0,0 +1,19 @@
+/* -*- 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 "TestDrawTargetBase.h"
+
+#include <d3d10_1.h>
+
+class TestDrawTargetD2D : public TestDrawTargetBase
+{
+public:
+ TestDrawTargetD2D();
+
+private:
+ RefPtr<ID3D10Device1> mDevice;
+};
diff --git a/gfx/2d/unittest/TestPoint.cpp b/gfx/2d/unittest/TestPoint.cpp
new file mode 100644
index 000000000..6aa2b6a35
--- /dev/null
+++ b/gfx/2d/unittest/TestPoint.cpp
@@ -0,0 +1,46 @@
+/* -*- 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 "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
new file mode 100644
index 000000000..813d66c2f
--- /dev/null
+++ b/gfx/2d/unittest/TestPoint.h
@@ -0,0 +1,17 @@
+/* -*- 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 TestPoint : public TestBase
+{
+public:
+ TestPoint();
+
+ void Addition();
+ void Subtraction();
+};
diff --git a/gfx/2d/unittest/TestScaling.cpp b/gfx/2d/unittest/TestScaling.cpp
new file mode 100644
index 000000000..60e1acbf0
--- /dev/null
+++ b/gfx/2d/unittest/TestScaling.cpp
@@ -0,0 +1,249 @@
+/* -*- 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 "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
new file mode 100644
index 000000000..e9bd1a8e0
--- /dev/null
+++ b/gfx/2d/unittest/TestScaling.h
@@ -0,0 +1,22 @@
+/* -*- 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 TestScaling : public TestBase
+{
+public:
+ TestScaling();
+
+ void BasicHalfScale();
+ void DoubleHalfScale();
+ void UnevenHalfScale();
+ void OddStrideHalfScale();
+ void VerticalHalfScale();
+ void HorizontalHalfScale();
+ void MixedHalfScale();
+};
diff --git a/gfx/2d/unittest/unittest.vcxproj b/gfx/2d/unittest/unittest.vcxproj
new file mode 100644
index 000000000..7ddf92530
--- /dev/null
+++ b/gfx/2d/unittest/unittest.vcxproj
@@ -0,0 +1,94 @@
+<?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">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{CCF4BC8B-0CED-47CA-B621-ABF1832527D9}</ProjectGuid>
+ <RootNamespace>unittest</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LibraryPath>$(DXSDK_DIR)\Lib\x86;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib</LibraryPath>
+ <IncludePath>$(ProjectDir)..\;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LibraryPath>$(DXSDK_DIR)\Lib\x86;$(VCInstallDir)lib;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib</LibraryPath>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>../$(Configuration)/gfx2d.lib;dxguid.lib;d3d10_1.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>../</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalDependencies>../$(Configuration)/gfx2d.lib;dxguid.lib;d3d10_1.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="Main.cpp" />
+ <ClCompile Include="SanityChecks.cpp" />
+ <ClCompile Include="TestBase.cpp" />
+ <ClCompile Include="TestDrawTargetBase.cpp" />
+ <ClCompile Include="TestDrawTargetD2D.cpp" />
+ <ClCompile Include="TestPoint.cpp" />
+ <ClCompile Include="TestScaling.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="TestDrawTargetBase.h" />
+ <ClInclude Include="SanityChecks.h" />
+ <ClInclude Include="TestBase.h" />
+ <ClInclude Include="TestDrawTargetD2D.h" />
+ <ClInclude Include="TestPoint.h" />
+ <ClInclude Include="TestScaling.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file