diff options
Diffstat (limited to 'gfx/2d')
151 files changed, 31737 insertions, 8736 deletions
diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index c1625e418..b1e0e3e7b 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -11,11 +11,20 @@ #include "Rect.h" #include "Matrix.h" #include "UserData.h" + +// GenericRefCountedBase allows us to hold on to refcounted objects of any type +// (contrary to RefCounted<T> which requires knowing the type T) and, in particular, +// without having a dependency on that type. This is used for DrawTargetSkia +// to be able to hold on to a GLContext. +#include "mozilla/GenericRefCounted.h" + // This RefPtr class isn't ideal for usage in Azure, as it doesn't allow T** // outparams using the &-operator. But it will have to do as there's no easy // solution. #include "mozilla/RefPtr.h" +#include "mozilla/DebugOnly.h" + #ifdef MOZ_ENABLE_FREETYPE #include <string> #endif @@ -28,9 +37,16 @@ typedef _cairo_scaled_font cairo_scaled_font_t; struct ID3D10Device1; struct ID3D10Texture2D; +struct ID3D11Texture2D; +struct ID3D11Device; +struct ID2D1Device; struct IDWriteRenderingParams; class GrContext; +struct GrGLInterface; + +struct CGContext; +typedef struct CGContext *CGContextRef; namespace mozilla { @@ -40,10 +56,13 @@ class SourceSurface; class DataSourceSurface; class DrawTarget; class DrawEventRecorder; +class FilterNode; +class LogForwarder; struct NativeSurface { NativeSurfaceType mType; SurfaceFormat mFormat; + gfx::IntSize mSize; void *mSurface; }; @@ -52,58 +71,41 @@ struct NativeFont { void *mFont; }; -/* +/** * This structure is used to send draw options that are universal to all drawing - * operations. It consists of the following: - * - * mAlpha - Alpha value by which the mask generated by this operation is - * multiplied. - * mCompositionOp - The operator that indicates how the source and destination - * patterns are blended. - * mAntiAliasMode - The AntiAlias mode used for this drawing operation. - * mSnapping - Whether this operation is snapped to pixel boundaries. + * operations. */ struct DrawOptions { - DrawOptions(Float aAlpha = 1.0f, - CompositionOp aCompositionOp = OP_OVER, - AntialiasMode aAntialiasMode = AA_DEFAULT, - Snapping aSnapping = SNAP_NONE) + /// For constructor parameter description, see member data documentation. + explicit DrawOptions(Float aAlpha = 1.0f, + CompositionOp aCompositionOp = CompositionOp::OP_OVER, + AntialiasMode aAntialiasMode = AntialiasMode::DEFAULT) : mAlpha(aAlpha) , mCompositionOp(aCompositionOp) , mAntialiasMode(aAntialiasMode) - , mSnapping(aSnapping) {} - Float mAlpha; - CompositionOp mCompositionOp : 8; - AntialiasMode mAntialiasMode : 3; - Snapping mSnapping : 1; + Float mAlpha; /**< Alpha value by which the mask generated by this + operation is multiplied. */ + CompositionOp mCompositionOp; /**< The operator that indicates how the source and + destination patterns are blended. */ + AntialiasMode mAntialiasMode; /**< The AntiAlias mode used for this drawing + operation. */ }; -/* +/** * This structure is used to send stroke options that are used in stroking - * operations. It consists of the following: - * - * mLineWidth - Width of the stroke in userspace. - * mLineJoin - Join style used for joining lines. - * mLineCap - Cap style used for capping lines. - * mMiterLimit - Miter limit in units of linewidth - * mDashPattern - Series of on/off userspace lengths defining dash. - * Owned by the caller; must live at least as long as - * this StrokeOptions. - * mDashPattern != null <=> mDashLength > 0. - * mDashLength - Number of on/off lengths in mDashPattern. - * mDashOffset - Userspace offset within mDashPattern at which stroking - * begins. + * operations. */ struct StrokeOptions { - StrokeOptions(Float aLineWidth = 1.0f, - JoinStyle aLineJoin = JOIN_MITER_OR_BEVEL, - CapStyle aLineCap = CAP_BUTT, - Float aMiterLimit = 10.0f, - size_t aDashLength = 0, - const Float* aDashPattern = 0, - Float aDashOffset = 0.f) + /// For constructor parameter description, see member data documentation. + explicit StrokeOptions(Float aLineWidth = 1.0f, + JoinStyle aLineJoin = JoinStyle::MITER_OR_BEVEL, + CapStyle aLineCap = CapStyle::BUTT, + Float aMiterLimit = 10.0f, + size_t aDashLength = 0, + const Float* aDashPattern = 0, + Float aDashOffset = 0.f) : mLineWidth(aLineWidth) , mMiterLimit(aMiterLimit) , mDashPattern(aDashLength > 0 ? aDashPattern : 0) @@ -115,36 +117,40 @@ struct StrokeOptions { MOZ_ASSERT(aDashLength == 0 || aDashPattern); } - Float mLineWidth; - Float mMiterLimit; - const Float* mDashPattern; - size_t mDashLength; - Float mDashOffset; - JoinStyle mLineJoin : 4; - CapStyle mLineCap : 3; + Float mLineWidth; //!< Width of the stroke in userspace. + Float mMiterLimit; //!< Miter limit in units of linewidth + const Float* mDashPattern; /**< Series of on/off userspace lengths defining dash. + Owned by the caller; must live at least as long as + this StrokeOptions. + mDashPattern != null <=> mDashLength > 0. */ + size_t mDashLength; //!< Number of on/off lengths in mDashPattern. + Float mDashOffset; /**< Userspace offset within mDashPattern at which + stroking begins. */ + JoinStyle mLineJoin; //!< Join style used for joining lines. + CapStyle mLineCap; //!< Cap style used for capping lines. }; -/* +/** * This structure supplies additional options for calls to DrawSurface. - * - * mFilter - Filter used when resampling source surface region to the - * destination region. - * aSamplingBounds - This indicates whether the implementation is allowed - * to sample pixels outside the source rectangle as - * specified in DrawSurface on the surface. */ struct DrawSurfaceOptions { - DrawSurfaceOptions(Filter aFilter = FILTER_LINEAR, - SamplingBounds aSamplingBounds = SAMPLING_UNBOUNDED) + /// For constructor parameter description, see member data documentation. + explicit DrawSurfaceOptions(Filter aFilter = Filter::LINEAR, + SamplingBounds aSamplingBounds = SamplingBounds::UNBOUNDED) : mFilter(aFilter) , mSamplingBounds(aSamplingBounds) { } - Filter mFilter : 3; - SamplingBounds mSamplingBounds : 1; + Filter mFilter; /**< Filter used when resampling source surface + region to the destination region. */ + SamplingBounds mSamplingBounds; /**< This indicates whether the implementation is + allowed to sample pixels outside the source + rectangle as specified in DrawSurface on + the surface. */ + }; -/* +/** * This class is used to store gradient stops, it can only be used with a * matching DrawTarget. Not adhering to this condition will make a draw call * fail. @@ -152,15 +158,17 @@ struct DrawSurfaceOptions { class GradientStops : public RefCounted<GradientStops> { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStops) virtual ~GradientStops() {} virtual BackendType GetBackendType() const = 0; + virtual bool IsValid() const { return true; } protected: GradientStops() {} }; -/* +/** * This is the base class for 'patterns'. Patterns describe the pixels used as * the source for a masked composition operation that is done by the different * drawing commands. These objects are not backend specific, however for @@ -180,16 +188,21 @@ protected: class ColorPattern : public Pattern { public: - ColorPattern(const Color &aColor) + // Explicit because consumers should generally use ToDeviceColor when + // creating a ColorPattern. + explicit ColorPattern(const Color &aColor) : mColor(aColor) {} - virtual PatternType GetType() const { return PATTERN_COLOR; } + virtual PatternType GetType() const override + { + return PatternType::COLOR; + } Color mColor; }; -/* +/** * This class is used for Linear Gradient Patterns, the gradient stops are * stored in a separate object and are backend dependent. This class itself * may be used on the stack. @@ -197,14 +210,7 @@ public: class LinearGradientPattern : public Pattern { public: - /* - * aBegin Start of the linear gradient - * aEnd End of the linear gradient - NOTE: In the case of a zero length - * gradient it will act as the color of the last stop. - * aStops GradientStops object for this gradient, this should match the - * backend type of the draw target this pattern will be used with. - * aMatrix A matrix that transforms the pattern into user space - */ + /// For constructor parameter description, see member data documentation. LinearGradientPattern(const Point &aBegin, const Point &aEnd, GradientStops *aStops, @@ -216,15 +222,23 @@ public: { } - virtual PatternType GetType() const { return PATTERN_LINEAR_GRADIENT; } + virtual PatternType GetType() const override + { + return PatternType::LINEAR_GRADIENT; + } - Point mBegin; - Point mEnd; - RefPtr<GradientStops> mStops; - Matrix mMatrix; + Point mBegin; //!< Start of the linear gradient + Point mEnd; /**< End of the linear gradient - NOTE: In the case + of a zero length gradient it will act as the + color of the last stop. */ + RefPtr<GradientStops> mStops; /**< GradientStops object for this gradient, this + should match the backend type of the draw + target this pattern will be used with. */ + Matrix mMatrix; /**< A matrix that transforms the pattern into + user space */ }; -/* +/** * This class is used for Radial Gradient Patterns, the gradient stops are * stored in a separate object and are backend dependent. This class itself * may be used on the stack. @@ -232,13 +246,7 @@ public: class RadialGradientPattern : public Pattern { public: - /* - * aBegin Start of the linear gradient - * aEnd End of the linear gradient - * aStops GradientStops object for this gradient, this should match the - * backend type of the draw target this pattern will be used with. - * aMatrix A matrix that transforms the pattern into user space - */ + /// For constructor parameter description, see member data documentation. RadialGradientPattern(const Point &aCenter1, const Point &aCenter2, Float aRadius1, @@ -254,160 +262,243 @@ public: { } - virtual PatternType GetType() const { return PATTERN_RADIAL_GRADIENT; } + virtual PatternType GetType() const override + { + return PatternType::RADIAL_GRADIENT; + } - Point mCenter1; - Point mCenter2; - Float mRadius1; - Float mRadius2; - RefPtr<GradientStops> mStops; - Matrix mMatrix; + Point mCenter1; //!< Center of the inner (focal) circle. + Point mCenter2; //!< Center of the outer circle. + Float mRadius1; //!< Radius of the inner (focal) circle. + Float mRadius2; //!< Radius of the outer circle. + RefPtr<GradientStops> mStops; /**< GradientStops object for this gradient, this + should match the backend type of the draw target + this pattern will be used with. */ + Matrix mMatrix; //!< A matrix that transforms the pattern into user space }; -/* +/** * This class is used for Surface Patterns, they wrap a surface and a * repetition mode for the surface. This may be used on the stack. */ class SurfacePattern : public Pattern { public: - /* - * aSourceSurface Surface to use for drawing - * aExtendMode This determines how the image is extended outside the bounds - * of the image. - * aMatrix A matrix that transforms the pattern into user space - * aFilter Resampling filter used for resampling the image. - */ + /// For constructor parameter description, see member data documentation. SurfacePattern(SourceSurface *aSourceSurface, ExtendMode aExtendMode, - const Matrix &aMatrix = Matrix(), Filter aFilter = FILTER_LINEAR) + const Matrix &aMatrix = Matrix(), Filter aFilter = Filter::GOOD, + const IntRect &aSamplingRect = IntRect()) : mSurface(aSourceSurface) , mExtendMode(aExtendMode) , mFilter(aFilter) , mMatrix(aMatrix) + , mSamplingRect(aSamplingRect) {} - virtual PatternType GetType() const { return PATTERN_SURFACE; } + virtual PatternType GetType() const override + { + return PatternType::SURFACE; + } + + RefPtr<SourceSurface> mSurface; //!< Surface to use for drawing + ExtendMode mExtendMode; /**< This determines how the image is extended + outside the bounds of the image */ + Filter mFilter; //!< Resampling filter for resampling the image. + Matrix mMatrix; //!< Transforms the pattern into user space - RefPtr<SourceSurface> mSurface; - ExtendMode mExtendMode; - Filter mFilter; - Matrix mMatrix; + IntRect mSamplingRect; /**< Rect that must not be sampled outside of, + or an empty rect if none has been specified. */ }; -/* +class StoredPattern; +class DrawTargetCaptureImpl; + +/** * This is the base class for source surfaces. These objects are surfaces - * which may be used as a source in a SurfacePattern of a DrawSurface call. + * which may be used as a source in a SurfacePattern or a DrawSurface call. * They cannot be drawn to directly. */ class SourceSurface : public RefCounted<SourceSurface> { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurface) virtual ~SourceSurface() {} virtual SurfaceType GetType() const = 0; virtual IntSize GetSize() const = 0; virtual SurfaceFormat GetFormat() const = 0; - /* This returns false if some event has made this source surface invalid for + /** This returns false if some event has made this source surface invalid for * usage with current DrawTargets. For example in the case of Direct2D this * could return false if we have switched devices since this surface was * created. */ virtual bool IsValid() const { return true; } - /* + /** * This function will get a DataSourceSurface for this surface, a * DataSourceSurface's data can be accessed directly. */ virtual TemporaryRef<DataSourceSurface> GetDataSurface() = 0; + + /** Tries to get this SourceSurface's native surface. This will fail if aType + * is not the type of this SourceSurface's native surface. + */ + virtual void *GetNativeSurface(NativeSurfaceType aType) { + return nullptr; + } + + void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) { + mUserData.Add(key, userData, destroy); + } + void *GetUserData(UserDataKey *key) { + return mUserData.Get(key); + } + +protected: + friend class DrawTargetCaptureImpl; + friend class StoredPattern; + + // This is for internal use, it ensures the SourceSurface's data remains + // valid during the lifetime of the SourceSurface. + // @todo XXX - We need something better here :(. But we may be able to get rid + // of CreateWrappingDataSourceSurface in the future. + virtual void GuaranteePersistance() {} + + UserData mUserData; }; class DataSourceSurface : public SourceSurface { public: - virtual SurfaceType GetType() const { return SURFACE_DATA; } - /* + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurface, override) + DataSourceSurface() + : mIsMapped(false) + { + } + +#ifdef DEBUG + virtual ~DataSourceSurface() + { + MOZ_ASSERT(!mIsMapped, "Someone forgot to call Unmap()"); + } +#endif + + struct MappedSurface { + uint8_t *mData; + int32_t mStride; + }; + + enum MapType { + READ, + WRITE, + READ_WRITE + }; + + virtual SurfaceType GetType() const override { return SurfaceType::DATA; } + /** @deprecated * Get the raw bitmap data of the surface. * Can return null if there was OOM allocating surface data. */ virtual uint8_t *GetData() = 0; - /* + /** @deprecated * Stride of the surface, distance in bytes between the start of the image * data belonging to row y and row y+1. This may be negative. * Can return 0 if there was OOM allocating surface data. */ virtual int32_t Stride() = 0; - /* - * This function is called after modifying the data on the source surface - * directly through the data pointer. + /** + * The caller is responsible for ensuring aMappedSurface is not null. */ - virtual void MarkDirty() {} + virtual bool Map(MapType, MappedSurface *aMappedSurface) + { + aMappedSurface->mData = GetData(); + aMappedSurface->mStride = Stride(); + mIsMapped = !!aMappedSurface->mData; + return mIsMapped; + } - virtual TemporaryRef<DataSourceSurface> GetDataSurface() { RefPtr<DataSourceSurface> temp = this; return temp.forget(); } + virtual void Unmap() + { + MOZ_ASSERT(mIsMapped); + mIsMapped = false; + } + + /** + * Returns a DataSourceSurface with the same data as this one, but + * guaranteed to have surface->GetType() == SurfaceType::DATA. + */ + virtual TemporaryRef<DataSourceSurface> GetDataSurface() override; + +protected: + bool mIsMapped; }; -/* This is an abstract object that accepts path segments. */ +/** This is an abstract object that accepts path segments. */ class PathSink : public RefCounted<PathSink> { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathSink) virtual ~PathSink() {} - /* Move the current point in the path, any figure currently being drawn will + /** Move the current point in the path, any figure currently being drawn will * be considered closed during fill operations, however when stroking the * closing line segment will not be drawn. */ virtual void MoveTo(const Point &aPoint) = 0; - /* Add a linesegment to the current figure */ + /** Add a linesegment to the current figure */ virtual void LineTo(const Point &aPoint) = 0; - /* Add a cubic bezier curve to the current figure */ + /** Add a cubic bezier curve to the current figure */ virtual void BezierTo(const Point &aCP1, const Point &aCP2, const Point &aCP3) = 0; - /* Add a quadratic bezier curve to the current figure */ + /** Add a quadratic bezier curve to the current figure */ virtual void QuadraticBezierTo(const Point &aCP1, const Point &aCP2) = 0; - /* Close the current figure, this will essentially generate a line segment + /** Close the current figure, this will essentially generate a line segment * from the current point to the starting point for the current figure */ virtual void Close() = 0; - /* Add an arc to the current figure */ + /** Add an arc to the current figure */ virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle, float aEndAngle, bool aAntiClockwise = false) = 0; - /* Point the current subpath is at - or where the next subpath will start + /** Point the current subpath is at - or where the next subpath will start * if there is no active subpath. */ virtual Point CurrentPoint() const = 0; }; class PathBuilder; +class FlattenedPath; -/* The path class is used to create (sets of) figures of any shape that can be +/** The path class is used to create (sets of) figures of any shape that can be * filled or stroked to a DrawTarget */ class Path : public RefCounted<Path> { public: - virtual ~Path() {} + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(Path) + virtual ~Path(); virtual BackendType GetBackendType() const = 0; - /* This returns a PathBuilder object that contains a copy of the contents of + /** This returns a PathBuilder object that contains a copy of the contents of * this path and is still writable. */ - virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const = 0; + virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const = 0; virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform, - FillRule aFillRule = FILL_WINDING) const = 0; + FillRule aFillRule = FillRule::FILL_WINDING) const = 0; - /* This function checks if a point lies within a path. It allows passing a + /** This function checks if a point lies within a path. It allows passing a * transform that will transform the path to the coordinate space in which * aPoint is given. */ virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const = 0; - /* This function checks if a point lies within the stroke of a path using the + /** This function checks if a point lies within the stroke of a path using the * specified strokeoptions. It allows passing a transform that will transform * the path to the coordinate space in which aPoint is given. */ @@ -415,13 +506,13 @@ public: const Point &aPoint, const Matrix &aTransform) const = 0; - /* This functions gets the bounds of this path. These bounds are not + /** This functions gets the bounds of this path. These bounds are not * guaranteed to be tight. A transform may be specified that gives the bounds * after application of the transform. */ virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const = 0; - /* This function gets the bounds of the stroke of this path using the + /** This function gets the bounds of the stroke of this path using the * specified strokeoptions. These bounds are not guaranteed to be tight. * A transform may be specified that gives the bounds after application of * the transform. @@ -429,22 +520,41 @@ public: virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions, const Matrix &aTransform = Matrix()) const = 0; - /* This gets the fillrule this path's builder was created with. This is not + /** Take the contents of this path and stream it to another sink, this works + * regardless of the backend that might be used for the destination sink. + */ + virtual void StreamToSink(PathSink *aSink) const = 0; + + /** This gets the fillrule this path's builder was created with. This is not * mutable. */ virtual FillRule GetFillRule() const = 0; + + virtual Float ComputeLength(); + + virtual Point ComputePointAtLength(Float aLength, + Point* aTangent = nullptr); + +protected: + Path(); + void EnsureFlattenedPath(); + + RefPtr<FlattenedPath> mFlattenedPath; }; -/* The PathBuilder class allows path creation. Once finish is called on the +/** The PathBuilder class allows path creation. Once finish is called on the * pathbuilder it may no longer be written to. */ class PathBuilder : public PathSink { public: - /* Finish writing to the path and return a Path object that can be used for + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilder) + /** Finish writing to the path and return a Path object that can be used for * drawing. Future use of the builder results in a crash! */ virtual TemporaryRef<Path> Finish() = 0; + + virtual BackendType GetBackendType() const = 0; }; struct Glyph @@ -453,44 +563,43 @@ struct Glyph Point mPosition; }; -/* This class functions as a glyph buffer that can be drawn to a DrawTarget. - * XXX - This should probably contain the guts of gfxTextRun in the future as +/** This class functions as a glyph buffer that can be drawn to a DrawTarget. + * @todo XXX - This should probably contain the guts of gfxTextRun in the future as * roc suggested. But for now it's a simple container for a glyph vector. */ struct GlyphBuffer { - // A pointer to a buffer of glyphs. Managed by the caller. - const Glyph *mGlyphs; - // Number of glyphs mGlyphs points to. - uint32_t mNumGlyphs; + const Glyph *mGlyphs; //!< A pointer to a buffer of glyphs. Managed by the caller. + uint32_t mNumGlyphs; //!< Number of glyphs mGlyphs points to. }; -/* This class is an abstraction of a backend/platform specific font object +/** This class is an abstraction of a backend/platform specific font object * at a particular size. It is passed into text drawing calls to describe * the font used for the drawing call. */ class ScaledFont : public RefCounted<ScaledFont> { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFont) virtual ~ScaledFont() {} typedef void (*FontFileDataOutput)(const uint8_t *aData, uint32_t aLength, uint32_t aIndex, Float aGlyphSize, void *aBaton); virtual FontType GetType() const = 0; - /* This allows getting a path that describes the outline of a set of glyphs. + /** This allows getting a path that describes the outline of a set of glyphs. * A target is passed in so that the guarantee is made the returned path * can be used with any DrawTarget that has the same backend as the one * passed in. */ virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) = 0; - /* This copies the path describing the glyphs into a PathBuilder. We use this + /** This copies the path describing the glyphs into a PathBuilder. We use this * API rather than a generic API to append paths because it allows easier * implementation in some backends, and more efficient implementation in * others. */ - virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder) = 0; + virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint = nullptr) = 0; virtual bool GetFontFileData(FontFileDataOutput, void *) { return false; } @@ -507,21 +616,7 @@ protected: UserData mUserData; }; -#ifdef MOZ_ENABLE_FREETYPE -/** - * Describes a font - * Used to pass the key informatin from a gfxFont into Azure - * XXX Should be replaced by a more long term solution, perhaps Bug 738014 - */ -struct FontOptions -{ - std::string mName; - FontStyle mStyle; -}; -#endif - - -/* This class is designed to allow passing additional glyph rendering +/** This class is designed to allow passing additional glyph rendering * parameters to the glyph drawing functions. This is an empty wrapper class * merely used to allow holding on to and passing around platform specific * parameters. This is because different platforms have unique rendering @@ -530,6 +625,7 @@ struct FontOptions class GlyphRenderingOptions : public RefCounted<GlyphRenderingOptions> { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptions) virtual ~GlyphRenderingOptions() {} virtual FontType GetType() const = 0; @@ -538,7 +634,9 @@ protected: GlyphRenderingOptions() {} }; -/* This is the main class used for all the drawing. It is created through the +class DrawTargetCapture; + +/** This is the main class used for all the drawing. It is created through the * factory and accepts drawing commands. The results of drawing to a target * may be used either through a Snapshot or by flushing the target and directly * accessing the backing store a DrawTarget was created with. @@ -546,10 +644,13 @@ protected: class DrawTarget : public RefCounted<DrawTarget> { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTarget) DrawTarget() : mTransformDirty(false), mPermitSubpixelAA(false) {} virtual ~DrawTarget() {} - virtual BackendType GetType() const = 0; + virtual DrawTargetType GetType() const = 0; + + virtual BackendType GetBackendType() const = 0; /** * Returns a SourceSurface which is a snapshot of the current contents of the DrawTarget. * Multiple calls to Snapshot() without any drawing operations in between will @@ -558,22 +659,40 @@ public: virtual TemporaryRef<SourceSurface> Snapshot() = 0; virtual IntSize GetSize() = 0; - /* Ensure that the DrawTarget backend has flushed all drawing operations to + /** + * If possible returns the bits to this DrawTarget for direct manipulation. While + * the bits is locked any modifications to this DrawTarget is forbidden. + * Release takes the original data pointer for safety. + */ + virtual bool LockBits(uint8_t** aData, IntSize* aSize, + int32_t* aStride, SurfaceFormat* aFormat) { return false; } + virtual void ReleaseBits(uint8_t* aData) {} + + /** Ensure that the DrawTarget backend has flushed all drawing operations to * this draw target. This must be called before using the backing surface of * this draw target outside of GFX 2D code. */ virtual void Flush() = 0; - /* + /** + * Realize a DrawTargetCapture onto the draw target. + * + * @param aSource Capture DrawTarget to draw + * @param aTransform Transform to apply when replaying commands + */ + virtual void DrawCapturedDT(DrawTargetCapture *aCaptureDT, + const Matrix& aTransform); + + /** * Draw a surface to the draw target. Possibly doing partial drawing or * applying scaling. No sampling happens outside the source. * - * aSurface Source surface to draw - * aDest Destination rectangle that this drawing operation should draw to - * aSource Source rectangle in aSurface coordinates, this area of aSurface - * will be stretched to the size of aDest. - * aOptions General draw options that are applied to the operation - * aSurfOptions DrawSurface options that are applied + * @param aSurface Source surface to draw + * @param aDest Destination rectangle that this drawing operation should draw to + * @param aSource Source rectangle in aSurface coordinates, this area of aSurface + * will be stretched to the size of aDest. + * @param aOptions General draw options that are applied to the operation + * @param aSurfOptions DrawSurface options that are applied */ virtual void DrawSurface(SourceSurface *aSurface, const Rect &aDest, @@ -581,19 +700,32 @@ public: const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(), const DrawOptions &aOptions = DrawOptions()) = 0; - /* + /** + * Draw the output of a FilterNode to the DrawTarget. + * + * @param aNode FilterNode to draw + * @param aSourceRect Source rectangle in FilterNode space to draw + * @param aDestPoint Destination point on the DrawTarget to draw the + * SourceRectangle of the filter output to + */ + virtual void DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions = DrawOptions()) = 0; + + /** * Blend a surface to the draw target with a shadow. The shadow is drawn as a * gaussian blur using a specified sigma. The shadow is clipped to the size * of the input surface, so the input surface should contain a transparent * border the size of the approximate coverage of the blur (3 * aSigma). * NOTE: This function works in device space! * - * aSurface Source surface to draw. - * aDest Destination point that this drawing operation should draw to. - * aColor Color of the drawn shadow - * aOffset Offset of the shadow - * aSigma Sigma used for the guassian filter kernel - * aOperator Composition operator used + * @param aSurface Source surface to draw. + * @param aDest Destination point that this drawing operation should draw to. + * @param aColor Color of the drawn shadow + * @param aOffset Offset of the shadow + * @param aSigma Sigma used for the guassian filter kernel + * @param aOperator Composition operator used */ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, @@ -602,57 +734,70 @@ public: Float aSigma, CompositionOp aOperator) = 0; - /* + /** * Clear a rectangle on the draw target to transparent black. This will * respect the clipping region and transform. * - * aRect Rectangle to clear + * @param aRect Rectangle to clear */ virtual void ClearRect(const Rect &aRect) = 0; - /* + /** * This is essentially a 'memcpy' between two surfaces. It moves a pixel * aligned area from the source surface unscaled directly onto the * drawtarget. This ignores both transform and clip. * - * aSurface Surface to copy from - * aSourceRect Source rectangle to be copied - * aDest Destination point to copy the surface to + * @param aSurface Surface to copy from + * @param aSourceRect Source rectangle to be copied + * @param aDest Destination point to copy the surface to */ virtual void CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect, const IntPoint &aDestination) = 0; - /* + /** @see CopySurface + * Same as CopySurface, except uses itself as the source. + * + * Some backends may be able to optimize this better + * than just taking a snapshot and using CopySurface. + */ + virtual void CopyRect(const IntRect &aSourceRect, + const IntPoint &aDestination) + { + RefPtr<SourceSurface> source = Snapshot(); + CopySurface(source, aSourceRect, aDestination); + } + + /** * Fill a rectangle on the DrawTarget with a certain source pattern. * - * aRect Rectangle that forms the mask of this filling operation - * aPattern Pattern that forms the source of this filling operation - * aOptions Options that are applied to this operation + * @param aRect Rectangle that forms the mask of this filling operation + * @param aPattern Pattern that forms the source of this filling operation + * @param aOptions Options that are applied to this operation */ virtual void FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions = DrawOptions()) = 0; - /* + /** * Stroke a rectangle on the DrawTarget with a certain source pattern. * - * aRect Rectangle that forms the mask of this stroking operation - * aPattern Pattern that forms the source of this stroking operation - * aOptions Options that are applied to this operation + * @param aRect Rectangle that forms the mask of this stroking operation + * @param aPattern Pattern that forms the source of this stroking operation + * @param aOptions Options that are applied to this operation */ virtual void StrokeRect(const Rect &aRect, const Pattern &aPattern, const StrokeOptions &aStrokeOptions = StrokeOptions(), const DrawOptions &aOptions = DrawOptions()) = 0; - /* + /** * Stroke a line on the DrawTarget with a certain source pattern. * - * aStart Starting point of the line - * aEnd End point of the line - * aPattern Pattern that forms the source of this stroking operation - * aOptions Options that are applied to this operation + * @param aStart Starting point of the line + * @param aEnd End point of the line + * @param aPattern Pattern that forms the source of this stroking operation + * @param aOptions Options that are applied to this operation */ virtual void StrokeLine(const Point &aStart, const Point &aEnd, @@ -660,88 +805,88 @@ public: const StrokeOptions &aStrokeOptions = StrokeOptions(), const DrawOptions &aOptions = DrawOptions()) = 0; - /* + /** * Stroke a path on the draw target with a certain source pattern. * - * aPath Path that is to be stroked - * aPattern Pattern that should be used for the stroke - * aStrokeOptions Stroke options used for this operation - * aOptions Draw options used for this operation + * @param aPath Path that is to be stroked + * @param aPattern Pattern that should be used for the stroke + * @param aStrokeOptions Stroke options used for this operation + * @param aOptions Draw options used for this operation */ virtual void Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions = StrokeOptions(), const DrawOptions &aOptions = DrawOptions()) = 0; - /* + /** * Fill a path on the draw target with a certain source pattern. * - * aPath Path that is to be filled - * aPattern Pattern that should be used for the fill - * aOptions Draw options used for this operation + * @param aPath Path that is to be filled + * @param aPattern Pattern that should be used for the fill + * @param aOptions Draw options used for this operation */ virtual void Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions = DrawOptions()) = 0; - /* + /** * Fill a series of clyphs on the draw target with a certain source pattern. */ virtual void FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aOptions = DrawOptions(), - const GlyphRenderingOptions *aRenderingOptions = NULL) = 0; + const GlyphRenderingOptions *aRenderingOptions = nullptr) = 0; - /* + /** * This takes a source pattern and a mask, and composites the source pattern * onto the destination surface using the alpha channel of the mask pattern * as a mask for the operation. * - * aSource Source pattern - * aMask Mask pattern - * aOptions Drawing options + * @param aSource Source pattern + * @param aMask Mask pattern + * @param aOptions Drawing options */ virtual void Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions = DrawOptions()) = 0; - /* + /** * This takes a source pattern and a mask, and composites the source pattern * onto the destination surface using the alpha channel of the mask source. * The operation is bound by the extents of the mask. * - * aSource Source pattern - * aMask Mask surface - * aOffset a transformed offset that the surface is masked at - * aOptions Drawing options + * @param aSource Source pattern + * @param aMask Mask surface + * @param aOffset a transformed offset that the surface is masked at + * @param aOptions Drawing options */ virtual void MaskSurface(const Pattern &aSource, SourceSurface *aMask, Point aOffset, const DrawOptions &aOptions = DrawOptions()) = 0; - /* + /** * Push a clip to the DrawTarget. * - * aPath The path to clip to + * @param aPath The path to clip to */ virtual void PushClip(const Path *aPath) = 0; - /* + /** * Push an axis-aligned rectangular clip to the DrawTarget. This rectangle * is specified in user space. * - * aRect The rect to clip to + * @param aRect The rect to clip to */ virtual void PushClipRect(const Rect &aRect) = 0; - /* Pop a clip from the DrawTarget. A pop without a corresponding push will + /** Pop a clip from the DrawTarget. A pop without a corresponding push will * be ignored. */ virtual void PopClip() = 0; - /* + /** * Create a SourceSurface optimized for use with this DrawTarget from * existing bitmap data in memory. * @@ -752,14 +897,14 @@ public: int32_t aStride, SurfaceFormat aFormat) const = 0; - /* - * Create a SourceSurface optimized for use with this DrawTarget from - * an arbitrary other SourceSurface. This may return aSourceSurface or some - * other existing surface. + /** + * Create a SourceSurface optimized for use with this DrawTarget from an + * arbitrary SourceSurface type supported by this backend. This may return + * aSourceSurface or some other existing surface. */ virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const = 0; - /* + /** * Create a SourceSurface for a type of NativeSurface. This may fail if the * draw target does not know how to deal with the type of NativeSurface passed * in. @@ -767,13 +912,21 @@ public: virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const = 0; - /* + /** * Create a DrawTarget whose snapshot is optimized for use with this DrawTarget. */ virtual TemporaryRef<DrawTarget> CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const = 0; - /* + /** + * Create a DrawTarget that captures the drawing commands and can be replayed + * onto a compatible DrawTarget afterwards. + * + * @param aSize Size of the area this DT will capture. + */ + virtual TemporaryRef<DrawTargetCapture> CreateCaptureDT(const IntSize& aSize); + + /** * Create a draw target optimized for drawing a shadow. * * Note that aSigma is the blur radius that must be used when we draw the @@ -788,54 +941,85 @@ public: return CreateSimilarDrawTarget(aSize, aFormat); } - /* + /** * Create a path builder with the specified fillmode. * * We need the fill mode up front because of Direct2D. * ID2D1SimplifiedGeometrySink requires the fill mode * to be set before calling BeginFigure(). */ - virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const = 0; + virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const = 0; - /* + /** * Create a GradientStops object that holds information about a set of * gradient stops, this object is required for linear or radial gradient * patterns to represent the color stops in the gradient. * - * aStops An array of gradient stops - * aNumStops Number of stops in the array aStops - * aExtendNone This describes how to extend the stop color outside of the - * gradient area. + * @param aStops An array of gradient stops + * @param aNumStops Number of stops in the array aStops + * @param aExtendNone This describes how to extend the stop color outside of the + * gradient area. */ virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, - ExtendMode aExtendMode = EXTEND_CLAMP) const = 0; + ExtendMode aExtendMode = ExtendMode::CLAMP) const = 0; - const Matrix &GetTransform() const { return mTransform; } + /** + * Create a FilterNode object that can be used to apply a filter to various + * inputs. + * + * @param aType Type of filter node to be created. + */ + virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) = 0; - /* + Matrix GetTransform() const { return mTransform; } + + /** * Set a transform on the surface, this transform is applied at drawing time * to both the mask and source of the operation. + * + * Performance note: For some backends it is expensive to change the current + * transform (because transforms affect a lot of the parts of the pipeline, + * so new transform change can result in a pipeline flush). To get around + * this, DrawTarget implementations buffer transform changes and try to only + * set the current transform on the backend when required. That tracking has + * its own performance impact though, and ideally callers would be smart + * enough not to require it. At a future date this method may stop this + * doing transform buffering so, if you're a consumer, please try to be smart + * about calling this method as little as possible. For example, instead of + * concatenating a translation onto the current transform then calling + * FillRect, try to integrate the translation into FillRect's aRect + * argument's x/y offset. */ virtual void SetTransform(const Matrix &aTransform) { mTransform = aTransform; mTransformDirty = true; } - SurfaceFormat GetFormat() { return mFormat; } + inline void ConcatTransform(const Matrix &aTransform) + { SetTransform(aTransform * Matrix(GetTransform())); } - /* Tries to get a native surface for a DrawTarget, this may fail if the + SurfaceFormat GetFormat() const { return mFormat; } + + /** Tries to get a native surface for a DrawTarget, this may fail if the * draw target cannot convert to this surface type. */ - virtual void *GetNativeSurface(NativeSurfaceType aType) { return NULL; } + virtual void *GetNativeSurface(NativeSurfaceType aType) { return nullptr; } + + virtual bool IsDualDrawTarget() const { return false; } + virtual bool IsTiledDrawTarget() const { return false; } + virtual bool SupportsRegionClipping() const { return true; } void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) { mUserData.Add(key, userData, destroy); } - void *GetUserData(UserDataKey *key) { + void *GetUserData(UserDataKey *key) const { return mUserData.Get(key); } + void *RemoveUserData(UserDataKey *key) { + return mUserData.Remove(key); + } - /* Within this rectangle all pixels will be opaque by the time the result of + /** Within this rectangle all pixels will be opaque by the time the result of * this DrawTarget is first used for drawing. Either by the underlying surface * being used as an input to external drawing, or Snapshot() being called. * This rectangle is specified in device space. @@ -856,6 +1040,15 @@ public: return mPermitSubpixelAA; } +#ifdef USE_SKIA_GPU + virtual bool InitWithGrContext(GrContext* aGrContext, + const IntSize &aSize, + SurfaceFormat aFormat) + { + MOZ_CRASH(); + } +#endif + protected: UserData mUserData; Matrix mTransform; @@ -866,18 +1059,65 @@ protected: SurfaceFormat mFormat; }; +class DrawTargetCapture : public DrawTarget +{ +}; + class DrawEventRecorder : public RefCounted<DrawEventRecorder> { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorder) virtual ~DrawEventRecorder() { } }; +struct Tile +{ + RefPtr<DrawTarget> mDrawTarget; + IntPoint mTileOrigin; +}; + +struct TileSet +{ + Tile* mTiles; + size_t mTileCount; +}; + +struct Config { + LogForwarder* mLogForwarder; + int32_t mMaxTextureSize; + int32_t mMaxAllocSize; + + Config() + : mLogForwarder(nullptr) + , mMaxTextureSize(8192) + , mMaxAllocSize(52000000) + {} +}; + class GFX2D_API Factory { public: + static void Init(const Config& aConfig); + static void ShutDown(); + static bool HasSSE2(); - static TemporaryRef<DrawTarget> CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize); + /** Make sure that the given dimensions don't overflow a 32-bit signed int + * using 4 bytes per pixel; optionally, make sure that either dimension + * doesn't exceed the given limit. + */ + static bool CheckSurfaceSize(const IntSize &sz, + int32_t limit = 0, + int32_t allocLimit = 0); + + /** Make sure the given dimension satisfies the CheckSurfaceSize and is + * within 8k limit. The 8k value is chosen a bit randomly. + */ + static bool ReasonableSurfaceSize(const IntSize &aSize); + + static bool AllowedSurfaceSize(const IntSize &aSize); + + static TemporaryRef<DrawTarget> CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr); static TemporaryRef<DrawTarget> CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat); @@ -894,16 +1134,16 @@ public: /** * This creates a ScaledFont from TrueType data. * - * aData - Pointer to the data - * aSize - Size of the TrueType data - * aFaceIndex - Index of the font face in the truetype data this ScaledFont needs to represent. - * aGlyphSize - Size of the glyphs in this ScaledFont - * aType - Type of ScaledFont that should be created. + * @param aData Pointer to the data + * @param aSize Size of the TrueType data + * @param aFaceIndex Index of the font face in the truetype data this ScaledFont needs to represent. + * @param aGlyphSize Size of the glyphs in this ScaledFont + * @param aType Type of ScaledFont that should be created. */ static TemporaryRef<ScaledFont> CreateScaledFontForTrueTypeData(uint8_t *aData, uint32_t aSize, uint32_t aFaceIndex, Float aGlyphSize, FontType aType); - /* + /** * This creates a scaled font with an associated cairo_scaled_font_t, and * must be used when using the Cairo backend. The NativeFont and * cairo_scaled_font_t* parameters must correspond to the same font. @@ -911,15 +1151,26 @@ public: static TemporaryRef<ScaledFont> CreateScaledFontWithCairo(const NativeFont &aNativeFont, Float aSize, cairo_scaled_font_t* aScaledFont); - /* + /** * This creates a simple data source surface for a certain size. It allocates * new memory for the surface. This memory is freed when the surface is - * destroyed. + * destroyed. The caller is responsible for handing the case where nullptr + * is returned. The surface is not zeroed unless requested. */ static TemporaryRef<DataSourceSurface> - CreateDataSourceSurface(const IntSize &aSize, SurfaceFormat aFormat); + CreateDataSourceSurface(const IntSize &aSize, SurfaceFormat aFormat, bool aZero = false); - /* + /** + * This creates a simple data source surface for a certain size with a + * specific stride, which must be large enough to fit all pixels. + * It allocates new memory for the surface. This memory is freed when + * the surface is destroyed. The caller is responsible for handling the case + * where nullptr is returned. The surface is not zeroed unless requested. + */ + static TemporaryRef<DataSourceSurface> + CreateDataSourceSurfaceWithStride(const IntSize &aSize, SurfaceFormat aFormat, int32_t aStride, bool aZero = false); + + /** * This creates a simple data source surface for some existing data. It will * wrap this data and the data for this source surface. The caller is * responsible for deallocating the memory only after destruction of the @@ -934,9 +1185,47 @@ public: static void SetGlobalEventRecorder(DrawEventRecorder *aRecorder); + // This is a little hacky at the moment, but we want to have this data. Bug 1068613. + static void SetLogForwarder(LogForwarder* aLogFwd); + + static uint32_t GetMaxSurfaceSize(BackendType aType); + + static LogForwarder* GetLogForwarder() { return sConfig ? sConfig->mLogForwarder : nullptr; } + +private: + static Config* sConfig; +public: + #ifdef USE_SKIA_GPU static TemporaryRef<DrawTarget> - CreateSkiaDrawTargetForFBO(unsigned int aFBOID, GrContext *aContext, const IntSize &aSize, SurfaceFormat aFormat); + CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext, + const IntSize &aSize, + SurfaceFormat aFormat); +#endif + + static void PurgeAllCaches(); + +#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE) + static TemporaryRef<GlyphRenderingOptions> + CreateCairoGlyphRenderingOptions(FontHinting aHinting, bool aAutoHinting); +#endif + static TemporaryRef<DrawTarget> + CreateDualDrawTarget(DrawTarget *targetA, DrawTarget *targetB); + + /* + * This creates a new tiled DrawTarget. When a tiled drawtarget is used the + * drawing is distributed over number of tiles which may each hold an + * individual offset. The tiles in the set must each have the same backend + * and format. + */ + static TemporaryRef<DrawTarget> CreateTiledDrawTarget(const TileSet& aTileSet); + + static bool DoesBackendSupportDataDrawtarget(BackendType aType); + +#ifdef XP_MACOSX + static TemporaryRef<DrawTarget> CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize); + static TemporaryRef<GlyphRenderingOptions> + CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor); #endif #ifdef WIN32 @@ -948,6 +1237,12 @@ public: static void SetDirect3D10Device(ID3D10Device1 *aDevice); static ID3D10Device1 *GetDirect3D10Device(); + static TemporaryRef<DrawTarget> CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat); + + static void SetDirect3D11Device(ID3D11Device *aDevice); + static ID3D11Device *GetDirect3D11Device(); + static ID2D1Device *GetD2D1Device(); + static bool SupportsD2D1(); static TemporaryRef<GlyphRenderingOptions> CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams); @@ -957,7 +1252,9 @@ public: static void D2DCleanup(); private: + static ID2D1Device *mD2D1Device; static ID3D10Device1 *mD3D10Device; + static ID3D11Device *mD3D11Device; #endif static DrawEventRecorder *mRecorder; diff --git a/gfx/2d/BaseCoord.h b/gfx/2d/BaseCoord.h new file mode 100644 index 000000000..bbccbc7ae --- /dev/null +++ b/gfx/2d/BaseCoord.h @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_BASECOORD_H_ +#define MOZILLA_GFX_BASECOORD_H_ + +#include "mozilla/Attributes.h" + +namespace mozilla { +namespace gfx { + +/** + * Do not use this class directly. Subclass it, pass that subclass as the + * Sub parameter, and only use that subclass. This allows methods to safely + * cast 'this' to 'Sub*'. + */ +template <class T, class Sub> +struct BaseCoord { + T value; + + // Constructors + MOZ_CONSTEXPR BaseCoord() : value(0) {} + explicit MOZ_CONSTEXPR BaseCoord(T aValue) : value(aValue) {} + + // Note that '=' isn't defined so we'll get the + // compiler generated default assignment operator + + operator T() const { return value; } + + friend bool operator==(Sub aA, Sub aB) { + return aA.value == aB.value; + } + friend bool operator!=(Sub aA, Sub aB) { + return aA.value != aB.value; + } + + friend Sub operator+(Sub aA, Sub aB) { + return Sub(aA.value + aB.value); + } + friend Sub operator-(Sub aA, Sub aB) { + return Sub(aA.value - aB.value); + } + friend Sub operator*(Sub aCoord, T aScale) { + return Sub(aCoord.value * aScale); + } + friend Sub operator*(T aScale, Sub aCoord) { + return Sub(aScale * aCoord.value); + } + friend Sub operator/(Sub aCoord, T aScale) { + return Sub(aCoord.value / aScale); + } + // 'scale / coord' is intentionally omitted because it doesn't make sense. + + Sub& operator+=(Sub aCoord) { + value += aCoord.value; + return *static_cast<Sub*>(this); + } + Sub& operator-=(Sub aCoord) { + value -= aCoord.value; + return *static_cast<Sub*>(this); + } + Sub& operator*=(T aScale) { + value *= aScale; + return *static_cast<Sub*>(this); + } + Sub& operator/=(T aScale) { + value /= aScale; + return *static_cast<Sub*>(this); + } + + // Since BaseCoord is implicitly convertible to its value type T, we need + // mixed-type operator overloads to avoid ambiguities at mixed-type call + // sites. As we transition more of our code to strongly-typed classes, we + // may be able to remove some or all of these overloads. + friend bool operator==(Sub aA, T aB) { + return aA.value == aB; + } + friend bool operator==(T aA, Sub aB) { + return aA == aB.value; + } + friend bool operator!=(Sub aA, T aB) { + return aA.value != aB; + } + friend bool operator!=(T aA, Sub aB) { + return aA != aB.value; + } + friend T operator+(Sub aA, T aB) { + return aA.value + aB; + } + friend T operator+(T aA, Sub aB) { + return aA + aB.value; + } + friend T operator-(Sub aA, T aB) { + return aA.value - aB; + } + friend T operator-(T aA, Sub aB) { + return aA - aB.value; + } + + Sub operator-() const { + return Sub(-value); + } +}; + +} +} + +#endif /* MOZILLA_GFX_BASECOORD_H_ */ diff --git a/gfx/2d/BaseMargin.h b/gfx/2d/BaseMargin.h index e7203b0af..33afd4658 100644 --- a/gfx/2d/BaseMargin.h +++ b/gfx/2d/BaseMargin.h @@ -9,6 +9,57 @@ #include "Types.h" namespace mozilla { + +/** + * Sides represents a set of physical sides. + */ +struct Sides final { + Sides() : mBits(0) {} + explicit Sides(SideBits aSideBits) + { + MOZ_ASSERT((aSideBits & ~eSideBitsAll) == 0, "illegal side bits"); + mBits = aSideBits; + } + bool IsEmpty() const { return mBits == 0; } + bool Top() const { return mBits & eSideBitsTop; } + bool Right() const { return mBits & eSideBitsRight; } + bool Bottom() const { return mBits & eSideBitsBottom; } + bool Left() const { return mBits & eSideBitsLeft; } + bool Contains(SideBits aSideBits) const + { + MOZ_ASSERT((aSideBits & ~eSideBitsAll) == 0, "illegal side bits"); + return (mBits & aSideBits) == aSideBits; + } + Sides operator|(Sides aOther) const + { + return Sides(SideBits(mBits | aOther.mBits)); + } + Sides operator|(SideBits aSideBits) const + { + return *this | Sides(aSideBits); + } + Sides& operator|=(Sides aOther) + { + mBits |= aOther.mBits; + return *this; + } + Sides& operator|=(SideBits aSideBits) + { + return *this |= Sides(aSideBits); + } + bool operator==(Sides aOther) const + { + return mBits == aOther.mBits; + } + bool operator!=(Sides aOther) const + { + return !(*this == aOther); + } + +private: + uint8_t mBits; +}; + namespace gfx { /** @@ -17,7 +68,7 @@ namespace gfx { */ template <class T, class Sub> struct BaseMargin { - typedef mozilla::css::Side SideT; + typedef mozilla::Side SideT; // because we have a method named Side // Do not change the layout of these members; the Side() methods below // depend on this order. @@ -38,11 +89,27 @@ struct BaseMargin { T& Side(SideT aSide) { // This is ugly! - return *(&top + aSide); + return *(&top + T(aSide)); } T Side(SideT aSide) const { // This is ugly! - return *(&top + aSide); + return *(&top + T(aSide)); + } + + void ApplySkipSides(Sides aSkipSides) + { + if (aSkipSides.Top()) { + top = 0; + } + if (aSkipSides.Right()) { + right = 0; + } + if (aSkipSides.Bottom()) { + bottom = 0; + } + if (aSkipSides.Left()) { + left = 0; + } } // Overloaded operators. Note that '=' isn't defined so we'll get the diff --git a/gfx/2d/BasePoint.h b/gfx/2d/BasePoint.h index a7da0cc03..4bd60275f 100644 --- a/gfx/2d/BasePoint.h +++ b/gfx/2d/BasePoint.h @@ -6,6 +6,12 @@ #ifndef MOZILLA_GFX_BASEPOINT_H_ #define MOZILLA_GFX_BASEPOINT_H_ +#include <cmath> +#include "mozilla/Attributes.h" +#include "mozilla/ToString.h" +#include "mozilla/FloatingPoint.h" +#include "mozilla/TypeTraits.h" + namespace mozilla { namespace gfx { @@ -14,13 +20,13 @@ namespace gfx { * Sub parameter, and only use that subclass. This allows methods to safely * cast 'this' to 'Sub*'. */ -template <class T, class Sub> +template <class T, class Sub, class Coord = T> struct BasePoint { T x, y; // Constructors - BasePoint() : x(0), y(0) {} - BasePoint(T aX, T aY) : x(aX), y(aY) {} + MOZ_CONSTEXPR BasePoint() : x(0), y(0) {} + MOZ_CONSTEXPR BasePoint(Coord aX, Coord aY) : x(aX), y(aY) {} void MoveTo(T aX, T aY) { x = aX; y = aY; } void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; } @@ -62,6 +68,32 @@ struct BasePoint { Sub operator-() const { return Sub(-x, -y); } + + T Length() const { + return hypot(x, y); + } + + // Round() is *not* rounding to nearest integer if the values are negative. + // They are always rounding as floor(n + 0.5). + // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14 + Sub& Round() { + x = Coord(floor(T(x) + T(0.5))); + y = Coord(floor(T(y) + T(0.5))); + return *static_cast<Sub*>(this); + } + + // "Finite" means not inf and not NaN + bool IsFinite() const + { + typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value, float, double>::Type FloatType; + return (mozilla::IsFinite(FloatType(x)) && mozilla::IsFinite(FloatType(y))); + return true; + } + + friend std::ostream& operator<<(std::ostream& stream, const BasePoint<T, Sub, Coord>& aPoint) { + return stream << '(' << aPoint.x << ',' << aPoint.y << ')'; + } + }; } diff --git a/gfx/2d/BasePoint3D.h b/gfx/2d/BasePoint3D.h index a5fb266d5..c64ea247a 100644 --- a/gfx/2d/BasePoint3D.h +++ b/gfx/2d/BasePoint3D.h @@ -6,6 +6,8 @@ #ifndef MOZILLA_BASEPOINT3D_H_ #define MOZILLA_BASEPOINT3D_H_ +#include "mozilla/Assertions.h" + namespace mozilla { namespace gfx { @@ -29,12 +31,12 @@ struct BasePoint3D { // compiler generated default assignment operator T& operator[](int aIndex) { - NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 2, "Invalid array index"); + MOZ_ASSERT(aIndex >= 0 && aIndex <= 2); return *((&x)+aIndex); } const T& operator[](int aIndex) const { - NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 2, "Invalid array index"); + MOZ_ASSERT(aIndex >= 0 && aIndex <= 2); return *((&x)+aIndex); } diff --git a/gfx/2d/BasePoint4D.h b/gfx/2d/BasePoint4D.h index ab9fa4523..e098876a8 100644 --- a/gfx/2d/BasePoint4D.h +++ b/gfx/2d/BasePoint4D.h @@ -6,6 +6,8 @@ #ifndef MOZILLA_BASEPOINT4D_H_ #define MOZILLA_BASEPOINT4D_H_ +#include "mozilla/Assertions.h" + namespace mozilla { namespace gfx { @@ -86,12 +88,12 @@ struct BasePoint4D { } T& operator[](int aIndex) { - NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid array index"); + MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid array index"); return *((&x)+aIndex); } const T& operator[](int aIndex) const { - NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid array index"); + MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid array index"); return *((&x)+aIndex); } @@ -114,6 +116,8 @@ struct BasePoint4D { void Normalize() { *this /= Length(); } + + bool HasPositiveWCoord() { return w > 0; } }; } diff --git a/gfx/2d/BaseRect.h b/gfx/2d/BaseRect.h index f0f5efec2..0b6810cfd 100644 --- a/gfx/2d/BaseRect.h +++ b/gfx/2d/BaseRect.h @@ -6,9 +6,14 @@ #ifndef MOZILLA_GFX_BASERECT_H_ #define MOZILLA_GFX_BASERECT_H_ -#include <cmath> -#include <mozilla/Assertions.h> #include <algorithm> +#include <cmath> +#include <ostream> + +#include "mozilla/Assertions.h" +#include "mozilla/FloatingPoint.h" +#include "mozilla/TypeTraits.h" +#include "Types.h" namespace mozilla { namespace gfx { @@ -36,7 +41,7 @@ namespace gfx { * Do not use this class directly. Subclass it, pass that subclass as the * Sub parameter, and only use that subclass. */ -template <class T, class Sub, class Point, class SizeT, class Margin> +template <class T, class Sub, class Point, class SizeT, class MarginT> struct BaseRect { T x, y, width, height; @@ -59,10 +64,11 @@ struct BaseRect { // "Finite" means not inf and not NaN bool IsFinite() const { - return (std::isfinite(x) && - std::isfinite(y) && - std::isfinite(width) && - std::isfinite(height)); + typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value, float, double>::Type FloatType; + return (mozilla::IsFinite(FloatType(x)) && + mozilla::IsFinite(FloatType(y)) && + mozilla::IsFinite(FloatType(width)) && + mozilla::IsFinite(FloatType(height))); } // Returns true if this rectangle contains the interior of aRect. Always @@ -74,13 +80,17 @@ struct BaseRect { (x <= aRect.x && aRect.XMost() <= XMost() && y <= aRect.y && aRect.YMost() <= YMost()); } - // Returns true if this rectangle contains the rectangle (aX,aY,1,1). + // Returns true if this rectangle contains the point. Points are considered + // in the rectangle if they are on the left or top edge, but outside if they + // are on the right or bottom edge. bool Contains(T aX, T aY) const { - return x <= aX && aX + 1 <= XMost() && - y <= aY && aY + 1 <= YMost(); + return x <= aX && aX < XMost() && + y <= aY && aY < YMost(); } - // Returns true if this rectangle contains the rectangle (aPoint.x,aPoint.y,1,1). + // Returns true if this rectangle contains the point. Points are considered + // in the rectangle if they are on the left or top edge, but outside if they + // are on the right or bottom edge. bool Contains(const Point& aPoint) const { return Contains(aPoint.x, aPoint.y); } // Intersection. Returns TRUE if the receiver's area has non-empty @@ -88,7 +98,8 @@ struct BaseRect { // Always returns false if aRect is empty or 'this' is empty. bool Intersects(const Sub& aRect) const { - return x < aRect.XMost() && aRect.x < XMost() && + return !IsEmpty() && !aRect.IsEmpty() && + x < aRect.XMost() && aRect.x < XMost() && y < aRect.YMost() && aRect.y < YMost(); } // Returns the rectangle containing the intersection of the points @@ -166,6 +177,23 @@ struct BaseRect { *static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2); } + // Expands the rect to include the point + void ExpandToEnclose(const Point& aPoint) + { + if (aPoint.x < x) { + width = XMost() - aPoint.x; + x = aPoint.x; + } else if (aPoint.x > XMost()) { + width = aPoint.x - x; + } + if (aPoint.y < y) { + height = YMost() - aPoint.y; + y = aPoint.y; + } else if (aPoint.y > YMost()) { + height = aPoint.y - y; + } + } + void SetRect(T aX, T aY, T aWidth, T aHeight) { x = aX; y = aY; width = aWidth; height = aHeight; @@ -189,7 +217,7 @@ struct BaseRect { width += 2 * aDx; height += 2 * aDy; } - void Inflate(const Margin& aMargin) + void Inflate(const MarginT& aMargin) { x -= aMargin.left; y -= aMargin.top; @@ -198,6 +226,20 @@ struct BaseRect { } void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); } + void InflateToMultiple(const SizeT& aMultiple) + { + T xMost = XMost(); + T yMost = YMost(); + + x = static_cast<T>(floor(x / aMultiple.width)) * aMultiple.width; + y = static_cast<T>(floor(y / aMultiple.height)) * aMultiple.height; + xMost = static_cast<T>(ceil(x / aMultiple.width)) * aMultiple.width; + yMost = static_cast<T>(ceil(y / aMultiple.height)) * aMultiple.height; + + width = xMost - x; + height = yMost - y; + } + void Deflate(T aD) { Deflate(aD, aD); } void Deflate(T aDx, T aDy) { @@ -206,7 +248,7 @@ struct BaseRect { width = std::max(T(0), width - 2 * aDx); height = std::max(T(0), height - 2 * aDy); } - void Deflate(const Margin& aMargin) + void Deflate(const MarginT& aMargin) { x += aMargin.left; y += aMargin.top; @@ -231,13 +273,25 @@ struct BaseRect { return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty()); } - Sub operator+(const Point& aPoint) const + friend Sub operator+(Sub aSub, const Point& aPoint) + { + aSub += aPoint; + return aSub; + } + friend Sub operator-(Sub aSub, const Point& aPoint) { - return Sub(x + aPoint.x, y + aPoint.y, width, height); + aSub -= aPoint; + return aSub; } - Sub operator-(const Point& aPoint) const + friend Sub operator+(Sub aSub, const SizeT& aSize) { - return Sub(x - aPoint.x, y - aPoint.y, width, height); + aSub += aSize; + return aSub; + } + friend Sub operator-(Sub aSub, const SizeT& aSize) + { + aSub -= aSize; + return aSub; } Sub& operator+=(const Point& aPoint) { @@ -249,14 +303,25 @@ struct BaseRect { MoveBy(-aPoint); return *static_cast<Sub*>(this); } - + Sub& operator+=(const SizeT& aSize) + { + width += aSize.width; + height += aSize.height; + return *static_cast<Sub*>(this); + } + Sub& operator-=(const SizeT& aSize) + { + width -= aSize.width; + height -= aSize.height; + return *static_cast<Sub*>(this); + } // Find difference as a Margin - Margin operator-(const Sub& aRect) const + MarginT operator-(const Sub& aRect) const { - return Margin(aRect.y - y, - XMost() - aRect.XMost(), - YMost() - aRect.YMost(), - aRect.x - x); + return MarginT(aRect.y - y, + XMost() - aRect.XMost(), + YMost() - aRect.YMost(), + aRect.x - x); } // Helpers for accessing the vertices @@ -264,6 +329,33 @@ struct BaseRect { Point TopRight() const { return Point(XMost(), y); } Point BottomLeft() const { return Point(x, YMost()); } Point BottomRight() const { return Point(XMost(), YMost()); } + Point AtCorner(int aCorner) const { + switch (aCorner) { + case RectCorner::TopLeft: return TopLeft(); + case RectCorner::TopRight: return TopRight(); + case RectCorner::BottomRight: return BottomRight(); + case RectCorner::BottomLeft: return BottomLeft(); + } + MOZ_CRASH("Incomplete switch"); + } + Point CCWCorner(mozilla::Side side) const { + switch (side) { + case NS_SIDE_TOP: return TopLeft(); + case NS_SIDE_RIGHT: return TopRight(); + case NS_SIDE_BOTTOM: return BottomRight(); + case NS_SIDE_LEFT: return BottomLeft(); + } + MOZ_CRASH("Incomplete switch"); + } + Point CWCorner(mozilla::Side side) const { + switch (side) { + case NS_SIDE_TOP: return TopRight(); + case NS_SIDE_RIGHT: return BottomRight(); + case NS_SIDE_BOTTOM: return BottomLeft(); + case NS_SIDE_LEFT: return TopLeft(); + } + MOZ_CRASH("Incomplete switch"); + } Point Center() const { return Point(x, y) + Point(width, height)/2; } SizeT Size() const { return SizeT(width, height); } @@ -440,21 +532,27 @@ struct BaseRect { } /** - * Clamp aRect to this rectangle. This returns aRect after it is forced - * inside the bounds of this rectangle. It will attempt to retain the size - * but will shrink the dimensions that don't fit. + * Clamp this rectangle to be inside aRect. The function returns a copy of + * this rect after it is forced inside the bounds of aRect. It will attempt to + * retain the size but will shrink the dimensions that don't fit. */ - Sub ClampRect(const Sub& aRect) const + Sub ForceInside(const Sub& aRect) const { Sub rect(std::max(aRect.x, x), std::max(aRect.y, y), std::min(aRect.width, width), std::min(aRect.height, height)); - rect.x = std::min(rect.XMost(), XMost()) - rect.width; - rect.y = std::min(rect.YMost(), YMost()) - rect.height; + rect.x = std::min(rect.XMost(), aRect.XMost()) - rect.width; + rect.y = std::min(rect.YMost(), aRect.YMost()) - rect.height; return rect; } + friend std::ostream& operator<<(std::ostream& stream, + const BaseRect<T, Sub, Point, SizeT, MarginT>& aRect) { + return stream << '(' << aRect.x << ',' << aRect.y << ',' + << aRect.width << ',' << aRect.height << ')'; + } + private: // Do not use the default operator== or operator!= ! // Use IsEqualEdges or IsEqualInterior explicitly. diff --git a/gfx/2d/BaseSize.h b/gfx/2d/BaseSize.h index 54c578d1d..bc057286e 100644 --- a/gfx/2d/BaseSize.h +++ b/gfx/2d/BaseSize.h @@ -6,6 +6,8 @@ #ifndef MOZILLA_GFX_BASESIZE_H_ #define MOZILLA_GFX_BASESIZE_H_ +#include "mozilla/Attributes.h" + namespace mozilla { namespace gfx { @@ -19,8 +21,8 @@ struct BaseSize { T width, height; // Constructors - BaseSize() : width(0), height(0) {} - BaseSize(T aWidth, T aHeight) : width(aWidth), height(aHeight) {} + MOZ_CONSTEXPR BaseSize() : width(0), height(0) {} + MOZ_CONSTEXPR BaseSize(T aWidth, T aHeight) : width(aWidth), height(aHeight) {} void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; } @@ -67,6 +69,10 @@ struct BaseSize { Sub operator/(T aScale) const { return Sub(width / aScale, height / aScale); } + void Scale(T aXScale, T aYScale) { + width *= aXScale; + height *= aYScale; + } Sub operator*(const Sub& aSize) const { return Sub(width * aSize.width, height * aSize.height); diff --git a/gfx/2d/Blur.cpp b/gfx/2d/Blur.cpp index 1c7e0209d..c852c2c43 100644 --- a/gfx/2d/Blur.cpp +++ b/gfx/2d/Blur.cpp @@ -4,91 +4,28 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "mozilla/gfx/Blur.h" +#include "Blur.h" #include <algorithm> #include <math.h> #include <string.h> -#ifdef WIN32 -#include <windows.h> -#else -#include "nspr.h" -#endif #include "mozilla/CheckedInt.h" #include "mozilla/Constants.h" -#include "mozilla/Util.h" #include "2D.h" +#include "DataSurfaceHelpers.h" #include "Tools.h" +#ifdef BUILD_ARM_NEON +#include "mozilla/arm.h" +#endif + using namespace std; namespace mozilla { namespace gfx { -static uint32_t NumberOfProcessors = 0; - - -#ifdef WIN32 -static void -GetNumberOfLogicalProcessors(void) -{ - SYSTEM_INFO SystemInfo; - - GetSystemInfo(&SystemInfo); - NumberOfProcessors = SystemInfo.dwNumberOfProcessors; -} -#endif - -static void -GetNumberOfProcessors(void) -{ -#ifdef WIN32 - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION SystemLogicalProcessorInformation = NULL; - DWORD SizeSystemLogicalProcessorInformation = 0; - - while(!GetLogicalProcessorInformation(SystemLogicalProcessorInformation, &SizeSystemLogicalProcessorInformation)) { - if(SystemLogicalProcessorInformation) free(SystemLogicalProcessorInformation); - - if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - SystemLogicalProcessorInformation = - static_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION>(malloc(SizeSystemLogicalProcessorInformation)); - } else { - GetNumberOfLogicalProcessors(); - return; - } - } - - DWORD ProcessorCore = 0; - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION Ptr = SystemLogicalProcessorInformation; - - for(DWORD Offset = sizeof SYSTEM_LOGICAL_PROCESSOR_INFORMATION; - Offset <= SizeSystemLogicalProcessorInformation; - Offset += sizeof SYSTEM_LOGICAL_PROCESSOR_INFORMATION) { - if(Ptr++->Relationship == RelationProcessorCore) ProcessorCore++; - } - - free(SystemLogicalProcessorInformation); - - if(ProcessorCore) { - NumberOfProcessors = ProcessorCore; - } else { - GetNumberOfLogicalProcessors(); - } -#else - //Use fallback check on non-windows - NumberOfProcessors = PR_GetNumberOfProcessors(); - - if (NumberOfProcessors == 0 || !NumberOfProcessors) - NumberOfProcessors = 1; - - if (NumberOfProcessors > 8) - NumberOfProcessors = 8; - -#endif -} - /** * Box blur involves looking at one pixel, and setting its value to the average * of its neighbouring pixels. @@ -101,7 +38,6 @@ GetNumberOfProcessors(void) * @param aSkipRect An area to skip blurring in. * XXX shouldn't we pass stride in separately here? */ - static void BoxBlurHorizontal(unsigned char* aInput, unsigned char* aOutput, @@ -120,10 +56,6 @@ BoxBlurHorizontal(unsigned char* aInput, memcpy(aOutput, aInput, aWidth*aRows); return; } - - // Pale Moon: Processor check. - if(NumberOfProcessors == 0) GetNumberOfProcessors(); - uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize); for (int32_t y = 0; y < aRows; y++) { @@ -136,9 +68,8 @@ BoxBlurHorizontal(unsigned char* aInput, y = aSkipRect.YMost() - 1; continue; } - + uint32_t alphaSum = 0; -#pragma loop(hint_parallel(0)) for (int32_t i = 0; i < boxSize; i++) { int32_t pos = i - aLeftLobe; // See assertion above; if aWidth is zero, then we would have no @@ -147,7 +78,6 @@ BoxBlurHorizontal(unsigned char* aInput, pos = min(pos, aWidth - 1); alphaSum += aInput[aWidth * y + pos]; } -#pragma loop(hint_parallel(0)) for (int32_t x = 0; x < aWidth; x++) { // Check whether we are within the skip rect. If so, go // to the next point outside the skip rect. @@ -215,7 +145,6 @@ BoxBlurVertical(unsigned char* aInput, } uint32_t alphaSum = 0; -#pragma loop(hint_parallel(0)) for (int32_t i = 0; i < boxSize; i++) { int32_t pos = i - aTopLobe; // See assertion above; if aRows is zero, then we would have no @@ -224,7 +153,6 @@ BoxBlurVertical(unsigned char* aInput, pos = min(pos, aRows - 1); alphaSum += aInput[aWidth * pos + x]; } -#pragma loop(hint_parallel(0)) for (int32_t y = 0; y < aRows; y++) { if (inSkipRectX && y >= aSkipRect.y && y < aSkipRect.YMost()) { @@ -325,8 +253,7 @@ SpreadHorizontal(unsigned char* aInput, y = aSkipRect.YMost() - 1; continue; } - -#pragma loop(hint_parallel(0)) + for (int32_t x = 0; x < aWidth; x++) { // Check whether we are within the skip rect. If so, go // to the next point outside the skip rect. @@ -372,7 +299,6 @@ SpreadVertical(unsigned char* aInput, continue; } -#pragma loop(hint_parallel(0)) for (int32_t y = 0; y < aRows; y++) { // Check whether we are within the skip rect. If so, go // to the next point outside the skip rect. @@ -413,7 +339,7 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect, const Rect* aSkipRect) : mSpreadRadius(aSpreadRadius), mBlurRadius(aBlurRadius), - mSurfaceAllocationSize(-1) + mSurfaceAllocationSize(0) { Rect rect(aRect); rect.Inflate(Size(aBlurRadius + aSpreadRadius)); @@ -462,28 +388,29 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect, // We need to leave room for an additional 3 bytes for a potential overrun // in our blurring code. - CheckedInt<int32_t> size = CheckedInt<int32_t>(mStride) * mRect.height + 3; - if (size.isValid()) { - mSurfaceAllocationSize = size.value(); + size_t size = BufferSizeFromStrideAndHeight(mStride, mRect.height, 3); + if (size != 0) { + mSurfaceAllocationSize = size; } } } AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect, int32_t aStride, - float aSigma) + float aSigmaX, + float aSigmaY) : mRect(int32_t(aRect.x), int32_t(aRect.y), int32_t(aRect.width), int32_t(aRect.height)), mSpreadRadius(), - mBlurRadius(CalculateBlurRadius(Point(aSigma, aSigma))), + mBlurRadius(CalculateBlurRadius(Point(aSigmaX, aSigmaY))), mStride(aStride), - mSurfaceAllocationSize(-1) + mSurfaceAllocationSize(0) { IntRect intRect; if (aRect.ToIntRect(&intRect)) { - CheckedInt<int32_t> minDataSize = CheckedInt<int32_t>(intRect.width)*intRect.height; - if (minDataSize.isValid()) { - mSurfaceAllocationSize = minDataSize.value(); + size_t minDataSize = BufferSizeFromStrideAndHeight(intRect.width, intRect.height); + if (minDataSize != 0) { + mSurfaceAllocationSize = minDataSize; } } } @@ -522,7 +449,7 @@ AlphaBoxBlur::GetDirtyRect() return nullptr; } -int32_t +size_t AlphaBoxBlur::GetSurfaceAllocationSize() const { return mSurfaceAllocationSize; @@ -575,7 +502,11 @@ AlphaBoxBlur::Blur(uint8_t* aData) // No need to use CheckedInt here - we have validated it in the constructor. size_t szB = stride * size.height; - uint8_t* tmpData = new uint8_t[szB]; + uint8_t* tmpData = new (std::nothrow) uint8_t[szB]; + if (!tmpData) { + return; + } + memset(tmpData, 0, szB); uint8_t* a = aData; @@ -606,11 +537,18 @@ AlphaBoxBlur::Blur(uint8_t* aData) // We need to leave room for an additional 12 bytes for a maximum overrun // of 3 pixels in the blurring code. - AlignedArray<uint32_t> integralImage((integralImageStride / 4) * integralImageSize.height + 12); + size_t bufLen = BufferSizeFromStrideAndHeight(integralImageStride, integralImageSize.height, 12); + if (bufLen == 0) { + return; + } + // bufLen is a byte count, but here we want a multiple of 32-bit ints, so + // we divide by 4. + AlignedArray<uint32_t> integralImage((bufLen / 4) + ((bufLen % 4) ? 1 : 0)); if (!integralImage) { return; } + #ifdef USE_SSE2 if (Factory::HasSSE2()) { BoxBlur_SSE2(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0], @@ -621,6 +559,16 @@ AlphaBoxBlur::Blur(uint8_t* aData) verticalLobes[2][1], integralImage, integralImageStride); } else #endif +#ifdef BUILD_ARM_NEON + if (mozilla::supports_neon()) { + BoxBlur_NEON(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0], + verticalLobes[0][1], integralImage, integralImageStride); + BoxBlur_NEON(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0], + verticalLobes[1][1], integralImage, integralImageStride); + BoxBlur_NEON(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0], + verticalLobes[2][1], integralImage, integralImageStride); + } else +#endif { BoxBlur_C(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0], verticalLobes[0][1], integralImage, integralImageStride); @@ -755,7 +703,6 @@ AlphaBoxBlur::BoxBlur_C(uint8_t* aData, IntRect skipRect = mSkipRect; uint8_t *data = aData; int32_t stride = mStride; -#pragma loop(hint_parallel(0)) for (int32_t y = 0; y < size.height; y++) { bool inSkipRectY = y > skipRect.y && y < skipRect.YMost(); @@ -780,7 +727,7 @@ AlphaBoxBlur::BoxBlur_C(uint8_t* aData, uint32_t value = bottomRight - topRight - bottomLeft; value += topLeft; - data[stride * y + x] = (uint64_t(reciprocal) * value) >> 32; + data[stride * y + x] = (uint64_t(reciprocal) * value + (uint64_t(1) << 31)) >> 32; } } } @@ -802,8 +749,8 @@ static const Float GAUSSIAN_SCALE_FACTOR = Float((3 * sqrt(2 * M_PI) / 4) * 1.5) IntSize AlphaBoxBlur::CalculateBlurRadius(const Point& aStd) { - IntSize size(static_cast<int32_t>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5)), - static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5))); + IntSize size(static_cast<int32_t>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5f)), + static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5f))); return size; } diff --git a/gfx/2d/Blur.h b/gfx/2d/Blur.h index 3de65a2bb..644b6a474 100644 --- a/gfx/2d/Blur.h +++ b/gfx/2d/Blur.h @@ -66,7 +66,8 @@ public: AlphaBoxBlur(const Rect& aRect, int32_t aStride, - float aSigma); + float aSigmaX, + float aSigmaY); ~AlphaBoxBlur(); @@ -93,12 +94,12 @@ public: /** * Return the minimum buffer size that should be given to Blur() method. If - * negative, the class is not properly setup for blurring. Note that this + * zero, the class is not properly setup for blurring. Note that this * includes the extra three bytes on top of the stride*width, where something * like gfxImageSurface::GetDataSize() would report without it, even if it * happens to have the extra bytes. */ - int32_t GetSurfaceAllocationSize() const; + size_t GetSurfaceAllocationSize() const; /** * Perform the blur in-place on the surface backed by specified 8-bit @@ -123,6 +124,11 @@ private: void BoxBlur_SSE2(uint8_t* aData, int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe, int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride); +#ifdef BUILD_ARM_NEON + void BoxBlur_NEON(uint8_t* aData, + int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe, + int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride); +#endif static CheckedInt<int32_t> RoundUpToMultipleOf4(int32_t aVal); @@ -161,7 +167,7 @@ private: /** * The minimum size of the buffer needed for the Blur() operation. */ - int32_t mSurfaceAllocationSize; + size_t mSurfaceAllocationSize; /** * Whether mDirtyRect contains valid data. diff --git a/gfx/2d/BlurNEON.cpp b/gfx/2d/BlurNEON.cpp new file mode 100644 index 000000000..978b3cdc0 --- /dev/null +++ b/gfx/2d/BlurNEON.cpp @@ -0,0 +1,288 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Blur.h" +#include <arm_neon.h> + +namespace mozilla { +namespace gfx { + +MOZ_ALWAYS_INLINE +uint16x4_t Divide(uint32x4_t aValues, uint32x2_t aDivisor) +{ + uint64x2_t roundingAddition = vdupq_n_u64(int64_t(1) << 31); + uint64x2_t multiplied21 = vmull_u32(vget_low_u32(aValues), aDivisor); + uint64x2_t multiplied43 = vmull_u32(vget_high_u32(aValues), aDivisor); + return vqmovn_u32(vcombine_u32(vshrn_n_u64(vaddq_u64(multiplied21, roundingAddition), 32), + vshrn_n_u64(vaddq_u64(multiplied43, roundingAddition), 32))); +} + +MOZ_ALWAYS_INLINE +uint16x4_t BlurFourPixels(const uint32x4_t& aTopLeft, const uint32x4_t& aTopRight, + const uint32x4_t& aBottomRight, const uint32x4_t& aBottomLeft, + const uint32x2_t& aDivisor) +{ + uint32x4_t values = vaddq_u32(vsubq_u32(vsubq_u32(aBottomRight, aTopRight), aBottomLeft), aTopLeft); + return Divide(values, aDivisor); +} + +MOZ_ALWAYS_INLINE +void LoadIntegralRowFromRow(uint32_t *aDest, const uint8_t *aSource, + int32_t aSourceWidth, int32_t aLeftInflation, + int32_t aRightInflation) +{ + int32_t currentRowSum = 0; + + for (int x = 0; x < aLeftInflation; x++) { + currentRowSum += aSource[0]; + aDest[x] = currentRowSum; + } + for (int x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x++) { + currentRowSum += aSource[(x - aLeftInflation)]; + aDest[x] = currentRowSum; + } + for (int x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) { + currentRowSum += aSource[aSourceWidth - 1]; + aDest[x] = currentRowSum; + } +} + +MOZ_ALWAYS_INLINE void +GenerateIntegralImage_NEON(int32_t aLeftInflation, int32_t aRightInflation, + int32_t aTopInflation, int32_t aBottomInflation, + uint32_t *aIntegralImage, size_t aIntegralImageStride, + uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize) +{ + MOZ_ASSERT(!(aLeftInflation & 3)); + + uint32_t stride32bit = aIntegralImageStride / 4; + IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation, + aSize.height + aTopInflation + aBottomInflation); + + LoadIntegralRowFromRow(aIntegralImage, aSource, aSize.width, aLeftInflation, aRightInflation); + + for (int y = 1; y < aTopInflation + 1; y++) { + uint32_t *intRow = aIntegralImage + (y * stride32bit); + uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit; + uint32_t *intFirstRow = aIntegralImage; + + for (int x = 0; x < integralImageSize.width; x += 4) { + uint32x4_t firstRow = vld1q_u32(intFirstRow + x); + uint32x4_t previousRow = vld1q_u32(intPrevRow + x); + vst1q_u32(intRow + x, vaddq_u32(firstRow, previousRow)); + } + } + + for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) { + uint32x4_t currentRowSum = vdupq_n_u32(0); + uint32_t *intRow = aIntegralImage + (y * stride32bit); + uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit; + uint8_t *sourceRow = aSource + aSourceStride * (y - aTopInflation); + + uint32_t pixel = sourceRow[0]; + for (int x = 0; x < aLeftInflation; x += 4) { + uint32_t temp[4]; + temp[0] = pixel; + temp[1] = temp[0] + pixel; + temp[2] = temp[1] + pixel; + temp[3] = temp[2] + pixel; + uint32x4_t sumPixels = vld1q_u32(temp); + sumPixels = vaddq_u32(sumPixels, currentRowSum); + currentRowSum = vdupq_n_u32(vgetq_lane_u32(sumPixels, 3)); + vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x))); + } + + for (int x = aLeftInflation; x < (aSize.width + aLeftInflation); x += 4) { + // It's important to shuffle here. When we exit this loop currentRowSum + // has to be set to sumPixels, so that the following loop can get the + // correct pixel for the currentRowSum. The highest order pixel in + // currentRowSum could've originated from accumulation in the stride. + currentRowSum = vdupq_n_u32(vgetq_lane_u32(currentRowSum, 3)); + + uint32_t temp[4]; + temp[0] = *(sourceRow + (x - aLeftInflation)); + temp[1] = temp[0] + *(sourceRow + (x - aLeftInflation) + 1); + temp[2] = temp[1] + *(sourceRow + (x - aLeftInflation) + 2); + temp[3] = temp[2] + *(sourceRow + (x - aLeftInflation) + 3); + uint32x4_t sumPixels = vld1q_u32(temp); + sumPixels = vaddq_u32(sumPixels, currentRowSum); + currentRowSum = sumPixels; + vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x))); + } + + pixel = sourceRow[aSize.width - 1]; + int x = (aSize.width + aLeftInflation); + if ((aSize.width & 3)) { + // Deal with unaligned portion. Get the correct pixel from currentRowSum, + // see explanation above. + uint32_t intCurrentRowSum = ((uint32_t*)¤tRowSum)[(aSize.width % 4) - 1]; + for (; x < integralImageSize.width; x++) { + // We could be unaligned here! + if (!(x & 3)) { + // aligned! + currentRowSum = vdupq_n_u32(intCurrentRowSum); + break; + } + intCurrentRowSum += pixel; + intRow[x] = intPrevRow[x] + intCurrentRowSum; + } + } else { + currentRowSum = vdupq_n_u32(vgetq_lane_u32(currentRowSum, 3)); + } + + for (; x < integralImageSize.width; x += 4) { + uint32_t temp[4]; + temp[0] = pixel; + temp[1] = temp[0] + pixel; + temp[2] = temp[1] + pixel; + temp[3] = temp[2] + pixel; + uint32x4_t sumPixels = vld1q_u32(temp); + sumPixels = vaddq_u32(sumPixels, currentRowSum); + currentRowSum = vdupq_n_u32(vgetq_lane_u32(sumPixels, 3)); + vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x))); + } + } + + if (aBottomInflation) { + // Store the last valid row of our source image in the last row of + // our integral image. This will be overwritten with the correct values + // in the upcoming loop. + LoadIntegralRowFromRow(aIntegralImage + (integralImageSize.height - 1) * stride32bit, + aSource + (aSize.height - 1) * aSourceStride, aSize.width, aLeftInflation, aRightInflation); + + for (int y = aSize.height + aTopInflation; y < integralImageSize.height; y++) { + uint32_t *intRow = aIntegralImage + (y * stride32bit); + uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit; + uint32_t *intLastRow = aIntegralImage + (integralImageSize.height - 1) * stride32bit; + for (int x = 0; x < integralImageSize.width; x += 4) { + vst1q_u32(intRow + x, + vaddq_u32(vld1q_u32(intLastRow + x), + vld1q_u32(intPrevRow + x))); + } + } + } +} + +/** + * Attempt to do an in-place box blur using an integral image. + */ +void +AlphaBoxBlur::BoxBlur_NEON(uint8_t* aData, + int32_t aLeftLobe, + int32_t aRightLobe, + int32_t aTopLobe, + int32_t aBottomLobe, + uint32_t *aIntegralImage, + size_t aIntegralImageStride) +{ + IntSize size = GetSize(); + + MOZ_ASSERT(size.height > 0); + + // Our 'left' or 'top' lobe will include the current pixel. i.e. when + // looking at an integral image the value of a pixel at 'x,y' is calculated + // using the value of the integral image values above/below that. + aLeftLobe++; + aTopLobe++; + int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe); + + MOZ_ASSERT(boxSize > 0); + + if (boxSize == 1) { + return; + } + + uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize); + uint32_t stride32bit = aIntegralImageStride / 4; + int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value(); + + GenerateIntegralImage_NEON(leftInflation, aRightLobe, aTopLobe, aBottomLobe, + aIntegralImage, aIntegralImageStride, aData, + mStride, size); + + uint32x2_t divisor = vdup_n_u32(reciprocal); + + // This points to the start of the rectangle within the IntegralImage that overlaps + // the surface being blurred. + uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation; + IntRect skipRect = mSkipRect; + int32_t stride = mStride; + uint8_t *data = aData; + + for (int32_t y = 0; y < size.height; y++) { + bool inSkipRectY = y > skipRect.y && y < skipRect.YMost(); + uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) - aLeftLobe); + uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) + aRightLobe); + uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) + aRightLobe); + uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) - aLeftLobe); + + int32_t x = 0; + // Process 16 pixels at a time for as long as possible. + for (; x <= size.width - 16; x += 16) { + if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) { + x = skipRect.XMost() - 16; + // Trigger early jump on coming loop iterations, this will be reset + // next line anyway. + inSkipRectY = false; + continue; + } + + uint32x4_t topLeft; + uint32x4_t topRight; + uint32x4_t bottomRight; + uint32x4_t bottomLeft; + topLeft = vld1q_u32(topLeftBase + x); + topRight = vld1q_u32(topRightBase + x); + bottomRight = vld1q_u32(bottomRightBase + x); + bottomLeft = vld1q_u32(bottomLeftBase + x); + uint16x4_t result1 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor); + + topLeft = vld1q_u32(topLeftBase + x + 4); + topRight = vld1q_u32(topRightBase + x + 4); + bottomRight = vld1q_u32(bottomRightBase + x + 4); + bottomLeft = vld1q_u32(bottomLeftBase + x + 4); + uint16x4_t result2 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor); + + topLeft = vld1q_u32(topLeftBase + x + 8); + topRight = vld1q_u32(topRightBase + x + 8); + bottomRight = vld1q_u32(bottomRightBase + x + 8); + bottomLeft = vld1q_u32(bottomLeftBase + x + 8); + uint16x4_t result3 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor); + + topLeft = vld1q_u32(topLeftBase + x + 12); + topRight = vld1q_u32(topRightBase + x + 12); + bottomRight = vld1q_u32(bottomRightBase + x + 12); + bottomLeft = vld1q_u32(bottomLeftBase + x + 12); + uint16x4_t result4 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor); + + uint8x8_t combine1 = vqmovn_u16(vcombine_u16(result1, result2)); + uint8x8_t combine2 = vqmovn_u16(vcombine_u16(result3, result4)); + uint8x16_t final = vcombine_u8(combine1, combine2); + vst1q_u8(data + stride * y + x, final); + } + + // Process the remaining pixels 4 bytes at a time. + for (; x < size.width; x += 4) { + if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) { + x = skipRect.XMost() - 4; + // Trigger early jump on coming loop iterations, this will be reset + // next line anyway. + inSkipRectY = false; + continue; + } + + uint32x4_t topLeft = vld1q_u32(topLeftBase + x); + uint32x4_t topRight = vld1q_u32(topRightBase + x); + uint32x4_t bottomRight = vld1q_u32(bottomRightBase + x); + uint32x4_t bottomLeft = vld1q_u32(bottomLeftBase + x); + uint16x4_t result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor); + uint32x2_t final = vreinterpret_u32_u8(vmovn_u16(vcombine_u16(result, vdup_n_u16(0)))); + *(uint32_t*)(data + stride * y + x) = vget_lane_u32(final, 0); + } + } +} + +} +} + diff --git a/gfx/2d/BlurSSE2.cpp b/gfx/2d/BlurSSE2.cpp index d67cf07bd..52ca189bb 100644 --- a/gfx/2d/BlurSSE2.cpp +++ b/gfx/2d/BlurSSE2.cpp @@ -12,14 +12,32 @@ namespace mozilla { namespace gfx { MOZ_ALWAYS_INLINE -uint32_t DivideAndPack(__m128i aValues, __m128i aDivisor, __m128i aMask) +__m128i Divide(__m128i aValues, __m128i aDivisor) { - __m128i multiplied = _mm_srli_epi64(_mm_mul_epu32(aValues, aDivisor), 32); // 00p300p1 - multiplied = _mm_or_si128(multiplied, _mm_and_si128(_mm_mul_epu32(_mm_srli_epi64(aValues, 32), aDivisor), - aMask)); // p4p3p2p1 - __m128i final = _mm_packus_epi16(_mm_packs_epi32(multiplied, _mm_setzero_si128()), _mm_setzero_si128()); + const __m128i mask = _mm_setr_epi32(0x0, 0xffffffff, 0x0, 0xffffffff); + static const union { + int64_t i64[2]; + __m128i m; + } roundingAddition = { { int64_t(1) << 31, int64_t(1) << 31 } }; + + __m128i multiplied31 = _mm_mul_epu32(aValues, aDivisor); + __m128i multiplied42 = _mm_mul_epu32(_mm_srli_epi64(aValues, 32), aDivisor); + + // Add 1 << 31 before shifting or masking the lower 32 bits away, so that the + // result is rounded. + __m128i p_3_1 = _mm_srli_epi64(_mm_add_epi64(multiplied31, roundingAddition.m), 32); + __m128i p4_2_ = _mm_and_si128(_mm_add_epi64(multiplied42, roundingAddition.m), mask); + __m128i p4321 = _mm_or_si128(p_3_1, p4_2_); + return p4321; +} - return _mm_cvtsi128_si32(final); +MOZ_ALWAYS_INLINE +__m128i BlurFourPixels(const __m128i& aTopLeft, const __m128i& aTopRight, + const __m128i& aBottomRight, const __m128i& aBottomLeft, + const __m128i& aDivisor) +{ + __m128i values = _mm_add_epi32(_mm_sub_epi32(_mm_sub_epi32(aBottomRight, aTopRight), aBottomLeft), aTopLeft); + return Divide(values, aDivisor); } MOZ_ALWAYS_INLINE @@ -175,7 +193,7 @@ GenerateIntegralImage_SSE2(int32_t aLeftInflation, int32_t aRightInflation, */ void AlphaBoxBlur::BoxBlur_SSE2(uint8_t* aData, - int32_t aLeftLobe, + int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe, int32_t aBottomLobe, @@ -209,7 +227,6 @@ AlphaBoxBlur::BoxBlur_SSE2(uint8_t* aData, mStride, size); __m128i divisor = _mm_set1_epi32(reciprocal); - __m128i mask = _mm_setr_epi32(0x0, 0xffffffff, 0x0, 0xffffffff); // This points to the start of the rectangle within the IntegralImage that overlaps // the surface being blurred. @@ -226,7 +243,53 @@ AlphaBoxBlur::BoxBlur_SSE2(uint8_t* aData, uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) + aRightLobe); uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) - aLeftLobe); - for (int32_t x = 0; x < size.width; x += 4) { + int32_t x = 0; + // Process 16 pixels at a time for as long as possible. + for (; x <= size.width - 16; x += 16) { + if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) { + x = skipRect.XMost() - 16; + // Trigger early jump on coming loop iterations, this will be reset + // next line anyway. + inSkipRectY = false; + continue; + } + + __m128i topLeft; + __m128i topRight; + __m128i bottomRight; + __m128i bottomLeft; + + topLeft = loadUnaligned128((__m128i*)(topLeftBase + x)); + topRight = loadUnaligned128((__m128i*)(topRightBase + x)); + bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x)); + bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x)); + __m128i result1 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor); + + topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 4)); + topRight = loadUnaligned128((__m128i*)(topRightBase + x + 4)); + bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 4)); + bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 4)); + __m128i result2 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor); + + topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 8)); + topRight = loadUnaligned128((__m128i*)(topRightBase + x + 8)); + bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 8)); + bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 8)); + __m128i result3 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor); + + topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 12)); + topRight = loadUnaligned128((__m128i*)(topRightBase + x + 12)); + bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 12)); + bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 12)); + __m128i result4 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor); + + __m128i final = _mm_packus_epi16(_mm_packs_epi32(result1, result2), _mm_packs_epi32(result3, result4)); + + _mm_storeu_si128((__m128i*)(data + stride * y + x), final); + } + + // Process the remaining pixels 4 bytes at a time. + for (; x < size.width; x += 4) { if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) { x = skipRect.XMost() - 4; // Trigger early jump on coming loop iterations, this will be reset @@ -239,9 +302,10 @@ AlphaBoxBlur::BoxBlur_SSE2(uint8_t* aData, __m128i bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x)); __m128i bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x)); - __m128i values = _mm_add_epi32(_mm_sub_epi32(_mm_sub_epi32(bottomRight, topRight), bottomLeft), topLeft); + __m128i result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor); + __m128i final = _mm_packus_epi16(_mm_packs_epi32(result, _mm_setzero_si128()), _mm_setzero_si128()); - *(uint32_t*)(data + stride * y + x) = DivideAndPack(values, divisor, mask); + *(uint32_t*)(data + stride * y + x) = _mm_cvtsi128_si32(final); } } diff --git a/gfx/2d/BorrowedContext.h b/gfx/2d/BorrowedContext.h new file mode 100644 index 000000000..b2d096bac --- /dev/null +++ b/gfx/2d/BorrowedContext.h @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MOZILLA_GFX_BORROWED_CONTEXT_H +#define _MOZILLA_GFX_BORROWED_CONTEXT_H + +#include "2D.h" + +struct _cairo; +typedef struct _cairo cairo_t; + +namespace mozilla { + +namespace gfx { + +/* This is a helper class that let's you borrow a cairo_t from a + * DrawTargetCairo. This is used for drawing themed widgets. + * + * Callers should check the cr member after constructing the object + * to see if it succeeded. The DrawTarget should not be used while + * the context is borrowed. */ +class BorrowedCairoContext +{ +public: + BorrowedCairoContext() + : mCairo(nullptr) + , mDT(nullptr) + { } + + explicit BorrowedCairoContext(DrawTarget *aDT) + : mDT(aDT) + { + mCairo = BorrowCairoContextFromDrawTarget(aDT); + } + + // We can optionally Init after construction in + // case we don't know what the DT will be at construction + // time. + cairo_t *Init(DrawTarget *aDT) + { + MOZ_ASSERT(!mDT, "Can't initialize twice!"); + mDT = aDT; + return mCairo = BorrowCairoContextFromDrawTarget(aDT); + } + + // The caller needs to call Finish if cr is non-null when + // they are done with the context. This is currently explicit + // instead of happening implicitly in the destructor to make + // what's happening in the caller more clear. It also + // let's you resume using the DrawTarget in the same scope. + void Finish() + { + if (mCairo) { + ReturnCairoContextToDrawTarget(mDT, mCairo); + mCairo = nullptr; + } + } + + ~BorrowedCairoContext() { + MOZ_ASSERT(!mCairo); + } + + cairo_t *mCairo; +private: + static cairo_t* BorrowCairoContextFromDrawTarget(DrawTarget *aDT); + static void ReturnCairoContextToDrawTarget(DrawTarget *aDT, cairo_t *aCairo); + DrawTarget *mDT; +}; + +#ifdef XP_MACOSX +/* This is a helper class that let's you borrow a CGContextRef from a + * DrawTargetCG. This is used for drawing themed widgets. + * + * Callers should check the cg member after constructing the object + * to see if it succeeded. The DrawTarget should not be used while + * the context is borrowed. */ +class BorrowedCGContext +{ +public: + BorrowedCGContext() + : cg(nullptr) + , mDT(nullptr) + { } + + explicit BorrowedCGContext(DrawTarget *aDT) + : mDT(aDT) + { + MOZ_ASSERT(aDT, "Caller should check for nullptr"); + cg = BorrowCGContextFromDrawTarget(aDT); + } + + // We can optionally Init after construction in + // case we don't know what the DT will be at construction + // time. + CGContextRef Init(DrawTarget *aDT) + { + MOZ_ASSERT(aDT, "Caller should check for nullptr"); + MOZ_ASSERT(!mDT, "Can't initialize twice!"); + mDT = aDT; + cg = BorrowCGContextFromDrawTarget(aDT); + return cg; + } + + // The caller needs to call Finish if cg is non-null when + // they are done with the context. This is currently explicit + // instead of happening implicitly in the destructor to make + // what's happening in the caller more clear. It also + // let's you resume using the DrawTarget in the same scope. + void Finish() + { + if (cg) { + ReturnCGContextToDrawTarget(mDT, cg); + cg = nullptr; + } + } + + ~BorrowedCGContext() { + MOZ_ASSERT(!cg); + } + + CGContextRef cg; +private: + static CGContextRef BorrowCGContextFromDrawTarget(DrawTarget *aDT); + static void ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg); + DrawTarget *mDT; +}; +#endif + +} +} + +#endif // _MOZILLA_GFX_BORROWED_CONTEXT_H diff --git a/gfx/2d/Coord.h b/gfx/2d/Coord.h new file mode 100644 index 000000000..2a981b0ef --- /dev/null +++ b/gfx/2d/Coord.h @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_COORD_H_ +#define MOZILLA_GFX_COORD_H_ + +#include "mozilla/Attributes.h" +#include "Types.h" +#include "BaseCoord.h" + +#include <cmath> + +namespace mozilla { + +template <typename> struct IsPixel; + +namespace gfx { + +template <class units> struct IntCoordTyped; +template <class units> struct CoordTyped; + +// CommonType<coord, primitive> is a metafunction that returns the type of the +// result of an arithmetic operation on the underlying type of a strongly-typed +// coordinate type 'coord', and a primitive type 'primitive'. C++ rules for +// arithmetic conversions are designed to avoid losing information - for +// example, the result of adding an int and a float is a float - and we want +// the same behaviour when mixing our coordinate types with primitive types. +// We get C++ to compute the desired result type using 'decltype'. + +template <class coord, class primitive> +struct CommonType; + +template <class units, class primitive> +struct CommonType<IntCoordTyped<units>, primitive> { + typedef decltype(int32_t() + primitive()) type; +}; + +template <class units, class primitive> +struct CommonType<CoordTyped<units>, primitive> { + typedef decltype(Float() + primitive()) type; +}; + +// This is a base class that provides mixed-type operator overloads between +// a strongly-typed Coord and a primitive value. It is needed to avoid +// ambiguities at mixed-type call sites, because Coord classes are implicitly +// convertible to their underlying value type. As we transition more of our code +// to strongly-typed classes, we may be able to remove some or all of these +// overloads. +template <class coord, class primitive> +struct CoordOperatorsHelper { + friend bool operator==(coord aA, primitive aB) { + return aA.value == aB; + } + friend bool operator==(primitive aA, coord aB) { + return aA == aB.value; + } + friend bool operator!=(coord aA, primitive aB) { + return aA.value != aB; + } + friend bool operator!=(primitive aA, coord aB) { + return aA != aB.value; + } + + typedef typename CommonType<coord, primitive>::type result_type; + + friend result_type operator+(coord aA, primitive aB) { + return aA.value + aB; + } + friend result_type operator+(primitive aA, coord aB) { + return aA + aB.value; + } + friend result_type operator-(coord aA, primitive aB) { + return aA.value - aB; + } + friend result_type operator-(primitive aA, coord aB) { + return aA - aB.value; + } + friend result_type operator*(coord aCoord, primitive aScale) { + return aCoord.value * aScale; + } + friend result_type operator*(primitive aScale, coord aCoord) { + return aScale * aCoord.value; + } + friend result_type operator/(coord aCoord, primitive aScale) { + return aCoord.value / aScale; + } + // 'scale / coord' is intentionally omitted because it doesn't make sense. +}; + +// Note: 'IntCoordTyped<units>' and 'CoordTyped<units>' do not derive from +// 'units' to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61959. + +template<class units> +struct IntCoordTyped : + public BaseCoord< int32_t, IntCoordTyped<units> >, + public CoordOperatorsHelper< IntCoordTyped<units>, float >, + public CoordOperatorsHelper< IntCoordTyped<units>, double > { + static_assert(IsPixel<units>::value, + "'units' must be a coordinate system tag"); + + typedef BaseCoord< int32_t, IntCoordTyped<units> > Super; + + MOZ_CONSTEXPR IntCoordTyped() : Super() {} + MOZ_CONSTEXPR MOZ_IMPLICIT IntCoordTyped(int32_t aValue) : Super(aValue) {} +}; + +template<class units> +struct CoordTyped : + public BaseCoord< Float, CoordTyped<units> >, + public CoordOperatorsHelper< CoordTyped<units>, int32_t >, + public CoordOperatorsHelper< CoordTyped<units>, uint32_t >, + public CoordOperatorsHelper< CoordTyped<units>, double > { + static_assert(IsPixel<units>::value, + "'units' must be a coordinate system tag"); + + typedef BaseCoord< Float, CoordTyped<units> > Super; + + MOZ_CONSTEXPR CoordTyped() : Super() {} + MOZ_CONSTEXPR MOZ_IMPLICIT CoordTyped(Float aValue) : Super(aValue) {} + explicit MOZ_CONSTEXPR CoordTyped(const IntCoordTyped<units>& aCoord) : Super(float(aCoord.value)) {} + + void Round() { + this->value = floor(this->value + 0.5); + } + void Truncate() { + this->value = int32_t(this->value); + } + + IntCoordTyped<units> Rounded() const { + return IntCoordTyped<units>(int32_t(floor(this->value + 0.5))); + } + IntCoordTyped<units> Truncated() const { + return IntCoordTyped<units>(int32_t(this->value)); + } +}; + +} +} + +#endif /* MOZILLA_GFX_COORD_H_ */ diff --git a/gfx/2d/ScaledFontFreetype.h b/gfx/2d/DataSourceSurface.cpp index bc492af12..573ae034a 100644 --- a/gfx/2d/ScaledFontFreetype.h +++ b/gfx/2d/DataSourceSurface.cpp @@ -3,22 +3,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef MOZILLA_GFX_SCALEDFONTFREETYPE_H_ -#define MOZILLA_GFX_SCALEDFONTFREETYPE_H_ - -#include "ScaledFontBase.h" +#include "2D.h" +#include "DataSourceSurfaceWrapper.h" namespace mozilla { namespace gfx { -class ScaledFontFreetype : public ScaledFontBase +TemporaryRef<DataSourceSurface> +DataSourceSurface::GetDataSurface() { -public: - - ScaledFontFreetype(FontOptions* aFont, Float aSize); -}; + return (GetType() == SurfaceType::DATA) ? this : new DataSourceSurfaceWrapper(this); +} } } - -#endif /* MOZILLA_GFX_SCALEDFONTFREETYPE_H_ */ diff --git a/gfx/2d/DataSourceSurfaceWrapper.h b/gfx/2d/DataSourceSurfaceWrapper.h new file mode 100644 index 000000000..445a4adc2 --- /dev/null +++ b/gfx/2d/DataSourceSurfaceWrapper.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_DATASOURCESURFACEWRAPPER_H_ +#define MOZILLA_GFX_DATASOURCESURFACEWRAPPER_H_ + +#include "2D.h" + +namespace mozilla { +namespace gfx { + +// Wraps a DataSourceSurface and forwards all methods except for GetType(), +// from which it always returns SurfaceType::DATA. +class DataSourceSurfaceWrapper : public DataSourceSurface +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceWrapper, override) + explicit DataSourceSurfaceWrapper(DataSourceSurface *aSurface) + : mSurface(aSurface) + {} + + virtual SurfaceType GetType() const override { return SurfaceType::DATA; } + + virtual uint8_t *GetData() override { return mSurface->GetData(); } + virtual int32_t Stride() override { return mSurface->Stride(); } + virtual IntSize GetSize() const override { return mSurface->GetSize(); } + virtual SurfaceFormat GetFormat() const override { return mSurface->GetFormat(); } + virtual bool IsValid() const override { return mSurface->IsValid(); } + +private: + RefPtr<DataSourceSurface> mSurface; +}; + +} +} + +#endif /* MOZILLA_GFX_DATASOURCESURFACEWRAPPER_H_ */ diff --git a/gfx/2d/DataSurfaceHelpers.cpp b/gfx/2d/DataSurfaceHelpers.cpp new file mode 100644 index 000000000..d0f08fa29 --- /dev/null +++ b/gfx/2d/DataSurfaceHelpers.cpp @@ -0,0 +1,285 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <cstring> + +#include "2D.h" +#include "DataSurfaceHelpers.h" +#include "Logging.h" +#include "mozilla/MathAlgorithms.h" +#include "mozilla/PodOperations.h" +#include "Tools.h" + +namespace mozilla { +namespace gfx { + +uint8_t* +DataAtOffset(DataSourceSurface* aSurface, IntPoint aPoint) +{ + if (!SurfaceContainsPoint(aSurface, aPoint)) { + MOZ_CRASH("sample position needs to be inside surface!"); + } + + MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()), + "surface size overflows - this should have been prevented when the surface was created"); + + uint8_t* data = aSurface->GetData() + aPoint.y * aSurface->Stride() + + aPoint.x * BytesPerPixel(aSurface->GetFormat()); + + if (data < aSurface->GetData()) { + MOZ_CRASH("out-of-range data access"); + } + + return data; +} + +// This check is safe against integer overflow. +bool +SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint) +{ + IntSize size = aSurface->GetSize(); + return aPoint.x >= 0 && aPoint.x < size.width && + aPoint.y >= 0 && aPoint.y < size.height; +} + +void +ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride) +{ + int height = aSize.height, width = aSize.width * 4; + + for (int row = 0; row < height; ++row) { + for (int column = 0; column < width; column += 4) { +#ifdef IS_BIG_ENDIAN + aData[column] = 0xFF; +#else + aData[column + 3] = 0xFF; +#endif + } + aData += aStride; + } +} + +void +CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize, + int32_t aSrcStride, int32_t aBytesPerPixel) +{ + MOZ_ASSERT(aBytesPerPixel > 0, + "Negative stride for aDst not currently supported"); + MOZ_ASSERT(BufferSizeFromStrideAndHeight(aSrcStride, aSrcSize.height) > 0, + "How did we end up with a surface with such a big buffer?"); + + int packedStride = aSrcSize.width * aBytesPerPixel; + + if (aSrcStride == packedStride) { + // aSrc is already packed, so we can copy with a single memcpy. + memcpy(aDst, aSrc, packedStride * aSrcSize.height); + } else { + // memcpy one row at a time. + for (int row = 0; row < aSrcSize.height; ++row) { + memcpy(aDst, aSrc, packedStride); + aSrc += aSrcStride; + aDst += packedStride; + } + } +} + +void +CopyBGRXSurfaceDataToPackedBGRArray(uint8_t* aSrc, uint8_t* aDst, + IntSize aSrcSize, int32_t aSrcStride) +{ + int packedStride = aSrcSize.width * 3; + + uint8_t* srcPx = aSrc; + uint8_t* dstPx = aDst; + + for (int row = 0; row < aSrcSize.height; ++row) { + for (int col = 0; col < aSrcSize.height; ++col) { + dstPx[0] = srcPx[0]; + dstPx[1] = srcPx[1]; + dstPx[2] = srcPx[2]; + // srcPx[3] (unused or alpha component) dropped on floor + srcPx += 4; + dstPx += 3; + } + srcPx = aSrc += aSrcStride; + dstPx = aDst += packedStride; + } +} + +uint8_t* +SurfaceToPackedBGRA(DataSourceSurface *aSurface) +{ + SurfaceFormat format = aSurface->GetFormat(); + if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) { + return nullptr; + } + + IntSize size = aSurface->GetSize(); + + uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * sizeof(uint32_t)]; + if (!imageBuffer) { + return nullptr; + } + + DataSourceSurface::MappedSurface map; + if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { + delete [] imageBuffer; + return nullptr; + } + + CopySurfaceDataToPackedArray(map.mData, imageBuffer, size, + map.mStride, 4 * sizeof(uint8_t)); + + aSurface->Unmap(); + + if (format == SurfaceFormat::B8G8R8X8) { + // Convert BGRX to BGRA by setting a to 255. + ConvertBGRXToBGRA(reinterpret_cast<uint8_t *>(imageBuffer), size, size.width * sizeof(uint32_t)); + } + + return imageBuffer; +} + +uint8_t* +SurfaceToPackedBGR(DataSourceSurface *aSurface) +{ + SurfaceFormat format = aSurface->GetFormat(); + MOZ_ASSERT(format == SurfaceFormat::B8G8R8X8, "Format not supported"); + + if (format != SurfaceFormat::B8G8R8X8) { + // To support B8G8R8A8 we'd need to un-pre-multiply alpha + return nullptr; + } + + IntSize size = aSurface->GetSize(); + + uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * 3 * sizeof(uint8_t)]; + if (!imageBuffer) { + return nullptr; + } + + DataSourceSurface::MappedSurface map; + if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { + delete [] imageBuffer; + return nullptr; + } + + CopyBGRXSurfaceDataToPackedBGRArray(map.mData, imageBuffer, size, + map.mStride); + + aSurface->Unmap(); + + return imageBuffer; +} + +void +ClearDataSourceSurface(DataSourceSurface *aSurface) +{ + DataSourceSurface::MappedSurface map; + if (!aSurface->Map(DataSourceSurface::MapType::WRITE, &map)) { + MOZ_ASSERT(false, "Failed to map DataSourceSurface"); + return; + } + + // We avoid writing into the gaps between the rows here since we can't be + // sure that some drivers don't use those bytes. + + uint32_t width = aSurface->GetSize().width; + uint32_t bytesPerRow = width * BytesPerPixel(aSurface->GetFormat()); + uint8_t* row = map.mData; + // converting to size_t here because otherwise the temporaries can overflow + // and we can end up with |end| being a bad address! + uint8_t* end = row + size_t(map.mStride) * size_t(aSurface->GetSize().height); + + while (row != end) { + memset(row, 0, bytesPerRow); + row += map.mStride; + } + + aSurface->Unmap(); +} + +size_t +BufferSizeFromStrideAndHeight(int32_t aStride, + int32_t aHeight, + int32_t aExtraBytes) +{ + if (MOZ_UNLIKELY(aHeight <= 0)) { + return 0; + } + + // We limit the length returned to values that can be represented by int32_t + // because we don't want to allocate buffers any bigger than that. This + // allows for a buffer size of over 2 GiB which is already rediculously + // large and will make the process janky. (Note the choice of the signed type + // is deliberate because we specifically don't want the returned value to + // overflow if someone stores the buffer length in an int32_t variable.) + + CheckedInt32 requiredBytes = + CheckedInt32(aStride) * CheckedInt32(aHeight) + CheckedInt32(aExtraBytes); + if (MOZ_UNLIKELY(!requiredBytes.isValid())) { + gfxWarning() << "Buffer size too big; returning zero"; + return 0; + } + return requiredBytes.value(); +} + +/** + * aSrcRect: Rect relative to the aSrc surface + * aDestPoint: Point inside aDest surface + */ +void +CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest, + IntRect aSrcRect, IntPoint aDestPoint) +{ + if (aSrcRect.Overflows() || + IntRect(aDestPoint, aSrcRect.Size()).Overflows()) { + MOZ_CRASH("we should never be getting invalid rects at this point"); + } + + MOZ_RELEASE_ASSERT(aSrc->GetFormat() == aDest->GetFormat(), + "different surface formats"); + MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect), + "source rect too big for source surface"); + MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(IntRect(aDestPoint, aSrcRect.Size())), + "dest surface too small"); + + if (aSrcRect.IsEmpty()) { + return; + } + + uint8_t* sourceData = DataAtOffset(aSrc, aSrcRect.TopLeft()); + uint32_t sourceStride = aSrc->Stride(); + uint8_t* destData = DataAtOffset(aDest, aDestPoint); + uint32_t destStride = aDest->Stride(); + + if (BytesPerPixel(aSrc->GetFormat()) == 4) { + for (int32_t y = 0; y < aSrcRect.height; y++) { + PodCopy((int32_t*)destData, (int32_t*)sourceData, aSrcRect.width); + sourceData += sourceStride; + destData += destStride; + } + } else if (BytesPerPixel(aSrc->GetFormat()) == 1) { + for (int32_t y = 0; y < aSrcRect.height; y++) { + PodCopy(destData, sourceData, aSrcRect.width); + sourceData += sourceStride; + destData += destStride; + } + } +} + +TemporaryRef<DataSourceSurface> +CreateDataSourceSurfaceByCloning(DataSourceSurface* aSource) +{ + RefPtr<DataSourceSurface> copy = + Factory::CreateDataSourceSurface(aSource->GetSize(), aSource->GetFormat(), true); + if (copy) { + CopyRect(aSource, copy, IntRect(IntPoint(), aSource->GetSize()), IntPoint()); + } + return copy.forget(); +} + +} +} diff --git a/gfx/2d/DataSurfaceHelpers.h b/gfx/2d/DataSurfaceHelpers.h new file mode 100644 index 000000000..e1afecee1 --- /dev/null +++ b/gfx/2d/DataSurfaceHelpers.h @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MOZILLA_GFX_DATASURFACEHELPERS_H +#define _MOZILLA_GFX_DATASURFACEHELPERS_H + +#include "2D.h" + +namespace mozilla { +namespace gfx { + +void +ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride); + +/** + * Copy the pixel data from aSrc and pack it into aDst. aSrcSize, aSrcStride + * and aBytesPerPixel give the size, stride and bytes per pixel for aSrc's + * surface. Callers are responsible for making sure that aDst is big enough to + * contain |aSrcSize.width * aSrcSize.height * aBytesPerPixel| bytes. + */ +void +CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize, + int32_t aSrcStride, int32_t aBytesPerPixel); + +/** + * Convert aSurface to a packed buffer in BGRA format. The pixel data is + * returned in a buffer allocated with new uint8_t[]. The caller then has + * ownership of the buffer and is responsible for delete[]'ing it. + */ +uint8_t* +SurfaceToPackedBGRA(DataSourceSurface *aSurface); + +/** + * Convert aSurface to a packed buffer in BGR format. The pixel data is + * returned in a buffer allocated with new uint8_t[]. The caller then has + * ownership of the buffer and is responsible for delete[]'ing it. + * + * This function is currently only intended for use with surfaces of format + * SurfaceFormat::B8G8R8X8 since the X components of the pixel data (if any) + * are simply dropped (no attempt is made to un-pre-multiply alpha from the + * color components). + */ +uint8_t* +SurfaceToPackedBGR(DataSourceSurface *aSurface); + +/** + * Clears all the bytes in a DataSourceSurface's data array to zero (so to + * transparent black for SurfaceFormat::B8G8R8A8, for example). + * Note that DataSourceSurfaces can be initialized to zero, which is + * more efficient than zeroing the surface after initialization. + */ +void +ClearDataSourceSurface(DataSourceSurface *aSurface); + +/** + * Multiplies aStride and aHeight and makes sure the result is limited to + * something sane. To keep things consistent, this should always be used + * wherever we allocate a buffer based on surface stride and height. + * + * @param aExtra Optional argument to specify an additional number of trailing + * bytes (useful for creating intermediate surfaces for filters, for + * example). + * + * @return The result of the multiplication if it is acceptable, or else zero. + */ +size_t +BufferSizeFromStrideAndHeight(int32_t aStride, + int32_t aHeight, + int32_t aExtraBytes = 0); + +/** + * Copy aSrcRect from aSrc to aDest starting at aDestPoint. + */ +void +CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest, + IntRect aSrcRect, IntPoint aDestPoint); + +/** + * Create a non aliasing copy of aSource. This creates a new DataSourceSurface + * using the factory and copies the bits. + * + * @return a dss allocated by Factory that contains a copy a aSource. + */ +TemporaryRef<DataSourceSurface> +CreateDataSourceSurfaceByCloning(DataSourceSurface* aSource); + +/** + * Return the byte at aPoint. + */ +uint8_t* +DataAtOffset(DataSourceSurface* aSurface, IntPoint aPoint); + +/** + * Check if aPoint is contained by the surface. + * + * @returns true if and only if aPoint is inside the surface. + */ +bool +SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint); + +} +} + +#endif // _MOZILLA_GFX_DATASURFACEHELPERS_H diff --git a/gfx/2d/DrawCommand.h b/gfx/2d/DrawCommand.h new file mode 100644 index 000000000..6f1466d8e --- /dev/null +++ b/gfx/2d/DrawCommand.h @@ -0,0 +1,504 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_DRAWCOMMAND_H_ +#define MOZILLA_GFX_DRAWCOMMAND_H_ + +#include "2D.h" +#include "Filters.h" +#include <vector> + +namespace mozilla { +namespace gfx { + +enum class CommandType : int8_t { + DRAWSURFACE = 0, + DRAWFILTER, + DRAWSURFACEWITHSHADOW, + CLEARRECT, + COPYSURFACE, + COPYRECT, + FILLRECT, + STROKERECT, + STROKELINE, + STROKE, + FILL, + FILLGLYPHS, + MASK, + MASKSURFACE, + PUSHCLIP, + PUSHCLIPRECT, + POPCLIP, + SETTRANSFORM +}; + +class DrawingCommand +{ +public: + virtual ~DrawingCommand() {} + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix& aTransform) = 0; + +protected: + explicit DrawingCommand(CommandType aType) + : mType(aType) + { + } + + CommandType GetType() { return mType; } + +private: + CommandType mType; +}; + +class StoredPattern +{ +public: + explicit StoredPattern(const Pattern& aPattern) + { + Assign(aPattern); + } + + void Assign(const Pattern& aPattern) + { + switch (aPattern.GetType()) { + case PatternType::COLOR: + new (mColor)ColorPattern(*static_cast<const ColorPattern*>(&aPattern)); + return; + case PatternType::SURFACE: + { + SurfacePattern* surfPat = new (mColor)SurfacePattern(*static_cast<const SurfacePattern*>(&aPattern)); + surfPat->mSurface->GuaranteePersistance(); + return; + } + case PatternType::LINEAR_GRADIENT: + new (mColor)LinearGradientPattern(*static_cast<const LinearGradientPattern*>(&aPattern)); + return; + case PatternType::RADIAL_GRADIENT: + new (mColor)RadialGradientPattern(*static_cast<const RadialGradientPattern*>(&aPattern)); + return; + } + } + + ~StoredPattern() + { + reinterpret_cast<Pattern*>(mColor)->~Pattern(); + } + + operator Pattern&() + { + return *reinterpret_cast<Pattern*>(mColor); + } + + operator const Pattern&() const + { + return *reinterpret_cast<const Pattern*>(mColor); + } + + StoredPattern(const StoredPattern& aPattern) + { + Assign(aPattern); + } + +private: + StoredPattern operator=(const StoredPattern& aOther) + { + // Block this so that we notice if someone's doing excessive assigning. + return *this; + } + + union { + char mColor[sizeof(ColorPattern)]; + char mLinear[sizeof(LinearGradientPattern)]; + char mRadial[sizeof(RadialGradientPattern)]; + char mSurface[sizeof(SurfacePattern)]; + }; +}; + +class DrawSurfaceCommand : public DrawingCommand +{ +public: + DrawSurfaceCommand(SourceSurface *aSurface, const Rect& aDest, + const Rect& aSource, const DrawSurfaceOptions& aSurfOptions, + const DrawOptions& aOptions) + : DrawingCommand(CommandType::DRAWSURFACE) + , mSurface(aSurface), mDest(aDest) + , mSource(aSource), mSurfOptions(aSurfOptions) + , mOptions(aOptions) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->DrawSurface(mSurface, mDest, mSource, mSurfOptions, mOptions); + } + +private: + RefPtr<SourceSurface> mSurface; + Rect mDest; + Rect mSource; + DrawSurfaceOptions mSurfOptions; + DrawOptions mOptions; +}; + +class DrawFilterCommand : public DrawingCommand +{ +public: + DrawFilterCommand(FilterNode* aFilter, const Rect& aSourceRect, + const Point& aDestPoint, const DrawOptions& aOptions) + : DrawingCommand(CommandType::DRAWSURFACE) + , mFilter(aFilter), mSourceRect(aSourceRect) + , mDestPoint(aDestPoint), mOptions(aOptions) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->DrawFilter(mFilter, mSourceRect, mDestPoint, mOptions); + } + +private: + RefPtr<FilterNode> mFilter; + Rect mSourceRect; + Point mDestPoint; + DrawOptions mOptions; +}; + +class ClearRectCommand : public DrawingCommand +{ +public: + explicit ClearRectCommand(const Rect& aRect) + : DrawingCommand(CommandType::CLEARRECT) + , mRect(aRect) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->ClearRect(mRect); + } + +private: + Rect mRect; +}; + +class CopySurfaceCommand : public DrawingCommand +{ +public: + CopySurfaceCommand(SourceSurface* aSurface, + const IntRect& aSourceRect, + const IntPoint& aDestination) + : DrawingCommand(CommandType::COPYSURFACE) + , mSurface(aSurface) + , mSourceRect(aSourceRect) + , mDestination(aDestination) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix& aTransform) + { + MOZ_ASSERT(!aTransform.HasNonIntegerTranslation()); + Point dest(Float(mDestination.x), Float(mDestination.y)); + dest = aTransform * dest; + aDT->CopySurface(mSurface, mSourceRect, IntPoint(uint32_t(dest.x), uint32_t(dest.y))); + } + +private: + RefPtr<SourceSurface> mSurface; + IntRect mSourceRect; + IntPoint mDestination; +}; + +class FillRectCommand : public DrawingCommand +{ +public: + FillRectCommand(const Rect& aRect, + const Pattern& aPattern, + const DrawOptions& aOptions) + : DrawingCommand(CommandType::FILLRECT) + , mRect(aRect) + , mPattern(aPattern) + , mOptions(aOptions) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->FillRect(mRect, mPattern, mOptions); + } + +private: + Rect mRect; + StoredPattern mPattern; + DrawOptions mOptions; +}; + +class StrokeRectCommand : public DrawingCommand +{ +public: + StrokeRectCommand(const Rect& aRect, + const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) + : DrawingCommand(CommandType::STROKERECT) + , mRect(aRect) + , mPattern(aPattern) + , mStrokeOptions(aStrokeOptions) + , mOptions(aOptions) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->StrokeRect(mRect, mPattern, mStrokeOptions, mOptions); + } + +private: + Rect mRect; + StoredPattern mPattern; + StrokeOptions mStrokeOptions; + DrawOptions mOptions; +}; + +class StrokeLineCommand : public DrawingCommand +{ +public: + StrokeLineCommand(const Point& aStart, + const Point& aEnd, + const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) + : DrawingCommand(CommandType::STROKELINE) + , mStart(aStart) + , mEnd(aEnd) + , mPattern(aPattern) + , mStrokeOptions(aStrokeOptions) + , mOptions(aOptions) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->StrokeLine(mStart, mEnd, mPattern, mStrokeOptions, mOptions); + } + +private: + Point mStart; + Point mEnd; + StoredPattern mPattern; + StrokeOptions mStrokeOptions; + DrawOptions mOptions; +}; + +class FillCommand : public DrawingCommand +{ +public: + FillCommand(const Path* aPath, + const Pattern& aPattern, + const DrawOptions& aOptions) + : DrawingCommand(CommandType::FILL) + , mPath(const_cast<Path*>(aPath)) + , mPattern(aPattern) + , mOptions(aOptions) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->Fill(mPath, mPattern, mOptions); + } + +private: + RefPtr<Path> mPath; + StoredPattern mPattern; + DrawOptions mOptions; +}; + +class StrokeCommand : public DrawingCommand +{ +public: + StrokeCommand(const Path* aPath, + const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) + : DrawingCommand(CommandType::STROKE) + , mPath(const_cast<Path*>(aPath)) + , mPattern(aPattern) + , mStrokeOptions(aStrokeOptions) + , mOptions(aOptions) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->Stroke(mPath, mPattern, mStrokeOptions, mOptions); + } + +private: + RefPtr<Path> mPath; + StoredPattern mPattern; + StrokeOptions mStrokeOptions; + DrawOptions mOptions; +}; + +class FillGlyphsCommand : public DrawingCommand +{ +public: + FillGlyphsCommand(ScaledFont* aFont, + const GlyphBuffer& aBuffer, + const Pattern& aPattern, + const DrawOptions& aOptions, + const GlyphRenderingOptions* aRenderingOptions) + : DrawingCommand(CommandType::FILLGLYPHS) + , mFont(aFont) + , mPattern(aPattern) + , mOptions(aOptions) + , mRenderingOptions(const_cast<GlyphRenderingOptions*>(aRenderingOptions)) + { + mGlyphs.resize(aBuffer.mNumGlyphs); + memcpy(&mGlyphs.front(), aBuffer.mGlyphs, sizeof(Glyph) * aBuffer.mNumGlyphs); + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + GlyphBuffer buf; + buf.mNumGlyphs = mGlyphs.size(); + buf.mGlyphs = &mGlyphs.front(); + aDT->FillGlyphs(mFont, buf, mPattern, mOptions, mRenderingOptions); + } + +private: + RefPtr<ScaledFont> mFont; + std::vector<Glyph> mGlyphs; + StoredPattern mPattern; + DrawOptions mOptions; + RefPtr<GlyphRenderingOptions> mRenderingOptions; +}; + +class MaskCommand : public DrawingCommand +{ +public: + MaskCommand(const Pattern& aSource, + const Pattern& aMask, + const DrawOptions& aOptions) + : DrawingCommand(CommandType::MASK) + , mSource(aSource) + , mMask(aMask) + , mOptions(aOptions) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->Mask(mSource, mMask, mOptions); + } + +private: + StoredPattern mSource; + StoredPattern mMask; + DrawOptions mOptions; +}; + +class MaskSurfaceCommand : public DrawingCommand +{ +public: + MaskSurfaceCommand(const Pattern& aSource, + const SourceSurface* aMask, + const Point& aOffset, + const DrawOptions& aOptions) + : DrawingCommand(CommandType::MASKSURFACE) + , mSource(aSource) + , mMask(const_cast<SourceSurface*>(aMask)) + , mOffset(aOffset) + , mOptions(aOptions) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->MaskSurface(mSource, mMask, mOffset, mOptions); + } + +private: + StoredPattern mSource; + RefPtr<SourceSurface> mMask; + Point mOffset; + DrawOptions mOptions; +}; + +class PushClipCommand : public DrawingCommand +{ +public: + explicit PushClipCommand(const Path* aPath) + : DrawingCommand(CommandType::PUSHCLIP) + , mPath(const_cast<Path*>(aPath)) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->PushClip(mPath); + } + +private: + RefPtr<Path> mPath; +}; + +class PushClipRectCommand : public DrawingCommand +{ +public: + explicit PushClipRectCommand(const Rect& aRect) + : DrawingCommand(CommandType::PUSHCLIPRECT) + , mRect(aRect) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->PushClipRect(mRect); + } + +private: + Rect mRect; +}; + +class PopClipCommand : public DrawingCommand +{ +public: + PopClipCommand() + : DrawingCommand(CommandType::POPCLIP) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->PopClip(); + } +}; + +class SetTransformCommand : public DrawingCommand +{ +public: + explicit SetTransformCommand(const Matrix& aTransform) + : DrawingCommand(CommandType::SETTRANSFORM) + , mTransform(aTransform) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix& aMatrix) + { + Matrix transform = mTransform; + transform *= aMatrix; + aDT->SetTransform(transform); + } + +private: + Matrix mTransform; +}; + +} /* namespace mozilla */ +} /* namespace gfx */ + +#endif /* MOZILLA_GFX_DRAWCOMMAND_H_ */ diff --git a/gfx/2d/DrawEventRecorder.cpp b/gfx/2d/DrawEventRecorder.cpp index 41d7680c6..3a663765c 100644 --- a/gfx/2d/DrawEventRecorder.cpp +++ b/gfx/2d/DrawEventRecorder.cpp @@ -29,7 +29,7 @@ DrawEventRecorderPrivate::RecordEvent(const RecordedEvent &aEvent) } DrawEventRecorderFile::DrawEventRecorderFile(const char *aFilename) - : DrawEventRecorderPrivate(NULL) + : DrawEventRecorderPrivate(nullptr) , mOutputFile(aFilename, ofstream::binary) { mOutputStream = &mOutputFile; diff --git a/gfx/2d/DrawEventRecorder.h b/gfx/2d/DrawEventRecorder.h index 3652996e7..e297a5871 100644 --- a/gfx/2d/DrawEventRecorder.h +++ b/gfx/2d/DrawEventRecorder.h @@ -12,7 +12,7 @@ #include <fstream> #if defined(_MSC_VER) -#include <hash_set> +#include <unordered_set> #else #include <set> #endif @@ -25,7 +25,8 @@ class PathRecording; class DrawEventRecorderPrivate : public DrawEventRecorder { public: - DrawEventRecorderPrivate(std::ostream *aStream); + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPrivate) + explicit DrawEventRecorderPrivate(std::ostream *aStream); virtual ~DrawEventRecorderPrivate() { } void RecordEvent(const RecordedEvent &aEvent); @@ -52,7 +53,7 @@ protected: virtual void Flush() = 0; #if defined(_MSC_VER) - typedef stdext::hash_set<const void*> ObjectSet; + typedef std::unordered_set<const void*> ObjectSet; #else typedef std::set<const void*> ObjectSet; #endif @@ -64,7 +65,8 @@ protected: class DrawEventRecorderFile : public DrawEventRecorderPrivate { public: - DrawEventRecorderFile(const char *aFilename); + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderFile) + explicit DrawEventRecorderFile(const char *aFilename); ~DrawEventRecorderFile(); private: diff --git a/gfx/2d/DrawTarget.cpp b/gfx/2d/DrawTarget.cpp new file mode 100644 index 000000000..63f728993 --- /dev/null +++ b/gfx/2d/DrawTarget.cpp @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "2D.h" +#include "Logging.h" + +#include "DrawTargetCapture.h" + +namespace mozilla { +namespace gfx { + +TemporaryRef<DrawTargetCapture> +DrawTarget::CreateCaptureDT(const IntSize& aSize) +{ + RefPtr<DrawTargetCaptureImpl> dt = new DrawTargetCaptureImpl(); + + if (!dt->Init(aSize, this)) { + gfxWarning() << "Failed to initialize Capture DrawTarget!"; + return nullptr; + } + + return dt; +} + +void +DrawTarget::DrawCapturedDT(DrawTargetCapture *aCaptureDT, + const Matrix& aTransform) +{ + if (aTransform.HasNonIntegerTranslation()) { + gfxWarning() << "Non integer translations are not supported for DrawCaptureDT at this time!"; + return; + } + static_cast<DrawTargetCaptureImpl*>(aCaptureDT)->ReplayToDrawTarget(this, aTransform); +} + +} +} diff --git a/gfx/2d/DrawTargetCG.cpp b/gfx/2d/DrawTargetCG.cpp index e24d2906a..1bd92f212 100644 --- a/gfx/2d/DrawTargetCG.cpp +++ b/gfx/2d/DrawTargetCG.cpp @@ -2,13 +2,25 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "BorrowedContext.h" +#include "DataSurfaceHelpers.h" #include "DrawTargetCG.h" +#include "Logging.h" #include "SourceSurfaceCG.h" #include "Rect.h" #include "ScaledFontMac.h" #include "Tools.h" #include <vector> -#include "QuartzSupport.h" +#include <algorithm> +#include "MacIOSurface.h" +#include "FilterNodeSoftware.h" +#include "mozilla/Assertions.h" +#include "mozilla/FloatingPoint.h" +#include "mozilla/Types.h" // for decltype +#include "mozilla/Vector.h" + +using namespace std; //CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode); @@ -18,12 +30,8 @@ CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform); namespace mozilla { namespace gfx { -static CGRect RectToCGRect(Rect r) -{ - return CGRectMake(r.x, r.y, r.width, r.height); -} - -static CGRect IntRectToCGRect(IntRect r) +template <typename T> +static CGRect RectToCGRect(const T& r) { return CGRectMake(r.x, r.y, r.width, r.height); } @@ -32,82 +40,82 @@ CGBlendMode ToBlendMode(CompositionOp op) { CGBlendMode mode; switch (op) { - case OP_OVER: + case CompositionOp::OP_OVER: mode = kCGBlendModeNormal; break; - case OP_ADD: + case CompositionOp::OP_ADD: mode = kCGBlendModePlusLighter; break; - case OP_ATOP: + case CompositionOp::OP_ATOP: mode = kCGBlendModeSourceAtop; break; - case OP_OUT: + case CompositionOp::OP_OUT: mode = kCGBlendModeSourceOut; break; - case OP_IN: + case CompositionOp::OP_IN: mode = kCGBlendModeSourceIn; break; - case OP_SOURCE: + case CompositionOp::OP_SOURCE: mode = kCGBlendModeCopy; break; - case OP_DEST_IN: + case CompositionOp::OP_DEST_IN: mode = kCGBlendModeDestinationIn; break; - case OP_DEST_OUT: + case CompositionOp::OP_DEST_OUT: mode = kCGBlendModeDestinationOut; break; - case OP_DEST_OVER: + case CompositionOp::OP_DEST_OVER: mode = kCGBlendModeDestinationOver; break; - case OP_DEST_ATOP: + case CompositionOp::OP_DEST_ATOP: mode = kCGBlendModeDestinationAtop; break; - case OP_XOR: + case CompositionOp::OP_XOR: mode = kCGBlendModeXOR; break; - case OP_MULTIPLY: + case CompositionOp::OP_MULTIPLY: mode = kCGBlendModeMultiply; break; - case OP_SCREEN: + case CompositionOp::OP_SCREEN: mode = kCGBlendModeScreen; break; - case OP_OVERLAY: + case CompositionOp::OP_OVERLAY: mode = kCGBlendModeOverlay; break; - case OP_DARKEN: + case CompositionOp::OP_DARKEN: mode = kCGBlendModeDarken; break; - case OP_LIGHTEN: + case CompositionOp::OP_LIGHTEN: mode = kCGBlendModeLighten; break; - case OP_COLOR_DODGE: + case CompositionOp::OP_COLOR_DODGE: mode = kCGBlendModeColorDodge; break; - case OP_COLOR_BURN: + case CompositionOp::OP_COLOR_BURN: mode = kCGBlendModeColorBurn; break; - case OP_HARD_LIGHT: + case CompositionOp::OP_HARD_LIGHT: mode = kCGBlendModeHardLight; break; - case OP_SOFT_LIGHT: + case CompositionOp::OP_SOFT_LIGHT: mode = kCGBlendModeSoftLight; break; - case OP_DIFFERENCE: + case CompositionOp::OP_DIFFERENCE: mode = kCGBlendModeDifference; break; - case OP_EXCLUSION: + case CompositionOp::OP_EXCLUSION: mode = kCGBlendModeExclusion; break; - case OP_HUE: + case CompositionOp::OP_HUE: mode = kCGBlendModeHue; break; - case OP_SATURATION: + case CompositionOp::OP_SATURATION: mode = kCGBlendModeSaturation; break; - case OP_COLOR: + case CompositionOp::OP_COLOR: mode = kCGBlendModeColor; break; - case OP_LUMINOSITY: + case CompositionOp::OP_LUMINOSITY: mode = kCGBlendModeLuminosity; break; /* @@ -120,33 +128,60 @@ CGBlendMode ToBlendMode(CompositionOp op) return mode; } +static CGInterpolationQuality +InterpolationQualityFromFilter(Filter aFilter) +{ + switch (aFilter) { + default: + case Filter::LINEAR: + return kCGInterpolationLow; + case Filter::POINT: + return kCGInterpolationNone; + case Filter::GOOD: + return kCGInterpolationLow; + } +} -DrawTargetCG::DrawTargetCG() : mCg(nullptr), mSnapshot(nullptr) +DrawTargetCG::DrawTargetCG() + : mColorSpace(nullptr) + , mCg(nullptr) + , mMayContainInvalidPremultipliedData(false) { } DrawTargetCG::~DrawTargetCG() { - MarkChanged(); + if (mSnapshot) { + if (mSnapshot->refCount() > 1) { + // We only need to worry about snapshots that someone else knows about + mSnapshot->DrawTargetWillGoAway(); + } + mSnapshot = nullptr; + } - // We need to conditionally release these because Init can fail without initializing these. - if (mColorSpace) - CGColorSpaceRelease(mColorSpace); - if (mCg) - CGContextRelease(mCg); - free(mData); + // Both of these are OK with nullptr arguments, so we do not + // need to check (these could be nullptr if Init fails) + CGColorSpaceRelease(mColorSpace); + CGContextRelease(mCg); } -BackendType +DrawTargetType DrawTargetCG::GetType() const { + return GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED ? + DrawTargetType::HARDWARE_RASTER : DrawTargetType::SOFTWARE_RASTER; +} + +BackendType +DrawTargetCG::GetBackendType() const +{ // It may be worth spliting Bitmap and IOSurface DrawTarget // into seperate classes. if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) { - return BACKEND_COREGRAPHICS_ACCELERATED; + return BackendType::COREGRAPHICS_ACCELERATED; } else { - return BACKEND_COREGRAPHICS; + return BackendType::COREGRAPHICS; } } @@ -156,9 +191,9 @@ DrawTargetCG::Snapshot() if (!mSnapshot) { if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) { return new SourceSurfaceCGIOSurfaceContext(this); - } else { - mSnapshot = new SourceSurfaceCGBitmapContext(this); } + Flush(); + mSnapshot = new SourceSurfaceCGBitmapContext(this); } return mSnapshot; @@ -170,11 +205,10 @@ DrawTargetCG::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aForma // XXX: in thebes we use CGLayers to do this kind of thing. It probably makes sense // to add that in somehow, but at a higher level RefPtr<DrawTargetCG> newTarget = new DrawTargetCG(); - if (newTarget->Init(GetType(), aSize, aFormat)) { - return newTarget; - } else { - return nullptr; + if (newTarget->Init(GetBackendType(), aSize, aFormat)) { + return newTarget.forget(); } + return nullptr; } TemporaryRef<SourceSurface> @@ -185,58 +219,88 @@ DrawTargetCG::CreateSourceSurfaceFromData(unsigned char *aData, { RefPtr<SourceSurfaceCG> newSurf = new SourceSurfaceCG(); - if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) { + if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) { return nullptr; } - return newSurf; + return newSurf.forget(); } +static void releaseDataSurface(void* info, const void *data, size_t size) +{ + static_cast<DataSourceSurface*>(info)->Release(); +} + +// This function returns a retained CGImage that needs to be released after +// use. The reason for this is that we want to either reuse an existing CGImage +// or create a new one. static CGImageRef -GetImageFromSourceSurface(SourceSurface *aSurface) +GetRetainedImageFromSourceSurface(SourceSurface *aSurface) { - if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) - return static_cast<SourceSurfaceCG*>(aSurface)->GetImage(); - else if (aSurface->GetType() == SURFACE_COREGRAPHICS_CGCONTEXT) - return static_cast<SourceSurfaceCGContext*>(aSurface)->GetImage(); - else if (aSurface->GetType() == SURFACE_DATA) - return static_cast<DataSourceSurfaceCG*>(aSurface)->GetImage(); - abort(); + switch(aSurface->GetType()) { + case SurfaceType::COREGRAPHICS_IMAGE: + return CGImageRetain(static_cast<SourceSurfaceCG*>(aSurface)->GetImage()); + + case SurfaceType::COREGRAPHICS_CGCONTEXT: + return CGImageRetain(static_cast<SourceSurfaceCGContext*>(aSurface)->GetImage()); + + default: + { + RefPtr<DataSourceSurface> data = aSurface->GetDataSurface(); + if (!data) { + MOZ_CRASH("unsupported source surface"); + } + data->AddRef(); + return CreateCGImage(releaseDataSurface, data.get(), + data->GetData(), data->GetSize(), + data->Stride(), data->GetFormat()); + } + } } TemporaryRef<SourceSurface> DrawTargetCG::OptimizeSourceSurface(SourceSurface *aSurface) const { - return nullptr; + return aSurface; } class UnboundnessFixer { CGRect mClipBounds; CGLayerRef mLayer; - CGContextRef mCg; + CGContextRef mLayerCg; public: - UnboundnessFixer() : mCg(nullptr) {} + UnboundnessFixer() : mLayerCg(nullptr) {} - CGContextRef Check(CGContextRef baseCg, CompositionOp blend) + CGContextRef Check(CGContextRef baseCg, CompositionOp blend, const Rect* maskBounds = nullptr) { + MOZ_ASSERT(baseCg); if (!IsOperatorBoundByMask(blend)) { mClipBounds = CGContextGetClipBoundingBox(baseCg); + // If we're entirely clipped out or if the drawing operation covers the entire clip then + // we don't need to create a temporary surface. + if (CGRectIsEmpty(mClipBounds) || + (maskBounds && maskBounds->Contains(CGRectToRect(mClipBounds)))) { + return baseCg; + } + // TransparencyLayers aren't blended using the blend mode so // we are forced to use CGLayers //XXX: The size here is in default user space units, of the layer relative to the graphics context. // is the clip bounds still correct if, for example, we have a scale applied to the context? mLayer = CGLayerCreateWithContext(baseCg, mClipBounds.size, nullptr); - //XXX: if the size is 0x0 we get a nullptr CGContext back from GetContext - mCg = CGLayerGetContext(mLayer); + mLayerCg = CGLayerGetContext(mLayer); // CGContext's default to have the origin at the bottom left // so flip it to the top left and adjust for the origin // of the layer - CGContextTranslateCTM(mCg, -mClipBounds.origin.x, mClipBounds.origin.y + mClipBounds.size.height); - CGContextScaleCTM(mCg, 1, -1); + if (MOZ2D_ERROR_IF(!mLayerCg)) { + return nullptr; + } + CGContextTranslateCTM(mLayerCg, -mClipBounds.origin.x, mClipBounds.origin.y + mClipBounds.size.height); + CGContextScaleCTM(mLayerCg, 1, -1); - return mCg; + return mLayerCg; } else { return baseCg; } @@ -244,12 +308,14 @@ class UnboundnessFixer void Fix(CGContextRef baseCg) { - if (mCg) { + if (mLayerCg) { + // we pushed a layer so draw it to baseCg + MOZ_ASSERT(baseCg); CGContextTranslateCTM(baseCg, 0, mClipBounds.size.height); CGContextScaleCTM(baseCg, 1, -1); mClipBounds.origin.y *= -1; CGContextDrawLayerAtPoint(baseCg, mClipBounds.origin, mLayer); - CGContextRelease(mCg); + CGContextRelease(mLayerCg); } } }; @@ -261,75 +327,76 @@ DrawTargetCG::DrawSurface(SourceSurface *aSurface, const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aDrawOptions) { + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } MarkChanged(); - CGImageRef image; - CGImageRef subimage = nullptr; CGContextSaveGState(mCg); CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp)); UnboundnessFixer fixer; - CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp); + CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp, &aDest); + if (MOZ2D_ERROR_IF(!cg)) { + return; + } CGContextSetAlpha(cg, aDrawOptions.mAlpha); + CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE); CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform)); - image = GetImageFromSourceSurface(aSurface); - /* we have two options here: - * - create a subimage -- this is slower - * - fancy things with clip and different dest rects */ - { - subimage = CGImageCreateWithImageInRect(image, RectToCGRect(aSource)); - image = subimage; - } - CGContextScaleCTM(cg, 1, -1); + CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(aSurfOptions.mFilter)); - CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + aDest.height), - aDest.width, aDest.height); + CGImageRef image = GetRetainedImageFromSourceSurface(aSurface); - //XXX: we should implement this for patterns too - if (aSurfOptions.mFilter == FILTER_POINT) - CGContextSetInterpolationQuality(cg, kCGInterpolationNone); + if (aSurfOptions.mFilter == Filter::POINT) { + CGImageRef subimage = CGImageCreateWithImageInRect(image, RectToCGRect(aSource)); + CGImageRelease(image); - CGContextDrawImage(cg, flippedRect, image); + CGContextScaleCTM(cg, 1, -1); - fixer.Fix(mCg); + CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + aDest.height), + aDest.width, aDest.height); - CGContextRestoreGState(mCg); + CGContextDrawImage(cg, flippedRect, subimage); + CGImageRelease(subimage); + } else { + CGRect destRect = CGRectMake(aDest.x, aDest.y, aDest.width, aDest.height); + CGContextClipToRect(cg, destRect); - CGImageRelease(subimage); -} + float xScale = aSource.width / aDest.width; + float yScale = aSource.height / aDest.height; + CGContextTranslateCTM(cg, aDest.x - aSource.x / xScale, aDest.y - aSource.y / yScale); -void -DrawTargetCG::MaskSurface(const Pattern &aSource, - SourceSurface *aMask, - Point aOffset, - const DrawOptions &aDrawOptions) -{ - MarkChanged(); + CGRect adjustedDestRect = CGRectMake(0, 0, CGImageGetWidth(image) / xScale, + CGImageGetHeight(image) / yScale); - CGImageRef image; - CGContextSaveGState(mCg); + CGContextTranslateCTM(cg, 0, CGRectGetHeight(adjustedDestRect)); + CGContextScaleCTM(cg, 1, -1); - CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp)); - UnboundnessFixer fixer; - CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp); - CGContextSetAlpha(cg, aDrawOptions.mAlpha); - - CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform)); - image = GetImageFromSourceSurface(aMask); - - CGContextScaleCTM(cg, 1, -1); + CGContextDrawImage(cg, adjustedDestRect, image); + CGImageRelease(image); + } - IntSize size = aMask->GetSize(); - CGContextClipToMask(cg, CGRectMake(aOffset.x, aOffset.y, size.width, size.height), image); - - FillRect(Rect(0, 0, size.width, size.height), aSource, aDrawOptions); - fixer.Fix(mCg); CGContextRestoreGState(mCg); +} +TemporaryRef<FilterNode> +DrawTargetCG::CreateFilter(FilterType aType) +{ + return FilterNodeSoftware::Create(aType); +} + +void +DrawTargetCG::DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions) +{ + FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode); + filter->Draw(this, aSourceRect, aDestPoint, aOptions); } static CGColorRef ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColor) @@ -341,78 +408,319 @@ static CGColorRef ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColo class GradientStopsCG : public GradientStops { public: - //XXX: The skia backend uses a vector and passes in aNumStops. It should do better - GradientStopsCG(GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsCG) + + GradientStopsCG(CGColorSpaceRef aColorSpace, + const std::vector<GradientStop>& aStops, + ExtendMode aExtendMode) + : mGradient(nullptr) { - //XXX: do the stops need to be in any particular order? - // what should we do about the color space here? we certainly shouldn't be - // recreating it all the time - std::vector<CGFloat> colors; - std::vector<CGFloat> offsets; - colors.reserve(aNumStops*4); - offsets.reserve(aNumStops); - - for (uint32_t i = 0; i < aNumStops; i++) { - colors.push_back(aStops[i].color.r); - colors.push_back(aStops[i].color.g); - colors.push_back(aStops[i].color.b); - colors.push_back(aStops[i].color.a); - - offsets.push_back(aStops[i].offset); + // This all works fine with empty aStops vector + + mExtend = aExtendMode; + if (aExtendMode == ExtendMode::CLAMP) { + size_t numStops = aStops.size(); + + std::vector<CGFloat> colors; + std::vector<CGFloat> offsets; + colors.reserve(numStops*4); + offsets.reserve(numStops); + + for (size_t i = 0; i < numStops; i++) { + colors.push_back(aStops[i].color.r); + colors.push_back(aStops[i].color.g); + colors.push_back(aStops[i].color.b); + colors.push_back(aStops[i].color.a); + + offsets.push_back(aStops[i].offset); + } + + mGradient = CGGradientCreateWithColorComponents(aColorSpace, + &colors.front(), + &offsets.front(), + offsets.size()); + } else { + mStops = aStops; } - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - mGradient = CGGradientCreateWithColorComponents(colorSpace, - &colors.front(), - &offsets.front(), - aNumStops); - CGColorSpaceRelease(colorSpace); } + virtual ~GradientStopsCG() { + // CGGradientRelease is OK with nullptr argument CGGradientRelease(mGradient); } - // Will always report BACKEND_COREGRAPHICS, but it is compatible - // with BACKEND_COREGRAPHICS_ACCELERATED - BackendType GetBackendType() const { return BACKEND_COREGRAPHICS; } + + // Will always report BackendType::COREGRAPHICS, but it is compatible + // with BackendType::COREGRAPHICS_ACCELERATED + BackendType GetBackendType() const { return BackendType::COREGRAPHICS; } + // XXX this should be a union CGGradientRef mGradient; + std::vector<GradientStop> mStops; + ExtendMode mExtend; }; TemporaryRef<GradientStops> DrawTargetCG::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const { - return new GradientStopsCG(aStops, aNumStops, aExtendMode); + std::vector<GradientStop> stops(aStops, aStops+aNumStops); + return new GradientStopsCG(mColorSpace, stops, aExtendMode); } static void -DrawGradient(CGContextRef cg, const Pattern &aPattern) +UpdateLinearParametersToIncludePoint(double *min_t, double *max_t, + CGPoint *start, + double dx, double dy, + double x, double y) { - if (aPattern.GetType() == PATTERN_LINEAR_GRADIENT) { - const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern); - GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get()); - // XXX: we should take the m out of the properties of LinearGradientPatterns - CGPoint startPoint = { pat.mBegin.x, pat.mBegin.y }; - CGPoint endPoint = { pat.mEnd.x, pat.mEnd.y }; + MOZ_ASSERT(IsFinite(x) && IsFinite(y)); + + /** + * Compute a parameter t such that a line perpendicular to the (dx,dy) + * vector, passing through (start->x + dx*t, start->y + dy*t), also + * passes through (x,y). + * + * Let px = x - start->x, py = y - start->y. + * t is given by + * (px - dx*t)*dx + (py - dy*t)*dy = 0 + * + * Solving for t we get + * numerator = dx*px + dy*py + * denominator = dx^2 + dy^2 + * t = numerator/denominator + * + * In CalculateRepeatingGradientParams we know the length of (dx,dy) + * is not zero. (This is checked in DrawLinearRepeatingGradient.) + */ + double px = x - start->x; + double py = y - start->y; + double numerator = dx * px + dy * py; + double denominator = dx * dx + dy * dy; + double t = numerator / denominator; + + if (*min_t > t) { + *min_t = t; + } + if (*max_t < t) { + *max_t = t; + } +} - // Canvas spec states that we should avoid drawing degenerate gradients (XXX: should this be in common code?) - //if (startPoint.x == endPoint.x && startPoint.y == endPoint.y) - // return; +/** + * Repeat the gradient line such that lines extended perpendicular to the + * gradient line at both start and end would completely enclose the drawing + * extents. + */ +static void +CalculateRepeatingGradientParams(CGPoint *aStart, CGPoint *aEnd, + CGRect aExtents, int *aRepeatStartFactor, + int *aRepeatEndFactor) +{ + double t_min = INFINITY; + double t_max = -INFINITY; + double dx = aEnd->x - aStart->x; + double dy = aEnd->y - aStart->y; + + double bounds_x1 = aExtents.origin.x; + double bounds_y1 = aExtents.origin.y; + double bounds_x2 = aExtents.origin.x + aExtents.size.width; + double bounds_y2 = aExtents.origin.y + aExtents.size.height; + + UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy, + bounds_x1, bounds_y1); + UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy, + bounds_x2, bounds_y1); + UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy, + bounds_x2, bounds_y2); + UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy, + bounds_x1, bounds_y2); + + MOZ_ASSERT(!isinf(t_min) && !isinf(t_max), + "The first call to UpdateLinearParametersToIncludePoint should have made t_min and t_max non-infinite."); + + // Move t_min and t_max to the nearest usable integer to try to avoid + // subtle variations due to numerical instability, especially accidentally + // cutting off a pixel. Extending the gradient repetitions is always safe. + t_min = floor (t_min); + t_max = ceil (t_max); + aEnd->x = aStart->x + dx * t_max; + aEnd->y = aStart->y + dy * t_max; + aStart->x = aStart->x + dx * t_min; + aStart->y = aStart->y + dy * t_min; + + *aRepeatStartFactor = t_min; + *aRepeatEndFactor = t_max; +} - CGContextDrawLinearGradient(cg, stops->mGradient, startPoint, endPoint, - kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); - } else if (aPattern.GetType() == PATTERN_RADIAL_GRADIENT) { - const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern); - GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get()); +static CGGradientRef +CreateRepeatingGradient(CGColorSpaceRef aColorSpace, + CGContextRef cg, GradientStopsCG* aStops, + int aRepeatStartFactor, int aRepeatEndFactor, + bool aReflect) +{ + int repeatCount = aRepeatEndFactor - aRepeatStartFactor; + uint32_t stopCount = aStops->mStops.size(); + double scale = 1./repeatCount; + + std::vector<CGFloat> colors; + std::vector<CGFloat> offsets; + colors.reserve(stopCount*repeatCount*4); + offsets.reserve(stopCount*repeatCount); + + for (int j = aRepeatStartFactor; j < aRepeatEndFactor; j++) { + bool isReflected = aReflect && (j % 2) != 0; + for (uint32_t i = 0; i < stopCount; i++) { + uint32_t stopIndex = isReflected ? stopCount - i - 1 : i; + colors.push_back(aStops->mStops[stopIndex].color.r); + colors.push_back(aStops->mStops[stopIndex].color.g); + colors.push_back(aStops->mStops[stopIndex].color.b); + colors.push_back(aStops->mStops[stopIndex].color.a); + + CGFloat offset = aStops->mStops[stopIndex].offset; + if (isReflected) { + offset = 1 - offset; + } + offsets.push_back((offset + (j - aRepeatStartFactor)) * scale); + } + } + + CGGradientRef gradient = CGGradientCreateWithColorComponents(aColorSpace, + &colors.front(), + &offsets.front(), + repeatCount*stopCount); + return gradient; +} + +static void +DrawLinearRepeatingGradient(CGColorSpaceRef aColorSpace, CGContextRef cg, + const LinearGradientPattern &aPattern, + const CGRect &aExtents, bool aReflect) +{ + GradientStopsCG *stops = static_cast<GradientStopsCG*>(aPattern.mStops.get()); + CGPoint startPoint = { aPattern.mBegin.x, aPattern.mBegin.y }; + CGPoint endPoint = { aPattern.mEnd.x, aPattern.mEnd.y }; + + int repeatStartFactor = 0, repeatEndFactor = 1; + // if we don't have a line then we can't extend it + if (aPattern.mEnd.x != aPattern.mBegin.x || + aPattern.mEnd.y != aPattern.mBegin.y) { + CalculateRepeatingGradientParams(&startPoint, &endPoint, aExtents, + &repeatStartFactor, &repeatEndFactor); + } - // XXX: we should take the m out of the properties of RadialGradientPatterns - CGPoint startCenter = { pat.mCenter1.x, pat.mCenter1.y }; - CGFloat startRadius = pat.mRadius1; - CGPoint endCenter = { pat.mCenter2.x, pat.mCenter2.y }; - CGFloat endRadius = pat.mRadius2; + CGGradientRef gradient = CreateRepeatingGradient(aColorSpace, cg, stops, repeatStartFactor, repeatEndFactor, aReflect); - //XXX: are there degenerate radial gradients that we should avoid drawing? - CGContextDrawRadialGradient(cg, stops->mGradient, startCenter, startRadius, endCenter, endRadius, - kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); + CGContextDrawLinearGradient(cg, gradient, startPoint, endPoint, + kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); + CGGradientRelease(gradient); +} + +static CGPoint CGRectTopLeft(CGRect a) +{ return a.origin; } +static CGPoint CGRectBottomLeft(CGRect a) +{ return CGPointMake(a.origin.x, a.origin.y + a.size.height); } +static CGPoint CGRectTopRight(CGRect a) +{ return CGPointMake(a.origin.x + a.size.width, a.origin.y); } +static CGPoint CGRectBottomRight(CGRect a) +{ return CGPointMake(a.origin.x + a.size.width, a.origin.y + a.size.height); } + +static CGFloat +CGPointDistance(CGPoint a, CGPoint b) +{ + return hypot(a.x-b.x, a.y-b.y); +} + +static void +DrawRadialRepeatingGradient(CGColorSpaceRef aColorSpace, CGContextRef cg, + const RadialGradientPattern &aPattern, + const CGRect &aExtents, bool aReflect) +{ + GradientStopsCG *stops = static_cast<GradientStopsCG*>(aPattern.mStops.get()); + CGPoint startCenter = { aPattern.mCenter1.x, aPattern.mCenter1.y }; + CGFloat startRadius = aPattern.mRadius1; + CGPoint endCenter = { aPattern.mCenter2.x, aPattern.mCenter2.y }; + CGFloat endRadius = aPattern.mRadius2; + + // find the maximum distance from endCenter to a corner of aExtents + CGFloat minimumEndRadius = endRadius; + minimumEndRadius = max(minimumEndRadius, CGPointDistance(endCenter, CGRectTopLeft(aExtents))); + minimumEndRadius = max(minimumEndRadius, CGPointDistance(endCenter, CGRectBottomLeft(aExtents))); + minimumEndRadius = max(minimumEndRadius, CGPointDistance(endCenter, CGRectTopRight(aExtents))); + minimumEndRadius = max(minimumEndRadius, CGPointDistance(endCenter, CGRectBottomRight(aExtents))); + + CGFloat length = endRadius - startRadius; + int repeatStartFactor = 0, repeatEndFactor = 1; + while (endRadius < minimumEndRadius) { + endRadius += length; + repeatEndFactor++; + } + + while (startRadius-length >= 0) { + startRadius -= length; + repeatStartFactor--; + } + + CGGradientRef gradient = CreateRepeatingGradient(aColorSpace, cg, stops, repeatStartFactor, repeatEndFactor, aReflect); + + //XXX: are there degenerate radial gradients that we should avoid drawing? + CGContextDrawRadialGradient(cg, gradient, startCenter, startRadius, endCenter, endRadius, + kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); + CGGradientRelease(gradient); +} + +static void +DrawGradient(CGColorSpaceRef aColorSpace, + CGContextRef cg, const Pattern &aPattern, const CGRect &aExtents) +{ + if (MOZ2D_ERROR_IF(!cg)) { + return; + } + + if (CGRectIsEmpty(aExtents)) { + return; + } + + if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) { + const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern); + GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get()); + CGAffineTransform patternMatrix = GfxMatrixToCGAffineTransform(pat.mMatrix); + CGContextConcatCTM(cg, patternMatrix); + CGRect extents = CGRectApplyAffineTransform(aExtents, CGAffineTransformInvert(patternMatrix)); + if (stops->mExtend == ExtendMode::CLAMP) { + + // XXX: we should take the m out of the properties of LinearGradientPatterns + CGPoint startPoint = { pat.mBegin.x, pat.mBegin.y }; + CGPoint endPoint = { pat.mEnd.x, pat.mEnd.y }; + + // Canvas spec states that we should avoid drawing degenerate gradients (XXX: should this be in common code?) + //if (startPoint.x == endPoint.x && startPoint.y == endPoint.y) + // return; + + CGContextDrawLinearGradient(cg, stops->mGradient, startPoint, endPoint, + kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); + } else if (stops->mExtend == ExtendMode::REPEAT || stops->mExtend == ExtendMode::REFLECT) { + DrawLinearRepeatingGradient(aColorSpace, cg, pat, extents, stops->mExtend == ExtendMode::REFLECT); + } + } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { + const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern); + CGAffineTransform patternMatrix = GfxMatrixToCGAffineTransform(pat.mMatrix); + CGContextConcatCTM(cg, patternMatrix); + CGRect extents = CGRectApplyAffineTransform(aExtents, CGAffineTransformInvert(patternMatrix)); + GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get()); + if (stops->mExtend == ExtendMode::CLAMP) { + + // XXX: we should take the m out of the properties of RadialGradientPatterns + CGPoint startCenter = { pat.mCenter1.x, pat.mCenter1.y }; + CGFloat startRadius = pat.mRadius1; + CGPoint endCenter = { pat.mCenter2.x, pat.mCenter2.y }; + CGFloat endRadius = pat.mRadius2; + + //XXX: are there degenerate radial gradients that we should avoid drawing? + CGContextDrawRadialGradient(cg, stops->mGradient, startCenter, startRadius, endCenter, endRadius, + kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); + } else if (stops->mExtend == ExtendMode::REPEAT || stops->mExtend == ExtendMode::REFLECT) { + DrawRadialRepeatingGradient(aColorSpace, cg, pat, extents, stops->mExtend == ExtendMode::REFLECT); + } } else { assert(0); } @@ -445,7 +753,14 @@ CGPatternCallbacks patternCallbacks = { static bool isGradient(const Pattern &aPattern) { - return aPattern.GetType() == PATTERN_LINEAR_GRADIENT || aPattern.GetType() == PATTERN_RADIAL_GRADIENT; + return aPattern.GetType() == PatternType::LINEAR_GRADIENT || aPattern.GetType() == PatternType::RADIAL_GRADIENT; +} + +static bool +isNonRepeatingSurface(const Pattern& aPattern) +{ + return aPattern.GetType() == PatternType::SURFACE && + static_cast<const SurfacePattern&>(aPattern).mExtendMode != ExtendMode::REPEAT; } /* CoreGraphics patterns ignore the userspace transform so @@ -455,17 +770,24 @@ CreateCGPattern(const Pattern &aPattern, CGAffineTransform aUserSpace) { const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); // XXX: is .get correct here? - CGImageRef image = GetImageFromSourceSurface(pat.mSurface.get()); + CGImageRef image = GetRetainedImageFromSourceSurface(pat.mSurface.get()); + Matrix patTransform = pat.mMatrix; + if (!pat.mSamplingRect.IsEmpty()) { + CGImageRef temp = CGImageCreateWithImageInRect(image, RectToCGRect(pat.mSamplingRect)); + CGImageRelease(image); + image = temp; + patTransform.PreTranslate(pat.mSamplingRect.x, pat.mSamplingRect.y); + } CGFloat xStep, yStep; switch (pat.mExtendMode) { - case EXTEND_CLAMP: + case ExtendMode::CLAMP: // The 1 << 22 comes from Webkit see Pattern::createPlatformPattern() in PatternCG.cpp for more info xStep = static_cast<CGFloat>(1 << 22); yStep = static_cast<CGFloat>(1 << 22); break; - case EXTEND_REFLECT: + case ExtendMode::REFLECT: assert(0); - case EXTEND_REPEAT: + case ExtendMode::REPEAT: xStep = static_cast<CGFloat>(CGImageGetWidth(image)); yStep = static_cast<CGFloat>(CGImageGetHeight(image)); // webkit uses wkCGPatternCreateWithImageAndTransform a wrapper around CGPatternCreateWithImage2 @@ -489,25 +811,29 @@ CreateCGPattern(const Pattern &aPattern, CGAffineTransform aUserSpace) CGAffineTransform transform = CGAffineTransformConcat(CGAffineTransformConcat(CGAffineTransformMakeScale(1, -1), - GfxMatrixToCGAffineTransform(pat.mMatrix)), + GfxMatrixToCGAffineTransform(patTransform)), aUserSpace); transform = CGAffineTransformTranslate(transform, 0, -static_cast<float>(CGImageGetHeight(image))); - return CGPatternCreate(CGImageRetain(image), bounds, transform, xStep, yStep, kCGPatternTilingConstantSpacing, + return CGPatternCreate(image, bounds, transform, xStep, yStep, kCGPatternTilingConstantSpacing, true, &patternCallbacks); } static void SetFillFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern &aPattern) { + if (MOZ2D_ERROR_IF(!cg)) { + return; + } + assert(!isGradient(aPattern)); - if (aPattern.GetType() == PATTERN_COLOR) { + if (aPattern.GetType() == PatternType::COLOR) { const Color& color = static_cast<const ColorPattern&>(aPattern).mColor; //XXX: we should cache colors CGColorRef cgcolor = ColorToCGColor(aColorSpace, color); CGContextSetFillColorWithColor(cg, cgcolor); CGColorRelease(cgcolor); - } else if (aPattern.GetType() == PATTERN_SURFACE) { + } else if (aPattern.GetType() == PatternType::SURFACE) { CGColorSpaceRef patternSpace; patternSpace = CGColorSpaceCreatePattern (nullptr); @@ -515,6 +841,8 @@ SetFillFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern & CGColorSpaceRelease(patternSpace); CGPatternRef pattern = CreateCGPattern(aPattern, CGContextGetCTM(cg)); + const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); + CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter)); CGFloat alpha = 1.; CGContextSetFillPattern(cg, pattern, &alpha); CGPatternRelease(pattern); @@ -525,19 +853,21 @@ static void SetStrokeFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern &aPattern) { assert(!isGradient(aPattern)); - if (aPattern.GetType() == PATTERN_COLOR) { + if (aPattern.GetType() == PatternType::COLOR) { const Color& color = static_cast<const ColorPattern&>(aPattern).mColor; //XXX: we should cache colors CGColorRef cgcolor = ColorToCGColor(aColorSpace, color); CGContextSetStrokeColorWithColor(cg, cgcolor); CGColorRelease(cgcolor); - } else if (aPattern.GetType() == PATTERN_SURFACE) { + } else if (aPattern.GetType() == PatternType::SURFACE) { CGColorSpaceRef patternSpace; patternSpace = CGColorSpaceCreatePattern (nullptr); CGContextSetStrokeColorSpace(cg, patternSpace); CGColorSpaceRelease(patternSpace); CGPatternRef pattern = CreateCGPattern(aPattern, CGContextGetCTM(cg)); + const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); + CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter)); CGFloat alpha = 1.; CGContextSetStrokePattern(cg, pattern, &alpha); CGPatternRelease(pattern); @@ -545,26 +875,112 @@ SetStrokeFromPattern(CGContextRef cg, CGColorSpaceRef aColorSpace, const Pattern } +void +DrawTargetCG::MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, + const DrawOptions &aDrawOptions) +{ + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } + + MarkChanged(); + + CGContextSaveGState(mCg); + + CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp)); + UnboundnessFixer fixer; + CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp); + if (MOZ2D_ERROR_IF(!cg)) { + return; + } + + CGContextSetAlpha(cg, aDrawOptions.mAlpha); + CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE); + + CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform)); + CGImageRef image = GetRetainedImageFromSourceSurface(aMask); + + // use a negative-y so that the mask image draws right ways up + CGContextScaleCTM(cg, 1, -1); + + IntSize size = aMask->GetSize(); + + CGContextClipToMask(cg, CGRectMake(aOffset.x, -(aOffset.y + size.height), size.width, size.height), image); + + CGContextScaleCTM(cg, 1, -1); + if (isGradient(aSource)) { + // we shouldn't need to clip to an additional rectangle + // as the cliping to the mask should be sufficient. + DrawGradient(mColorSpace, cg, aSource, CGRectMake(aOffset.x, aOffset.y, size.width, size.height)); + } else { + SetFillFromPattern(cg, mColorSpace, aSource); + CGContextFillRect(cg, CGRectMake(aOffset.x, aOffset.y, size.width, size.height)); + } + + CGImageRelease(image); + + fixer.Fix(mCg); + + CGContextRestoreGState(mCg); +} + + void DrawTargetCG::FillRect(const Rect &aRect, - const Pattern &aPattern, - const DrawOptions &aDrawOptions) + const Pattern &aPattern, + const DrawOptions &aDrawOptions) { + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } + MarkChanged(); CGContextSaveGState(mCg); UnboundnessFixer fixer; - CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp); + CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp, &aRect); + if (MOZ2D_ERROR_IF(!cg)) { + return; + } + CGContextSetAlpha(mCg, aDrawOptions.mAlpha); + CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE); CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp)); CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform)); if (isGradient(aPattern)) { CGContextClipToRect(cg, RectToCGRect(aRect)); - DrawGradient(cg, aPattern); + CGRect clipBounds = CGContextGetClipBoundingBox(cg); + DrawGradient(mColorSpace, cg, aPattern, clipBounds); + } else if (isNonRepeatingSurface(aPattern)) { + // SetFillFromPattern can handle this case but using CGContextDrawImage + // should give us better performance, better output, smaller PDF and + // matches what cairo does. + const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); + CGImageRef image = GetRetainedImageFromSourceSurface(pat.mSurface.get()); + Matrix transform = pat.mMatrix; + if (!pat.mSamplingRect.IsEmpty()) { + CGImageRef temp = CGImageCreateWithImageInRect(image, RectToCGRect(pat.mSamplingRect)); + CGImageRelease(image); + image = temp; + transform.PreTranslate(pat.mSamplingRect.x, pat.mSamplingRect.y); + } + CGContextClipToRect(cg, RectToCGRect(aRect)); + CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(transform)); + CGContextTranslateCTM(cg, 0, CGImageGetHeight(image)); + CGContextScaleCTM(cg, 1, -1); + + CGRect imageRect = CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)); + + CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter)); + + CGContextDrawImage(cg, imageRect, image); + CGImageRelease(image); } else { SetFillFromPattern(cg, mColorSpace, aPattern); CGContextFillRect(cg, RectToCGRect(aRect)); @@ -584,13 +1000,21 @@ DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPatte return; } + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } + MarkChanged(); CGContextSaveGState(mCg); UnboundnessFixer fixer; CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp); + if (MOZ2D_ERROR_IF(!cg)) { + return; + } CGContextSetAlpha(mCg, aDrawOptions.mAlpha); + CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE); CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp)); CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform)); @@ -603,9 +1027,10 @@ DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPatte if (isGradient(aPattern)) { CGContextReplacePathWithStrokedPath(cg); + CGRect extents = CGContextGetPathBoundingBox(cg); //XXX: should we use EO clip here? CGContextClip(cg); - DrawGradient(cg, aPattern); + DrawGradient(mColorSpace, cg, aPattern, extents); } else { SetStrokeFromPattern(cg, mColorSpace, aPattern); CGContextStrokePath(cg); @@ -615,12 +1040,31 @@ DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPatte CGContextRestoreGState(mCg); } +static bool +IsInteger(Float aValue) +{ + return floorf(aValue) == aValue; +} + +static bool +IsPixelAlignedStroke(const Rect& aRect, Float aLineWidth) +{ + Float halfWidth = aLineWidth/2; + return IsInteger(aLineWidth) && + IsInteger(aRect.x - halfWidth) && IsInteger(aRect.y - halfWidth) && + IsInteger(aRect.XMost() - halfWidth) && IsInteger(aRect.YMost() - halfWidth); +} + void DrawTargetCG::StrokeRect(const Rect &aRect, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions) { + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } + if (!aRect.IsFinite()) { return; } @@ -631,39 +1075,52 @@ DrawTargetCG::StrokeRect(const Rect &aRect, UnboundnessFixer fixer; CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp); + if (MOZ2D_ERROR_IF(!cg)) { + return; + } CGContextSetAlpha(mCg, aDrawOptions.mAlpha); CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp)); + // Work around Quartz bug where antialiasing causes corner pixels to be off by + // 1 channel value (e.g. rgb(1,1,1) values appear at the corner of solid + // black stroke), by turning off antialiasing when the edges of the stroke + // are pixel-aligned. Note that when a transform's components are all + // integers, it maps integers coordinates to integer coordinates. + bool pixelAlignedStroke = mTransform.IsAllIntegers() && + mTransform.PreservesAxisAlignedRectangles() && + aPattern.GetType() == PatternType::COLOR && + IsPixelAlignedStroke(aRect, aStrokeOptions.mLineWidth); + CGContextSetShouldAntialias(cg, + aDrawOptions.mAntialiasMode != AntialiasMode::NONE && !pixelAlignedStroke); + CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform)); - // we don't need to set all of the stroke state because - // it doesn't apply when stroking rects - switch (aStrokeOptions.mLineJoin) - { - case JOIN_BEVEL: - CGContextSetLineJoin(cg, kCGLineJoinBevel); - break; - case JOIN_ROUND: - CGContextSetLineJoin(cg, kCGLineJoinRound); - break; - case JOIN_MITER: - case JOIN_MITER_OR_BEVEL: - CGContextSetLineJoin(cg, kCGLineJoinMiter); - break; - } - CGContextSetLineWidth(cg, aStrokeOptions.mLineWidth); + SetStrokeOptions(cg, aStrokeOptions); if (isGradient(aPattern)) { // There's no CGContextClipStrokeRect so we do it by hand CGContextBeginPath(cg); CGContextAddRect(cg, RectToCGRect(aRect)); CGContextReplacePathWithStrokedPath(cg); + CGRect extents = CGContextGetPathBoundingBox(cg); //XXX: should we use EO clip here? CGContextClip(cg); - DrawGradient(cg, aPattern); + DrawGradient(mColorSpace, cg, aPattern, extents); } else { SetStrokeFromPattern(cg, mColorSpace, aPattern); - CGContextStrokeRect(cg, RectToCGRect(aRect)); + // We'd like to use CGContextStrokeRect(cg, RectToCGRect(aRect)); + // Unfortunately, newer versions of OS X no longer start at the top-left + // corner and stroke clockwise as older OS X versions and all the other + // Moz2D backends do. (Newer versions start at the top right-hand corner + // and stroke counter-clockwise.) For consistency we draw the rect by hand. + CGRect rect = RectToCGRect(aRect); + CGContextBeginPath(cg); + CGContextMoveToPoint(cg, CGRectGetMinX(rect), CGRectGetMinY(rect)); + CGContextAddLineToPoint(cg, CGRectGetMaxX(rect), CGRectGetMinY(rect)); + CGContextAddLineToPoint(cg, CGRectGetMaxX(rect), CGRectGetMaxY(rect)); + CGContextAddLineToPoint(cg, CGRectGetMinX(rect), CGRectGetMaxY(rect)); + CGContextClosePath(cg); + CGContextStrokePath(cg); } fixer.Fix(mCg); @@ -674,6 +1131,10 @@ DrawTargetCG::StrokeRect(const Rect &aRect, void DrawTargetCG::ClearRect(const Rect &aRect) { + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } + MarkChanged(); CGContextSaveGState(mCg); @@ -687,6 +1148,10 @@ DrawTargetCG::ClearRect(const Rect &aRect) void DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions) { + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } + if (!aPath->GetBounds().IsFinite()) { return; } @@ -697,7 +1162,12 @@ DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOpt UnboundnessFixer fixer; CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp); + if (MOZ2D_ERROR_IF(!cg)) { + return; + } + CGContextSetAlpha(mCg, aDrawOptions.mAlpha); + CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE); CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp)); CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform)); @@ -705,7 +1175,7 @@ DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOpt CGContextBeginPath(cg); - assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS); + assert(aPath->GetBackendType() == BackendType::COREGRAPHICS); const PathCG *cgPath = static_cast<const PathCG*>(aPath); CGContextAddPath(cg, cgPath->GetPath()); @@ -713,9 +1183,10 @@ DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOpt if (isGradient(aPattern)) { CGContextReplacePathWithStrokedPath(cg); + CGRect extents = CGContextGetPathBoundingBox(cg); //XXX: should we use EO clip here? CGContextClip(cg); - DrawGradient(cg, aPattern); + DrawGradient(mColorSpace, cg, aPattern, extents); } else { // XXX: we could put fill mode into the path fill rule if we wanted @@ -730,16 +1201,25 @@ DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOpt void DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aDrawOptions) { + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } + MarkChanged(); - assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS); + assert(aPath->GetBackendType() == BackendType::COREGRAPHICS); CGContextSaveGState(mCg); CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp)); UnboundnessFixer fixer; CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp); + if (MOZ2D_ERROR_IF(!cg)) { + return; + } + CGContextSetAlpha(cg, aDrawOptions.mAlpha); + CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE); CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform)); @@ -749,25 +1229,28 @@ DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions if (isGradient(aPattern)) { // setup a clip to draw the gradient through + CGRect extents; if (CGPathIsEmpty(cgPath->GetPath())) { // Adding an empty path will cause us not to clip // so clip everything explicitly CGContextClipToRect(mCg, CGRectZero); + extents = CGRectZero; } else { CGContextAddPath(cg, cgPath->GetPath()); - if (cgPath->GetFillRule() == FILL_EVEN_ODD) + extents = CGContextGetPathBoundingBox(cg); + if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD) CGContextEOClip(mCg); else CGContextClip(mCg); } - DrawGradient(cg, aPattern); + DrawGradient(mColorSpace, cg, aPattern, extents); } else { CGContextAddPath(cg, cgPath->GetPath()); SetFillFromPattern(cg, mColorSpace, aPattern); - if (cgPath->GetFillRule() == FILL_EVEN_ODD) + if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD) CGContextEOFillPath(cg); else CGContextFillPath(cg); @@ -777,34 +1260,116 @@ DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions CGContextRestoreGState(mCg); } +CGRect ComputeGlyphsExtents(CGRect *bboxes, CGPoint *positions, CFIndex count, float scale) +{ + CGFloat x1, x2, y1, y2; + if (count < 1) + return CGRectZero; + + x1 = bboxes[0].origin.x + positions[0].x; + x2 = bboxes[0].origin.x + positions[0].x + scale*bboxes[0].size.width; + y1 = bboxes[0].origin.y + positions[0].y; + y2 = bboxes[0].origin.y + positions[0].y + scale*bboxes[0].size.height; + + // accumulate max and minimum coordinates + for (int i = 1; i < count; i++) { + x1 = min(x1, bboxes[i].origin.x + positions[i].x); + y1 = min(y1, bboxes[i].origin.y + positions[i].y); + x2 = max(x2, bboxes[i].origin.x + positions[i].x + scale*bboxes[i].size.width); + y2 = max(y2, bboxes[i].origin.y + positions[i].y + scale*bboxes[i].size.height); + } + + CGRect extents = {{x1, y1}, {x2-x1, y2-y1}}; + return extents; +} + +typedef void (*CGContextSetFontSmoothingBackgroundColorFunc) (CGContextRef cgContext, CGColorRef color); + +static CGContextSetFontSmoothingBackgroundColorFunc +GetCGContextSetFontSmoothingBackgroundColorFunc() +{ + static CGContextSetFontSmoothingBackgroundColorFunc func = nullptr; + static bool lookedUpFunc = false; + if (!lookedUpFunc) { + func = (CGContextSetFontSmoothingBackgroundColorFunc)dlsym( + RTLD_DEFAULT, "CGContextSetFontSmoothingBackgroundColor"); + lookedUpFunc = true; + } + return func; +} void DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aDrawOptions, - const GlyphRenderingOptions*) + const GlyphRenderingOptions *aGlyphRenderingOptions) { + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } + MarkChanged(); assert(aBuffer.mNumGlyphs); CGContextSaveGState(mCg); + if (aGlyphRenderingOptions && aGlyphRenderingOptions->GetType() == FontType::MAC) { + Color fontSmoothingBackgroundColor = + static_cast<const GlyphRenderingOptionsCG*>(aGlyphRenderingOptions)->FontSmoothingBackgroundColor(); + if (fontSmoothingBackgroundColor.a > 0) { + CGContextSetFontSmoothingBackgroundColorFunc setFontSmoothingBGColorFunc = + GetCGContextSetFontSmoothingBackgroundColorFunc(); + if (setFontSmoothingBGColorFunc) { + CGColorRef color = ColorToCGColor(mColorSpace, fontSmoothingBackgroundColor); + setFontSmoothingBGColorFunc(mCg, color); + CGColorRelease(color); + + // Font rendering with a non-transparent font smoothing background color + // can leave pixels in our buffer where the rgb components exceed the alpha + // component. When this happens we need to clean up the data afterwards. + // The purpose of this is probably the following: Correct compositing of + // subpixel anti-aliased fonts on transparent backgrounds requires + // different alpha values per RGB component. Usually, premultiplied color + // values are derived by multiplying all components with the same per-pixel + // alpha value. However, if you multiply each component with a *different* + // alpha, and set the alpha component of the pixel to, say, the average + // of the alpha values that you used during the premultiplication of the + // RGB components, you can trick OVER compositing into doing a simplified + // form of component alpha compositing. (You just need to make sure to + // clamp the components of the result pixel to [0,255] afterwards.) + mMayContainInvalidPremultipliedData = true; + } + } + } + CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp)); UnboundnessFixer fixer; CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp); + if (MOZ2D_ERROR_IF(!cg)) { + return; + } + CGContextSetAlpha(cg, aDrawOptions.mAlpha); + CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE); + if (aDrawOptions.mAntialiasMode != AntialiasMode::DEFAULT) { + CGContextSetShouldSmoothFonts(cg, aDrawOptions.mAntialiasMode == AntialiasMode::SUBPIXEL); + } CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform)); ScaledFontMac* macFont = static_cast<ScaledFontMac*>(aFont); - //XXX: we should use a stack vector here when we have a class like that - std::vector<CGGlyph> glyphs; - std::vector<CGPoint> positions; - glyphs.resize(aBuffer.mNumGlyphs); - positions.resize(aBuffer.mNumGlyphs); + // This code can execute millions of times in short periods, so we want to + // avoid heap allocation whenever possible. So we use an inline vector + // capacity of 64 elements, which is enough to typically avoid heap + // allocation in ~99% of cases. + Vector<CGGlyph, 64> glyphs; + Vector<CGPoint, 64> positions; + if (!glyphs.resizeUninitialized(aBuffer.mNumGlyphs) || + !positions.resizeUninitialized(aBuffer.mNumGlyphs)) { + MOZ_CRASH("glyphs/positions allocation failed"); + } // Handle the flip - CGAffineTransform matrix = CGAffineTransformMakeScale(1, -1); - CGContextConcatCTM(cg, matrix); + CGContextScaleCTM(cg, 1, -1); // CGContextSetTextMatrix works differently with kCGTextClip && kCGTextFill // It seems that it transforms the positions with TextFill and not with TextClip // Therefore we'll avoid it. See also: @@ -820,30 +1385,41 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pa //XXX: CGContextShowGlyphsAtPositions is 10.5+ for older versions use CGContextShowGlyphsWithAdvances if (isGradient(aPattern)) { CGContextSetTextDrawingMode(cg, kCGTextClip); + CGRect extents; if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) { - ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, &glyphs.front(), - &positions.front(), - aBuffer.mNumGlyphs, cg); + CGRect *bboxes = new CGRect[aBuffer.mNumGlyphs]; + CTFontGetBoundingRectsForGlyphs(macFont->mCTFont, kCTFontDefaultOrientation, + glyphs.begin(), bboxes, aBuffer.mNumGlyphs); + extents = ComputeGlyphsExtents(bboxes, positions.begin(), aBuffer.mNumGlyphs, 1.0f); + ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, glyphs.begin(), + positions.begin(), aBuffer.mNumGlyphs, cg); + delete[] bboxes; } else { + CGRect *bboxes = new CGRect[aBuffer.mNumGlyphs]; + CGFontGetGlyphBBoxes(macFont->mFont, glyphs.begin(), aBuffer.mNumGlyphs, bboxes); + extents = ComputeGlyphsExtents(bboxes, positions.begin(), aBuffer.mNumGlyphs, macFont->mSize); + CGContextSetFont(cg, macFont->mFont); CGContextSetFontSize(cg, macFont->mSize); - CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(), + CGContextShowGlyphsAtPositions(cg, glyphs.begin(), positions.begin(), aBuffer.mNumGlyphs); + delete[] bboxes; } - DrawGradient(cg, aPattern); + CGContextScaleCTM(cg, 1, -1); + DrawGradient(mColorSpace, cg, aPattern, extents); } else { //XXX: with CoreGraphics we can stroke text directly instead of going // through GetPath. It would be nice to add support for using that CGContextSetTextDrawingMode(cg, kCGTextFill); SetFillFromPattern(cg, mColorSpace, aPattern); if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) { - ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, &glyphs.front(), - &positions.front(), + ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, glyphs.begin(), + positions.begin(), aBuffer.mNumGlyphs, cg); } else { CGContextSetFont(cg, macFont->mFont); CGContextSetFontSize(cg, macFont->mSize); - CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(), + CGContextShowGlyphsAtPositions(cg, glyphs.begin(), positions.begin(), aBuffer.mNumGlyphs); } } @@ -862,47 +1438,56 @@ DrawTargetCG::CopySurface(SourceSurface *aSurface, const IntRect& aSourceRect, const IntPoint &aDestination) { + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } + MarkChanged(); - CGImageRef image; - CGImageRef subimage = nullptr; - if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) { - image = GetImageFromSourceSurface(aSurface); - /* we have two options here: - * - create a subimage -- this is slower - * - fancy things with clip and different dest rects */ - { - subimage = CGImageCreateWithImageInRect(image, IntRectToCGRect(aSourceRect)); - image = subimage; - } + if (aSurface->GetType() == SurfaceType::COREGRAPHICS_IMAGE || + aSurface->GetType() == SurfaceType::COREGRAPHICS_CGCONTEXT || + aSurface->GetType() == SurfaceType::DATA) { + CGImageRef image = GetRetainedImageFromSourceSurface(aSurface); + // XXX: it might be more efficient for us to do the copy directly if we have access to the bits CGContextSaveGState(mCg); // CopySurface ignores the clip, so we need to use private API to temporarily reset it CGContextResetClip(mCg); + CGRect destRect = CGRectMake(aDestination.x, aDestination.y, + aSourceRect.width, aSourceRect.height); + CGContextClipToRect(mCg, destRect); + CGContextSetBlendMode(mCg, kCGBlendModeCopy); CGContextScaleCTM(mCg, 1, -1); - CGRect flippedRect = CGRectMake(aDestination.x, -(aDestination.y + aSourceRect.height), - aSourceRect.width, aSourceRect.height); + CGRect flippedRect = CGRectMake(aDestination.x - aSourceRect.x, -(aDestination.y - aSourceRect.y + double(CGImageGetHeight(image))), + CGImageGetWidth(image), CGImageGetHeight(image)); + // Quartz seems to copy A8 surfaces incorrectly if we don't initialize them + // to transparent first. + if (mFormat == SurfaceFormat::A8) { + CGContextClearRect(mCg, flippedRect); + } CGContextDrawImage(mCg, flippedRect, image); CGContextRestoreGState(mCg); - - CGImageRelease(subimage); + CGImageRelease(image); } } void DrawTargetCG::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, CompositionOp aOperator) { + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } + MarkChanged(); - CGImageRef image; - image = GetImageFromSourceSurface(aSurface); + CGImageRef image = GetRetainedImageFromSourceSurface(aSurface); IntSize size = aSurface->GetSize(); CGContextSaveGState(mCg); @@ -922,6 +1507,7 @@ DrawTargetCG::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, CGContextDrawImage(mCg, flippedRect, image); + CGImageRelease(image); CGContextRestoreGState(mCg); } @@ -935,13 +1521,14 @@ DrawTargetCG::Init(BackendType aType, { // XXX: we should come up with some consistent semantics for dealing // with zero area drawtargets - if (aSize.width <= 0 || aSize.height <= 0 || - // 32767 is the maximum size supported by cairo - // we clamp to that to make it easier to interoperate - aSize.width > 32767 || aSize.height > 32767) { + if (aSize.width <= 0 || + aSize.height <= 0 || + size_t(aSize.width) > GetMaxSurfaceSize() || + size_t(aSize.height) > GetMaxSurfaceSize()) + { + gfxWarning() << "Failed to Init() DrawTargetCG because of bad size."; mColorSpace = nullptr; mCg = nullptr; - mData = nullptr; return false; } @@ -950,32 +1537,50 @@ DrawTargetCG::Init(BackendType aType, //XXX: we'd be better off reusing the Colorspace across draw targets mColorSpace = CGColorSpaceCreateDeviceRGB(); - if (aData == nullptr && aType != BACKEND_COREGRAPHICS_ACCELERATED) { + if (aData == nullptr && aType != BackendType::COREGRAPHICS_ACCELERATED) { // XXX: Currently, Init implicitly clears, that can often be a waste of time - mData = calloc(aSize.height * aStride, 1); - aData = static_cast<unsigned char*>(mData); - } else { - // mData == nullptr means DrawTargetCG doesn't own the image data and will not - // delete it in the destructor - mData = nullptr; + size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height); + if (bufLen == 0) { + mColorSpace = nullptr; + mCg = nullptr; + return false; + } + static_assert(sizeof(decltype(mData[0])) == 1, + "mData.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen"); + mData.Realloc(/* actually an object count */ bufLen, true); + aData = static_cast<unsigned char*>(mData); } mSize = aSize; - if (aType == BACKEND_COREGRAPHICS_ACCELERATED) { + if (aType == BackendType::COREGRAPHICS_ACCELERATED) { RefPtr<MacIOSurface> ioSurface = MacIOSurface::CreateIOSurface(aSize.width, aSize.height); mCg = ioSurface->CreateIOSurfaceContext(); // If we don't have the symbol for 'CreateIOSurfaceContext' mCg will be null // and we will fallback to software below - mData = nullptr; } - if (!mCg || aType == BACKEND_COREGRAPHICS) { + mFormat = SurfaceFormat::B8G8R8A8; + + if (!mCg || aType == BackendType::COREGRAPHICS) { int bitsPerComponent = 8; CGBitmapInfo bitinfo; - bitinfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst; - + if (aFormat == SurfaceFormat::A8) { + if (mColorSpace) + CGColorSpaceRelease(mColorSpace); + mColorSpace = nullptr; + bitinfo = kCGImageAlphaOnly; + mFormat = SurfaceFormat::A8; + } else { + bitinfo = kCGBitmapByteOrder32Host; + if (aFormat == SurfaceFormat::B8G8R8X8) { + bitinfo |= kCGImageAlphaNoneSkipFirst; + mFormat = aFormat; + } else { + bitinfo |= kCGImageAlphaPremultipliedFirst; + } + } // XXX: what should we do if this fails? mCg = CGBitmapContextCreate (aData, mSize.width, @@ -987,6 +1592,11 @@ DrawTargetCG::Init(BackendType aType, } assert(mCg); + if (!mCg) { + gfxCriticalError() << "Failed to create CG context"; + return false; + } + // CGContext's default to have the origin at the bottom left // so flip it to the top left CGContextTranslateCTM(mCg, 0, mSize.height); @@ -1000,10 +1610,8 @@ DrawTargetCG::Init(BackendType aType, // use the default for content. CGContextSetInterpolationQuality(mCg, kCGInterpolationLow); - // XXX: set correct format - mFormat = FORMAT_B8G8R8A8; - if (aType == BACKEND_COREGRAPHICS_ACCELERATED) { + if (aType == BackendType::COREGRAPHICS_ACCELERATED) { // The bitmap backend uses callac to clear, we can't do that without // reading back the surface. This should trigger something equivilent // to glClear. @@ -1013,10 +1621,55 @@ DrawTargetCG::Init(BackendType aType, return true; } +static void +EnsureValidPremultipliedData(CGContextRef aContext) +{ + if (CGBitmapContextGetBitsPerPixel(aContext) != 32 || + CGBitmapContextGetAlphaInfo(aContext) != kCGImageAlphaPremultipliedFirst) { + return; + } + + uint8_t* bitmapData = (uint8_t*)CGBitmapContextGetData(aContext); + int w = CGBitmapContextGetWidth(aContext); + int h = CGBitmapContextGetHeight(aContext); + int stride = CGBitmapContextGetBytesPerRow(aContext); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int i = y * stride + x * 4; + uint8_t a = bitmapData[i + 3]; + + // Clamp rgb components to the alpha component. + if (bitmapData[i + 0] > a) { + bitmapData[i + 0] = a; + } + if (bitmapData[i + 1] > a) { + bitmapData[i + 1] = a; + } + if (bitmapData[i + 2] > a) { + bitmapData[i + 2] = a; + } + } + } +} + void DrawTargetCG::Flush() { - CGContextFlush(mCg); + if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) { + CGContextFlush(mCg); + } else if (GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP && + mMayContainInvalidPremultipliedData) { + // We can't guarantee that all our users can handle pixel data where an RGB + // component value exceeds the pixel's alpha value. In particular, the + // color conversion that CG does when we draw a CGImage snapshot of this + // context into a context that has a different color space throws up on + // invalid premultiplied data and creates completely wrong colors. + // Sanitizing the data means that we lose some of the fake component alpha + // behavior that font rendering tries to give us, but the result still + // looks good enough to prefer it over grayscale font anti-aliasing. + EnsureValidPremultipliedData(mCg); + mMayContainInvalidPremultipliedData = false; + } } bool @@ -1027,7 +1680,6 @@ DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize) if (aSize.width == 0 || aSize.height == 0) { mColorSpace = nullptr; mCg = nullptr; - mData = nullptr; return false; } @@ -1039,17 +1691,33 @@ DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize) mSize = aSize; mCg = cgContext; - - mData = nullptr; + CGContextRetain(mCg); assert(mCg); - // CGContext's default to have the origin at the bottom left - // so flip it to the top left - CGContextTranslateCTM(mCg, 0, mSize.height); - CGContextScaleCTM(mCg, 1, -1); + if (!mCg) { + gfxCriticalError() << "Invalid CG context at Init"; + return false; + } - //XXX: set correct format - mFormat = FORMAT_B8G8R8A8; + // CGContext's default to have the origin at the bottom left. + // However, currently the only use of this function is to construct a + // DrawTargetCG around a CGContextRef from a cairo quartz surface which + // already has it's origin adjusted. + // + // CGContextTranslateCTM(mCg, 0, mSize.height); + // CGContextScaleCTM(mCg, 1, -1); + + mFormat = SurfaceFormat::B8G8R8A8; + if (GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP) { + CGColorSpaceRef colorspace; + CGBitmapInfo bitinfo = CGBitmapContextGetBitmapInfo(mCg); + colorspace = CGBitmapContextGetColorSpace (mCg); + if (CGColorSpaceGetNumberOfComponents(colorspace) == 1) { + mFormat = SurfaceFormat::A8; + } else if ((bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst) { + mFormat = SurfaceFormat::B8G8R8X8; + } + } return true; } @@ -1057,7 +1725,7 @@ DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize) bool DrawTargetCG::Init(BackendType aType, const IntSize &aSize, SurfaceFormat &aFormat) { - int stride = aSize.width*4; + int32_t stride = GetAlignedStride<16>(aSize.width * BytesPerPixel(aFormat)); // Calling Init with aData == nullptr will allocate. return Init(aType, nullptr, aSize, stride, aFormat); @@ -1066,15 +1734,14 @@ DrawTargetCG::Init(BackendType aType, const IntSize &aSize, SurfaceFormat &aForm TemporaryRef<PathBuilder> DrawTargetCG::CreatePathBuilder(FillRule aFillRule) const { - RefPtr<PathBuilderCG> pb = new PathBuilderCG(aFillRule); - return pb; + return new PathBuilderCG(aFillRule); } void* DrawTargetCG::GetNativeSurface(NativeSurfaceType aType) { - if ((aType == NATIVE_SURFACE_CGCONTEXT && GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP) || - (aType == NATIVE_SURFACE_CGCONTEXT_ACCELERATED && GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE)) { + if ((aType == NativeSurfaceType::CGCONTEXT && GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP) || + (aType == NativeSurfaceType::CGCONTEXT_ACCELERATED && GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE)) { return mCg; } else { return nullptr; @@ -1093,7 +1760,7 @@ DrawTargetCG::Mask(const Pattern &aSource, if (isGradient(aMask)) { assert(0); } else { - if (aMask.GetType() == PATTERN_COLOR) { + if (aMask.GetType() == PatternType::COLOR) { DrawOptions drawOptions(aDrawOptions); const Color& color = static_cast<const ColorPattern&>(aMask).mColor; drawOptions.mAlpha *= color.a; @@ -1101,13 +1768,15 @@ DrawTargetCG::Mask(const Pattern &aSource, // XXX: we need to get a rect that when transformed covers the entire surface //Rect //FillRect(rect, aSource, drawOptions); - } else if (aMask.GetType() == PATTERN_SURFACE) { + } else if (aMask.GetType() == PatternType::SURFACE) { const SurfacePattern& pat = static_cast<const SurfacePattern&>(aMask); - CGImageRef mask = GetImageFromSourceSurface(pat.mSurface.get()); + CGImageRef mask = GetRetainedImageFromSourceSurface(pat.mSurface.get()); + MOZ_ASSERT(pat.mSamplingRect.IsEmpty(), "Sampling rect not supported with masks!"); Rect rect(0,0, CGImageGetWidth(mask), CGImageGetHeight(mask)); // XXX: probably we need to do some flipping of the image or something CGContextClipToMask(mCg, RectToCGRect(rect), mask); FillRect(rect, aSource, aDrawOptions); + CGImageRelease(mask); } } @@ -1117,6 +1786,10 @@ DrawTargetCG::Mask(const Pattern &aSource, void DrawTargetCG::PushClipRect(const Rect &aRect) { + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } + CGContextSaveGState(mCg); /* We go through a bit of trouble to temporarilly set the transform @@ -1131,10 +1804,14 @@ DrawTargetCG::PushClipRect(const Rect &aRect) void DrawTargetCG::PushClip(const Path *aPath) { + if (MOZ2D_ERROR_IF(!mCg)) { + return; + } + CGContextSaveGState(mCg); CGContextBeginPath(mCg); - assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS); + assert(aPath->GetBackendType() == BackendType::COREGRAPHICS); const PathCG *cgPath = static_cast<const PathCG*>(aPath); @@ -1155,7 +1832,7 @@ DrawTargetCG::PushClip(const Path *aPath) CGContextAddPath(mCg, cgPath->GetPath()); CGContextRestoreGState(mCg); - if (cgPath->GetFillRule() == FILL_EVEN_ODD) + if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD) CGContextEOClip(mCg); else CGContextClip(mCg); @@ -1179,6 +1856,41 @@ DrawTargetCG::MarkChanged() } } +CGContextRef +BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget *aDT) +{ + if ((aDT->GetBackendType() == BackendType::COREGRAPHICS || + aDT->GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED) && + !aDT->IsTiledDrawTarget() && !aDT->IsDualDrawTarget()) { + DrawTargetCG* cgDT = static_cast<DrawTargetCG*>(aDT); + cgDT->Flush(); + cgDT->MarkChanged(); + + // swap out the context + CGContextRef cg = cgDT->mCg; + if (MOZ2D_ERROR_IF(!cg)) { + return nullptr; + } + cgDT->mCg = nullptr; + + // save the state to make it easier for callers to avoid mucking with things + CGContextSaveGState(cg); + + CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(cgDT->mTransform)); + + return cg; + } + return nullptr; +} + +void +BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg) +{ + DrawTargetCG* cgDT = static_cast<DrawTargetCG*>(aDT); + + CGContextRestoreGState(cg); + cgDT->mCg = cg; +} } diff --git a/gfx/2d/DrawTargetCG.h b/gfx/2d/DrawTargetCG.h index 9ddf29bbd..67651dabb 100644 --- a/gfx/2d/DrawTargetCG.h +++ b/gfx/2d/DrawTargetCG.h @@ -3,6 +3,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef mozilla_gfx_DrawTargetCG_h +#define mozilla_gfx_DrawTargetCG_h + #include <ApplicationServices/ApplicationServices.h> #include "2D.h" @@ -10,6 +13,7 @@ #include "PathCG.h" #include "SourceSurfaceCG.h" #include "GLDefs.h" +#include "Tools.h" namespace mozilla { namespace gfx { @@ -36,32 +40,38 @@ CGRectToRect(CGRect rect) rect.size.height); } +static inline Point +CGPointToPoint(CGPoint point) +{ + return Point(point.x, point.y); +} + static inline void SetStrokeOptions(CGContextRef cg, const StrokeOptions &aStrokeOptions) { switch (aStrokeOptions.mLineCap) { - case CAP_BUTT: + case CapStyle::BUTT: CGContextSetLineCap(cg, kCGLineCapButt); break; - case CAP_ROUND: + case CapStyle::ROUND: CGContextSetLineCap(cg, kCGLineCapRound); break; - case CAP_SQUARE: + case CapStyle::SQUARE: CGContextSetLineCap(cg, kCGLineCapSquare); break; } switch (aStrokeOptions.mLineJoin) { - case JOIN_BEVEL: + case JoinStyle::BEVEL: CGContextSetLineJoin(cg, kCGLineJoinBevel); break; - case JOIN_ROUND: + case JoinStyle::ROUND: CGContextSetLineJoin(cg, kCGLineJoinRound); break; - case JOIN_MITER: - case JOIN_MITER_OR_BEVEL: + case JoinStyle::MITER: + case JoinStyle::MITER_OR_BEVEL: CGContextSetLineJoin(cg, kCGLineJoinMiter); break; } @@ -81,29 +91,53 @@ SetStrokeOptions(CGContextRef cg, const StrokeOptions &aStrokeOptions) } } +class GlyphRenderingOptionsCG : public GlyphRenderingOptions +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptionsCG, override) + + explicit GlyphRenderingOptionsCG(const Color &aFontSmoothingBackgroundColor) + : mFontSmoothingBackgroundColor(aFontSmoothingBackgroundColor) + {} + + const Color &FontSmoothingBackgroundColor() const { return mFontSmoothingBackgroundColor; } + + virtual FontType GetType() const override { return FontType::MAC; } + +private: + Color mFontSmoothingBackgroundColor; +}; class DrawTargetCG : public DrawTarget { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetCG, override) + friend class BorrowedCGContext; + friend class SourceSurfaceCGBitmapContext; DrawTargetCG(); virtual ~DrawTargetCG(); - virtual BackendType GetType() const; - virtual TemporaryRef<SourceSurface> Snapshot(); + virtual DrawTargetType GetType() const override; + virtual BackendType GetBackendType() const override; + virtual TemporaryRef<SourceSurface> Snapshot() override; virtual void DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource, const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; + virtual void DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions = DrawOptions()) override; virtual void MaskSurface(const Pattern &aSource, SourceSurface *aMask, Point aOffset, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void FillRect(const Rect &aRect, const Pattern &aPattern, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; //XXX: why do we take a reference to SurfaceFormat? @@ -112,42 +146,50 @@ public: bool Init(CGContextRef cgContext, const IntSize &aSize); // Flush if using IOSurface context - virtual void Flush(); - - virtual void DrawSurfaceWithShadow(SourceSurface *, const Point &, const Color &, const Point &, Float, CompositionOp); - virtual void ClearRect(const Rect &); - virtual void CopySurface(SourceSurface *, const IntRect&, const IntPoint&); - virtual void StrokeRect(const Rect &, const Pattern &, const StrokeOptions&, const DrawOptions&); - virtual void StrokeLine(const Point &, const Point &, const Pattern &, const StrokeOptions &, const DrawOptions &); - virtual void Stroke(const Path *, const Pattern &, const StrokeOptions &, const DrawOptions &); - virtual void Fill(const Path *, const Pattern &, const DrawOptions &); - virtual void FillGlyphs(ScaledFont *, const GlyphBuffer&, const Pattern &, const DrawOptions &, const GlyphRenderingOptions *); + virtual void Flush() override; + + virtual void DrawSurfaceWithShadow(SourceSurface *, const Point &, const Color &, const Point &, Float, CompositionOp) override; + virtual void ClearRect(const Rect &) override; + virtual void CopySurface(SourceSurface *, const IntRect&, const IntPoint&) override; + virtual void StrokeRect(const Rect &, const Pattern &, const StrokeOptions&, const DrawOptions&) override; + virtual void StrokeLine(const Point &, const Point &, const Pattern &, const StrokeOptions &, const DrawOptions &) override; + virtual void Stroke(const Path *, const Pattern &, const StrokeOptions &, const DrawOptions &) override; + virtual void Fill(const Path *, const Pattern &, const DrawOptions &) override; + virtual void FillGlyphs(ScaledFont *, const GlyphBuffer&, const Pattern &, const DrawOptions &, const GlyphRenderingOptions *) override; virtual void Mask(const Pattern &aSource, const Pattern &aMask, - const DrawOptions &aOptions = DrawOptions()); - virtual void PushClip(const Path *); - virtual void PushClipRect(const Rect &aRect); - virtual void PopClip(); - virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromNativeSurface(const NativeSurface&) const { return nullptr;} - virtual TemporaryRef<DrawTarget> CreateSimilarDrawTarget(const IntSize &, SurfaceFormat) const; - virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule) const; + const DrawOptions &aOptions = DrawOptions()) override; + virtual void PushClip(const Path *) override; + virtual void PushClipRect(const Rect &aRect) override; + virtual void PopClip() override; + virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromNativeSurface(const NativeSurface&) const override { return nullptr;} + virtual TemporaryRef<DrawTarget> CreateSimilarDrawTarget(const IntSize &, SurfaceFormat) const override; + virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule) const override; virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *, uint32_t, - ExtendMode aExtendMode = EXTEND_CLAMP) const; + ExtendMode aExtendMode = ExtendMode::CLAMP) const override; + virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override; - virtual void *GetNativeSurface(NativeSurfaceType); + virtual void *GetNativeSurface(NativeSurfaceType) override; - virtual IntSize GetSize() { return mSize; } + virtual IntSize GetSize() override { return mSize; } /* This is for creating good compatible surfaces */ virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, int32_t aStride, - SurfaceFormat aFormat) const; - virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const; + SurfaceFormat aFormat) const override; + virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override; CGContextRef GetCGContext() { return mCg; } + + // 32767 is the maximum size supported by cairo. We clamp to that to make it + // easier to interoperate. + static size_t GetMaxSurfaceSize() { + return 32767; + } + private: void MarkChanged(); @@ -156,17 +198,19 @@ private: CGContextRef mCg; /** - * A pointer to the image buffer if the buffer is owned by this class (set to - * nullptr otherwise). - * The data is not considered owned by DrawTargetCG if the DrawTarget was - * created for a pre-existing buffer or if the buffer's lifetime is managed - * by CoreGraphics. - * Data owned by DrawTargetCG will be deallocated in the destructor. + * The image buffer, if the buffer is owned by this class. + * If the DrawTarget was created for a pre-existing buffer or if the buffer's + * lifetime is managed by CoreGraphics, mData will be null. + * Data owned by DrawTargetCG will be deallocated in the destructor. */ - void *mData; + AlignedArray<uint8_t> mData; RefPtr<SourceSurfaceCGContext> mSnapshot; + bool mMayContainInvalidPremultipliedData; }; } } + +#endif + diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp index 32d107256..441068a3d 100644 --- a/gfx/2d/DrawTargetCairo.cpp +++ b/gfx/2d/DrawTargetCairo.cpp @@ -9,6 +9,10 @@ #include "PathCairo.h" #include "HelpersCairo.h" #include "ScaledFontBase.h" +#include "BorrowedContext.h" +#include "FilterNodeSoftware.h" +#include "mozilla/Scoped.h" +#include "mozilla/Vector.h" #include "cairo.h" #include "cairo-tee.h" @@ -25,13 +29,26 @@ #ifdef CAIRO_HAS_XLIB_SURFACE #include "cairo-xlib.h" +#include "cairo-xlib-xrender.h" +#endif + +#ifdef CAIRO_HAS_WIN32_SURFACE +#include "cairo-win32.h" #endif #include <algorithm> +// 2^23 +#define CAIRO_COORD_MAX (Float(0x7fffff)) + namespace mozilla { + +MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCairoSurface, cairo_surface_t, cairo_surface_destroy); + namespace gfx { +cairo_surface_t *DrawTargetCairo::mDummySurface; + namespace { // An RAII class to prepare to draw a context and optional path. Saves and @@ -55,7 +72,14 @@ public: MOZ_ASSERT(cairo_status(mCtx) || dt->GetTransform() == GetTransform()); } - ~AutoPrepareForDrawing() { cairo_restore(mCtx); } + ~AutoPrepareForDrawing() + { + cairo_restore(mCtx); + cairo_status_t status = cairo_status(mCtx); + if (status) { + gfxWarning() << "DrawTargetCairo context in error state: " << cairo_status_to_string(status) << "(" << status << ")"; + } + } private: #ifdef DEBUG @@ -70,42 +94,81 @@ private: cairo_t* mCtx; }; +/* Clamp r to (0,0) (2^23,2^23) + * these are to be device coordinates. + * + * Returns false if the rectangle is completely out of bounds, + * true otherwise. + * + * This function assumes that it will be called with a rectangle being + * drawn into a surface with an identity transformation matrix; that + * is, anything above or to the left of (0,0) will be offscreen. + * + * First it checks if the rectangle is entirely beyond + * CAIRO_COORD_MAX; if so, it can't ever appear on the screen -- + * false is returned. + * + * Then it shifts any rectangles with x/y < 0 so that x and y are = 0, + * and adjusts the width and height appropriately. For example, a + * rectangle from (0,-5) with dimensions (5,10) will become a + * rectangle from (0,0) with dimensions (5,5). + * + * If after negative x/y adjustment to 0, either the width or height + * is negative, then the rectangle is completely offscreen, and + * nothing is drawn -- false is returned. + * + * Finally, if x+width or y+height are greater than CAIRO_COORD_MAX, + * the width and height are clamped such x+width or y+height are equal + * to CAIRO_COORD_MAX, and true is returned. + */ +static bool +ConditionRect(Rect& r) { + // if either x or y is way out of bounds; + // note that we don't handle negative w/h here + if (r.X() > CAIRO_COORD_MAX || r.Y() > CAIRO_COORD_MAX) + return false; + + if (r.X() < 0.f) { + r.width += r.X(); + if (r.width < 0.f) + return false; + r.x = 0.f; + } + + if (r.XMost() > CAIRO_COORD_MAX) { + r.width = CAIRO_COORD_MAX - r.X(); + } + + if (r.Y() < 0.f) { + r.height += r.Y(); + if (r.Height() < 0.f) + return false; + + r.y = 0.f; + } + + if (r.YMost() > CAIRO_COORD_MAX) { + r.height = CAIRO_COORD_MAX - r.Y(); + } + return true; +} + } // end anonymous namespace static bool -GetCairoSurfaceSize(cairo_surface_t* surface, IntSize& size) +SupportsSelfCopy(cairo_surface_t* surface) { switch (cairo_surface_get_type(surface)) { - case CAIRO_SURFACE_TYPE_IMAGE: - { - size.width = cairo_image_surface_get_width(surface); - size.height = cairo_image_surface_get_height(surface); - return true; - } - -#ifdef CAIRO_HAS_XLIB_SURFACE - case CAIRO_SURFACE_TYPE_XLIB: - { - size.width = cairo_xlib_surface_get_width(surface); - size.height = cairo_xlib_surface_get_height(surface); - return true; - } -#endif - #ifdef CAIRO_HAS_QUARTZ_SURFACE case CAIRO_SURFACE_TYPE_QUARTZ: - { - CGContextRef cgc = cairo_quartz_surface_get_cg_context(surface); - - // It's valid to call these CGBitmapContext functions on non-bitmap - // contexts; they'll just return 0 in that case. - size.width = CGBitmapContextGetWidth(cgc); - size.height = CGBitmapContextGetWidth(cgc); return true; - } #endif - +#ifdef CAIRO_HAS_WIN32_SURFACE + case CAIRO_SURFACE_TYPE_WIN32: + case CAIRO_SURFACE_TYPE_WIN32_PRINTING: + return true; +#endif default: return false; } @@ -116,15 +179,15 @@ PatternIsCompatible(const Pattern& aPattern) { switch (aPattern.GetType()) { - case PATTERN_LINEAR_GRADIENT: + case PatternType::LINEAR_GRADIENT: { const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern); - return pattern.mStops->GetBackendType() == BACKEND_CAIRO; + return pattern.mStops->GetBackendType() == BackendType::CAIRO; } - case PATTERN_RADIAL_GRADIENT: + case PatternType::RADIAL_GRADIENT: { const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern); - return pattern.mStops->GetBackendType() == BACKEND_CAIRO; + return pattern.mStops->GetBackendType() == BackendType::CAIRO; } default: return true; @@ -136,7 +199,123 @@ static cairo_user_data_key_t surfaceDataKey; void ReleaseData(void* aData) { - static_cast<DataSourceSurface*>(aData)->Release(); + DataSourceSurface *data = static_cast<DataSourceSurface*>(aData); + data->Unmap(); + data->Release(); +} + +cairo_surface_t* +CopyToImageSurface(unsigned char *aData, + const IntRect &aRect, + int32_t aStride, + SurfaceFormat aFormat) +{ + MOZ_ASSERT(aData); + + cairo_surface_t* surf = cairo_image_surface_create(GfxFormatToCairoFormat(aFormat), + aRect.width, + aRect.height); + // In certain scenarios, requesting larger than 8k image fails. Bug 803568 + // covers the details of how to run into it, but the full detailed + // investigation hasn't been done to determine the underlying cause. We + // will just handle the failure to allocate the surface to avoid a crash. + if (cairo_surface_status(surf)) { + return nullptr; + } + + unsigned char* surfData = cairo_image_surface_get_data(surf); + int surfStride = cairo_image_surface_get_stride(surf); + int32_t pixelWidth = BytesPerPixel(aFormat); + + unsigned char* source = aData + + aRect.y * aStride + + aRect.x * pixelWidth; + + MOZ_ASSERT(aStride >= aRect.width * pixelWidth); + for (int32_t y = 0; y < aRect.height; ++y) { + memcpy(surfData + y * surfStride, + source + y * aStride, + aRect.width * pixelWidth); + } + cairo_surface_mark_dirty(surf); + return surf; +} + +/** + * If aSurface can be represented as a surface of type + * CAIRO_SURFACE_TYPE_IMAGE then returns that surface. Does + * not add a reference. + */ +cairo_surface_t* GetAsImageSurface(cairo_surface_t* aSurface) +{ + if (cairo_surface_get_type(aSurface) == CAIRO_SURFACE_TYPE_IMAGE) { + return aSurface; +#ifdef CAIRO_HAS_WIN32_SURFACE + } else if (cairo_surface_get_type(aSurface) == CAIRO_SURFACE_TYPE_WIN32) { + return cairo_win32_surface_get_image(aSurface); +#endif + } + + return nullptr; +} + +cairo_surface_t* CreateSubImageForData(unsigned char* aData, + const IntRect& aRect, + int aStride, + SurfaceFormat aFormat) +{ + if (!aData) { + gfxWarning() << "DrawTargetCairo.CreateSubImageForData null aData"; + return nullptr; + } + unsigned char *data = aData + + aRect.y * aStride + + aRect.x * BytesPerPixel(aFormat); + + cairo_surface_t *image = + cairo_image_surface_create_for_data(data, + GfxFormatToCairoFormat(aFormat), + aRect.width, + aRect.height, + aStride); + cairo_surface_set_device_offset(image, -aRect.x, -aRect.y); + return image; +} + +/** + * Returns a referenced cairo_surface_t representing the + * sub-image specified by aSubImage. + */ +cairo_surface_t* ExtractSubImage(cairo_surface_t* aSurface, + const IntRect& aSubImage, + SurfaceFormat aFormat) +{ + // No need to worry about retaining a reference to the original + // surface since the only caller of this function guarantees + // that aSurface will stay alive as long as the result + + cairo_surface_t* image = GetAsImageSurface(aSurface); + if (image) { + image = CreateSubImageForData(cairo_image_surface_get_data(image), + aSubImage, + cairo_image_surface_get_stride(image), + aFormat); + return image; + } + + cairo_surface_t* similar = + cairo_surface_create_similar(aSurface, + cairo_surface_get_content(aSurface), + aSubImage.width, aSubImage.height); + + cairo_t* ctx = cairo_create(similar); + cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface(ctx, aSurface, -aSubImage.x, -aSubImage.y); + cairo_paint(ctx); + cairo_destroy(ctx); + + cairo_surface_set_device_offset(similar, -aSubImage.x, -aSubImage.y); + return similar; } /** @@ -147,48 +326,135 @@ ReleaseData(void* aData) * result when it is done with it. */ cairo_surface_t* -GetCairoSurfaceForSourceSurface(SourceSurface *aSurface) +GetCairoSurfaceForSourceSurface(SourceSurface *aSurface, + bool aExistingOnly = false, + const IntRect& aSubImage = IntRect()) { - if (aSurface->GetType() == SURFACE_CAIRO) { + IntRect subimage = IntRect(IntPoint(), aSurface->GetSize()); + if (!aSubImage.IsEmpty()) { + MOZ_ASSERT(!aExistingOnly); + MOZ_ASSERT(subimage.Contains(aSubImage)); + subimage = aSubImage; + } + + if (aSurface->GetType() == SurfaceType::CAIRO) { cairo_surface_t* surf = static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface(); - cairo_surface_reference(surf); + if (aSubImage.IsEmpty()) { + cairo_surface_reference(surf); + } else { + surf = ExtractSubImage(surf, subimage, aSurface->GetFormat()); + } return surf; } - if (aSurface->GetType() == SURFACE_CAIRO_IMAGE) { + if (aSurface->GetType() == SurfaceType::CAIRO_IMAGE) { cairo_surface_t* surf = static_cast<const DataSourceSurfaceCairo*>(aSurface)->GetSurface(); - cairo_surface_reference(surf); + if (aSubImage.IsEmpty()) { + cairo_surface_reference(surf); + } else { + surf = ExtractSubImage(surf, subimage, aSurface->GetFormat()); + } return surf; } + if (aExistingOnly) { + return nullptr; + } + RefPtr<DataSourceSurface> data = aSurface->GetDataSurface(); if (!data) { return nullptr; } + DataSourceSurface::MappedSurface map; + if (!data->Map(DataSourceSurface::READ, &map)) { + return nullptr; + } + cairo_surface_t* surf = - cairo_image_surface_create_for_data(data->GetData(), - GfxFormatToCairoFormat(data->GetFormat()), - data->GetSize().width, - data->GetSize().height, - data->Stride()); + CreateSubImageForData(map.mData, subimage, + map.mStride, data->GetFormat()); // In certain scenarios, requesting larger than 8k image fails. Bug 803568 // covers the details of how to run into it, but the full detailed // investigation hasn't been done to determine the underlying cause. We // will just handle the failure to allocate the surface to avoid a crash. - if (cairo_surface_status(surf)) { + if (!surf || cairo_surface_status(surf)) { + if (surf && (cairo_surface_status(surf) == CAIRO_STATUS_INVALID_STRIDE)) { + // If we failed because of an invalid stride then copy into + // a new surface with a stride that cairo chooses. No need to + // set user data since we're not dependent on the original + // data. + cairo_surface_t* result = + CopyToImageSurface(map.mData, + subimage, + map.mStride, + data->GetFormat()); + data->Unmap(); + return result; + } + data->Unmap(); return nullptr; } cairo_surface_set_user_data(surf, - &surfaceDataKey, - data.forget().drop(), - ReleaseData); + &surfaceDataKey, + data.forget().take(), + ReleaseData); return surf; } +// An RAII class to temporarily clear any device offset set +// on a surface. Note that this does not take a reference to the +// surface. +class AutoClearDeviceOffset +{ +public: + explicit AutoClearDeviceOffset(SourceSurface* aSurface) + : mSurface(nullptr) + { + Init(aSurface); + } + + explicit AutoClearDeviceOffset(const Pattern& aPattern) + : mSurface(nullptr) + { + if (aPattern.GetType() == PatternType::SURFACE) { + const SurfacePattern& pattern = static_cast<const SurfacePattern&>(aPattern); + Init(pattern.mSurface); + } + } + + ~AutoClearDeviceOffset() + { + if (mSurface) { + cairo_surface_set_device_offset(mSurface, mX, mY); + } + } + +private: + void Init(SourceSurface* aSurface) + { + cairo_surface_t* surface = GetCairoSurfaceForSourceSurface(aSurface, true); + if (surface) { + Init(surface); + cairo_surface_destroy(surface); + } + } + + void Init(cairo_surface_t *aSurface) + { + mSurface = aSurface; + cairo_surface_get_device_offset(mSurface, &mX, &mY); + cairo_surface_set_device_offset(mSurface, 0, 0); + } + + cairo_surface_t* mSurface; + double mX; + double mY; +}; + // Never returns nullptr. As such, you must always pass in Cairo-compatible // patterns, most notably gradients with a GradientStopCairo. // The pattern returned must have cairo_pattern_destroy() called on it by the @@ -200,50 +466,49 @@ static cairo_pattern_t* GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha) { cairo_pattern_t* pat; + const Matrix* matrix = nullptr; switch (aPattern.GetType()) { - case PATTERN_COLOR: + case PatternType::COLOR: { Color color = static_cast<const ColorPattern&>(aPattern).mColor; pat = cairo_pattern_create_rgba(color.r, color.g, color.b, color.a * aAlpha); break; } - case PATTERN_SURFACE: + case PatternType::SURFACE: { const SurfacePattern& pattern = static_cast<const SurfacePattern&>(aPattern); - cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(pattern.mSurface); + cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(pattern.mSurface, + false, + pattern.mSamplingRect); + if (!surf) + return nullptr; pat = cairo_pattern_create_for_surface(surf); - // The pattern matrix is a matrix that transforms the pattern into user - // space. Cairo takes a matrix that converts from user space to pattern - // space. Cairo therefore needs the inverse. - - cairo_matrix_t mat; - GfxMatrixToCairoMatrix(pattern.mMatrix, mat); - cairo_matrix_invert(&mat); - cairo_pattern_set_matrix(pat, &mat); + matrix = &pattern.mMatrix; cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(pattern.mFilter)); cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(pattern.mExtendMode)); cairo_surface_destroy(surf); - break; } - case PATTERN_LINEAR_GRADIENT: + case PatternType::LINEAR_GRADIENT: { const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern); pat = cairo_pattern_create_linear(pattern.mBegin.x, pattern.mBegin.y, pattern.mEnd.x, pattern.mEnd.y); - MOZ_ASSERT(pattern.mStops->GetBackendType() == BACKEND_CAIRO); + MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO); GradientStopsCairo* cairoStops = static_cast<GradientStopsCairo*>(pattern.mStops.get()); cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode())); + matrix = &pattern.mMatrix; + const std::vector<GradientStop>& stops = cairoStops->GetStops(); for (size_t i = 0; i < stops.size(); ++i) { const GradientStop& stop = stops[i]; @@ -254,17 +519,19 @@ GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha) break; } - case PATTERN_RADIAL_GRADIENT: + case PatternType::RADIAL_GRADIENT: { const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern); pat = cairo_pattern_create_radial(pattern.mCenter1.x, pattern.mCenter1.y, pattern.mRadius1, pattern.mCenter2.x, pattern.mCenter2.y, pattern.mRadius2); - MOZ_ASSERT(pattern.mStops->GetBackendType() == BACKEND_CAIRO); + MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO); GradientStopsCairo* cairoStops = static_cast<GradientStopsCairo*>(pattern.mStops.get()); cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode())); + matrix = &pattern.mMatrix; + const std::vector<GradientStop>& stops = cairoStops->GetStops(); for (size_t i = 0; i < stops.size(); ++i) { const GradientStop& stop = stops[i]; @@ -282,6 +549,16 @@ GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha) } } + // The pattern matrix is a matrix that transforms the pattern into user + // space. Cairo takes a matrix that converts from user space to pattern + // space. Cairo therefore needs the inverse. + if (matrix) { + cairo_matrix_t mat; + GfxMatrixToCairoMatrix(*matrix, mat); + cairo_matrix_invert(&mat); + cairo_pattern_set_matrix(pat, &mat); + } + return pat; } @@ -290,7 +567,7 @@ NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions) { // We pre-multiply colours' alpha by the global alpha, so we don't need to // use an intermediate surface for them. - if (aPattern.GetType() == PATTERN_COLOR) + if (aPattern.GetType() == PatternType::COLOR) return false; if (aOptions.mAlpha == 1.0) @@ -302,20 +579,71 @@ NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions) DrawTargetCairo::DrawTargetCairo() : mContext(nullptr) , mSurface(nullptr) - , mPathObserver(nullptr) + , mLockedBits(nullptr) { } DrawTargetCairo::~DrawTargetCairo() { - MarkSnapshotIndependent(); - if (mPathObserver) { - mPathObserver->ForgetDrawTarget(); - } cairo_destroy(mContext); if (mSurface) { cairo_surface_destroy(mSurface); } + MOZ_ASSERT(!mLockedBits); +} + +DrawTargetType +DrawTargetCairo::GetType() const +{ + if (mContext) { + cairo_surface_type_t type = cairo_surface_get_type(mSurface); + if (type == CAIRO_SURFACE_TYPE_TEE) { + type = cairo_surface_get_type(cairo_tee_surface_index(mSurface, 0)); + MOZ_ASSERT(type != CAIRO_SURFACE_TYPE_TEE, "C'mon!"); + MOZ_ASSERT(type == cairo_surface_get_type(cairo_tee_surface_index(mSurface, 1)), + "What should we do here?"); + } + switch (type) { + case CAIRO_SURFACE_TYPE_PDF: + case CAIRO_SURFACE_TYPE_PS: + case CAIRO_SURFACE_TYPE_SVG: + case CAIRO_SURFACE_TYPE_WIN32_PRINTING: + case CAIRO_SURFACE_TYPE_XML: + return DrawTargetType::VECTOR; + + case CAIRO_SURFACE_TYPE_VG: + case CAIRO_SURFACE_TYPE_GL: + case CAIRO_SURFACE_TYPE_GLITZ: + case CAIRO_SURFACE_TYPE_QUARTZ: + case CAIRO_SURFACE_TYPE_DIRECTFB: + return DrawTargetType::HARDWARE_RASTER; + + case CAIRO_SURFACE_TYPE_SKIA: + case CAIRO_SURFACE_TYPE_QT: + MOZ_ASSERT(false, "Can't determine actual DrawTargetType for DrawTargetCairo - assuming SOFTWARE_RASTER"); + // fallthrough + case CAIRO_SURFACE_TYPE_IMAGE: + case CAIRO_SURFACE_TYPE_XLIB: + case CAIRO_SURFACE_TYPE_XCB: + case CAIRO_SURFACE_TYPE_WIN32: + case CAIRO_SURFACE_TYPE_BEOS: + case CAIRO_SURFACE_TYPE_OS2: + case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: + case CAIRO_SURFACE_TYPE_SCRIPT: + case CAIRO_SURFACE_TYPE_RECORDING: + case CAIRO_SURFACE_TYPE_DRM: + case CAIRO_SURFACE_TYPE_SUBSURFACE: +#ifdef CAIRO_HAS_D2D_SURFACE + case CAIRO_SURFACE_TYPE_D2D: +#endif + case CAIRO_SURFACE_TYPE_TEE: // included to silence warning about unhandled enum value + return DrawTargetType::SOFTWARE_RASTER; + default: + MOZ_CRASH("Unsupported cairo surface type"); + } + } + MOZ_ASSERT(false, "Could not determine DrawTargetType for DrawTargetCairo"); + return DrawTargetType::SOFTWARE_RASTER; } IntSize @@ -333,14 +661,38 @@ DrawTargetCairo::Snapshot() IntSize size = GetSize(); - cairo_content_t content = cairo_surface_get_content(mSurface); mSnapshot = new SourceSurfaceCairo(mSurface, size, - CairoContentToGfxFormat(content), + GfxFormatForCairoSurface(mSurface), this); return mSnapshot; } +bool +DrawTargetCairo::LockBits(uint8_t** aData, IntSize* aSize, + int32_t* aStride, SurfaceFormat* aFormat) +{ + if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) { + WillChange(); + + mLockedBits = cairo_image_surface_get_data(mSurface); + *aData = mLockedBits; + *aSize = GetSize(); + *aStride = cairo_image_surface_get_stride(mSurface); + *aFormat = GetFormat(); + return true; + } + + return false; +} + +void +DrawTargetCairo::ReleaseBits(uint8_t* aData) +{ + MOZ_ASSERT(mLockedBits == aData); + mLockedBits = nullptr; +} + void DrawTargetCairo::Flush() { @@ -354,6 +706,18 @@ DrawTargetCairo::PrepareForDrawing(cairo_t* aContext, const Path* aPath /* = nul WillChange(aPath); } +cairo_surface_t* +DrawTargetCairo::GetDummySurface() +{ + if (mDummySurface) { + return mDummySurface; + } + + mDummySurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); + + return mDummySurface; +} + void DrawTargetCairo::DrawSurface(SourceSurface *aSurface, const Rect &aDest, @@ -362,6 +726,7 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface, const DrawOptions &aOptions) { AutoPrepareForDrawing prep(this, mContext); + AutoClearDeviceOffset clear(aSurface); float sx = aSource.Width() / aDest.Width(); float sy = aSource.Height() / aDest.Height(); @@ -378,20 +743,27 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface, cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(aSurfOptions.mFilter)); cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD); + cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode)); + + // If the destination rect covers the entire clipped area, then unbounded and bounded + // operations are identical, and we don't need to push a group. + bool needsGroup = !IsOperatorBoundByMask(aOptions.mCompositionOp) && + !aDest.Contains(GetUserSpaceClip()); + cairo_translate(mContext, aDest.X(), aDest.Y()); - if (IsOperatorBoundByMask(aOptions.mCompositionOp)) { - cairo_new_path(mContext); - cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height()); - cairo_clip(mContext); - cairo_set_source(mContext, pat); - } else { + if (needsGroup) { cairo_push_group(mContext); cairo_new_path(mContext); cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height()); cairo_set_source(mContext, pat); cairo_fill(mContext); cairo_pop_group_to_source(mContext); + } else { + cairo_new_path(mContext); + cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height()); + cairo_clip(mContext); + cairo_set_source(mContext, pat); } cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp)); @@ -402,6 +774,16 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface, } void +DrawTargetCairo::DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions) +{ + FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode); + filter->Draw(this, aSourceRect, aDestPoint, aOptions); +} + +void DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, @@ -409,10 +791,12 @@ DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface, Float aSigma, CompositionOp aOperator) { - if (aSurface->GetType() != SURFACE_CAIRO) { + if (aSurface->GetType() != SurfaceType::CAIRO) { return; } + AutoClearDeviceOffset clear(aSurface); + Float width = Float(aSurface->GetSize().width); Float height = Float(aSurface->GetSize().height); @@ -431,7 +815,7 @@ DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface, Rect extents(0, 0, width, height); AlphaBoxBlur blur(extents, cairo_image_surface_get_stride(blursurf), - aSigma); + aSigma, aSigma); blur.Blur(cairo_image_surface_get_data(blursurf)); } else { blursurf = sourcesurf; @@ -476,21 +860,33 @@ void DrawTargetCairo::DrawPattern(const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aOptions, - DrawPatternType aDrawType) + DrawPatternType aDrawType, + bool aPathBoundsClip) { if (!PatternIsCompatible(aPattern)) { return; } + AutoClearDeviceOffset clear(aPattern); + cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha); + if (!pat) { + return; + } + if (cairo_pattern_status(pat)) { + cairo_pattern_destroy(pat); + gfxWarning() << "Invalid pattern"; + return; + } + cairo_set_source(mContext, pat); + cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode)); + if (NeedIntermediateSurface(aPattern, aOptions) || - !IsOperatorBoundByMask(aOptions.mCompositionOp)) { + (!IsOperatorBoundByMask(aOptions.mCompositionOp) && !aPathBoundsClip)) { cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA); - ClearSurfaceForUnboundedSource(aOptions.mCompositionOp); - // Don't want operators to be applied twice cairo_set_operator(mContext, CAIRO_OPERATOR_OVER); @@ -507,7 +903,6 @@ DrawTargetCairo::DrawPattern(const Pattern& aPattern, cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp)); cairo_paint_with_alpha(mContext, aOptions.mAlpha); } else { - ClearSurfaceForUnboundedSource(aOptions.mCompositionOp); cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp)); if (aDrawType == DRAW_STROKE) { @@ -528,10 +923,72 @@ DrawTargetCairo::FillRect(const Rect &aRect, { AutoPrepareForDrawing prep(this, mContext); + bool restoreTransform = false; + Matrix mat; + Rect r = aRect; + + /* Clamp coordinates to work around a design bug in cairo */ + if (r.width > CAIRO_COORD_MAX || + r.height > CAIRO_COORD_MAX || + r.x < -CAIRO_COORD_MAX || + r.x > CAIRO_COORD_MAX || + r.y < -CAIRO_COORD_MAX || + r.y > CAIRO_COORD_MAX) + { + if (!mat.IsRectilinear()) { + gfxWarning() << "DrawTargetCairo::FillRect() misdrawing huge Rect " + "with non-rectilinear transform"; + } + + mat = GetTransform(); + r = mat.TransformBounds(r); + + if (!ConditionRect(r)) { + gfxWarning() << "Ignoring DrawTargetCairo::FillRect() call with " + "out-of-bounds Rect"; + return; + } + + restoreTransform = true; + SetTransform(Matrix()); + } + cairo_new_path(mContext); - cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height()); + cairo_rectangle(mContext, r.x, r.y, r.Width(), r.Height()); - DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL); + bool pathBoundsClip = false; + + if (r.Contains(GetUserSpaceClip())) { + pathBoundsClip = true; + } + + DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL, pathBoundsClip); + + if (restoreTransform) { + SetTransform(mat); + } +} + +void +DrawTargetCairo::CopySurfaceInternal(cairo_surface_t* aSurface, + const IntRect &aSource, + const IntPoint &aDest) +{ + if (cairo_surface_status(aSurface)) { + gfxWarning() << "Invalid surface"; + return; + } + + cairo_identity_matrix(mContext); + + cairo_set_source_surface(mContext, aSurface, aDest.x - aSource.x, aDest.y - aSource.y); + cairo_set_operator(mContext, CAIRO_OPERATOR_SOURCE); + cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE); + + cairo_reset_clip(mContext); + cairo_new_path(mContext); + cairo_rectangle(mContext, aDest.x, aDest.y, aSource.width, aSource.height); + cairo_fill(mContext); } void @@ -540,23 +997,54 @@ DrawTargetCairo::CopySurface(SourceSurface *aSurface, const IntPoint &aDest) { AutoPrepareForDrawing prep(this, mContext); + AutoClearDeviceOffset clear(aSurface); - if (!aSurface || aSurface->GetType() != SURFACE_CAIRO) { + if (!aSurface) { gfxWarning() << "Unsupported surface type specified"; return; } - cairo_surface_t* surf = static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface(); + cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface); + if (!surf) { + gfxWarning() << "Unsupported surface type specified"; + return; + } - cairo_identity_matrix(mContext); + CopySurfaceInternal(surf, aSource, aDest); + cairo_surface_destroy(surf); +} - cairo_set_source_surface(mContext, surf, aDest.x - aSource.x, aDest.y - aSource.y); - cairo_set_operator(mContext, CAIRO_OPERATOR_SOURCE); +void +DrawTargetCairo::CopyRect(const IntRect &aSource, + const IntPoint &aDest) +{ + AutoPrepareForDrawing prep(this, mContext); - cairo_reset_clip(mContext); - cairo_new_path(mContext); - cairo_rectangle(mContext, aDest.x, aDest.y, aSource.width, aSource.height); - cairo_fill(mContext); + IntRect source = aSource; + cairo_surface_t* surf = mSurface; + + if (!SupportsSelfCopy(mSurface) && + aDest.y >= aSource.y && + aDest.y < aSource.YMost()) { + cairo_surface_t* similar = cairo_surface_create_similar(mSurface, + GfxFormatToCairoContent(GetFormat()), + aSource.width, aSource.height); + cairo_t* ctx = cairo_create(similar); + cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface(ctx, surf, -aSource.x, -aSource.y); + cairo_paint(ctx); + cairo_destroy(ctx); + + source.x = 0; + source.y = 0; + surf = similar; + } + + CopySurfaceInternal(surf, source, aDest); + + if (surf != mSurface) { + cairo_surface_destroy(surf); + } } void @@ -564,6 +1052,13 @@ DrawTargetCairo::ClearRect(const Rect& aRect) { AutoPrepareForDrawing prep(this, mContext); + if (!mContext || aRect.Width() <= 0 || aRect.Height() <= 0 || + !IsFinite(aRect.X()) || !IsFinite(aRect.Width()) || + !IsFinite(aRect.Y()) || !IsFinite(aRect.Height())) { + gfxCriticalError(CriticalLog::DefaultOptions(false)) << "ClearRect with invalid argument " << gfx::hexa(mContext) << " with " << aRect.Width() << "x" << aRect.Height() << " [" << aRect.X() << ", " << aRect.Y() << "]"; + } + + cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE); cairo_new_path(mContext); cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR); cairo_rectangle(mContext, aRect.X(), aRect.Y(), @@ -609,11 +1104,11 @@ DrawTargetCairo::Stroke(const Path *aPath, { AutoPrepareForDrawing prep(this, mContext, aPath); - if (aPath->GetBackendType() != BACKEND_CAIRO) + if (aPath->GetBackendType() != BackendType::CAIRO) return; PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath)); - path->CopyPathTo(mContext, this); + path->SetPathOnContext(mContext); DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE); } @@ -625,11 +1120,11 @@ DrawTargetCairo::Fill(const Path *aPath, { AutoPrepareForDrawing prep(this, mContext, aPath); - if (aPath->GetBackendType() != BACKEND_CAIRO) + if (aPath->GetBackendType() != BackendType::CAIRO) return; PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath)); - path->CopyPathTo(mContext, this); + path->SetPathOnContext(mContext); DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL); } @@ -652,16 +1147,30 @@ DrawTargetCairo::FillGlyphs(ScaledFont *aFont, const GlyphRenderingOptions*) { AutoPrepareForDrawing prep(this, mContext); + AutoClearDeviceOffset clear(aPattern); ScaledFontBase* scaledFont = static_cast<ScaledFontBase*>(aFont); cairo_set_scaled_font(mContext, scaledFont->GetCairoScaledFont()); cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha); + if (!pat) + return; + cairo_set_source(mContext, pat); cairo_pattern_destroy(pat); - // Convert our GlyphBuffer into an array of Cairo glyphs. - std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs); + cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode)); + + // Convert our GlyphBuffer into a vector of Cairo glyphs. This code can + // execute millions of times in short periods, so we want to avoid heap + // allocation whenever possible. So we use an inline vector capacity of 1024 + // bytes (the maximum allowed by mozilla::Vector), which gives an inline + // length of 1024 / 24 = 42 elements, which is enough to typically avoid heap + // allocation in ~99% of cases. + Vector<cairo_glyph_t, 1024 / sizeof(cairo_glyph_t)> glyphs; + if (!glyphs.resizeUninitialized(aBuffer.mNumGlyphs)) { + MOZ_CRASH("glyphs allocation failed"); + } for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) { glyphs[i].index = aBuffer.mGlyphs[i].mIndex; glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x; @@ -677,70 +1186,101 @@ DrawTargetCairo::Mask(const Pattern &aSource, const DrawOptions &aOptions /* = DrawOptions() */) { AutoPrepareForDrawing prep(this, mContext); + AutoClearDeviceOffset clearSource(aSource); + AutoClearDeviceOffset clearMask(aMask); + + cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode)); cairo_pattern_t* source = GfxPatternToCairoPattern(aSource, aOptions.mAlpha); - cairo_set_source(mContext, source); + if (!source) { + return; + } cairo_pattern_t* mask = GfxPatternToCairoPattern(aMask, aOptions.mAlpha); + if (!mask) { + cairo_pattern_destroy(source); + return; + } + + if (cairo_pattern_status(source) || cairo_pattern_status(mask)) { + cairo_pattern_destroy(source); + cairo_pattern_destroy(mask); + gfxWarning() << "Invalid pattern"; + return; + } + + cairo_set_source(mContext, source); cairo_mask(mContext, mask); cairo_pattern_destroy(mask); cairo_pattern_destroy(source); } -#if 0 -DrawTargetCairo::MaskSurface(SourceSurface *aSurface, - const Rect &aDest, - const Rect &aSource, - const DrawSurfaceOptions &aSurfOptions, + +void +DrawTargetCairo::MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, const DrawOptions &aOptions) { AutoPrepareForDrawing prep(this, mContext); + AutoClearDeviceOffset clearSource(aSource); + AutoClearDeviceOffset clearMask(aMask); - float sx = aSource.Width() / aDest.Width(); - float sy = aSource.Height() / aDest.Height(); + if (!PatternIsCompatible(aSource)) { + return; + } - cairo_matrix_t src_mat; - cairo_matrix_init_translate(&src_mat, aSource.X(), aSource.Y()); - cairo_matrix_scale(&src_mat, sx, sy); + cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode)); - cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface); - cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf); - cairo_surface_destroy(surf); + cairo_pattern_t* pat = GfxPatternToCairoPattern(aSource, aOptions.mAlpha); + if (!pat) { + return; + } - cairo_pattern_set_matrix(pat, &src_mat); - cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(aSurfOptions.mFilter)); - cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD); + if (cairo_pattern_status(pat)) { + cairo_pattern_destroy(pat); + gfxWarning() << "Invalid pattern"; + return; + } - cairo_translate(mContext, aDest.X(), aDest.Y()); + cairo_set_source(mContext, pat); + + if (NeedIntermediateSurface(aSource, aOptions)) { + cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA); + + // Don't want operators to be applied twice + cairo_set_operator(mContext, CAIRO_OPERATOR_OVER); + + // Now draw the content using the desired operator + cairo_paint_with_alpha(mContext, aOptions.mAlpha); - if (IsOperatorBoundByMask(aOptions.mCompositionOp)) { - cairo_new_path(mContext); - cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height()); - cairo_clip(mContext); - cairo_set_source(mContext, pat); - } else { - cairo_push_group(mContext); - cairo_new_path(mContext); - cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height()); - cairo_set_source(mContext, pat); - cairo_fill(mContext); cairo_pop_group_to_source(mContext); } + cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask); + if (!surf) { + cairo_pattern_destroy(pat); + return; + } + cairo_pattern_t* mask = cairo_pattern_create_for_surface(surf); + cairo_matrix_t matrix; + + cairo_matrix_init_translate (&matrix, -aOffset.x, -aOffset.y); + cairo_pattern_set_matrix (mask, &matrix); + cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp)); - DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL); - cairo_paint_with_alpha(mContext, aOptions.mAlpha); + cairo_mask(mContext, mask); + cairo_surface_destroy(surf); + cairo_pattern_destroy(mask); cairo_pattern_destroy(pat); } -#endif - void DrawTargetCairo::PushClip(const Path *aPath) { - if (aPath->GetBackendType() != BACKEND_CAIRO) { + if (aPath->GetBackendType() != BackendType::CAIRO) { return; } @@ -748,7 +1288,7 @@ DrawTargetCairo::PushClip(const Path *aPath) cairo_save(mContext); PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath)); - path->CopyPathTo(mContext, this); + path->SetPathOnContext(mContext); cairo_clip_preserve(mContext); } @@ -767,23 +1307,30 @@ void DrawTargetCairo::PopClip() { // save/restore does not affect the path, so no need to call WillChange() + + // cairo_restore will restore the transform too and we don't want to do that + // so we'll save it now and restore it after the cairo_restore + cairo_matrix_t mat; + cairo_get_matrix(mContext, &mat); + cairo_restore(mContext); + + cairo_set_matrix(mContext, &mat); + + MOZ_ASSERT(cairo_status(mContext) || GetTransform() == Matrix(mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0), + "Transforms are out of sync"); } TemporaryRef<PathBuilder> -DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FILL_WINDING */) const +DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FillRule::FILL_WINDING */) const { - RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mContext, - const_cast<DrawTargetCairo*>(this), - aFillRule); - - return builder; + return new PathBuilderCairo(aFillRule); } void DrawTargetCairo::ClearSurfaceForUnboundedSource(const CompositionOp &aOperator) { - if (aOperator != OP_SOURCE) + if (aOperator != CompositionOp::OP_SOURCE) return; cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR); // It doesn't really matter what the source is here, since Paint @@ -797,36 +1344,13 @@ TemporaryRef<GradientStops> DrawTargetCairo::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const { - RefPtr<GradientStopsCairo> stops = new GradientStopsCairo(aStops, aNumStops, - aExtendMode); - return stops; + return new GradientStopsCairo(aStops, aNumStops, aExtendMode); } -/** - * Copies pixel data from aData into aSurface; aData must have the dimensions - * given in aSize, with a stride of aStride bytes and aPixelWidth bytes per pixel - */ -static void -CopyDataToCairoSurface(cairo_surface_t* aSurface, - unsigned char *aData, - const IntSize &aSize, - int32_t aStride, - int32_t aPixelWidth) +TemporaryRef<FilterNode> +DrawTargetCairo::CreateFilter(FilterType aType) { - unsigned char* surfData = cairo_image_surface_get_data(aSurface); - // In certain scenarios, requesting larger than 8k image fails. Bug 803568 - // covers the details of how to run into it, but the full detailed - // investigation hasn't been done to determine the underlying cause. We - // will just handle the failure to allocate the surface to avoid a crash. - if (!surfData) { - return; - } - for (int32_t y = 0; y < aSize.height; ++y) { - memcpy(surfData + y * aSize.width * aPixelWidth, - aData + y * aStride, - aSize.width * aPixelWidth); - } - cairo_surface_mark_dirty(aSurface); + return FilterNodeSoftware::Create(aType); } TemporaryRef<SourceSurface> @@ -835,42 +1359,141 @@ DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData, int32_t aStride, SurfaceFormat aFormat) const { - cairo_surface_t* surf = cairo_image_surface_create(GfxFormatToCairoFormat(aFormat), - aSize.width, - aSize.height); - // In certain scenarios, requesting larger than 8k image fails. Bug 803568 - // covers the details of how to run into it, but the full detailed - // investigation hasn't been done to determine the underlying cause. We - // will just handle the failure to allocate the surface to avoid a crash. - if (cairo_surface_status(surf)) { + if (!aData) { + gfxWarning() << "DrawTargetCairo::CreateSourceSurfaceFromData null aData"; return nullptr; } - CopyDataToCairoSurface(surf, aData, aSize, aStride, BytesPerPixel(aFormat)); + cairo_surface_t* surf = CopyToImageSurface(aData, IntRect(IntPoint(), aSize), + aStride, aFormat); + if (!surf) { + return nullptr; + } RefPtr<SourceSurfaceCairo> source_surf = new SourceSurfaceCairo(surf, aSize, aFormat); cairo_surface_destroy(surf); - return source_surf; + return source_surf.forget(); } +#ifdef CAIRO_HAS_XLIB_SURFACE +static cairo_user_data_key_t gDestroyPixmapKey; + +struct DestroyPixmapClosure { + DestroyPixmapClosure(Drawable d, Screen *s) : mPixmap(d), mScreen(s) {} + ~DestroyPixmapClosure() { + XFreePixmap(DisplayOfScreen(mScreen), mPixmap); + } + Drawable mPixmap; + Screen *mScreen; +}; + +static void +DestroyPixmap(void *data) +{ + delete static_cast<DestroyPixmapClosure*>(data); +} +#endif + TemporaryRef<SourceSurface> DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const { +#ifdef CAIRO_HAS_XLIB_SURFACE + cairo_surface_type_t ctype = cairo_surface_get_type(mSurface); + if (aSurface->GetType() == SurfaceType::CAIRO && + cairo_surface_get_type( + static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface()) == ctype) { + return aSurface; + } + + if (ctype != CAIRO_SURFACE_TYPE_XLIB) { + return aSurface; + } + + IntSize size = aSurface->GetSize(); + if (!size.width || !size.height) { + return aSurface; + } + + // Although the dimension parameters in the xCreatePixmapReq wire protocol are + // 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if + // either dimension cannot be represented by a 16-bit *signed* integer. + #define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff + + if (size.width > XLIB_IMAGE_SIDE_SIZE_LIMIT || + size.height > XLIB_IMAGE_SIDE_SIZE_LIMIT) { + return aSurface; + } + + SurfaceFormat format = aSurface->GetFormat(); + Screen *screen = cairo_xlib_surface_get_screen(mSurface); + Display *dpy = DisplayOfScreen(screen); + XRenderPictFormat* xrenderFormat = nullptr; + switch (format) { + case SurfaceFormat::B8G8R8A8: + xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardARGB32); + break; + case SurfaceFormat::B8G8R8X8: + xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardRGB24); + break; + case SurfaceFormat::A8: + xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardA8); + break; + default: + return aSurface; + } + if (!xrenderFormat) { + return aSurface; + } + + Drawable pixmap = XCreatePixmap(dpy, RootWindowOfScreen(screen), + size.width, size.height, + xrenderFormat->depth); + if (!pixmap) { + return aSurface; + } + + ScopedDeletePtr<DestroyPixmapClosure> closure( + new DestroyPixmapClosure(pixmap, screen)); + + ScopedCairoSurface csurf( + cairo_xlib_surface_create_with_xrender_format(dpy, pixmap, + screen, xrenderFormat, + size.width, size.height)); + if (!csurf || cairo_surface_status(csurf)) { + return aSurface; + } + + cairo_surface_set_user_data(csurf, &gDestroyPixmapKey, + closure.forget(), DestroyPixmap); + + RefPtr<DrawTargetCairo> dt = new DrawTargetCairo(); + if (!dt->Init(csurf, size, &format)) { + return aSurface; + } + + dt->CopySurface(aSurface, + IntRect(0, 0, size.width, size.height), + IntPoint(0, 0)); + dt->Flush(); + + return new SourceSurfaceCairo(csurf, size, format); +#endif + return aSurface; } TemporaryRef<SourceSurface> DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const { - if (aSurface.mType == NATIVE_SURFACE_CAIRO_SURFACE) { - IntSize size; - cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface); - if (GetCairoSurfaceSize(surf, size)) { - RefPtr<SourceSurfaceCairo> source = - new SourceSurfaceCairo(surf, size, aSurface.mFormat); - return source; + if (aSurface.mType == NativeSurfaceType::CAIRO_SURFACE) { + if (aSurface.mSize.width <= 0 || + aSurface.mSize.height <= 0) { + gfxWarning() << "Can't create a SourceSurface without a valid size"; + return nullptr; } + cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface); + return new SourceSurfaceCairo(surf, aSurface.mSize, aSurface.mFormat); } return nullptr; @@ -879,25 +1502,29 @@ DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurf TemporaryRef<DrawTarget> DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const { - cairo_surface_t* similar = cairo_surface_create_similar(cairo_get_target(mContext), + cairo_surface_t* similar = cairo_surface_create_similar(mSurface, GfxFormatToCairoContent(aFormat), aSize.width, aSize.height); if (!cairo_surface_status(similar)) { RefPtr<DrawTargetCairo> target = new DrawTargetCairo(); if (target->InitAlreadyReferenced(similar, aSize)) { - return target; + return target.forget(); } } + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "Failed to create similar cairo surface! Size: " << aSize << " Status: " << cairo_surface_status(similar); + return nullptr; } bool -DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize) +DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat) { - //check if aSurface is valid. Destroy and return early if not a valid surface. if (cairo_surface_status(aSurface)) { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) + << "Attempt to create DrawTarget for invalid surface. " + << aSize << " Cairo Status: " << cairo_surface_status(aSurface); cairo_surface_destroy(aSurface); return false; } @@ -905,10 +1532,17 @@ DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& mContext = cairo_create(aSurface); mSurface = aSurface; mSize = aSize; - mFormat = CairoContentToGfxFormat(cairo_surface_get_content(aSurface)); + mFormat = aFormat ? *aFormat : GfxFormatForCairoSurface(aSurface); + + // Cairo image surface have a bug where they will allocate a mask surface (for clipping) + // the size of the clip extents, and don't take the surface extents into account. + // Add a manual clip to the surface extents to prevent this. + cairo_new_path(mContext); + cairo_rectangle(mContext, 0, 0, mSize.width, mSize.height); + cairo_clip(mContext); - if (mFormat == FORMAT_B8G8R8A8 || - mFormat == FORMAT_R8G8B8A8) { + if (mFormat == SurfaceFormat::B8G8R8A8 || + mFormat == SurfaceFormat::R8G8B8A8) { SetPermitSubpixelAA(false); } else { SetPermitSubpixelAA(true); @@ -934,9 +1568,9 @@ DrawTargetCairo::CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFor if (aSigma == 0.0F) { RefPtr<DrawTargetCairo> target = new DrawTargetCairo(); if (target->InitAlreadyReferenced(similar, aSize)) { - return target; + return target.forget(); } else { - return nullptr; + return nullptr; } } @@ -960,24 +1594,46 @@ DrawTargetCairo::CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFor RefPtr<DrawTargetCairo> target = new DrawTargetCairo(); if (target->InitAlreadyReferenced(tee, aSize)) { - return target; + return target.forget(); } return nullptr; } bool -DrawTargetCairo::Init(cairo_surface_t* aSurface, const IntSize& aSize) +DrawTargetCairo::Init(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat) { cairo_surface_reference(aSurface); - return InitAlreadyReferenced(aSurface, aSize); + return InitAlreadyReferenced(aSurface, aSize, aFormat); +} + +bool +DrawTargetCairo::Init(const IntSize& aSize, SurfaceFormat aFormat) +{ + cairo_surface_t *surf = cairo_image_surface_create(GfxFormatToCairoFormat(aFormat), aSize.width, aSize.height); + return InitAlreadyReferenced(surf, aSize); +} + +bool +DrawTargetCairo::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat) +{ + cairo_surface_t* surf = + cairo_image_surface_create_for_data(aData, + GfxFormatToCairoFormat(aFormat), + aSize.width, + aSize.height, + aStride); + return InitAlreadyReferenced(surf, aSize); } void * DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType) { - if (aType == NATIVE_SURFACE_CAIRO_SURFACE) { + if (aType == NativeSurfaceType::CAIRO_SURFACE) { return cairo_get_target(mContext); } + if (aType == NativeSurfaceType::CAIRO_CONTEXT) { + return mContext; + } return nullptr; } @@ -998,21 +1654,7 @@ void DrawTargetCairo::WillChange(const Path* aPath /* = nullptr */) { MarkSnapshotIndependent(); - - if (mPathObserver && - (!aPath || !mPathObserver->ContainsPath(aPath))) { - mPathObserver->PathWillChange(); - mPathObserver = nullptr; - } -} - -void -DrawTargetCairo::SetPathObserver(CairoPathContext* aPathObserver) -{ - if (mPathObserver && mPathObserver != aPathObserver) { - mPathObserver->PathWillChange(); - } - mPathObserver = aPathObserver; + MOZ_ASSERT(!mLockedBits); } void @@ -1025,5 +1667,50 @@ DrawTargetCairo::SetTransform(const Matrix& aTransform) cairo_set_matrix(mContext, &mat); } +Rect +DrawTargetCairo::GetUserSpaceClip() +{ + double clipX1, clipY1, clipX2, clipY2; + cairo_clip_extents(mContext, &clipX1, &clipY1, &clipX2, &clipY2); + return Rect(clipX1, clipY1, clipX2 - clipX1, clipY2 - clipY1); // Narrowing of doubles to floats +} + +cairo_t* +BorrowedCairoContext::BorrowCairoContextFromDrawTarget(DrawTarget* aDT) +{ + if (aDT->GetBackendType() != BackendType::CAIRO || + aDT->IsDualDrawTarget() || + aDT->IsTiledDrawTarget()) { + return nullptr; + } + DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT); + + cairoDT->WillChange(); + + // save the state to make it easier for callers to avoid mucking with things + cairo_save(cairoDT->mContext); + + // Neuter the DrawTarget while the context is being borrowed + cairo_t* cairo = cairoDT->mContext; + cairoDT->mContext = nullptr; + + return cairo; +} + +void +BorrowedCairoContext::ReturnCairoContextToDrawTarget(DrawTarget* aDT, + cairo_t* aCairo) +{ + if (aDT->GetBackendType() != BackendType::CAIRO || + aDT->IsDualDrawTarget() || + aDT->IsTiledDrawTarget()) { + return; + } + DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT); + + cairo_restore(aCairo); + cairoDT->mContext = aCairo; +} + } } diff --git a/gfx/2d/DrawTargetCairo.h b/gfx/2d/DrawTargetCairo.h index d89de240b..db9e8b4a3 100644 --- a/gfx/2d/DrawTargetCairo.h +++ b/gfx/2d/DrawTargetCairo.h @@ -20,6 +20,7 @@ class SourceSurfaceCairo; class GradientStopsCairo : public GradientStops { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsCairo) GradientStopsCairo(GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) : mExtendMode(aExtendMode) @@ -41,7 +42,7 @@ class GradientStopsCairo : public GradientStops return mExtendMode; } - virtual BackendType GetBackendType() const { return BACKEND_CAIRO; } + virtual BackendType GetBackendType() const { return BackendType::CAIRO; } private: std::vector<GradientStop> mStops; @@ -51,115 +52,144 @@ class GradientStopsCairo : public GradientStops class DrawTargetCairo : public DrawTarget { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetCairo, override) + friend class BorrowedCairoContext; + DrawTargetCairo(); virtual ~DrawTargetCairo(); - virtual BackendType GetType() const { return BACKEND_CAIRO; } - virtual TemporaryRef<SourceSurface> Snapshot(); - virtual IntSize GetSize(); + virtual DrawTargetType GetType() const override; + virtual BackendType GetBackendType() const override { return BackendType::CAIRO; } + virtual TemporaryRef<SourceSurface> Snapshot() override; + virtual IntSize GetSize() override; + + virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) override; + + virtual bool LockBits(uint8_t** aData, IntSize* aSize, + int32_t* aStride, SurfaceFormat* aFormat) override; + virtual void ReleaseBits(uint8_t* aData) override; - virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA); - - virtual void Flush(); + virtual void Flush() override; virtual void DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource, const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; + virtual void DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions = DrawOptions()) override; virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, - CompositionOp aOperator); + CompositionOp aOperator) override; - virtual void ClearRect(const Rect &aRect); + virtual void ClearRect(const Rect &aRect) override; virtual void CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect, - const IntPoint &aDestination); + const IntPoint &aDestination) override; + virtual void CopyRect(const IntRect &aSourceRect, + const IntPoint &aDestination) override; virtual void FillRect(const Rect &aRect, const Pattern &aPattern, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void StrokeRect(const Rect &aRect, const Pattern &aPattern, const StrokeOptions &aStrokeOptions = StrokeOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void StrokeLine(const Point &aStart, const Point &aEnd, const Pattern &aPattern, const StrokeOptions &aStrokeOptions = StrokeOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions = StrokeOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void Fill(const Path *aPath, const Pattern &aPattern, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aOptions, - const GlyphRenderingOptions *aRenderingOptions = nullptr); + const GlyphRenderingOptions *aRenderingOptions = nullptr) override; virtual void Mask(const Pattern &aSource, const Pattern &aMask, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void MaskSurface(const Pattern &aSource, SourceSurface *aMask, Point aOffset, - const DrawOptions &aOptions = DrawOptions()) { MOZ_ASSERT(0); }; + const DrawOptions &aOptions = DrawOptions()) override; - virtual void PushClip(const Path *aPath); - virtual void PushClipRect(const Rect &aRect); - virtual void PopClip(); + virtual void PushClip(const Path *aPath) override; + virtual void PushClipRect(const Rect &aRect) override; + virtual void PopClip() override; - virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const; + virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override; virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, int32_t aStride, - SurfaceFormat aFormat) const; - virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const; + SurfaceFormat aFormat) const override; + virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override; virtual TemporaryRef<SourceSurface> - CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const; + CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override; virtual TemporaryRef<DrawTarget> - CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const; + CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override; virtual TemporaryRef<DrawTarget> CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat, - float aSigma) const; + float aSigma) const override; virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, - ExtendMode aExtendMode = EXTEND_CLAMP) const; + ExtendMode aExtendMode = ExtendMode::CLAMP) const override; - virtual void *GetNativeSurface(NativeSurfaceType aType); + virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override; - bool Init(cairo_surface_t* aSurface, const IntSize& aSize); + virtual void *GetNativeSurface(NativeSurfaceType aType) override; - void SetPathObserver(CairoPathContext* aPathObserver); + bool Init(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr); + bool Init(const IntSize& aSize, SurfaceFormat aFormat); + bool Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat); - virtual void SetTransform(const Matrix& aTransform); + virtual void SetTransform(const Matrix& aTransform) override; // Call to set up aContext for drawing (with the current transform, etc). // Pass the path you're going to be using if you have one. // Implicitly calls WillChange(aPath). void PrepareForDrawing(cairo_t* aContext, const Path* aPath = nullptr); + static cairo_surface_t *GetDummySurface(); + + // Cairo hardcodes this as its maximum surface size. + static size_t GetMaxSurfaceSize() { + return 32767; + } + private: // methods // Init cairo surface without doing a cairo_surface_reference() call. - bool InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize); - + bool InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr); enum DrawPatternType { DRAW_FILL, DRAW_STROKE }; void DrawPattern(const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aOptions, - DrawPatternType aDrawType); + DrawPatternType aDrawType, + bool aPathBoundsClip = false); + + void CopySurfaceInternal(cairo_surface_t* aSurface, + const IntRect& aSource, + const IntPoint& aDest); + + Rect GetUserSpaceClip(); // Call before you make any changes to the backing surface with which this // context is associated. Pass the path you're going to be using if you have @@ -173,20 +203,18 @@ private: // methods // If the current operator is "source" then clear the destination before we // draw into it, to simulate the effect of an unbounded source operator. void ClearSurfaceForUnboundedSource(const CompositionOp &aOperator); + private: // data cairo_t* mContext; cairo_surface_t* mSurface; IntSize mSize; + uint8_t* mLockedBits; + // The latest snapshot of this surface. This needs to be told when this // target is modified. We keep it alive as a cache. RefPtr<SourceSurfaceCairo> mSnapshot; - - // It is safe to use a regular pointer here because the CairoPathContext will - // deregister itself on destruction. Using a RefPtr would extend the life- - // span of the CairoPathContext. This causes a problem when - // PathBuilderCairo.Finish() - mutable CairoPathContext* mPathObserver; + static cairo_surface_t *mDummySurface; }; } diff --git a/gfx/2d/DrawTargetCapture.cpp b/gfx/2d/DrawTargetCapture.cpp new file mode 100644 index 000000000..d53a98ec7 --- /dev/null +++ b/gfx/2d/DrawTargetCapture.cpp @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DrawTargetCapture.h" +#include "DrawCommand.h" + +namespace mozilla { +namespace gfx { + + +DrawTargetCaptureImpl::~DrawTargetCaptureImpl() +{ + uint8_t* start = &mDrawCommandStorage.front(); + + uint8_t* current = start; + + while (current < start + mDrawCommandStorage.size()) { + reinterpret_cast<DrawingCommand*>(current + sizeof(uint32_t))->~DrawingCommand(); + current += *(uint32_t*)current; + } +} + +bool +DrawTargetCaptureImpl::Init(const IntSize& aSize, DrawTarget* aRefDT) +{ + if (!aRefDT) { + return false; + } + + mRefDT = aRefDT; + + mSize = aSize; + return true; +} + +TemporaryRef<SourceSurface> +DrawTargetCaptureImpl::Snapshot() +{ + RefPtr<DrawTarget> dt = mRefDT->CreateSimilarDrawTarget(mSize, mRefDT->GetFormat()); + + ReplayToDrawTarget(dt, Matrix()); + + return dt->Snapshot(); +} + +#define AppendCommand(arg) new (AppendToCommandList<arg>()) arg + +void +DrawTargetCaptureImpl::DrawSurface(SourceSurface *aSurface, + const Rect &aDest, + const Rect &aSource, + const DrawSurfaceOptions &aSurfOptions, + const DrawOptions &aOptions) +{ + aSurface->GuaranteePersistance(); + AppendCommand(DrawSurfaceCommand)(aSurface, aDest, aSource, aSurfOptions, aOptions); +} + +void +DrawTargetCaptureImpl::DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions) +{ + // @todo XXX - this won't work properly long term yet due to filternodes not + // being immutable. + AppendCommand(DrawFilterCommand)(aNode, aSourceRect, aDestPoint, aOptions); +} + +void +DrawTargetCaptureImpl::ClearRect(const Rect &aRect) +{ + AppendCommand(ClearRectCommand)(aRect); +} + +void +DrawTargetCaptureImpl::MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, + const DrawOptions &aOptions) +{ + aMask->GuaranteePersistance(); + AppendCommand(MaskSurfaceCommand)(aSource, aMask, aOffset, aOptions); +} + +void +DrawTargetCaptureImpl::CopySurface(SourceSurface* aSurface, + const IntRect& aSourceRect, + const IntPoint& aDestination) +{ + aSurface->GuaranteePersistance(); + AppendCommand(CopySurfaceCommand)(aSurface, aSourceRect, aDestination); +} + +void +DrawTargetCaptureImpl::FillRect(const Rect& aRect, + const Pattern& aPattern, + const DrawOptions& aOptions) +{ + AppendCommand(FillRectCommand)(aRect, aPattern, aOptions); +} + +void +DrawTargetCaptureImpl::StrokeRect(const Rect& aRect, + const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) +{ + AppendCommand(StrokeRectCommand)(aRect, aPattern, aStrokeOptions, aOptions); +} + +void +DrawTargetCaptureImpl::StrokeLine(const Point& aStart, + const Point& aEnd, + const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) +{ + AppendCommand(StrokeLineCommand)(aStart, aEnd, aPattern, aStrokeOptions, aOptions); +} + +void +DrawTargetCaptureImpl::Stroke(const Path* aPath, + const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) +{ + AppendCommand(StrokeCommand)(aPath, aPattern, aStrokeOptions, aOptions); +} + +void +DrawTargetCaptureImpl::Fill(const Path* aPath, + const Pattern& aPattern, + const DrawOptions& aOptions) +{ + AppendCommand(FillCommand)(aPath, aPattern, aOptions); +} + +void +DrawTargetCaptureImpl::FillGlyphs(ScaledFont* aFont, + const GlyphBuffer& aBuffer, + const Pattern& aPattern, + const DrawOptions& aOptions, + const GlyphRenderingOptions* aRenderingOptions) +{ + AppendCommand(FillGlyphsCommand)(aFont, aBuffer, aPattern, aOptions, aRenderingOptions); +} + +void +DrawTargetCaptureImpl::Mask(const Pattern &aSource, + const Pattern &aMask, + const DrawOptions &aOptions) +{ + AppendCommand(MaskCommand)(aSource, aMask, aOptions); +} + +void +DrawTargetCaptureImpl::PushClip(const Path* aPath) +{ + AppendCommand(PushClipCommand)(aPath); +} + +void +DrawTargetCaptureImpl::PushClipRect(const Rect& aRect) +{ + AppendCommand(PushClipRectCommand)(aRect); +} + +void +DrawTargetCaptureImpl::PopClip() +{ + AppendCommand(PopClipCommand)(); +} + +void +DrawTargetCaptureImpl::SetTransform(const Matrix& aTransform) +{ + AppendCommand(SetTransformCommand)(aTransform); +} + +void +DrawTargetCaptureImpl::ReplayToDrawTarget(DrawTarget* aDT, const Matrix& aTransform) +{ + uint8_t* start = &mDrawCommandStorage.front(); + + uint8_t* current = start; + + while (current < start + mDrawCommandStorage.size()) { + reinterpret_cast<DrawingCommand*>(current + sizeof(uint32_t))->ExecuteOnDT(aDT, aTransform); + current += *(uint32_t*)current; + } +} + +} +} diff --git a/gfx/2d/DrawTargetCapture.h b/gfx/2d/DrawTargetCapture.h new file mode 100644 index 000000000..2ef8f0e62 --- /dev/null +++ b/gfx/2d/DrawTargetCapture.h @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_DRAWTARGETCAPTURE_H_ +#define MOZILLA_GFX_DRAWTARGETCAPTURE_H_ + +#include "2D.h" +#include <vector> + +#include "Filters.h" + +namespace mozilla { +namespace gfx { + +class DrawingCommand; + +class DrawTargetCaptureImpl : public DrawTargetCapture +{ +public: + DrawTargetCaptureImpl() + {} + + bool Init(const IntSize& aSize, DrawTarget* aRefDT); + + virtual BackendType GetBackendType() const { return mRefDT->GetBackendType(); } + virtual DrawTargetType GetType() const { return mRefDT->GetType(); } + + virtual TemporaryRef<SourceSurface> Snapshot(); + virtual IntSize GetSize() { return mSize; } + + virtual void Flush() {} + virtual void DrawSurface(SourceSurface *aSurface, + const Rect &aDest, + const Rect &aSource, + const DrawSurfaceOptions &aSurfOptions, + const DrawOptions &aOptions); + virtual void DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions = DrawOptions()); + virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, + const Point &aDest, + const Color &aColor, + const Point &aOffset, + Float aSigma, + CompositionOp aOperator) { /* Not implemented */ } + + virtual void ClearRect(const Rect &aRect); + virtual void MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, + const DrawOptions &aOptions = DrawOptions()); + + virtual void CopySurface(SourceSurface *aSurface, + const IntRect &aSourceRect, + const IntPoint &aDestination); + + virtual void FillRect(const Rect &aRect, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions()); + virtual void StrokeRect(const Rect &aRect, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()); + virtual void StrokeLine(const Point &aStart, + const Point &aEnd, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()); + virtual void Stroke(const Path *aPath, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()); + virtual void Fill(const Path *aPath, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions()); + virtual void FillGlyphs(ScaledFont *aFont, + const GlyphBuffer &aBuffer, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions(), + const GlyphRenderingOptions *aRenderingOptions = nullptr); + virtual void Mask(const Pattern &aSource, + const Pattern &aMask, + const DrawOptions &aOptions = DrawOptions()); + virtual void PushClip(const Path *aPath); + virtual void PushClipRect(const Rect &aRect); + virtual void PopClip(); + + virtual void SetTransform(const Matrix &aTransform); + + virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat) const + { + return mRefDT->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat); + } + virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const + { + return mRefDT->OptimizeSourceSurface(aSurface); + } + + virtual TemporaryRef<SourceSurface> + CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const + { + return mRefDT->CreateSourceSurfaceFromNativeSurface(aSurface); + } + + virtual TemporaryRef<DrawTarget> + CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const + { + return mRefDT->CreateSimilarDrawTarget(aSize, aFormat); + } + + virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const + { + return mRefDT->CreatePathBuilder(aFillRule); + } + + virtual TemporaryRef<GradientStops> + CreateGradientStops(GradientStop *aStops, + uint32_t aNumStops, + ExtendMode aExtendMode = ExtendMode::CLAMP) const + { + return mRefDT->CreateGradientStops(aStops, aNumStops, aExtendMode); + } + virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) + { + return mRefDT->CreateFilter(aType); + } + + void ReplayToDrawTarget(DrawTarget* aDT, const Matrix& aTransform); + +protected: + ~DrawTargetCaptureImpl(); + +private: + + // This storage system was used to minimize the amount of heap allocations + // that are required while recording. It should be noted there's no + // guarantees on the alignments of DrawingCommands allocated in this array. + template<typename T> + T* AppendToCommandList() + { + size_t oldSize = mDrawCommandStorage.size(); + mDrawCommandStorage.resize(mDrawCommandStorage.size() + sizeof(T) + sizeof(uint32_t)); + uint8_t* nextDrawLocation = &mDrawCommandStorage.front() + oldSize; + *(uint32_t*)(nextDrawLocation) = sizeof(T) + sizeof(uint32_t); + return reinterpret_cast<T*>(nextDrawLocation + sizeof(uint32_t)); + } + RefPtr<DrawTarget> mRefDT; + + IntSize mSize; + + std::vector<uint8_t> mDrawCommandStorage; +}; + +} /* namespace mozilla */ +} /* namespace gfx */ + +#endif /* MOZILLA_GFX_DRAWTARGETCAPTURE_H_ */ diff --git a/gfx/2d/DrawTargetD2D.cpp b/gfx/2d/DrawTargetD2D.cpp index a6f6af2a2..2d80f2a7a 100644 --- a/gfx/2d/DrawTargetD2D.cpp +++ b/gfx/2d/DrawTargetD2D.cpp @@ -3,8 +3,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include <initguid.h> #include "DrawTargetD2D.h" #include "SourceSurfaceD2D.h" +#include "SourceSurfaceD2D1.h" #include "SourceSurfaceD2DTarget.h" #include "ShadersD2D.h" #include "PathD2D.h" @@ -15,9 +17,14 @@ #include "Tools.h" #include <algorithm> #include "mozilla/Constants.h" +#include "FilterNodeSoftware.h" + +#include "FilterNodeD2D1.h" +#include "ExtendInputEffectD2D1.h" #include <dwrite.h> +// decltype is not usable for overloaded functions. typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( D2D1_FACTORY_TYPE factoryType, REFIID iid, @@ -25,21 +32,6 @@ typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( void **factory ); -typedef HRESULT (WINAPI*D3D10CreateEffectFromMemoryFunc)( - void *pData, - SIZE_T DataLength, - UINT FXFlags, - ID3D10Device *pDevice, - ID3D10EffectPool *pEffectPool, - ID3D10Effect **ppEffect -); - -typedef HRESULT (WINAPI*DWriteCreateFactoryFunc)( - DWRITE_FACTORY_TYPE factoryType, - REFIID iid, - IUnknown **factory -); - using namespace std; namespace mozilla { @@ -81,13 +73,12 @@ public: HRESULT hr = mDT->mDevice->CreateTexture2D(&desc, nullptr, byRef(tmpTexture)); if (FAILED(hr)) { - gfxWarning() << "Failed to create temporary texture to hold surface data."; + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(size))) << "[D2D] 1 CreateTexture2D failure " << size << " Code: " << hexa(hr); + return; } mDT->mDevice->CopyResource(tmpTexture, mDT->mTexture); - D2D1_BITMAP_PROPERTIES props = - D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(format), - AlphaMode(format))); + D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(format)); RefPtr<IDXGISurface> surf; @@ -97,7 +88,8 @@ public: &props, byRef(mOldSurfBitmap)); if (FAILED(hr)) { - gfxWarning() << "Failed to create shared bitmap for old surface."; + gfxCriticalError() << "[D2D] CreateSharedBitmap failure " << size << " Code: " << hexa(hr); + return; } IntRect clipBounds; @@ -242,7 +234,7 @@ DrawTargetD2D::Flush() HRESULT hr = mRT->Flush(); if (FAILED(hr)) { - gfxWarning() << "Error reported when trying to flush D2D rendertarget. Code: " << hr; + gfxWarning() << "Error reported when trying to flush D2D rendertarget. Code: " << hexa(hr); } // We no longer depend on any target. @@ -270,13 +262,13 @@ DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface, switch (aSurface->GetType()) { - case SURFACE_D2D1_BITMAP: + case SurfaceType::D2D1_BITMAP: { SourceSurfaceD2D *srcSurf = static_cast<SourceSurfaceD2D*>(aSurface); bitmap = srcSurf->GetBitmap(); } break; - case SURFACE_D2D1_DRAWTARGET: + case SurfaceType::D2D1_DRAWTARGET: { SourceSurfaceD2DTarget *srcSurf = static_cast<SourceSurfaceD2DTarget*>(aSurface); bitmap = srcSurf->GetBitmap(mRT); @@ -292,8 +284,17 @@ DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface, return nullptr; } - if (aSource.width > mRT->GetMaximumBitmapSize() || - aSource.height > mRT->GetMaximumBitmapSize()) { + // We need to include any pixels that are overlapped by aSource + Rect sourceRect(aSource); + sourceRect.RoundOut(); + + if (sourceRect.IsEmpty()) { + gfxDebug() << "Bitmap source is empty. DrawBitmap will silently fail."; + return nullptr; + } + + if (sourceRect.width > mRT->GetMaximumBitmapSize() || + sourceRect.height > mRT->GetMaximumBitmapSize()) { gfxDebug() << "Bitmap source larger than texture size specified. DrawBitmap will silently fail."; // Don't know how to deal with this yet. return nullptr; @@ -302,12 +303,12 @@ DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface, int stride = srcSurf->Stride(); unsigned char *data = srcSurf->GetData() + - (uint32_t)aSource.y * stride + - (uint32_t)aSource.x * BytesPerPixel(srcSurf->GetFormat()); + (uint32_t)sourceRect.y * stride + + (uint32_t)sourceRect.x * BytesPerPixel(srcSurf->GetFormat()); D2D1_BITMAP_PROPERTIES props = - D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(srcSurf->GetFormat()), AlphaMode(srcSurf->GetFormat()))); - mRT->CreateBitmap(D2D1::SizeU(UINT32(aSource.width), UINT32(aSource.height)), data, stride, props, byRef(bitmap)); + D2D1::BitmapProperties(D2DPixelFormat(srcSurf->GetFormat())); + mRT->CreateBitmap(D2D1::SizeU(UINT32(sourceRect.width), UINT32(sourceRect.height)), data, stride, props, byRef(bitmap)); // subtract the integer part leaving the fractional part aSource.x -= (uint32_t)aSource.x; @@ -319,6 +320,17 @@ DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface, return bitmap; } +TemporaryRef<ID2D1Image> +DrawTargetD2D::GetImageForSurface(SourceSurface *aSurface) +{ + RefPtr<ID2D1Image> image; + + Rect r(Point(), Size(aSurface->GetSize())); + image = GetBitmapForSurface(aSurface, r); + + return image; +} + void DrawTargetD2D::DrawSurface(SourceSurface *aSurface, const Rect &aDest, @@ -347,6 +359,47 @@ DrawTargetD2D::DrawSurface(SourceSurface *aSurface, } void +DrawTargetD2D::DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions) +{ + RefPtr<ID2D1DeviceContext> dc; + HRESULT hr; + + hr = mRT->QueryInterface((ID2D1DeviceContext**)byRef(dc)); + + if (SUCCEEDED(hr) && aNode->GetBackendType() == FILTER_BACKEND_DIRECT2D1_1) { + ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color())); + + PrepareForDrawing(rt); + + rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + hr = rt->QueryInterface((ID2D1DeviceContext**)byRef(dc)); + + if (SUCCEEDED(hr)) { + FilterNodeD2D1* node = static_cast<FilterNodeD2D1*>(aNode); + node->WillDraw(this); + + dc->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect)); + + Rect destRect = aSourceRect; + destRect.MoveBy(aDestPoint); + FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), destRect); + return; + } + } + + if (aNode->GetBackendType() != FILTER_BACKEND_SOFTWARE) { + gfxWarning() << "Invalid filter backend passed to DrawTargetD2D!"; + return; + } + + FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode); + filter->Draw(this, aSourceRect, aDestPoint, aOptions); +} + +void DrawTargetD2D::MaskSurface(const Pattern &aSource, SourceSurface *aMask, Point aOffset, @@ -384,7 +437,7 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface, CompositionOp aOperator) { RefPtr<ID3D10ShaderResourceView> srView = nullptr; - if (aSurface->GetType() != SURFACE_D2D1_DRAWTARGET) { + if (aSurface->GetType() != SurfaceType::D2D1_DRAWTARGET) { return; } @@ -410,7 +463,7 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface, HRESULT hr = mDevice->CreateRenderTargetView(mTempTexture, nullptr, byRef(mTempRTView)); if (FAILED(hr)) { - gfxWarning() << "Failure to create RenderTargetView. Code: " << hr; + gfxWarning() << "Failure to create RenderTargetView. Code: " << hexa(hr); return; } } @@ -471,8 +524,7 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface, hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mipTexture)); if (FAILED(hr)) { - gfxWarning() << "Failure to create temporary texture. Size: " << - aSurface->GetSize() << " Code: " << hr; + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSurface->GetSize()))) << "[D2D] 2 CreateTexture2D failure " << aSurface->GetSize() << " Code: " << hexa(hr); return; } @@ -499,7 +551,7 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface, hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(tmpDSTexture)); if (FAILED(hr)) { - gfxWarning() << "Failure to create temporary texture. Size: " << dsSize << " Code: " << hr; + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(dsSize))) << "[D2D] 3 CreateTexture2D failure " << dsSize << " Code: " << hexa(hr); return; } @@ -538,7 +590,7 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface, mPrivateData->mEffect->GetTechniqueByName("SampleTexture")-> GetPassByIndex(0)->Apply(0); - mDevice->OMSetBlendState(GetBlendStateForOperator(OP_OVER), nullptr, 0xffffffff); + mDevice->OMSetBlendState(GetBlendStateForOperator(CompositionOp::OP_OVER), nullptr, 0xffffffff); mDevice->Draw(4, 0); @@ -721,27 +773,37 @@ void DrawTargetD2D::ClearRect(const Rect &aRect) { MarkChanged(); + PushClipRect(aRect); - FlushTransformToRT(); PopAllClips(); AutoSaveRestoreClippedOut restoreClippedOut(this); - restoreClippedOut.Save(); - - bool needsClip = false; - - needsClip = aRect.x > 0 || aRect.y > 0 || - aRect.XMost() < mSize.width || - aRect.YMost() < mSize.height; + D2D1_RECT_F clipRect; + bool isPixelAligned; + bool pushedClip = false; + if (mTransform.IsRectilinear() && + GetDeviceSpaceClipRect(clipRect, isPixelAligned)) { + if (mTransformDirty || + !mTransform.IsIdentity()) { + mRT->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + } - if (needsClip) { - mRT->PushAxisAlignedClip(D2DRect(aRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + mRT->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + pushedClip = true; + } else { + FlushTransformToRT(); + restoreClippedOut.Save(); } + mRT->Clear(D2D1::ColorF(0, 0.0f)); - if (needsClip) { + + if (pushedClip) { mRT->PopAxisAlignedClip(); } + + PopClip(); return; } @@ -763,33 +825,22 @@ DrawTargetD2D::CopySurface(SourceSurface *aSurface, mRT->Clear(D2D1::ColorF(0, 0.0f)); mRT->PopAxisAlignedClip(); - RefPtr<ID2D1Bitmap> bitmap; - - switch (aSurface->GetType()) { - case SURFACE_D2D1_BITMAP: - { - SourceSurfaceD2D *srcSurf = static_cast<SourceSurfaceD2D*>(aSurface); - bitmap = srcSurf->GetBitmap(); - } - break; - case SURFACE_D2D1_DRAWTARGET: - { - SourceSurfaceD2DTarget *srcSurf = static_cast<SourceSurfaceD2DTarget*>(aSurface); - bitmap = srcSurf->GetBitmap(mRT); - AddDependencyOnSource(srcSurf); - } - break; - default: - return; - } - + RefPtr<ID2D1Bitmap> bitmap = GetBitmapForSurface(aSurface, srcRect); if (!bitmap) { return; } - mRT->DrawBitmap(bitmap, D2DRect(dstRect), 1.0f, - D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, - D2DRect(srcRect)); + if (aSurface->GetFormat() == SurfaceFormat::A8) { + RefPtr<ID2D1SolidColorBrush> brush; + mRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), + D2D1::BrushProperties(), byRef(brush)); + mRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + mRT->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS); + } else { + mRT->DrawBitmap(bitmap, D2DRect(dstRect), 1.0f, + D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, + D2DRect(srcRect)); + } } void @@ -865,7 +916,7 @@ DrawTargetD2D::Stroke(const Path *aPath, const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) { - if (aPath->GetBackendType() != BACKEND_DIRECT2D) { + if (aPath->GetBackendType() != BackendType::DIRECT2D) { gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; return; } @@ -894,7 +945,7 @@ DrawTargetD2D::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions) { - if (aPath->GetBackendType() != BACKEND_DIRECT2D) { + if (aPath->GetBackendType() != BackendType::DIRECT2D) { gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; return; } @@ -914,7 +965,7 @@ DrawTargetD2D::Fill(const Path *aPath, } Rect bounds; - if (aOptions.mCompositionOp != OP_OVER) { + if (aOptions.mCompositionOp != CompositionOp::OP_OVER) { D2D1_RECT_F d2dbounds; d2dPath->mGeometry->GetBounds(D2D1::IdentityMatrix(), &d2dbounds); bounds = ToRect(d2dbounds); @@ -929,7 +980,7 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aFont, const DrawOptions &aOptions, const GlyphRenderingOptions* aRenderOptions) { - if (aFont->GetType() != FONT_DWRITE) { + if (aFont->GetType() != FontType::DWRITE) { gfxDebug() << *this << ": Ignoring drawing call for incompatible font."; return; } @@ -938,7 +989,7 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aFont, IDWriteRenderingParams *params = nullptr; if (aRenderOptions) { - if (aRenderOptions->GetType() != FONT_DWRITE) { + if (aRenderOptions->GetType() != FontType::DWRITE) { gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions."; // This should never happen. MOZ_ASSERT(false); @@ -949,13 +1000,13 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aFont, AntialiasMode aaMode = font->GetDefaultAAMode(); - if (aOptions.mAntialiasMode != AA_DEFAULT) { + if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) { aaMode = aOptions.mAntialiasMode; } - if (mFormat == FORMAT_B8G8R8A8 && mPermitSubpixelAA && - aOptions.mCompositionOp == OP_OVER && aPattern.GetType() == PATTERN_COLOR && - aaMode == AA_SUBPIXEL) { + if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA && + aOptions.mCompositionOp == CompositionOp::OP_OVER && aPattern.GetType() == PatternType::COLOR && + aaMode == AntialiasMode::SUBPIXEL) { if (FillGlyphsManual(font, aBuffer, static_cast<const ColorPattern*>(&aPattern)->mColor, params, aOptions)) { @@ -966,31 +1017,25 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aFont, ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); PrepareForDrawing(rt); - - bool forceClearType = false; - if (mFormat == FORMAT_B8G8R8A8 && mPermitSubpixelAA && - aOptions.mCompositionOp == OP_OVER && aaMode == AA_SUBPIXEL) { - forceClearType = true; - } D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; switch (aaMode) { - case AA_NONE: + case AntialiasMode::NONE: d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; break; - case AA_GRAY: + case AntialiasMode::GRAY: d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; break; - case AA_SUBPIXEL: + case AntialiasMode::SUBPIXEL: d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; break; default: d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; } - + if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && - mFormat != FORMAT_B8G8R8X8 && !forceClearType) { + mFormat != SurfaceFormat::B8G8R8X8) { d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; } @@ -1050,7 +1095,7 @@ DrawTargetD2D::Mask(const Pattern &aSource, void DrawTargetD2D::PushClip(const Path *aPath) { - if (aPath->GetBackendType() != BACKEND_DIRECT2D) { + if (aPath->GetBackendType() != BackendType::DIRECT2D) { gfxDebug() << *this << ": Ignoring clipping call for incompatible path."; return; } @@ -1145,20 +1190,39 @@ DrawTargetD2D::CreateSourceSurfaceFromData(unsigned char *aData, return nullptr; } - return newSurf; + return newSurf.forget(); } TemporaryRef<SourceSurface> DrawTargetD2D::OptimizeSourceSurface(SourceSurface *aSurface) const { - // Unsupported! - return nullptr; + if (aSurface->GetType() == SurfaceType::D2D1_BITMAP || + aSurface->GetType() == SurfaceType::D2D1_DRAWTARGET) { + return aSurface; + } + + RefPtr<DataSourceSurface> data = aSurface->GetDataSurface(); + + DataSourceSurface::MappedSurface map; + if (!data->Map(DataSourceSurface::MapType::READ, &map)) { + return nullptr; + } + + RefPtr<SourceSurfaceD2D> newSurf = new SourceSurfaceD2D(); + bool success = newSurf->InitFromData(map.mData, data->GetSize(), map.mStride, data->GetFormat(), mRT); + + data->Unmap(); + + if (!success) { + return data.forget(); + } + return newSurf.forget(); } TemporaryRef<SourceSurface> DrawTargetD2D::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const { - if (aSurface.mType != NATIVE_SURFACE_D3D10_TEXTURE) { + if (aSurface.mType != NativeSurfaceType::D3D10_TEXTURE) { gfxDebug() << *this << ": Failure to create source surface from non-D3D10 texture native surface."; return nullptr; } @@ -1172,7 +1236,7 @@ DrawTargetD2D::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurfac return nullptr; } - return newSurf; + return newSurf.forget(); } TemporaryRef<DrawTarget> @@ -1186,7 +1250,7 @@ DrawTargetD2D::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aForm return nullptr; } - return newTarget; + return newTarget.forget(); } TemporaryRef<PathBuilder> @@ -1196,22 +1260,22 @@ DrawTargetD2D::CreatePathBuilder(FillRule aFillRule) const HRESULT hr = factory()->CreatePathGeometry(byRef(path)); if (FAILED(hr)) { - gfxWarning() << "Failed to create Direct2D Path Geometry. Code: " << hr; + gfxWarning() << "Failed to create Direct2D Path Geometry. Code: " << hexa(hr); return nullptr; } RefPtr<ID2D1GeometrySink> sink; hr = path->Open(byRef(sink)); if (FAILED(hr)) { - gfxWarning() << "Failed to access Direct2D Path Geometry. Code: " << hr; + gfxWarning() << "Failed to access Direct2D Path Geometry. Code: " << hexa(hr); return nullptr; } - if (aFillRule == FILL_WINDING) { + if (aFillRule == FillRule::FILL_WINDING) { sink->SetFillMode(D2D1_FILL_MODE_WINDING); } - return new PathBuilderD2D(sink, path, aFillRule); + return new PathBuilderD2D(sink, path, aFillRule, BackendType::DIRECT2D); } TemporaryRef<GradientStops> @@ -1233,17 +1297,29 @@ DrawTargetD2D::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, E delete [] stops; if (FAILED(hr)) { - gfxWarning() << "Failed to create GradientStopCollection. Code: " << hr; + gfxWarning() << "Failed to create GradientStopCollection. Code: " << hexa(hr); return nullptr; } - return new GradientStopsD2D(stopCollection); + return new GradientStopsD2D(stopCollection, Factory::GetDirect3D11Device()); +} + +TemporaryRef<FilterNode> +DrawTargetD2D::CreateFilter(FilterType aType) +{ + RefPtr<ID2D1DeviceContext> dc; + HRESULT hr = mRT->QueryInterface((ID2D1DeviceContext**)byRef(dc)); + + if (SUCCEEDED(hr)) { + return FilterNodeD2D1::Create(dc, aType); + } + return FilterNodeSoftware::Create(aType); } void* DrawTargetD2D::GetNativeSurface(NativeSurfaceType aType) { - if (aType != NATIVE_SURFACE_D3D10_TEXTURE) { + if (aType != NativeSurfaceType::D3D10_TEXTURE) { return nullptr; } @@ -1262,7 +1338,7 @@ DrawTargetD2D::Init(const IntSize &aSize, SurfaceFormat aFormat) mFormat = aFormat; if (!Factory::GetDirect3D10Device()) { - gfxDebug() << "Failed to Init Direct2D DrawTarget (No D3D10 Device set.)"; + gfxCriticalError() << "Failed to Init Direct2D DrawTarget (No D3D10 Device set.)"; return false; } mDevice = Factory::GetDirect3D10Device(); @@ -1276,7 +1352,7 @@ DrawTargetD2D::Init(const IntSize &aSize, SurfaceFormat aFormat) hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mTexture)); if (FAILED(hr)) { - gfxDebug() << "Failed to init Direct2D DrawTarget. Size: " << mSize << " Code: " << hr; + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "Failed to init Direct2D DrawTarget. Size: " << mSize << " Code: " << hexa(hr); return false; } @@ -1297,7 +1373,7 @@ DrawTargetD2D::Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat) mFormat = aFormat; if (!mTexture) { - gfxDebug() << "No valid texture for Direct2D draw target initialization."; + gfxCriticalError() << "No valid texture for Direct2D draw target initialization."; return false; } @@ -1307,7 +1383,7 @@ DrawTargetD2D::Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat) hr = device->QueryInterface((ID3D10Device1**)byRef(mDevice)); if (FAILED(hr)) { - gfxWarning() << "Failed to get D3D10 device from texture."; + gfxCriticalError() << "Failed to get D3D10 device from texture."; return false; } @@ -1338,15 +1414,15 @@ DrawTargetD2D::InitD3D10Data() mPrivateData = new PrivateD3D10DataD2D; - D3D10CreateEffectFromMemoryFunc createD3DEffect; + decltype(D3D10CreateEffectFromMemory)* createD3DEffect; HMODULE d3dModule = LoadLibraryW(L"d3d10_1.dll"); - createD3DEffect = (D3D10CreateEffectFromMemoryFunc) + createD3DEffect = (decltype(D3D10CreateEffectFromMemory)*) GetProcAddress(d3dModule, "D3D10CreateEffectFromMemory"); hr = createD3DEffect((void*)d2deffect, sizeof(d2deffect), 0, mDevice, nullptr, byRef(mPrivateData->mEffect)); if (FAILED(hr)) { - gfxWarning() << "Failed to initialize Direct2D required effects. Code: " << hr; + gfxWarning() << "Failed to initialize Direct2D required effects. Code: " << hexa(hr); return false; } @@ -1368,7 +1444,7 @@ DrawTargetD2D::InitD3D10Data() byRef(mPrivateData->mInputLayout)); if (FAILED(hr)) { - gfxWarning() << "Failed to initialize Direct2D required InputLayout. Code: " << hr; + gfxWarning() << "Failed to initialize Direct2D required InputLayout. Code: " << hexa(hr); return false; } @@ -1380,7 +1456,7 @@ DrawTargetD2D::InitD3D10Data() hr = mDevice->CreateBuffer(&bufferDesc, &data, byRef(mPrivateData->mVB)); if (FAILED(hr)) { - gfxWarning() << "Failed to initialize Direct2D required VertexBuffer. Code: " << hr; + gfxWarning() << "Failed to initialize Direct2D required VertexBuffer. Code: " << hexa(hr); return false; } @@ -1412,7 +1488,7 @@ DrawTargetD2D::GetCachedLayer() } mCurrentCachedLayer++; - return layer; + return layer.forget(); } void @@ -1426,6 +1502,7 @@ bool DrawTargetD2D::InitD2DRenderTarget() { if (!factory()) { + gfxCriticalError() << "No valid D2D factory available."; return false; } @@ -1437,7 +1514,7 @@ DrawTargetD2D::InitD2DRenderTarget() mRT->BeginDraw(); - if (mFormat == FORMAT_B8G8R8X8) { + if (mFormat == SurfaceFormat::B8G8R8X8) { mRT->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE); } @@ -1497,8 +1574,9 @@ DrawTargetD2D::MarkChanged() ID3D10BlendState* DrawTargetD2D::GetBlendStateForOperator(CompositionOp aOperator) { - if (mPrivateData->mBlendStates[aOperator]) { - return mPrivateData->mBlendStates[aOperator]; + size_t operatorIndex = static_cast<size_t>(aOperator); + if (mPrivateData->mBlendStates[operatorIndex]) { + return mPrivateData->mBlendStates[operatorIndex]; } D3D10_BLEND_DESC desc; @@ -1511,43 +1589,43 @@ DrawTargetD2D::GetBlendStateForOperator(CompositionOp aOperator) desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; switch (aOperator) { - case OP_ADD: + case CompositionOp::OP_ADD: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; break; - case OP_IN: + case CompositionOp::OP_IN: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; break; - case OP_OUT: + case CompositionOp::OP_OUT: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; break; - case OP_ATOP: + case CompositionOp::OP_ATOP: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; break; - case OP_DEST_IN: + case CompositionOp::OP_DEST_IN: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; break; - case OP_DEST_OUT: + case CompositionOp::OP_DEST_OUT: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; break; - case OP_DEST_ATOP: + case CompositionOp::OP_DEST_ATOP: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; break; - case OP_DEST_OVER: + case CompositionOp::OP_DEST_OVER: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; break; - case OP_XOR: + case CompositionOp::OP_XOR: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; break; - case OP_SOURCE: + case CompositionOp::OP_SOURCE: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; break; @@ -1556,9 +1634,9 @@ DrawTargetD2D::GetBlendStateForOperator(CompositionOp aOperator) desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; } - mDevice->CreateBlendState(&desc, byRef(mPrivateData->mBlendStates[aOperator])); + mDevice->CreateBlendState(&desc, byRef(mPrivateData->mBlendStates[operatorIndex])); - return mPrivateData->mBlendStates[aOperator]; + return mPrivateData->mBlendStates[operatorIndex]; } /* This function prepares the temporary RT for drawing and returns it when a @@ -1567,13 +1645,13 @@ DrawTargetD2D::GetBlendStateForOperator(CompositionOp aOperator) ID2D1RenderTarget* DrawTargetD2D::GetRTForOperation(CompositionOp aOperator, const Pattern &aPattern) { - if (aOperator == OP_OVER && IsPatternSupportedByD2D(aPattern)) { + if (aOperator == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) { return mRT; } PopAllClips(); - if (aOperator > OP_XOR) { + if (aOperator > CompositionOp::OP_XOR) { mRT->Flush(); } @@ -1585,11 +1663,11 @@ DrawTargetD2D::GetRTForOperation(CompositionOp aOperator, const Pattern &aPatter EnsureViews(); if (!mRTView || !mSRView) { - gfxDebug() << *this << ": Failed to get required views. Defaulting to OP_OVER."; + gfxDebug() << *this << ": Failed to get required views. Defaulting to CompositionOp::OP_OVER."; return mRT; } - mTempRT = CreateRTForTexture(mTempTexture, FORMAT_B8G8R8A8); + mTempRT = CreateRTForTexture(mTempTexture, SurfaceFormat::B8G8R8A8); if (!mTempRT) { return mRT; @@ -1614,7 +1692,7 @@ DrawTargetD2D::GetRTForOperation(CompositionOp aOperator, const Pattern &aPatter void DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aPattern, const Rect &aBounds) { - if (aOperator == OP_OVER && IsPatternSupportedByD2D(aPattern)) { + if (aOperator == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) { return; } @@ -1668,7 +1746,7 @@ DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aP mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(mSRView); // Handle the case where we blend with the backdrop - if (aOperator > OP_XOR) { + if (aOperator > CompositionOp::OP_XOR) { IntSize size = mSize; SurfaceFormat format = mFormat; @@ -1683,7 +1761,7 @@ DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aP mDevice->CopyResource(tmpTexture, mTexture); if (FAILED(hr)) { - gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hr; + gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hexa(hr); return; } @@ -1692,18 +1770,18 @@ DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aP hr = mDevice->CreateShaderResourceView(tmpTexture, nullptr, byRef(mBckSRView)); if (FAILED(hr)) { - gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hr; + gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hexa(hr); return; } - unsigned int compop = (unsigned int)aOperator - (unsigned int)OP_XOR; + unsigned int compop = (unsigned int)aOperator - (unsigned int)CompositionOp::OP_XOR; mPrivateData->mEffect->GetVariableByName("bcktex")->AsShaderResource()->SetResource(mBckSRView); mPrivateData->mEffect->GetVariableByName("blendop")->AsScalar()->SetInt(compop); - if (aOperator > OP_EXCLUSION) + if (aOperator > CompositionOp::OP_EXCLUSION) mPrivateData->mEffect->GetTechniqueByName("SampleTextureForNonSeparableBlending")-> GetPassByIndex(0)->Apply(0); - else if (aOperator > OP_COLOR_DODGE) + else if (aOperator > CompositionOp::OP_COLOR_DODGE) mPrivateData->mEffect->GetTechniqueByName("SampleTextureForSeparableBlending_2")-> GetPassByIndex(0)->Apply(0); else @@ -1714,7 +1792,7 @@ DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aP mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->GetPassByIndex(0)->Apply(0); } - } else if (aPattern.GetType() == PATTERN_RADIAL_GRADIENT) { + } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern); if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) { @@ -1741,9 +1819,40 @@ IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2) result.top = max(aRect1.top, aRect2.top); result.right = min(aRect1.right, aRect2.right); result.bottom = min(aRect1.bottom, aRect2.bottom); + + result.right = max(result.right, result.left); + result.bottom = max(result.bottom, result.top); + return result; } +bool +DrawTargetD2D::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned) +{ + if (!mPushedClips.size()) { + return false; + } + + std::vector<DrawTargetD2D::PushedClip>::iterator iter = mPushedClips.begin(); + if (iter->mPath) { + return false; + } + aClipRect = iter->mBounds; + aIsPixelAligned = iter->mIsPixelAligned; + + iter++; + for (;iter != mPushedClips.end(); iter++) { + if (iter->mPath) { + return false; + } + aClipRect = IntersectRect(aClipRect, iter->mBounds); + if (!iter->mIsPixelAligned) { + aIsPixelAligned = false; + } + } + return true; +} + TemporaryRef<ID2D1Geometry> DrawTargetD2D::GetClippedGeometry(IntRect *aClipBounds) { @@ -1819,10 +1928,10 @@ DrawTargetD2D::GetClippedGeometry(IntRect *aClipBounds) pathGeom = newGeom.forget(); } - // For now we need mCurrentClippedGeometry to always be non-NULL. This method - // might seem a little strange but it is just fine, if pathGeom is NULL - // pathRect will always still contain 1 clip unaccounted for regardless of - // mCurrentClipBounds. + // For now we need mCurrentClippedGeometry to always be non-nullptr. This + // method might seem a little strange but it is just fine, if pathGeom is + // nullptr pathRect will always still contain 1 clip unaccounted for + // regardless of mCurrentClipBounds. if (!pathGeom) { pathGeom = ConvertRectToGeometry(pathRect); } @@ -1842,7 +1951,7 @@ DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aForm hr = aTexture->QueryInterface((IDXGISurface**)byRef(surface)); if (FAILED(hr)) { - gfxWarning() << "Failed to QI texture to surface."; + gfxCriticalError() << "Failed to QI texture to surface. Code: " << hr; return nullptr; } @@ -1851,7 +1960,7 @@ DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aForm D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; - if (aFormat == FORMAT_B8G8R8X8 && aTexture == mTexture) { + if (aFormat == SurfaceFormat::B8G8R8X8 && aTexture == mTexture) { alphaMode = D2D1_ALPHA_MODE_IGNORE; } @@ -1860,11 +1969,11 @@ DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aForm hr = factory()->CreateDxgiSurfaceRenderTarget(surface, props, byRef(rt)); if (FAILED(hr)) { - gfxWarning() << "Failed to create D2D render target for texture."; + gfxCriticalError() << "Failed to create D2D render target for texture. Code:" << hr << " " << mSize << " Format: " << uint32_t(aFormat); return nullptr; } - return rt; + return rt.forget(); } void @@ -1886,21 +1995,21 @@ DrawTargetD2D::EnsureViews() if (FAILED(hr)) { gfxWarning() << *this << "Failed to create temporary texture for rendertarget. Size: " - << mSize << " Code: " << hr; + << mSize << " Code: " << hexa(hr); return; } hr = mDevice->CreateShaderResourceView(mTempTexture, nullptr, byRef(mSRView)); if (FAILED(hr)) { - gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hr; + gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hexa(hr); return; } hr = mDevice->CreateRenderTargetView(mTexture, nullptr, byRef(mRTView)); if (FAILED(hr)) { - gfxWarning() << *this << "Failed to create rendertarget view for temp texture. Code: " << hr; + gfxWarning() << *this << "Failed to create rendertarget view for temp texture. Code: " << hexa(hr); } } @@ -1962,7 +2071,7 @@ DrawTargetD2D::EnsureClipMaskTexture(IntRect *aBounds) return; } - RefPtr<ID2D1RenderTarget> rt = CreateRTForTexture(mCurrentClipMaskTexture, FORMAT_A8); + RefPtr<ID2D1RenderTarget> rt = CreateRTForTexture(mCurrentClipMaskTexture, SurfaceFormat::A8); if (!rt) { gfxWarning() << "Failed to create RT for ClipMask!"; @@ -2164,18 +2273,19 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) if (!IsPatternSupportedByD2D(aPattern)) { RefPtr<ID2D1SolidColorBrush> colBrush; mRT->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), byRef(colBrush)); - return colBrush; + return colBrush.forget(); } - if (aPattern.GetType() == PATTERN_COLOR) { + if (aPattern.GetType() == PatternType::COLOR) { RefPtr<ID2D1SolidColorBrush> colBrush; Color color = static_cast<const ColorPattern*>(&aPattern)->mColor; mRT->CreateSolidColorBrush(D2D1::ColorF(color.r, color.g, color.b, color.a), D2D1::BrushProperties(aAlpha), byRef(colBrush)); - return colBrush; - } else if (aPattern.GetType() == PATTERN_LINEAR_GRADIENT) { + return colBrush.forget(); + } + if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) { RefPtr<ID2D1LinearGradientBrush> gradBrush; const LinearGradientPattern *pat = static_cast<const LinearGradientPattern*>(&aPattern); @@ -2195,7 +2305,7 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) mRT->CreateSolidColorBrush(d2dStops.back().color, D2D1::BrushProperties(aAlpha), byRef(colBrush)); - return colBrush; + return colBrush.forget(); } mRT->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin), @@ -2203,8 +2313,9 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), stops->mStopCollection, byRef(gradBrush)); - return gradBrush; - } else if (aPattern.GetType() == PATTERN_RADIAL_GRADIENT) { + return gradBrush.forget(); + } + if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { RefPtr<ID2D1RadialGradientBrush> gradBrush; const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern); @@ -2225,8 +2336,9 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) stops->mStopCollection, byRef(gradBrush)); - return gradBrush; - } else if (aPattern.GetType() == PATTERN_SURFACE) { + return gradBrush.forget(); + } + if (aPattern.GetType() == PatternType::SURFACE) { RefPtr<ID2D1BitmapBrush> bmBrush; const SurfacePattern *pat = static_cast<const SurfacePattern*>(&aPattern); @@ -2239,11 +2351,32 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) RefPtr<ID2D1Bitmap> bitmap; Matrix mat = pat->mMatrix; + + RefPtr<SourceSurface> source = pat->mSurface; + + if (!pat->mSamplingRect.IsEmpty() && + (source->GetType() == SurfaceType::D2D1_BITMAP || + source->GetType() == SurfaceType::D2D1_DRAWTARGET)) { + IntRect samplingRect = pat->mSamplingRect; + + RefPtr<DrawTargetD2D> dt = new DrawTargetD2D(); + if (!dt->Init(samplingRect.Size(), + source->GetFormat())) { + // FIXME: Uncomment assertion, bug 1068195 + // MOZ_ASSERT(false, "Invalid sampling rect size!"); + return nullptr; + } + + dt->CopySurface(source, samplingRect, IntPoint()); + source = dt->Snapshot(); + + mat.PreTranslate(samplingRect.x, samplingRect.y); + } - switch (pat->mSurface->GetType()) { - case SURFACE_D2D1_BITMAP: + switch (source->GetType()) { + case SurfaceType::D2D1_BITMAP: { - SourceSurfaceD2D *surf = static_cast<SourceSurfaceD2D*>(pat->mSurface.get()); + SourceSurfaceD2D *surf = static_cast<SourceSurfaceD2D*>(source.get()); bitmap = surf->mBitmap; @@ -2252,25 +2385,32 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) } } break; - case SURFACE_D2D1_DRAWTARGET: + case SurfaceType::D2D1_DRAWTARGET: { SourceSurfaceD2DTarget *surf = - static_cast<SourceSurfaceD2DTarget*>(pat->mSurface.get()); + static_cast<SourceSurfaceD2DTarget*>(source.get()); bitmap = surf->GetBitmap(mRT); AddDependencyOnSource(surf); } break; default: { - RefPtr<DataSourceSurface> dataSurf = pat->mSurface->GetDataSurface(); + RefPtr<DataSourceSurface> dataSurf = source->GetDataSurface(); if (!dataSurf) { gfxWarning() << "Invalid surface type."; return nullptr; } - bitmap = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, pat->mExtendMode, mat, mRT); + IntRect sourceRect = pat->mSamplingRect; + if (sourceRect.IsEmpty()) { + sourceRect = IntRect(0, 0, source->GetSize().width, source->GetSize().height); + } + + bitmap = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, pat->mExtendMode, mat, mRT, &sourceRect); if (!bitmap) { - return nullptr; + RefPtr<ID2D1SolidColorBrush> colBrush; + mRT->CreateSolidColorBrush(D2D1::ColorF(0, 0), byRef(colBrush)); + return colBrush.forget(); } } break; @@ -2283,7 +2423,7 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), byRef(bmBrush)); - return bmBrush; + return bmBrush.forget(); } gfxWarning() << "Invalid pattern type detected."; @@ -2359,7 +2499,7 @@ DrawTargetD2D::CreateGradientTexture(const GradientStopsD2D *aStops) RefPtr<ID3D10Texture2D> tex; mDevice->CreateTexture2D(&desc, &data, byRef(tex)); - return tex; + return tex.forget(); } TemporaryRef<ID3D10Texture2D> @@ -2424,7 +2564,7 @@ DrawTargetD2D::CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, cons return nullptr; } - return tex; + return tex.forget(); } void @@ -2547,6 +2687,7 @@ DrawTargetD2D::factory() #else options.debugLevel = D2D1_DEBUG_LEVEL_NONE; #endif + //options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, __uuidof(ID2D1Factory), @@ -2557,6 +2698,12 @@ DrawTargetD2D::factory() gfxWarning() << "Failed to create Direct2D factory."; } + RefPtr<ID2D1Factory1> factoryD2D1; + hr = mFactory->QueryInterface((ID2D1Factory1**)byRef(factoryD2D1)); + if (SUCCEEDED(hr)) { + ExtendInputEffectD2D1::Register(factoryD2D1); + } + return mFactory; } @@ -2564,6 +2711,12 @@ void DrawTargetD2D::CleanupD2D() { if (mFactory) { + RefPtr<ID2D1Factory1> factoryD2D1; + HRESULT hr = mFactory->QueryInterface((ID2D1Factory1**)byRef(factoryD2D1)); + if (SUCCEEDED(hr)) { + ExtendInputEffectD2D1::Unregister(factoryD2D1); + } + mFactory->Release(); mFactory = nullptr; } @@ -2576,9 +2729,9 @@ DrawTargetD2D::GetDWriteFactory() return mDWriteFactory; } - DWriteCreateFactoryFunc createDWriteFactory; + decltype(DWriteCreateFactory)* createDWriteFactory; HMODULE dwriteModule = LoadLibraryW(L"dwrite.dll"); - createDWriteFactory = (DWriteCreateFactoryFunc) + createDWriteFactory = (decltype(DWriteCreateFactory)*) GetProcAddress(dwriteModule, "DWriteCreateFactory"); if (!createDWriteFactory) { diff --git a/gfx/2d/DrawTargetD2D.h b/gfx/2d/DrawTargetD2D.h index 0df48621d..5eea0a132 100644 --- a/gfx/2d/DrawTargetD2D.h +++ b/gfx/2d/DrawTargetD2D.h @@ -14,11 +14,7 @@ #include <vector> #include <sstream> -#ifdef _MSC_VER -#include <hash_set> -#else #include <unordered_set> -#endif struct IDWriteFactory; @@ -37,16 +33,18 @@ struct PrivateD3D10DataD2D RefPtr<ID3D10Effect> mEffect; RefPtr<ID3D10InputLayout> mInputLayout; RefPtr<ID3D10Buffer> mVB; - RefPtr<ID3D10BlendState> mBlendStates[OP_COUNT]; + RefPtr<ID3D10BlendState> mBlendStates[size_t(CompositionOp::OP_COUNT)]; }; class DrawTargetD2D : public DrawTarget { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetD2D) DrawTargetD2D(); virtual ~DrawTargetD2D(); - virtual BackendType GetType() const { return BACKEND_DIRECT2D; } + virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; } + virtual BackendType GetBackendType() const { return BackendType::DIRECT2D; } virtual TemporaryRef<SourceSurface> Snapshot(); virtual IntSize GetSize() { return mSize; } @@ -56,6 +54,10 @@ public: const Rect &aSource, const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(), const DrawOptions &aOptions = DrawOptions()); + virtual void DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions = DrawOptions()); virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, @@ -116,12 +118,16 @@ public: virtual TemporaryRef<DrawTarget> CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const; - virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const; + virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const; virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, - ExtendMode aExtendMode = EXTEND_CLAMP) const; + ExtendMode aExtendMode = ExtendMode::CLAMP) const; + + virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType); + + virtual bool SupportsRegionClipping() const { return false; } virtual void *GetNativeSurface(NativeSurfaceType aType); @@ -132,9 +138,16 @@ public: TemporaryRef<ID2D1Layer> GetCachedLayer(); void PopCachedLayer(ID2D1RenderTarget *aRT); + TemporaryRef<ID2D1Image> GetImageForSurface(SourceSurface *aSurface); + static ID2D1Factory *factory(); static void CleanupD2D(); static IDWriteFactory *GetDWriteFactory(); + ID2D1RenderTarget *GetRT() { return mRT; } + + static uint32_t GetMaxSurfaceSize() { + return D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; + } operator std::string() const { std::stringstream stream; @@ -147,16 +160,12 @@ public: private: TemporaryRef<ID2D1Bitmap> - DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface, - Rect &aSource); + GetBitmapForSurface(SourceSurface *aSurface, + Rect &aSource); friend class AutoSaveRestoreClippedOut; friend class SourceSurfaceD2DTarget; -#ifdef _MSC_VER - typedef stdext::hash_set<DrawTargetD2D*> TargetSet; -#else typedef std::unordered_set<DrawTargetD2D*> TargetSet; -#endif bool InitD2DRenderTarget(); void PrepareForDrawing(ID2D1RenderTarget *aRT); @@ -198,6 +207,8 @@ private: // bounds to correctly reflect the total clip. This is in device space. TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds); + bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned); + TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f); TemporaryRef<ID3D10Texture2D> CreateGradientTexture(const GradientStopsD2D *aStops); @@ -207,7 +218,7 @@ private: void SetupStateForRendering(); // Set the scissor rect to a certain IntRects, resets the scissor rect to - // surface bounds when NULL is specified. + // surface bounds when nullptr is specified. void SetScissorToRect(IntRect *aRect); void PushD2DLayer(ID2D1RenderTarget *aRT, ID2D1Geometry *aGeometry, ID2D1Layer *aLayer, const D2D1_MATRIX_3X2_F &aTransform); @@ -242,7 +253,7 @@ private: RefPtr<ID2D1Layer> mLayer; D2D1_RECT_F mBounds; union { - // If mPath is non-NULL, the mTransform member will be used, otherwise + // If mPath is non-nullptr, the mTransform member will be used, otherwise // the mIsPixelAligned member is valid. D2D1_MATRIX_3X2_F mTransform; bool mIsPixelAligned; diff --git a/gfx/2d/DrawTargetD2D1.cpp b/gfx/2d/DrawTargetD2D1.cpp new file mode 100644 index 000000000..c949716f0 --- /dev/null +++ b/gfx/2d/DrawTargetD2D1.cpp @@ -0,0 +1,1462 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <initguid.h> +#include "DrawTargetD2D1.h" +#include "DrawTargetD2D.h" +#include "FilterNodeSoftware.h" +#include "GradientStopsD2D.h" +#include "SourceSurfaceD2D1.h" +#include "SourceSurfaceD2D.h" +#include "RadialGradientEffectD2D1.h" + +#include "HelpersD2D.h" +#include "FilterNodeD2D1.h" +#include "Tools.h" + +using namespace std; + +namespace mozilla { +namespace gfx { + +uint64_t DrawTargetD2D1::mVRAMUsageDT; +uint64_t DrawTargetD2D1::mVRAMUsageSS; +ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr; + +ID2D1Factory1 *D2DFactory1() +{ + return DrawTargetD2D1::factory(); +} + +DrawTargetD2D1::DrawTargetD2D1() + : mClipsArePushed(false) +{ +} + +DrawTargetD2D1::~DrawTargetD2D1() +{ + PopAllClips(); + + if (mSnapshot) { + // We may hold the only reference. MarkIndependent will clear mSnapshot; + // keep the snapshot object alive so it doesn't get destroyed while + // MarkIndependent is running. + RefPtr<SourceSurfaceD2D1> deathGrip = mSnapshot; + // mSnapshot can be treated as independent of this DrawTarget since we know + // this DrawTarget won't change again. + deathGrip->MarkIndependent(); + // mSnapshot will be cleared now. + } + + mDC->EndDraw(); + + // Targets depending on us can break that dependency, since we're obviously not going to + // be modified in the future. + for (auto iter = mDependentTargets.begin(); + iter != mDependentTargets.end(); iter++) { + (*iter)->mDependingOnTargets.erase(this); + } + // Our dependencies on other targets no longer matter. + for (TargetSet::iterator iter = mDependingOnTargets.begin(); + iter != mDependingOnTargets.end(); iter++) { + (*iter)->mDependentTargets.erase(this); + } +} + +TemporaryRef<SourceSurface> +DrawTargetD2D1::Snapshot() +{ + if (mSnapshot) { + return mSnapshot; + } + PopAllClips(); + + mDC->Flush(); + + mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this); + + return mSnapshot; +} + +void +DrawTargetD2D1::Flush() +{ + mDC->Flush(); + + // We no longer depend on any target. + for (TargetSet::iterator iter = mDependingOnTargets.begin(); + iter != mDependingOnTargets.end(); iter++) { + (*iter)->mDependentTargets.erase(this); + } + mDependingOnTargets.clear(); +} + +void +DrawTargetD2D1::DrawSurface(SourceSurface *aSurface, + const Rect &aDest, + const Rect &aSource, + const DrawSurfaceOptions &aSurfOptions, + const DrawOptions &aOptions) +{ + PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color())); + + D2D1_RECT_F samplingBounds; + + if (aSurfOptions.mSamplingBounds == SamplingBounds::BOUNDED) { + samplingBounds = D2DRect(aSource); + } else { + samplingBounds = D2D1::RectF(0, 0, Float(aSurface->GetSize().width), Float(aSurface->GetSize().height)); + } + + Float xScale = aDest.width / aSource.width; + Float yScale = aDest.height / aSource.height; + + RefPtr<ID2D1ImageBrush> brush; + + // Here we scale the source pattern up to the size and position where we want + // it to be. + Matrix transform; + transform.PreTranslate(aDest.x - aSource.x * xScale, aDest.y - aSource.y * yScale); + transform.PreScale(xScale, yScale); + + RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, transform, ExtendMode::CLAMP); + + if (!image) { + gfxWarning() << *this << ": Unable to get D2D image for surface."; + return; + } + + RefPtr<ID2D1Bitmap> bitmap; + if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) { + // If this is called with a DataSourceSurface it might do a partial upload + // that our DrawBitmap call doesn't support. + image->QueryInterface((ID2D1Bitmap**)byRef(bitmap)); + } + + if (bitmap && aSurfOptions.mSamplingBounds == SamplingBounds::UNBOUNDED) { + mDC->DrawBitmap(bitmap, D2DRect(aDest), aOptions.mAlpha, D2DFilter(aSurfOptions.mFilter), D2DRect(aSource)); + } else { + // This has issues ignoring the alpha channel on windows 7 with images marked opaque. + MOZ_ASSERT(aSurface->GetFormat() != SurfaceFormat::B8G8R8X8); + mDC->CreateImageBrush(image, + D2D1::ImageBrushProperties(samplingBounds, + D2D1_EXTEND_MODE_CLAMP, + D2D1_EXTEND_MODE_CLAMP, + D2DInterpolationMode(aSurfOptions.mFilter)), + D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)), + byRef(brush)); + mDC->FillRectangle(D2DRect(aDest), brush); + } + + FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color())); +} + +void +DrawTargetD2D1::DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions) +{ + if (aNode->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) { + gfxWarning() << *this << ": Incompatible filter passed to DrawFilter."; + return; + } + + PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color())); + + mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + + FilterNodeD2D1* node = static_cast<FilterNodeD2D1*>(aNode); + node->WillDraw(this); + + mDC->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect)); + + FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color())); +} + +void +DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface *aSurface, + const Point &aDest, + const Color &aColor, + const Point &aOffset, + Float aSigma, + CompositionOp aOperator) +{ + MarkChanged(); + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + Matrix mat; + RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP); + + if (!image) { + gfxWarning() << "Couldn't get image for surface."; + return; + } + + if (!mat.IsIdentity()) { + gfxDebug() << *this << ": At this point complex partial uploads are not supported for Shadow surfaces."; + return; + } + + // Step 1, create the shadow effect. + RefPtr<ID2D1Effect> shadowEffect; + mDC->CreateEffect(CLSID_D2D1Shadow, byRef(shadowEffect)); + shadowEffect->SetInput(0, image); + shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, aSigma); + D2D1_VECTOR_4F color = { aColor.r, aColor.g, aColor.b, aColor.a }; + shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color); + + D2D1_POINT_2F shadowPoint = D2DPoint(aDest + aOffset); + mDC->DrawImage(shadowEffect, &shadowPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator)); + + D2D1_POINT_2F imgPoint = D2DPoint(aDest); + mDC->DrawImage(image, &imgPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator)); +} + +void +DrawTargetD2D1::ClearRect(const Rect &aRect) +{ + MarkChanged(); + + PopAllClips(); + + PushClipRect(aRect); + + if (mTransformDirty || + !mTransform.IsIdentity()) { + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + } + + D2D1_RECT_F clipRect; + bool isPixelAligned; + if (mTransform.IsRectilinear() && + GetDeviceSpaceClipRect(clipRect, isPixelAligned)) { + mDC->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + mDC->Clear(); + mDC->PopAxisAlignedClip(); + + PopClip(); + return; + } + + mDC->SetTarget(mTempBitmap); + mDC->Clear(); + + IntRect addClipRect; + RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&addClipRect); + + RefPtr<ID2D1SolidColorBrush> brush; + mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), byRef(brush)); + mDC->PushAxisAlignedClip(D2D1::RectF(addClipRect.x, addClipRect.y, addClipRect.XMost(), addClipRect.YMost()), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + mDC->FillGeometry(geom, brush); + mDC->PopAxisAlignedClip(); + + mDC->SetTarget(mBitmap); + mDC->DrawImage(mTempBitmap, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_DESTINATION_OUT); + + PopClip(); + + return; +} + +void +DrawTargetD2D1::MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, + const DrawOptions &aOptions) +{ + MarkChanged(); + + RefPtr<ID2D1Bitmap> bitmap; + + RefPtr<ID2D1Image> image = GetImageForSurface(aMask, ExtendMode::CLAMP); + + if (!image) { + gfxWarning() << "Failed to get image for surface."; + return; + } + + PrepareForDrawing(aOptions.mCompositionOp, aSource); + + // FillOpacityMask only works if the antialias mode is MODE_ALIASED + mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + + IntSize size = aMask->GetSize(); + Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height)); + image->QueryInterface((ID2D1Bitmap**)&bitmap); + if (!bitmap) { + gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces."; + return; + } + + Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height)); + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha); + mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect)); + + mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + + FinalizeDrawing(aOptions.mCompositionOp, aSource); +} + +void +DrawTargetD2D1::CopySurface(SourceSurface *aSurface, + const IntRect &aSourceRect, + const IntPoint &aDestination) +{ + MarkChanged(); + + PopAllClips(); + + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + Matrix mat; + RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP); + + if (!image) { + gfxWarning() << "Couldn't get image for surface."; + return; + } + + if (!mat.IsIdentity()) { + gfxDebug() << *this << ": At this point complex partial uploads are not supported for CopySurface."; + return; + } + + if (mFormat == SurfaceFormat::A8) { + RefPtr<ID2D1Bitmap> bitmap; + image->QueryInterface((ID2D1Bitmap**)byRef(bitmap)); + + mDC->PushAxisAlignedClip(D2D1::RectF(aDestination.x, aDestination.y, + aDestination.x + aSourceRect.width, + aDestination.y + aSourceRect.height), + D2D1_ANTIALIAS_MODE_ALIASED); + mDC->Clear(); + mDC->PopAxisAlignedClip(); + + RefPtr<ID2D1SolidColorBrush> brush; + mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), + D2D1::BrushProperties(), byRef(brush)); + mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS); + mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + return; + } + + mDC->DrawImage(image, D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)), + D2D1::RectF(Float(aSourceRect.x), Float(aSourceRect.y), + Float(aSourceRect.XMost()), Float(aSourceRect.YMost())), + D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY); +} + +void +DrawTargetD2D1::FillRect(const Rect &aRect, + const Pattern &aPattern, + const DrawOptions &aOptions) +{ + PrepareForDrawing(aOptions.mCompositionOp, aPattern); + + mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + mDC->FillRectangle(D2DRect(aRect), brush); + + FinalizeDrawing(aOptions.mCompositionOp, aPattern); +} + +void +DrawTargetD2D1::StrokeRect(const Rect &aRect, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions, + const DrawOptions &aOptions) +{ + PrepareForDrawing(aOptions.mCompositionOp, aPattern); + + mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); + + mDC->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle); + + FinalizeDrawing(aOptions.mCompositionOp, aPattern); +} + +void +DrawTargetD2D1::StrokeLine(const Point &aStart, + const Point &aEnd, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions, + const DrawOptions &aOptions) +{ + PrepareForDrawing(aOptions.mCompositionOp, aPattern); + + mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); + + mDC->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle); + + FinalizeDrawing(aOptions.mCompositionOp, aPattern); +} + +void +DrawTargetD2D1::Stroke(const Path *aPath, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions, + const DrawOptions &aOptions) +{ + if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) { + gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; + return; + } + const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath); + + PrepareForDrawing(aOptions.mCompositionOp, aPattern); + + mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); + + mDC->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle); + + FinalizeDrawing(aOptions.mCompositionOp, aPattern); +} + +void +DrawTargetD2D1::Fill(const Path *aPath, + const Pattern &aPattern, + const DrawOptions &aOptions) +{ + if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) { + gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; + return; + } + const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath); + + PrepareForDrawing(aOptions.mCompositionOp, aPattern); + + mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + + mDC->FillGeometry(d2dPath->mGeometry, brush); + + FinalizeDrawing(aOptions.mCompositionOp, aPattern); +} + +void +DrawTargetD2D1::FillGlyphs(ScaledFont *aFont, + const GlyphBuffer &aBuffer, + const Pattern &aPattern, + const DrawOptions &aOptions, + const GlyphRenderingOptions *aRenderingOptions) +{ + if (aFont->GetType() != FontType::DWRITE) { + gfxDebug() << *this << ": Ignoring drawing call for incompatible font."; + return; + } + + ScaledFontDWrite *font = static_cast<ScaledFontDWrite*>(aFont); + + IDWriteRenderingParams *params = nullptr; + if (aRenderingOptions) { + if (aRenderingOptions->GetType() != FontType::DWRITE) { + gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions."; + // This should never happen. + MOZ_ASSERT(false); + } else { + params = static_cast<const GlyphRenderingOptionsDWrite*>(aRenderingOptions)->mParams; + } + } + + AntialiasMode aaMode = font->GetDefaultAAMode(); + + if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) { + aaMode = aOptions.mAntialiasMode; + } + + PrepareForDrawing(aOptions.mCompositionOp, aPattern); + + bool forceClearType = false; + if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA && + aOptions.mCompositionOp == CompositionOp::OP_OVER && aaMode == AntialiasMode::SUBPIXEL) { + forceClearType = true; + } + + + D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; + + switch (aaMode) { + case AntialiasMode::NONE: + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; + break; + case AntialiasMode::GRAY: + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + break; + case AntialiasMode::SUBPIXEL: + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; + break; + default: + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; + } + + if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && + mFormat != SurfaceFormat::B8G8R8X8 && !forceClearType) { + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + } + + mDC->SetTextAntialiasMode(d2dAAMode); + + if (params != mTextRenderingParams) { + mDC->SetTextRenderingParams(params); + mTextRenderingParams = params; + } + + RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + + AutoDWriteGlyphRun autoRun; + DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun); + + if (brush) { + mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush); + } + + FinalizeDrawing(aOptions.mCompositionOp, aPattern); +} + +void +DrawTargetD2D1::Mask(const Pattern &aSource, + const Pattern &aMask, + const DrawOptions &aOptions) +{ + PrepareForDrawing(aOptions.mCompositionOp, aSource); + + RefPtr<ID2D1Brush> source = CreateBrushForPattern(aSource, aOptions.mAlpha); + RefPtr<ID2D1Brush> mask = CreateBrushForPattern(aMask, 1.0f); + mDC->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, + D2D1::IdentityMatrix(), + 1.0f, mask), + nullptr); + + Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height); + Matrix mat = mTransform; + mat.Invert(); + + mDC->FillRectangle(D2DRect(mat.TransformBounds(rect)), source); + + mDC->PopLayer(); + + FinalizeDrawing(aOptions.mCompositionOp, aSource); +} + +void +DrawTargetD2D1::PushClip(const Path *aPath) +{ + if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) { + gfxDebug() << *this << ": Ignoring clipping call for incompatible path."; + return; + } + + mCurrentClippedGeometry = nullptr; + + RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath)); + + PushedClip clip; + clip.mTransform = D2DMatrix(mTransform); + clip.mPath = pathD2D; + + pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds); + + mPushedClips.push_back(clip); + + // The transform of clips is relative to the world matrix, since we use the total + // transform for the clips, make the world matrix identity. + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + if (mClipsArePushed) { + PushD2DLayer(mDC, pathD2D->mGeometry, clip.mTransform); + } +} + +void +DrawTargetD2D1::PushClipRect(const Rect &aRect) +{ + if (!mTransform.IsRectilinear()) { + // Whoops, this isn't a rectangle in device space, Direct2D will not deal + // with this transform the way we want it to. + // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx + + RefPtr<PathBuilder> pathBuilder = CreatePathBuilder(); + pathBuilder->MoveTo(aRect.TopLeft()); + pathBuilder->LineTo(aRect.TopRight()); + pathBuilder->LineTo(aRect.BottomRight()); + pathBuilder->LineTo(aRect.BottomLeft()); + pathBuilder->Close(); + RefPtr<Path> path = pathBuilder->Finish(); + return PushClip(path); + } + + mCurrentClippedGeometry = nullptr; + + PushedClip clip; + Rect rect = mTransform.TransformBounds(aRect); + IntRect intRect; + clip.mIsPixelAligned = rect.ToIntRect(&intRect); + + // Do not store the transform, just store the device space rectangle directly. + clip.mBounds = D2DRect(rect); + + mPushedClips.push_back(clip); + + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + if (mClipsArePushed) { + mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + } +} + +void +DrawTargetD2D1::PopClip() +{ + mCurrentClippedGeometry = nullptr; + + if (mClipsArePushed) { + if (mPushedClips.back().mPath) { + mDC->PopLayer(); + } else { + mDC->PopAxisAlignedClip(); + } + } + mPushedClips.pop_back(); +} + +TemporaryRef<SourceSurface> +DrawTargetD2D1::CreateSourceSurfaceFromData(unsigned char *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat) const +{ + RefPtr<ID2D1Bitmap1> bitmap; + + HRESULT hr = mDC->CreateBitmap(D2DIntSize(aSize), aData, aStride, + D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(aFormat)), + byRef(bitmap)); + + if (FAILED(hr) || !bitmap) { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D2D1.1] 1CreateBitmap failure " << aSize << " Code: " << hexa(hr); + return nullptr; + } + + return new SourceSurfaceD2D1(bitmap.get(), mDC, aFormat, aSize); +} + +TemporaryRef<DrawTarget> +DrawTargetD2D1::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const +{ + RefPtr<DrawTargetD2D1> dt = new DrawTargetD2D1(); + + if (!dt->Init(aSize, aFormat)) { + return nullptr; + } + + return dt.forget(); +} + +TemporaryRef<PathBuilder> +DrawTargetD2D1::CreatePathBuilder(FillRule aFillRule) const +{ + RefPtr<ID2D1PathGeometry> path; + HRESULT hr = factory()->CreatePathGeometry(byRef(path)); + + if (FAILED(hr)) { + gfxWarning() << *this << ": Failed to create Direct2D Path Geometry. Code: " << hexa(hr); + return nullptr; + } + + RefPtr<ID2D1GeometrySink> sink; + hr = path->Open(byRef(sink)); + if (FAILED(hr)) { + gfxWarning() << *this << ": Failed to access Direct2D Path Geometry. Code: " << hexa(hr); + return nullptr; + } + + if (aFillRule == FillRule::FILL_WINDING) { + sink->SetFillMode(D2D1_FILL_MODE_WINDING); + } + + return new PathBuilderD2D(sink, path, aFillRule, BackendType::DIRECT2D1_1); +} + +TemporaryRef<GradientStops> +DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const +{ + if (aNumStops == 0) { + gfxWarning() << *this << ": Failed to create GradientStopCollection with no stops."; + return nullptr; + } + + D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops]; + + for (uint32_t i = 0; i < aNumStops; i++) { + stops[i].position = rawStops[i].offset; + stops[i].color = D2DColor(rawStops[i].color); + } + + RefPtr<ID2D1GradientStopCollection> stopCollection; + + HRESULT hr = + mDC->CreateGradientStopCollection(stops, aNumStops, + D2D1_GAMMA_2_2, D2DExtend(aExtendMode), + byRef(stopCollection)); + delete [] stops; + + if (FAILED(hr)) { + gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: " << hexa(hr); + return nullptr; + } + + return new GradientStopsD2D(stopCollection, Factory::GetDirect3D11Device()); +} + +TemporaryRef<FilterNode> +DrawTargetD2D1::CreateFilter(FilterType aType) +{ + return FilterNodeD2D1::Create(mDC, aType); +} + +bool +DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat) +{ + HRESULT hr; + + hr = Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, byRef(mDC)); + + if (FAILED(hr)) { + gfxCriticalError() <<"[D2D1.1] 1Failed to create a DeviceContext, code: " << hexa(hr); + return false; + } + + RefPtr<IDXGISurface> dxgiSurface; + aTexture->QueryInterface(__uuidof(IDXGISurface), + (void**)((IDXGISurface**)byRef(dxgiSurface))); + if (!dxgiSurface) { + gfxCriticalError() <<"[D2D1.1] Failed to obtain a DXGI surface."; + return false; + } + + D2D1_BITMAP_PROPERTIES1 props; + props.dpiX = 96; + props.dpiY = 96; + props.pixelFormat = D2DPixelFormat(aFormat); + props.colorContext = nullptr; + props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET; + hr = mDC->CreateBitmapFromDxgiSurface(dxgiSurface, props, (ID2D1Bitmap1**)byRef(mBitmap)); + + if (FAILED(hr)) { + gfxCriticalError() << "[D2D1.1] CreateBitmapFromDxgiSurface failure Code: " << hexa(hr); + return false; + } + + mFormat = aFormat; + D3D11_TEXTURE2D_DESC desc; + aTexture->GetDesc(&desc); + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + mSize.width = desc.Width; + mSize.height = desc.Height; + props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; + props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM; + + hr = mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mTempBitmap)); + + if (FAILED(hr)) { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 2CreateBitmap failure " << mSize << " Code: " << hexa(hr); + return false; + } + + mDC->SetTarget(mBitmap); + + mDC->BeginDraw(); + return true; +} + +bool +DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat) +{ + HRESULT hr; + + hr = Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, byRef(mDC)); + + if (FAILED(hr)) { + gfxCriticalError() <<"[D2D1.1] 2Failed to create a DeviceContext, code: " << hexa(hr); + return false; + } + + if (mDC->GetMaximumBitmapSize() < UINT32(aSize.width) || + mDC->GetMaximumBitmapSize() < UINT32(aSize.height)) { + // This is 'ok', so don't assert + gfxCriticalError(CriticalLog::DefaultOptions(false)) << "[D2D1.1] Attempt to use unsupported surface size " << aSize; + return false; + } + + D2D1_BITMAP_PROPERTIES1 props; + props.dpiX = 96; + props.dpiY = 96; + props.pixelFormat = D2DPixelFormat(aFormat); + props.colorContext = nullptr; + props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET; + hr = mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mBitmap)); + + if (FAILED(hr)) { + gfxCriticalError() << "[D2D1.1] 3CreateBitmap failure " << aSize << " Code: " << hexa(hr); + return false; + } + + props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; + props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM; + + hr = mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mTempBitmap)); + + if (FAILED(hr)) { + gfxCriticalError() << "[D2D1.1] failed to create new TempBitmap " << aSize << " Code: " << hexa(hr); + return false; + } + + mDC->SetTarget(mBitmap); + + mDC->BeginDraw(); + + mDC->Clear(); + + mFormat = aFormat; + mSize = aSize; + + return true; +} + +/** + * Private helpers. + */ +uint32_t +DrawTargetD2D1::GetByteSize() const +{ + return mSize.width * mSize.height * BytesPerPixel(mFormat); +} + +ID2D1Factory1* +DrawTargetD2D1::factory() +{ + if (mFactory) { + return mFactory; + } + + ID2D1Factory* d2dFactory = D2DFactory(); + if (!d2dFactory) { + return nullptr; + } + + HRESULT hr = d2dFactory->QueryInterface((ID2D1Factory1**)&mFactory); + + if (FAILED(hr)) { + return nullptr; + } + + RadialGradientEffectD2D1::Register(mFactory); + + return mFactory; +} + +void +DrawTargetD2D1::CleanupD2D() +{ + if (mFactory) { + RadialGradientEffectD2D1::Unregister(mFactory); + mFactory->Release(); + mFactory = nullptr; + } +} + +void +DrawTargetD2D1::MarkChanged() +{ + if (mSnapshot) { + if (mSnapshot->hasOneRef()) { + // Just destroy it, since no-one else knows about it. + mSnapshot = nullptr; + } else { + mSnapshot->DrawTargetWillChange(); + // The snapshot will no longer depend on this target. + MOZ_ASSERT(!mSnapshot); + } + } + if (mDependentTargets.size()) { + // Copy mDependentTargets since the Flush()es below will modify it. + TargetSet tmpTargets = mDependentTargets; + for (TargetSet::iterator iter = tmpTargets.begin(); + iter != tmpTargets.end(); iter++) { + (*iter)->Flush(); + } + // The Flush() should have broken all dependencies on this target. + MOZ_ASSERT(!mDependentTargets.size()); + } +} + +void +DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern) +{ + MarkChanged(); + + if (aOp == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) { + // It's important to do this before FlushTransformToDC! As this will cause + // the transform to become dirty. + PushAllClips(); + + FlushTransformToDC(); + return; + } + + PopAllClips(); + + mDC->SetTarget(mTempBitmap); + mDC->Clear(D2D1::ColorF(0, 0)); + + PushAllClips(); + FlushTransformToDC(); +} + +void +DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern) +{ + bool patternSupported = IsPatternSupportedByD2D(aPattern); + + if (aOp == CompositionOp::OP_OVER && patternSupported) { + return; + } + + PopAllClips(); + + RefPtr<ID2D1Image> image; + mDC->GetTarget(byRef(image)); + + mDC->SetTarget(mBitmap); + + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + if (patternSupported) { + if (D2DSupportsCompositeMode(aOp)) { + D2D1_RECT_F rect; + bool isAligned; + RefPtr<ID2D1Bitmap> tmpBitmap; + bool clipIsComplex = mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned); + + if (clipIsComplex) { + if (!IsOperatorBoundByMask(aOp)) { + HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), byRef(tmpBitmap)); + if (FAILED(hr)) { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr); + // For now, crash in this scenario; this should happen because tmpBitmap is + // null and CopyFromBitmap call below dereferences it. + // return; + } + mDC->Flush(); + + tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr); + } + } else { + PushAllClips(); + } + mDC->DrawImage(image, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp)); + + if (tmpBitmap) { + RefPtr<ID2D1BitmapBrush> brush; + RefPtr<ID2D1Geometry> inverseGeom = GetInverseClippedGeometry(); + mDC->CreateBitmapBrush(tmpBitmap, byRef(brush)); + + mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY); + mDC->FillGeometry(inverseGeom, brush); + mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER); + } + return; + } + + if (!mBlendEffect) { + mDC->CreateEffect(CLSID_D2D1Blend, byRef(mBlendEffect)); + + if (!mBlendEffect) { + gfxWarning() << "Failed to create blend effect!"; + return; + } + } + + RefPtr<ID2D1Bitmap> tmpBitmap; + mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), byRef(tmpBitmap)); + + // This flush is important since the copy method will not know about the context drawing to the surface. + // We also need to pop all the clips to make sure any drawn content will have made it to the final bitmap. + mDC->Flush(); + + // We need to use a copy here because affects don't accept a surface on + // both their in- and outputs. + tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr); + + mBlendEffect->SetInput(0, tmpBitmap); + mBlendEffect->SetInput(1, mTempBitmap); + mBlendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp)); + + PushAllClips(); + + mDC->DrawImage(mBlendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY); + return; + } + + const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern); + if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) { + // Draw nothing! + return; + } + + PushAllClips(); + + RefPtr<ID2D1Effect> radialGradientEffect; + + mDC->CreateEffect(CLSID_RadialGradientEffect, byRef(radialGradientEffect)); + + radialGradientEffect->SetValue(RADIAL_PROP_STOP_COLLECTION, + static_cast<const GradientStopsD2D*>(pat->mStops.get())->mStopCollection); + radialGradientEffect->SetValue(RADIAL_PROP_CENTER_1, D2D1::Vector2F(pat->mCenter1.x, pat->mCenter1.y)); + radialGradientEffect->SetValue(RADIAL_PROP_CENTER_2, D2D1::Vector2F(pat->mCenter2.x, pat->mCenter2.y)); + radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_1, pat->mRadius1); + radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2); + radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2); + radialGradientEffect->SetValue(RADIAL_PROP_TRANSFORM, D2DMatrix(pat->mMatrix * mTransform)); + radialGradientEffect->SetInput(0, image); + + mDC->DrawImage(radialGradientEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp)); +} + +void +DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* aSource) +{ + if (aSource->mDrawTarget && !mDependingOnTargets.count(aSource->mDrawTarget)) { + aSource->mDrawTarget->mDependentTargets.insert(this); + mDependingOnTargets.insert(aSource->mDrawTarget); + } +} + +static D2D1_RECT_F +IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2) +{ + D2D1_RECT_F result; + result.left = max(aRect1.left, aRect2.left); + result.top = max(aRect1.top, aRect2.top); + result.right = min(aRect1.right, aRect2.right); + result.bottom = min(aRect1.bottom, aRect2.bottom); + + result.right = max(result.right, result.left); + result.bottom = max(result.bottom, result.top); + + return result; +} + +bool +DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned) +{ + if (!mPushedClips.size()) { + return false; + } + + aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height); + for (auto iter = mPushedClips.begin();iter != mPushedClips.end(); iter++) { + if (iter->mPath) { + return false; + } + aClipRect = IntersectRect(aClipRect, iter->mBounds); + if (!iter->mIsPixelAligned) { + aIsPixelAligned = false; + } + } + return true; +} + +TemporaryRef<ID2D1Geometry> +DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds) +{ + if (mCurrentClippedGeometry) { + *aClipBounds = mCurrentClipBounds; + return mCurrentClippedGeometry; + } + + MOZ_ASSERT(mPushedClips.size()); + + mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize); + + // if pathGeom is null then pathRect represents the path. + RefPtr<ID2D1Geometry> pathGeom; + D2D1_RECT_F pathRect; + bool pathRectIsAxisAligned = false; + auto iter = mPushedClips.begin(); + + if (iter->mPath) { + pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform); + } else { + pathRect = iter->mBounds; + pathRectIsAxisAligned = iter->mIsPixelAligned; + } + + iter++; + for (;iter != mPushedClips.end(); iter++) { + // Do nothing but add it to the current clip bounds. + if (!iter->mPath && iter->mIsPixelAligned) { + mCurrentClipBounds.IntersectRect(mCurrentClipBounds, + IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top), + int32_t(iter->mBounds.right - iter->mBounds.left), + int32_t(iter->mBounds.bottom - iter->mBounds.top))); + continue; + } + + if (!pathGeom) { + if (pathRectIsAxisAligned) { + mCurrentClipBounds.IntersectRect(mCurrentClipBounds, + IntRect(int32_t(pathRect.left), int32_t(pathRect.top), + int32_t(pathRect.right - pathRect.left), + int32_t(pathRect.bottom - pathRect.top))); + } + if (iter->mPath) { + // See if pathRect needs to go into the path geometry. + if (!pathRectIsAxisAligned) { + pathGeom = ConvertRectToGeometry(pathRect); + } else { + pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform); + } + } else { + pathRect = IntersectRect(pathRect, iter->mBounds); + pathRectIsAxisAligned = false; + continue; + } + } + + RefPtr<ID2D1PathGeometry> newGeom; + factory()->CreatePathGeometry(byRef(newGeom)); + + RefPtr<ID2D1GeometrySink> currentSink; + newGeom->Open(byRef(currentSink)); + + if (iter->mPath) { + pathGeom->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT, + iter->mTransform, currentSink); + } else { + RefPtr<ID2D1Geometry> rectGeom = ConvertRectToGeometry(iter->mBounds); + pathGeom->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT, + D2D1::IdentityMatrix(), currentSink); + } + + currentSink->Close(); + + pathGeom = newGeom.forget(); + } + + // For now we need mCurrentClippedGeometry to always be non-nullptr. This + // method might seem a little strange but it is just fine, if pathGeom is + // nullptr pathRect will always still contain 1 clip unaccounted for + // regardless of mCurrentClipBounds. + if (!pathGeom) { + pathGeom = ConvertRectToGeometry(pathRect); + } + mCurrentClippedGeometry = pathGeom.forget(); + *aClipBounds = mCurrentClipBounds; + return mCurrentClippedGeometry; +} + +TemporaryRef<ID2D1Geometry> +DrawTargetD2D1::GetInverseClippedGeometry() +{ + IntRect bounds; + RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&bounds); + RefPtr<ID2D1RectangleGeometry> rectGeom; + RefPtr<ID2D1PathGeometry> inverseGeom; + + factory()->CreateRectangleGeometry(D2D1::RectF(0, 0, mSize.width, mSize.height), byRef(rectGeom)); + factory()->CreatePathGeometry(byRef(inverseGeom)); + RefPtr<ID2D1GeometrySink> sink; + inverseGeom->Open(byRef(sink)); + rectGeom->CombineWithGeometry(geom, D2D1_COMBINE_MODE_EXCLUDE, D2D1::IdentityMatrix(), sink); + sink->Close(); + + return inverseGeom; +} + +void +DrawTargetD2D1::PopAllClips() +{ + if (mClipsArePushed) { + PopClipsFromDC(mDC); + + mClipsArePushed = false; + } +} + +void +DrawTargetD2D1::PushAllClips() +{ + if (!mClipsArePushed) { + PushClipsToDC(mDC); + + mClipsArePushed = true; + } +} + +void +DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC) +{ + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + for (std::vector<PushedClip>::iterator iter = mPushedClips.begin(); + iter != mPushedClips.end(); iter++) { + if (iter->mPath) { + PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform); + } else { + mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + } + } +} + +void +DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC) +{ + for (int i = mPushedClips.size() - 1; i >= 0; i--) { + if (mPushedClips[i].mPath) { + aDC->PopLayer(); + } else { + aDC->PopAxisAlignedClip(); + } + } +} + +TemporaryRef<ID2D1Brush> +DrawTargetD2D1::CreateTransparentBlackBrush() +{ + RefPtr<ID2D1SolidColorBrush> brush; + mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0), byRef(brush)); + return brush; +} + +TemporaryRef<ID2D1Brush> +DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) +{ + if (!IsPatternSupportedByD2D(aPattern)) { + RefPtr<ID2D1SolidColorBrush> colBrush; + mDC->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), byRef(colBrush)); + return colBrush.forget(); + } + + if (aPattern.GetType() == PatternType::COLOR) { + RefPtr<ID2D1SolidColorBrush> colBrush; + Color color = static_cast<const ColorPattern*>(&aPattern)->mColor; + mDC->CreateSolidColorBrush(D2D1::ColorF(color.r, color.g, + color.b, color.a), + D2D1::BrushProperties(aAlpha), + byRef(colBrush)); + return colBrush.forget(); + } + if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) { + RefPtr<ID2D1LinearGradientBrush> gradBrush; + const LinearGradientPattern *pat = + static_cast<const LinearGradientPattern*>(&aPattern); + + GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get()); + + if (!stops) { + gfxDebug() << "No stops specified for gradient pattern."; + return CreateTransparentBlackBrush(); + } + + if (pat->mBegin == pat->mEnd) { + RefPtr<ID2D1SolidColorBrush> colBrush; + uint32_t stopCount = stops->mStopCollection->GetGradientStopCount(); + vector<D2D1_GRADIENT_STOP> d2dStops(stopCount); + stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount); + mDC->CreateSolidColorBrush(d2dStops.back().color, + D2D1::BrushProperties(aAlpha), + byRef(colBrush)); + return colBrush.forget(); + } + + mDC->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin), + D2DPoint(pat->mEnd)), + D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), + stops->mStopCollection, + byRef(gradBrush)); + + if (!gradBrush) { + gfxWarning() << "Couldn't create gradient brush."; + return CreateTransparentBlackBrush(); + } + + return gradBrush.forget(); + } + if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { + RefPtr<ID2D1RadialGradientBrush> gradBrush; + const RadialGradientPattern *pat = + static_cast<const RadialGradientPattern*>(&aPattern); + + GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get()); + + if (!stops) { + gfxDebug() << "No stops specified for gradient pattern."; + return CreateTransparentBlackBrush(); + } + + // This will not be a complex radial gradient brush. + mDC->CreateRadialGradientBrush( + D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter2), + D2DPoint(pat->mCenter1 - pat->mCenter2), + pat->mRadius2, pat->mRadius2), + D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), + stops->mStopCollection, + byRef(gradBrush)); + + if (!gradBrush) { + gfxWarning() << "Couldn't create gradient brush."; + return CreateTransparentBlackBrush(); + } + + return gradBrush.forget(); + } + if (aPattern.GetType() == PatternType::SURFACE) { + const SurfacePattern *pat = + static_cast<const SurfacePattern*>(&aPattern); + + if (!pat->mSurface) { + gfxDebug() << "No source surface specified for surface pattern"; + return CreateTransparentBlackBrush(); + } + + D2D1_RECT_F samplingBounds; + Matrix mat = pat->mMatrix; + + bool useSamplingRect = false; + if (!pat->mSamplingRect.IsEmpty() && + (pat->mSurface->GetType() == SurfaceType::D2D1_1_IMAGE)) { + samplingBounds = D2DRect(pat->mSamplingRect); + mat.PreTranslate(pat->mSamplingRect.x, pat->mSamplingRect.y); + } else if (!pat->mSamplingRect.IsEmpty()) { + // We will do a partial upload of the sampling restricted area from GetImageForSurface. + samplingBounds = D2D1::RectF(0, 0, pat->mSamplingRect.width, pat->mSamplingRect.height); + } else { + samplingBounds = D2D1::RectF(0, 0, + Float(pat->mSurface->GetSize().width), + Float(pat->mSurface->GetSize().height)); + } + + MOZ_ASSERT(pat->mSurface->IsValid()); + + RefPtr<ID2D1ImageBrush> imageBrush; + RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode, !pat->mSamplingRect.IsEmpty() ? &pat->mSamplingRect : nullptr); + + if (!image) { + return CreateTransparentBlackBrush(); + } + + mDC->CreateImageBrush(image, + D2D1::ImageBrushProperties(samplingBounds, + D2DExtend(pat->mExtendMode), + D2DExtend(pat->mExtendMode), + D2DInterpolationMode(pat->mFilter)), + D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), + byRef(imageBrush)); + return imageBrush.forget(); + } + + gfxWarning() << "Invalid pattern type detected."; + return CreateTransparentBlackBrush(); +} + +TemporaryRef<ID2D1Image> +DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform, + ExtendMode aExtendMode, const IntRect* aSourceRect) +{ + RefPtr<ID2D1Image> image; + + switch (aSurface->GetType()) { + case SurfaceType::D2D1_1_IMAGE: + { + SourceSurfaceD2D1 *surf = static_cast<SourceSurfaceD2D1*>(aSurface); + image = surf->GetImage(); + AddDependencyOnSource(surf); + } + break; + default: + { + RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface(); + if (!dataSurf) { + gfxWarning() << "Invalid surface type."; + return nullptr; + } + return CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, aExtendMode, + aSourceTransform, mDC, aSourceRect); + } + break; + } + + return image.forget(); +} + +TemporaryRef<SourceSurface> +DrawTargetD2D1::OptimizeSourceSurface(SourceSurface* aSurface) const +{ + if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) { + return aSurface; + } + + RefPtr<DataSourceSurface> data = aSurface->GetDataSurface(); + + DataSourceSurface::MappedSurface map; + if (!data->Map(DataSourceSurface::MapType::READ, &map)) { + return nullptr; + } + + RefPtr<ID2D1Bitmap1> bitmap; + HRESULT hr = mDC->CreateBitmap(D2DIntSize(data->GetSize()), map.mData, map.mStride, + D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(data->GetFormat())), + byRef(bitmap)); + + if (FAILED(hr)) { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(data->GetSize()))) << "[D2D1.1] 4CreateBitmap failure " << data->GetSize() << " Code: " << hexa(hr); + } + + data->Unmap(); + + if (!bitmap) { + return data.forget(); + } + + return new SourceSurfaceD2D1(bitmap.get(), mDC, data->GetFormat(), data->GetSize()); +} + +void +DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform) +{ + D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE; + + if (aDC->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) { + options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND; + } + + mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform, + 1.0, nullptr, options), nullptr); +} + +} +} diff --git a/gfx/2d/DrawTargetD2D1.h b/gfx/2d/DrawTargetD2D1.h new file mode 100644 index 000000000..f769e0a2b --- /dev/null +++ b/gfx/2d/DrawTargetD2D1.h @@ -0,0 +1,240 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_DRAWTARGETD2D1_H_ +#define MOZILLA_GFX_DRAWTARGETD2D1_H_ + +#include "2D.h" +#include <d3d11.h> +#include <d2d1_1.h> +#include "PathD2D.h" +#include "HelpersD2D.h" + +#include <vector> +#include <sstream> + +#include <unordered_set> + +struct IDWriteFactory; + +namespace mozilla { +namespace gfx { + +class SourceSurfaceD2D1; +class GradientStopsD2D; +class ScaledFontDWrite; + +const int32_t kLayerCacheSize1 = 5; + +class DrawTargetD2D1 : public DrawTarget +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetD2D1) + DrawTargetD2D1(); + virtual ~DrawTargetD2D1(); + + virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; } + virtual BackendType GetBackendType() const { return BackendType::DIRECT2D1_1; } + virtual TemporaryRef<SourceSurface> Snapshot(); + virtual IntSize GetSize() { return mSize; } + + virtual void Flush(); + virtual void DrawSurface(SourceSurface *aSurface, + const Rect &aDest, + const Rect &aSource, + const DrawSurfaceOptions &aSurfOptions, + const DrawOptions &aOptions); + virtual void DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions = DrawOptions()); + virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, + const Point &aDest, + const Color &aColor, + const Point &aOffset, + Float aSigma, + CompositionOp aOperator); + virtual void ClearRect(const Rect &aRect); + virtual void MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, + const DrawOptions &aOptions = DrawOptions()); + + virtual void CopySurface(SourceSurface *aSurface, + const IntRect &aSourceRect, + const IntPoint &aDestination); + + virtual void FillRect(const Rect &aRect, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions()); + virtual void StrokeRect(const Rect &aRect, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()); + virtual void StrokeLine(const Point &aStart, + const Point &aEnd, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()); + virtual void Stroke(const Path *aPath, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()); + virtual void Fill(const Path *aPath, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions()); + virtual void FillGlyphs(ScaledFont *aFont, + const GlyphBuffer &aBuffer, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions(), + const GlyphRenderingOptions *aRenderingOptions = nullptr); + virtual void Mask(const Pattern &aSource, + const Pattern &aMask, + const DrawOptions &aOptions = DrawOptions()); + virtual void PushClip(const Path *aPath); + virtual void PushClipRect(const Rect &aRect); + virtual void PopClip(); + + virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat) const; + virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const; + + virtual TemporaryRef<SourceSurface> + CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const { return nullptr; } + + virtual TemporaryRef<DrawTarget> + CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const; + + virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const; + + virtual TemporaryRef<GradientStops> + CreateGradientStops(GradientStop *aStops, + uint32_t aNumStops, + ExtendMode aExtendMode = ExtendMode::CLAMP) const; + + virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType); + + virtual bool SupportsRegionClipping() const { return false; } + + virtual void *GetNativeSurface(NativeSurfaceType aType) { return nullptr; } + + bool Init(const IntSize &aSize, SurfaceFormat aFormat); + bool Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat); + uint32_t GetByteSize() const; + + TemporaryRef<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform, + ExtendMode aExtendMode, const IntRect* aSourceRect = nullptr); + + TemporaryRef<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, ExtendMode aExtendMode) { + Matrix mat; + return GetImageForSurface(aSurface, mat, aExtendMode, nullptr); + } + + static ID2D1Factory1 *factory(); + static void CleanupD2D(); + static IDWriteFactory *GetDWriteFactory(); + + operator std::string() const { + std::stringstream stream; + stream << "DrawTargetD2D 1.1 (" << this << ")"; + return stream.str(); + } + + static uint32_t GetMaxSurfaceSize() { + return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; + } + + static uint64_t mVRAMUsageDT; + static uint64_t mVRAMUsageSS; + +private: + friend class SourceSurfaceD2D1; + + typedef std::unordered_set<DrawTargetD2D1*> TargetSet; + + // This function will mark the surface as changing, and make sure any + // copy-on-write snapshots are notified. + void MarkChanged(); + void PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern); + void FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern); + void FlushTransformToDC() { + if (mTransformDirty) { + mDC->SetTransform(D2DMatrix(mTransform)); + mTransformDirty = false; + } + } + void AddDependencyOnSource(SourceSurfaceD2D1* aSource); + + // This returns the clipped geometry, in addition it returns aClipBounds which + // represents the intersection of all pixel-aligned rectangular clips that + // are currently set. The returned clipped geometry must be clipped by these + // bounds to correctly reflect the total clip. This is in device space. + TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds); + + TemporaryRef<ID2D1Geometry> GetInverseClippedGeometry(); + + bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned); + + void PopAllClips(); + void PushAllClips(); + void PushClipsToDC(ID2D1DeviceContext *aDC); + void PopClipsFromDC(ID2D1DeviceContext *aDC); + + TemporaryRef<ID2D1Brush> CreateTransparentBlackBrush(); + TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f); + + void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform); + + IntSize mSize; + + RefPtr<ID3D11Device> mDevice; + RefPtr<ID3D11Texture2D> mTexture; + RefPtr<ID2D1Geometry> mCurrentClippedGeometry; + // This is only valid if mCurrentClippedGeometry is non-null. And will + // only be the intersection of all pixel-aligned retangular clips. This is in + // device space. + IntRect mCurrentClipBounds; + mutable RefPtr<ID2D1DeviceContext> mDC; + RefPtr<ID2D1Bitmap1> mBitmap; + RefPtr<ID2D1Bitmap1> mTempBitmap; + RefPtr<ID2D1Effect> mBlendEffect; + + // We store this to prevent excessive SetTextRenderingParams calls. + RefPtr<IDWriteRenderingParams> mTextRenderingParams; + + // List of pushed clips. + struct PushedClip + { + D2D1_RECT_F mBounds; + union { + // If mPath is non-null, the mTransform member will be used, otherwise + // the mIsPixelAligned member is valid. + D2D1_MATRIX_3X2_F mTransform; + bool mIsPixelAligned; + }; + RefPtr<PathD2D> mPath; + }; + std::vector<PushedClip> mPushedClips; + + // The latest snapshot of this surface. This needs to be told when this + // target is modified. We keep it alive as a cache. + RefPtr<SourceSurfaceD2D1> mSnapshot; + // A list of targets we need to flush when we're modified. + TargetSet mDependentTargets; + // A list of targets which have this object in their mDependentTargets set + TargetSet mDependingOnTargets; + + // True of the current clip stack is pushed to the main RT. + bool mClipsArePushed; + static ID2D1Factory1 *mFactory; + static IDWriteFactory *mDWriteFactory; +}; + +} +} + +#endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */ diff --git a/gfx/2d/DrawTargetDual.cpp b/gfx/2d/DrawTargetDual.cpp index 3af57e23f..e971337b2 100644 --- a/gfx/2d/DrawTargetDual.cpp +++ b/gfx/2d/DrawTargetDual.cpp @@ -5,6 +5,7 @@ #include "DrawTargetDual.h" #include "Tools.h" +#include "Logging.h" namespace mozilla { namespace gfx { @@ -12,9 +13,9 @@ namespace gfx { class DualSurface { public: - inline DualSurface(SourceSurface *aSurface) + inline explicit DualSurface(SourceSurface *aSurface) { - if (aSurface->GetType() != SURFACE_DUAL_DT) { + if (aSurface->GetType() != SurfaceType::DUAL_DT) { mA = mB = aSurface; return; } @@ -36,10 +37,10 @@ public: class DualPattern { public: - inline DualPattern(const Pattern &aPattern) + inline explicit DualPattern(const Pattern &aPattern) : mPatternsInitialized(false) { - if (aPattern.GetType() != PATTERN_SURFACE) { + if (aPattern.GetType() != PatternType::SURFACE) { mA = mB = &aPattern; return; } @@ -47,7 +48,7 @@ public: const SurfacePattern *surfPat = static_cast<const SurfacePattern*>(&aPattern); - if (surfPat->mSurface->GetType() != SURFACE_DUAL_DT) { + if (surfPat->mSurface->GetType() != SurfaceType::DUAL_DT) { mA = mB = &aPattern; return; } @@ -186,6 +187,11 @@ DrawTargetDual::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFor RefPtr<DrawTarget> dtA = mA->CreateSimilarDrawTarget(aSize, aFormat); RefPtr<DrawTarget> dtB = mB->CreateSimilarDrawTarget(aSize, aFormat); + if (!dtA || !dtB) { + gfxWarning() << "Failure to allocate a similar DrawTargetDual. Size: " << aSize; + return nullptr; + } + return new DrawTargetDual(dtA, dtB); } diff --git a/gfx/2d/DrawTargetDual.h b/gfx/2d/DrawTargetDual.h index 8f519603a..0cb0fe420 100644 --- a/gfx/2d/DrawTargetDual.h +++ b/gfx/2d/DrawTargetDual.h @@ -12,14 +12,15 @@ #include "SourceSurfaceDual.h" #include "2D.h" +#include "Filters.h" namespace mozilla { namespace gfx { #define FORWARD_FUNCTION(funcName) \ - virtual void funcName() { mA->funcName(); mB->funcName(); } + virtual void funcName() override { mA->funcName(); mB->funcName(); } #define FORWARD_FUNCTION1(funcName, var1Type, var1Name) \ - virtual void funcName(var1Type var1Name) { mA->funcName(var1Name); mB->funcName(var1Name); } + virtual void funcName(var1Type var1Name) override { mA->funcName(var1Name); mB->funcName(var1Name); } /* This is a special type of DrawTarget. It duplicates all drawing calls * accross two drawtargets. An exception to this is when a snapshot of another @@ -34,6 +35,7 @@ namespace gfx { class DrawTargetDual : public DrawTarget { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetDual, override) DrawTargetDual(DrawTarget *aA, DrawTarget *aB) : mA(aA) , mB(aB) @@ -41,9 +43,10 @@ public: mFormat = aA->GetFormat(); } - virtual BackendType GetType() const { return mA->GetType(); } - virtual TemporaryRef<SourceSurface> Snapshot() { return new SourceSurfaceDual(mA, mB); } - virtual IntSize GetSize() { return mA->GetSize(); } + virtual DrawTargetType GetType() const override { return mA->GetType(); } + virtual BackendType GetBackendType() const override { return mA->GetBackendType(); } + virtual TemporaryRef<SourceSurface> Snapshot() override { return new SourceSurfaceDual(mA, mB); } + virtual IntSize GetSize() override { return mA->GetSize(); } FORWARD_FUNCTION(Flush) FORWARD_FUNCTION1(PushClip, const Path *, aPath) @@ -51,70 +54,79 @@ public: FORWARD_FUNCTION(PopClip) FORWARD_FUNCTION1(ClearRect, const Rect &, aRect) - virtual void SetTransform(const Matrix &aTransform) { + virtual void SetTransform(const Matrix &aTransform) override { mTransform = aTransform; mA->SetTransform(aTransform); mB->SetTransform(aTransform); } virtual void DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect & aSource, - const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions); + const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions) override; + + virtual void DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions = DrawOptions()) override + { + mA->DrawFilter(aNode, aSourceRect, aDestPoint, aOptions); + mB->DrawFilter(aNode, aSourceRect, aDestPoint, aOptions); + } virtual void MaskSurface(const Pattern &aSource, SourceSurface *aMask, Point aOffset, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, - Float aSigma, CompositionOp aOp); + Float aSigma, CompositionOp aOp) override; virtual void CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect, - const IntPoint &aDestination); + const IntPoint &aDestination) override; - virtual void FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions); + virtual void FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions) override; virtual void StrokeRect(const Rect &aRect, const Pattern &aPattern, - const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions); + const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override; virtual void StrokeLine(const Point &aStart, const Point &aEnd, const Pattern &aPattern, - const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions); + const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override; virtual void Stroke(const Path *aPath, const Pattern &aPattern, - const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions); + const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override; - virtual void Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions); + virtual void Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions) override; virtual void FillGlyphs(ScaledFont *aScaledFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aOptions, - const GlyphRenderingOptions *aRenderingOptions); + const GlyphRenderingOptions *aRenderingOptions) override; - virtual void Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions); + virtual void Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions) override; virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, int32_t aStride, - SurfaceFormat aFormat) const + SurfaceFormat aFormat) const override { return mA->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat); } - virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const + virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override { return mA->OptimizeSourceSurface(aSurface); } virtual TemporaryRef<SourceSurface> - CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const + CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override { return mA->CreateSourceSurfaceFromNativeSurface(aSurface); } virtual TemporaryRef<DrawTarget> - CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const; + CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override; - virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const + virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override { return mA->CreatePathBuilder(aFillRule); } @@ -122,15 +134,25 @@ public: virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, - ExtendMode aExtendMode = EXTEND_CLAMP) const + ExtendMode aExtendMode = ExtendMode::CLAMP) const override { return mA->CreateGradientStops(aStops, aNumStops, aExtendMode); } - - virtual void *GetNativeSurface(NativeSurfaceType aType) + + virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override + { + return mA->CreateFilter(aType); + } + + virtual void *GetNativeSurface(NativeSurfaceType aType) override { return nullptr; } + + virtual bool IsDualDrawTarget() const override + { + return true; + } private: RefPtr<DrawTarget> mA; diff --git a/gfx/2d/DrawTargetRecording.cpp b/gfx/2d/DrawTargetRecording.cpp index 37b386bac..a39f97844 100644 --- a/gfx/2d/DrawTargetRecording.cpp +++ b/gfx/2d/DrawTargetRecording.cpp @@ -9,6 +9,7 @@ #include "Logging.h" #include "Tools.h" +#include "Filters.h" namespace mozilla { namespace gfx { @@ -16,6 +17,7 @@ namespace gfx { class SourceSurfaceRecording : public SourceSurface { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceRecording) SourceSurfaceRecording(SourceSurface *aFinalSurface, DrawEventRecorderPrivate *aRecorder) : mFinalSurface(aFinalSurface), mRecorder(aRecorder) { @@ -26,7 +28,7 @@ public: mRecorder->RecordEvent(RecordedSourceSurfaceDestruction(this)); } - virtual SurfaceType GetType() const { return SURFACE_RECORDING; } + virtual SurfaceType GetType() const { return SurfaceType::RECORDING; } virtual IntSize GetSize() const { return mFinalSurface->GetSize(); } virtual SurfaceFormat GetFormat() const { return mFinalSurface->GetFormat(); } virtual TemporaryRef<DataSourceSurface> GetDataSurface() { return mFinalSurface->GetDataSurface(); } @@ -38,6 +40,7 @@ public: class GradientStopsRecording : public GradientStops { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsRecording) GradientStopsRecording(GradientStops *aFinalGradientStops, DrawEventRecorderPrivate *aRecorder) : mFinalGradientStops(aFinalGradientStops), mRecorder(aRecorder) { @@ -48,7 +51,7 @@ public: mRecorder->RecordEvent(RecordedGradientStopsDestruction(this)); } - virtual BackendType GetBackendType() const { return BACKEND_RECORDING; } + virtual BackendType GetBackendType() const { return BackendType::RECORDING; } RefPtr<GradientStops> mFinalGradientStops; RefPtr<DrawEventRecorderPrivate> mRecorder; @@ -57,7 +60,7 @@ public: static SourceSurface * GetSourceSurface(SourceSurface *aSurface) { - if (aSurface->GetType() != SURFACE_RECORDING) { + if (aSurface->GetType() != SurfaceType::RECORDING) { return aSurface; } @@ -67,17 +70,95 @@ GetSourceSurface(SourceSurface *aSurface) static GradientStops * GetGradientStops(GradientStops *aStops) { - if (aStops->GetBackendType() != BACKEND_RECORDING) { + if (aStops->GetBackendType() != BackendType::RECORDING) { return aStops; } return static_cast<GradientStopsRecording*>(aStops)->mFinalGradientStops; } +class FilterNodeRecording : public FilterNode +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeRecording, override) + using FilterNode::SetAttribute; + + FilterNodeRecording(FilterNode *aFinalFilterNode, DrawEventRecorderPrivate *aRecorder) + : mFinalFilterNode(aFinalFilterNode), mRecorder(aRecorder) + { + } + + ~FilterNodeRecording() + { + mRecorder->RecordEvent(RecordedFilterNodeDestruction(this)); + } + + virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override + { + mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aSurface)); + mFinalFilterNode->SetInput(aIndex, GetSourceSurface(aSurface)); + } + virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override + { + FilterNode *finalNode = aFilter; + if (aFilter->GetBackendType() != FILTER_BACKEND_RECORDING) { + gfxWarning() << "Non recording filter node used with recording DrawTarget!"; + } else { + finalNode = static_cast<FilterNodeRecording*>(aFilter)->mFinalFilterNode; + } + + mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aFilter)); + mFinalFilterNode->SetInput(aIndex, finalNode); + } + + +#define FORWARD_SET_ATTRIBUTE(type, argtype) \ + virtual void SetAttribute(uint32_t aIndex, type aValue) override { \ + mRecorder->RecordEvent(RecordedFilterNodeSetAttribute(this, aIndex, aValue, RecordedFilterNodeSetAttribute::ARGTYPE_##argtype)); \ + mFinalFilterNode->SetAttribute(aIndex, aValue); \ + } + + FORWARD_SET_ATTRIBUTE(bool, BOOL); + FORWARD_SET_ATTRIBUTE(uint32_t, UINT32); + FORWARD_SET_ATTRIBUTE(Float, FLOAT); + FORWARD_SET_ATTRIBUTE(const Size&, SIZE); + FORWARD_SET_ATTRIBUTE(const IntSize&, INTSIZE); + FORWARD_SET_ATTRIBUTE(const IntPoint&, INTPOINT); + FORWARD_SET_ATTRIBUTE(const Rect&, RECT); + FORWARD_SET_ATTRIBUTE(const IntRect&, INTRECT); + FORWARD_SET_ATTRIBUTE(const Point&, POINT); + FORWARD_SET_ATTRIBUTE(const Matrix5x4&, MATRIX5X4); + FORWARD_SET_ATTRIBUTE(const Point3D&, POINT3D); + FORWARD_SET_ATTRIBUTE(const Color&, COLOR); + +#undef FORWARD_SET_ATTRIBUTE + + virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override { + mRecorder->RecordEvent(RecordedFilterNodeSetAttribute(this, aIndex, aFloat, aSize)); + mFinalFilterNode->SetAttribute(aIndex, aFloat, aSize); + } + + virtual FilterBackend GetBackendType() override { return FILTER_BACKEND_RECORDING; } + + RefPtr<FilterNode> mFinalFilterNode; + RefPtr<DrawEventRecorderPrivate> mRecorder; +}; + +static FilterNode* +GetFilterNode(FilterNode* aNode) +{ + if (aNode->GetBackendType() != FILTER_BACKEND_RECORDING) { + gfxWarning() << "Non recording filter node used with recording DrawTarget!"; + return aNode; + } + + return static_cast<FilterNodeRecording*>(aNode)->mFinalFilterNode; +} + struct AdjustedPattern { - AdjustedPattern(const Pattern &aPattern) - : mPattern(NULL) + explicit AdjustedPattern(const Pattern &aPattern) + : mPattern(nullptr) { mOrigPattern = const_cast<Pattern*>(&aPattern); } @@ -91,9 +172,9 @@ struct AdjustedPattern operator Pattern*() { switch(mOrigPattern->GetType()) { - case PATTERN_COLOR: + case PatternType::COLOR: return mOrigPattern; - case PATTERN_SURFACE: + case PatternType::SURFACE: { SurfacePattern *surfPat = static_cast<SurfacePattern*>(mOrigPattern); mPattern = @@ -102,7 +183,7 @@ struct AdjustedPattern surfPat->mFilter); return mPattern; } - case PATTERN_LINEAR_GRADIENT: + case PatternType::LINEAR_GRADIENT: { LinearGradientPattern *linGradPat = static_cast<LinearGradientPattern*>(mOrigPattern); mPattern = @@ -111,7 +192,7 @@ struct AdjustedPattern linGradPat->mMatrix); return mPattern; } - case PATTERN_RADIAL_GRADIENT: + case PatternType::RADIAL_GRADIENT: { RadialGradientPattern *radGradPat = static_cast<RadialGradientPattern*>(mOrigPattern); mPattern = @@ -139,11 +220,16 @@ struct AdjustedPattern Pattern *mPattern; }; -DrawTargetRecording::DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT) +DrawTargetRecording::DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT, bool aHasData) : mRecorder(static_cast<DrawEventRecorderPrivate*>(aRecorder)) , mFinalDT(aDT) { - mRecorder->RecordEvent(RecordedDrawTargetCreation(this, mFinalDT->GetType(), mFinalDT->GetSize(), mFinalDT->GetFormat())); + RefPtr<SourceSurface> snapshot = aHasData ? mFinalDT->Snapshot() : nullptr; + mRecorder->RecordEvent(RecordedDrawTargetCreation(this, + mFinalDT->GetBackendType(), + mFinalDT->GetSize(), + mFinalDT->GetFormat(), + aHasData, snapshot)); mFormat = mFinalDT->GetFormat(); } @@ -185,8 +271,8 @@ DrawTargetRecording::StrokeLine(const Point &aBegin, Path* DrawTargetRecording::GetPathForPathRecording(const Path *aPath) const { - if (aPath->GetBackendType() != BACKEND_RECORDING) { - return NULL; + if (aPath->GetBackendType() != BackendType::RECORDING) { + return nullptr; } return static_cast<const PathRecording*>(aPath)->mPath; @@ -214,7 +300,10 @@ void RecordingFontUserDataDestroyFunc(void *aUserData) RecordingFontUserData *userData = static_cast<RecordingFontUserData*>(aUserData); + // TODO support font in b2g recordings +#ifndef MOZ_WIDGET_GONK userData->recorder->RecordEvent(RecordedScaledFontDestruction(userData->refPtr)); +#endif delete userData; } @@ -227,7 +316,10 @@ DrawTargetRecording::FillGlyphs(ScaledFont *aFont, const GlyphRenderingOptions *aRenderingOptions) { if (!aFont->GetUserData(reinterpret_cast<UserDataKey*>(mRecorder.get()))) { + // TODO support font in b2g recordings +#ifndef MOZ_WIDGET_GONK mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, aFont)); +#endif RecordingFontUserData *userData = new RecordingFontUserData; userData->refPtr = aFont; userData->recorder = mRecorder; @@ -235,7 +327,10 @@ DrawTargetRecording::FillGlyphs(ScaledFont *aFont, &RecordingFontUserDataDestroyFunc); } + // TODO support font in b2g recordings +#ifndef MOZ_WIDGET_GONK mRecorder->RecordEvent(RecordedFillGlyphs(this, aFont, aPattern, aOptions, aBuffer.mGlyphs, aBuffer.mNumGlyphs)); +#endif mFinalDT->FillGlyphs(aFont, aBuffer, aPattern, aOptions, aRenderingOptions); } @@ -279,7 +374,7 @@ DrawTargetRecording::Snapshot() mRecorder->RecordEvent(RecordedSnapshot(retSurf, this)); - return retSurf; + return retSurf.forget(); } void @@ -306,6 +401,28 @@ DrawTargetRecording::DrawSurfaceWithShadow(SourceSurface *aSurface, } void +DrawTargetRecording::DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions) +{ + mRecorder->RecordEvent(RecordedDrawFilter(this, aNode, aSourceRect, aDestPoint, aOptions)); + mFinalDT->DrawFilter(GetFilterNode(aNode), aSourceRect, aDestPoint, aOptions); +} + +TemporaryRef<FilterNode> +DrawTargetRecording::CreateFilter(FilterType aType) +{ + RefPtr<FilterNode> node = mFinalDT->CreateFilter(aType); + + RefPtr<FilterNode> retNode = new FilterNodeRecording(node, mRecorder); + + mRecorder->RecordEvent(RecordedFilterNodeCreation(retNode, aType)); + + return retNode.forget(); +} + +void DrawTargetRecording::ClearRect(const Rect &aRect) { mRecorder->RecordEvent(RecordedClearRect(this, aRect)); @@ -356,7 +473,7 @@ DrawTargetRecording::CreateSourceSurfaceFromData(unsigned char *aData, mRecorder->RecordEvent(RecordedSourceSurfaceCreation(retSurf, aData, aStride, aSize, aFormat)); - return retSurf; + return retSurf.forget(); } TemporaryRef<SourceSurface> @@ -389,7 +506,7 @@ DrawTargetRecording::OptimizeSourceSurface(SourceSurface *aSurface) const dataSurf->GetSize(), dataSurf->GetFormat())); } - return retSurf; + return retSurf.forget(); } TemporaryRef<SourceSurface> @@ -417,17 +534,14 @@ DrawTargetRecording::CreateSourceSurfaceFromNativeSurface(const NativeSurface &a dataSurf->GetSize(), dataSurf->GetFormat())); } - return retSurf; + return retSurf.forget(); } TemporaryRef<DrawTarget> DrawTargetRecording::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const { RefPtr<DrawTarget> dt = mFinalDT->CreateSimilarDrawTarget(aSize, aFormat); - - RefPtr<DrawTarget> retDT = new DrawTargetRecording(mRecorder.get(), dt); - - return retDT; + return new DrawTargetRecording(mRecorder.get(), dt); } TemporaryRef<PathBuilder> @@ -448,7 +562,7 @@ DrawTargetRecording::CreateGradientStops(GradientStop *aStops, mRecorder->RecordEvent(RecordedGradientStopsCreation(retStops, aStops, aNumStops, aExtendMode)); - return retStops; + return retStops.forget(); } void @@ -463,7 +577,7 @@ void DrawTargetRecording::EnsureStored(const Path *aPath) { if (!mRecorder->HasStoredPath(aPath)) { - if (aPath->GetBackendType() != BACKEND_RECORDING) { + if (aPath->GetBackendType() != BackendType::RECORDING) { gfxWarning() << "Cannot record this fill path properly!"; } else { PathRecording *recPath = const_cast<PathRecording*>(static_cast<const PathRecording*>(aPath)); diff --git a/gfx/2d/DrawTargetRecording.h b/gfx/2d/DrawTargetRecording.h index f1e7da428..ba89769c2 100644 --- a/gfx/2d/DrawTargetRecording.h +++ b/gfx/2d/DrawTargetRecording.h @@ -15,20 +15,22 @@ namespace gfx { class DrawTargetRecording : public DrawTarget { public: - DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT); + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetRecording, override) + DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT, bool aHasData = false); ~DrawTargetRecording(); - virtual BackendType GetType() const { return mFinalDT->GetType(); } + virtual DrawTargetType GetType() const override { return mFinalDT->GetType(); } + virtual BackendType GetBackendType() const override { return mFinalDT->GetBackendType(); } - virtual TemporaryRef<SourceSurface> Snapshot(); + virtual TemporaryRef<SourceSurface> Snapshot() override; - virtual IntSize GetSize() { return mFinalDT->GetSize(); } + virtual IntSize GetSize() override { return mFinalDT->GetSize(); } /* Ensure that the DrawTarget backend has flushed all drawing operations to * this draw target. This must be called before using the backing surface of * this draw target outside of GFX 2D code. */ - virtual void Flush() { mFinalDT->Flush(); } + virtual void Flush() override { mFinalDT->Flush(); } /* * Draw a surface to the draw target. Possibly doing partial drawing or @@ -45,7 +47,12 @@ public: const Rect &aDest, const Rect &aSource, const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; + + virtual void DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions = DrawOptions()) override; /* * Blend a surface to the draw target with a shadow. The shadow is drawn as a @@ -66,7 +73,7 @@ public: const Color &aColor, const Point &aOffset, Float aSigma, - CompositionOp aOperator); + CompositionOp aOperator) override; /* * Clear a rectangle on the draw target to transparent black. This will @@ -74,7 +81,7 @@ public: * * aRect Rectangle to clear */ - virtual void ClearRect(const Rect &aRect); + virtual void ClearRect(const Rect &aRect) override; /* * This is essentially a 'memcpy' between two surfaces. It moves a pixel @@ -87,7 +94,7 @@ public: */ virtual void CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect, - const IntPoint &aDestination); + const IntPoint &aDestination) override; /* * Fill a rectangle on the DrawTarget with a certain source pattern. @@ -98,7 +105,7 @@ public: */ virtual void FillRect(const Rect &aRect, const Pattern &aPattern, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; /* * Stroke a rectangle on the DrawTarget with a certain source pattern. @@ -110,7 +117,7 @@ public: virtual void StrokeRect(const Rect &aRect, const Pattern &aPattern, const StrokeOptions &aStrokeOptions = StrokeOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; /* * Stroke a line on the DrawTarget with a certain source pattern. @@ -124,7 +131,7 @@ public: const Point &aEnd, const Pattern &aPattern, const StrokeOptions &aStrokeOptions = StrokeOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; /* * Stroke a path on the draw target with a certain source pattern. @@ -137,7 +144,7 @@ public: virtual void Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions = StrokeOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; /* * Fill a path on the draw target with a certain source pattern. @@ -148,7 +155,7 @@ public: */ virtual void Fill(const Path *aPath, const Pattern &aPattern, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; /* * Fill a series of clyphs on the draw target with a certain source pattern. @@ -157,7 +164,7 @@ public: const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aOptions = DrawOptions(), - const GlyphRenderingOptions *aRenderingOptions = NULL); + const GlyphRenderingOptions *aRenderingOptions = nullptr) override; /* * This takes a source pattern and a mask, and composites the source pattern @@ -170,19 +177,19 @@ public: */ virtual void Mask(const Pattern &aSource, const Pattern &aMask, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void MaskSurface(const Pattern &aSource, SourceSurface *aMask, Point aOffset, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; /* * Push a clip to the DrawTarget. * * aPath The path to clip to */ - virtual void PushClip(const Path *aPath); + virtual void PushClip(const Path *aPath) override; /* * Push an axis-aligned rectangular clip to the DrawTarget. This rectangle @@ -190,12 +197,12 @@ public: * * aRect The rect to clip to */ - virtual void PushClipRect(const Rect &aRect); + virtual void PushClipRect(const Rect &aRect) override; /* Pop a clip from the DrawTarget. A pop without a corresponding push will * be ignored. */ - virtual void PopClip(); + virtual void PopClip() override; /* * Create a SourceSurface optimized for use with this DrawTarget from @@ -206,14 +213,14 @@ public: virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, int32_t aStride, - SurfaceFormat aFormat) const; + SurfaceFormat aFormat) const override; /* * Create a SourceSurface optimized for use with this DrawTarget from * an arbitrary other SourceSurface. This may return aSourceSurface or some * other existing surface. */ - virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const; + virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override; /* * Create a SourceSurface for a type of NativeSurface. This may fail if the @@ -221,13 +228,13 @@ public: * in. */ virtual TemporaryRef<SourceSurface> - CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const; + CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override; /* * Create a DrawTarget whose snapshot is optimized for use with this DrawTarget. */ virtual TemporaryRef<DrawTarget> - CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const; + CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override; /* * Create a path builder with the specified fillmode. @@ -236,7 +243,7 @@ public: * ID2D1SimplifiedGeometrySink requires the fill mode * to be set before calling BeginFigure(). */ - virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const; + virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override; /* * Create a GradientStops object that holds information about a set of @@ -251,18 +258,20 @@ public: virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, - ExtendMode aExtendMode = EXTEND_CLAMP) const; + ExtendMode aExtendMode = ExtendMode::CLAMP) const override; + + virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override; /* * Set a transform on the surface, this transform is applied at drawing time * to both the mask and source of the operation. */ - virtual void SetTransform(const Matrix &aTransform); + virtual void SetTransform(const Matrix &aTransform) override; /* Tries to get a native surface for a DrawTarget, this may fail if the * draw target cannot convert to this surface type. */ - virtual void *GetNativeSurface(NativeSurfaceType aType) { return mFinalDT->GetNativeSurface(aType); } + virtual void *GetNativeSurface(NativeSurfaceType aType) override { return mFinalDT->GetNativeSurface(aType); } private: Path *GetPathForPathRecording(const Path *aPath) const; diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp index 005ba2b6d..837cf9922 100644 --- a/gfx/2d/DrawTargetSkia.cpp +++ b/gfx/2d/DrawTargetSkia.cpp @@ -4,12 +4,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DrawTargetSkia.h" +#include "SourceSurfaceCairo.h" #include "SourceSurfaceSkia.h" #include "ScaledFontBase.h" -#include "skia/SkDevice.h" +#include "ScaledFontCairo.h" +#include "skia/SkGpuDevice.h" +#include "skia/SkBitmapDevice.h" +#include "FilterNodeSoftware.h" #ifdef USE_SKIA_GPU #include "skia/SkGpuDevice.h" +#include "skia/GrGLInterface.h" #endif #include "skia/SkTypeface.h" @@ -17,26 +22,22 @@ #include "skia/SkBlurDrawLooper.h" #include "skia/SkBlurMaskFilter.h" #include "skia/SkColorFilter.h" +#include "skia/SkDropShadowImageFilter.h" #include "skia/SkLayerRasterizer.h" #include "skia/SkLayerDrawLooper.h" #include "skia/SkDashPathEffect.h" #include "Logging.h" -#include "HelpersSkia.h" #include "Tools.h" +#include "DataSurfaceHelpers.h" #include <algorithm> -#ifdef ANDROID -# define USE_SOFT_CLIPPING false -#else -# define USE_SOFT_CLIPPING true -#endif - namespace mozilla { namespace gfx { class GradientStopsSkia : public GradientStops { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsSkia) GradientStopsSkia(const std::vector<GradientStop>& aStops, uint32_t aNumStops, ExtendMode aExtendMode) : mCount(aNumStops) , mExtendMode(aExtendMode) @@ -71,7 +72,7 @@ public: } } - BackendType GetBackendType() const { return BACKEND_SKIA; } + BackendType GetBackendType() const { return BackendType::SKIA; } std::vector<SkColor> mColors; std::vector<SkScalar> mPositions; @@ -79,43 +80,85 @@ public: ExtendMode mExtendMode; }; +/** + * When constructing a temporary SkBitmap via GetBitmapForSurface, we may also + * have to construct a temporary DataSourceSurface, which must live as long as + * the SkBitmap. So we return a pair of the SkBitmap and the (optional) + * temporary surface. + */ +struct TempBitmap +{ + SkBitmap mBitmap; + RefPtr<SourceSurface> mTmpSurface; +}; + +static TempBitmap +GetBitmapForSurface(SourceSurface* aSurface) +{ + TempBitmap result; + + if (aSurface->GetType() == SurfaceType::SKIA) { + result.mBitmap = static_cast<SourceSurfaceSkia*>(aSurface)->GetBitmap(); + return result; + } + + RefPtr<DataSourceSurface> surf = aSurface->GetDataSurface(); + if (!surf) { + MOZ_CRASH("Non-skia SourceSurfaces need to be DataSourceSurfaces"); + } + + SkAlphaType alphaType = (surf->GetFormat() == SurfaceFormat::B8G8R8X8) ? + kOpaque_SkAlphaType : kPremul_SkAlphaType; + + SkImageInfo info = SkImageInfo::Make(surf->GetSize().width, + surf->GetSize().height, + GfxFormatToSkiaColorType(surf->GetFormat()), + alphaType); + result.mBitmap.setInfo(info, surf->Stride()); + + result.mBitmap.setPixels(surf->GetData()); + result.mTmpSurface = surf.forget(); + return result; +} + DrawTargetSkia::DrawTargetSkia() + : +#ifdef USE_SKIA_GPU + mTexture(0), +#endif + mSnapshot(nullptr) { } DrawTargetSkia::~DrawTargetSkia() { - if (mSnapshots.size()) { - for (std::vector<SourceSurfaceSkia*>::iterator iter = mSnapshots.begin(); - iter != mSnapshots.end(); iter++) { - (*iter)->DrawTargetDestroyed(); - } - // All snapshots will now have copied data. - mSnapshots.clear(); - } } TemporaryRef<SourceSurface> DrawTargetSkia::Snapshot() { - RefPtr<SourceSurfaceSkia> source = new SourceSurfaceSkia(); - - if (!source->InitFromCanvas(mCanvas.get(), mFormat, this)) - return nullptr; + RefPtr<SourceSurfaceSkia> snapshot = mSnapshot; + if (!snapshot) { + snapshot = new SourceSurfaceSkia(); + mSnapshot = snapshot; + if (!snapshot->InitFromCanvas(mCanvas.get(), mFormat, this)) + return nullptr; + } - AppendSnapshot(source); - return source; + return snapshot.forget(); } -void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0) +static void +SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap, + Float aAlpha = 1.0) { switch (aPattern.GetType()) { - case PATTERN_COLOR: { + case PatternType::COLOR: { Color color = static_cast<const ColorPattern&>(aPattern).mColor; aPaint.setColor(ColorToSkColor(color, aAlpha)); break; } - case PATTERN_LINEAR_GRADIENT: { + case PatternType::LINEAR_GRADIENT: { const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern); GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get()); SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode); @@ -125,17 +168,18 @@ void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1. points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x), SkFloatToScalar(pat.mBegin.y)); points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x), SkFloatToScalar(pat.mEnd.y)); - SkShader* shader = SkGradientShader::CreateLinear(points, - &stops->mColors.front(), - &stops->mPositions.front(), - stops->mCount, + SkShader* shader = SkGradientShader::CreateLinear(points, + &stops->mColors.front(), + &stops->mPositions.front(), + stops->mCount, mode); if (shader) { SkMatrix mat; GfxMatrixToSkiaMatrix(pat.mMatrix, mat); - shader->setLocalMatrix(mat); - SkSafeUnref(aPaint.setShader(shader)); + SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat); + SkSafeUnref(shader); + SkSafeUnref(aPaint.setShader(matrixShader)); } } else { @@ -143,7 +187,7 @@ void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1. } break; } - case PATTERN_RADIAL_GRADIENT: { + case PatternType::RADIAL_GRADIENT: { const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern); GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get()); SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode); @@ -153,19 +197,20 @@ void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1. points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x), SkFloatToScalar(pat.mCenter1.y)); points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x), SkFloatToScalar(pat.mCenter2.y)); - SkShader* shader = SkGradientShader::CreateTwoPointConical(points[0], + SkShader* shader = SkGradientShader::CreateTwoPointConical(points[0], SkFloatToScalar(pat.mRadius1), - points[1], + points[1], SkFloatToScalar(pat.mRadius2), - &stops->mColors.front(), - &stops->mPositions.front(), - stops->mCount, + &stops->mColors.front(), + &stops->mPositions.front(), + stops->mCount, mode); if (shader) { SkMatrix mat; GfxMatrixToSkiaMatrix(pat.mMatrix, mat); - shader->setLocalMatrix(mat); - SkSafeUnref(aPaint.setShader(shader)); + SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat); + SkSafeUnref(shader); + SkSafeUnref(aPaint.setShader(matrixShader)); } } else { @@ -173,36 +218,53 @@ void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1. } break; } - case PATTERN_SURFACE: { + case PatternType::SURFACE: { const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); - const SkBitmap& bitmap = static_cast<SourceSurfaceSkia*>(pat.mSurface.get())->GetBitmap(); + aTmpBitmap = GetBitmapForSurface(pat.mSurface); + SkBitmap& bitmap = aTmpBitmap.mBitmap; - SkShader::TileMode mode = ExtendModeToTileMode(pat.mExtendMode); - SkShader* shader = SkShader::CreateBitmapShader(bitmap, mode, mode); SkMatrix mat; GfxMatrixToSkiaMatrix(pat.mMatrix, mat); - shader->setLocalMatrix(mat); - SkSafeUnref(aPaint.setShader(shader)); - if (pat.mFilter == FILTER_POINT) { - aPaint.setFilterBitmap(false); + + if (!pat.mSamplingRect.IsEmpty()) { + SkIRect rect = IntRectToSkIRect(pat.mSamplingRect); + bitmap.extractSubset(&bitmap, rect); + mat.preTranslate(rect.x(), rect.y()); + } + + SkShader::TileMode mode = ExtendModeToTileMode(pat.mExtendMode); + SkShader* shader = SkShader::CreateBitmapShader(bitmap, mode, mode); + SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat); + SkSafeUnref(shader); + SkSafeUnref(aPaint.setShader(matrixShader)); + if (pat.mFilter == Filter::POINT) { + aPaint.setFilterLevel(SkPaint::kNone_FilterLevel); } break; } } } +static inline Rect +GetClipBounds(SkCanvas *aCanvas) +{ + SkRect clipBounds; + aCanvas->getClipBounds(&clipBounds); + return SkRectToRect(clipBounds); +} + struct AutoPaintSetup { - AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Pattern& aPattern) + AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Pattern& aPattern, const Rect* aMaskBounds = nullptr) : mNeedsRestore(false), mAlpha(1.0) { - Init(aCanvas, aOptions); - SetPaintPattern(mPaint, aPattern, mAlpha); + Init(aCanvas, aOptions, aMaskBounds); + SetPaintPattern(mPaint, aPattern, mTmpBitmap, mAlpha); } - AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions) + AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr) : mNeedsRestore(false), mAlpha(1.0) { - Init(aCanvas, aOptions); + Init(aCanvas, aOptions, aMaskBounds); } ~AutoPaintSetup() @@ -212,40 +274,43 @@ struct AutoPaintSetup { } } - void Init(SkCanvas *aCanvas, const DrawOptions& aOptions) + void Init(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds) { mPaint.setXfermodeMode(GfxOpToSkiaOp(aOptions.mCompositionOp)); mCanvas = aCanvas; //TODO: Can we set greyscale somehow? - if (aOptions.mAntialiasMode != AA_NONE) { + if (aOptions.mAntialiasMode != AntialiasMode::NONE) { mPaint.setAntiAlias(true); } else { mPaint.setAntiAlias(false); } - MOZ_ASSERT(aOptions.mSnapping == SNAP_NONE, "Pixel snapping not supported yet!"); - + Rect clipBounds = GetClipBounds(aCanvas); + bool needsGroup = !IsOperatorBoundByMask(aOptions.mCompositionOp) && + (!aMaskBounds || !aMaskBounds->Contains(clipBounds)); + // TODO: We could skip the temporary for operator_source and just // clear the clip rect. The other operators would be harder // but could be worth it to skip pushing a group. - if (!IsOperatorBoundByMask(aOptions.mCompositionOp)) { + if (needsGroup) { mPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode); SkPaint temp; temp.setXfermodeMode(GfxOpToSkiaOp(aOptions.mCompositionOp)); - temp.setAlpha(U8CPU(aOptions.mAlpha*255)); + temp.setAlpha(ColorFloatToByte(aOptions.mAlpha)); //TODO: Get a rect here mCanvas->saveLayer(nullptr, &temp); mNeedsRestore = true; } else { - mPaint.setAlpha(U8CPU(aOptions.mAlpha*255.0)); + mPaint.setAlpha(ColorFloatToByte(aOptions.mAlpha)); mAlpha = aOptions.mAlpha; } - mPaint.setFilterBitmap(true); + mPaint.setFilterLevel(SkPaint::kLow_FilterLevel); } // TODO: Maybe add an operator overload to access this easier? SkPaint mPaint; + TempBitmap mTmpBitmap; bool mNeedsRestore; SkCanvas* mCanvas; Float mAlpha; @@ -264,8 +329,15 @@ DrawTargetSkia::DrawSurface(SourceSurface *aSurface, const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions) { - if (aSurface->GetType() != SURFACE_SKIA) { - return; + RefPtr<SourceSurface> dataSurface; + + if (!(aSurface->GetType() == SurfaceType::SKIA || aSurface->GetType() == SurfaceType::DATA)) { + dataSurface = aSurface->GetDataSurface(); + if (!dataSurface) { + gfxDebug() << *this << ": DrawSurface() can't draw surface"; + return; + } + aSurface = dataSurface.get(); } if (aSource.IsEmpty()) { @@ -277,19 +349,35 @@ DrawTargetSkia::DrawSurface(SourceSurface *aSurface, SkRect destRect = RectToSkRect(aDest); SkRect sourceRect = RectToSkRect(aSource); - SkMatrix matrix; - matrix.setRectToRect(sourceRect, destRect, SkMatrix::kFill_ScaleToFit); - - const SkBitmap& bitmap = static_cast<SourceSurfaceSkia*>(aSurface)->GetBitmap(); - - AutoPaintSetup paint(mCanvas.get(), aOptions); - SkShader *shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); - shader->setLocalMatrix(matrix); - SkSafeUnref(paint.mPaint.setShader(shader)); - if (aSurfOptions.mFilter != FILTER_LINEAR) { - paint.mPaint.setFilterBitmap(false); + TempBitmap bitmap = GetBitmapForSurface(aSurface); + + AutoPaintSetup paint(mCanvas.get(), aOptions, &aDest); + if (aSurfOptions.mFilter == Filter::POINT) { + paint.mPaint.setFilterLevel(SkPaint::kNone_FilterLevel); + } + + mCanvas->drawBitmapRectToRect(bitmap.mBitmap, &sourceRect, destRect, &paint.mPaint); +} + +DrawTargetType +DrawTargetSkia::GetType() const +{ +#ifdef USE_SKIA_GPU + if (mGrContext) { + return DrawTargetType::HARDWARE_RASTER; } - mCanvas->drawRect(destRect, paint.mPaint); +#endif + return DrawTargetType::SOFTWARE_RASTER; +} + +void +DrawTargetSkia::DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions) +{ + FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode); + filter->Draw(this, aSourceRect, aDestPoint, aOptions); } void @@ -300,59 +388,27 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface, Float aSigma, CompositionOp aOperator) { + if (!(aSurface->GetType() == SurfaceType::SKIA || aSurface->GetType() == SurfaceType::DATA)) { + return; + } + MarkChanged(); - mCanvas->save(SkCanvas::kMatrix_SaveFlag); + + mCanvas->save(); mCanvas->resetMatrix(); - uint32_t blurFlags = SkBlurMaskFilter::kHighQuality_BlurFlag | - SkBlurMaskFilter::kIgnoreTransform_BlurFlag; - const SkBitmap& bitmap = static_cast<SourceSurfaceSkia*>(aSurface)->GetBitmap(); - SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); - SkMatrix matrix; - matrix.reset(); - matrix.setTranslateX(SkFloatToScalar(aDest.x)); - matrix.setTranslateY(SkFloatToScalar(aDest.y)); - shader->setLocalMatrix(matrix); - SkLayerDrawLooper* dl = new SkLayerDrawLooper; - SkLayerDrawLooper::LayerInfo info; - info.fPaintBits |= SkLayerDrawLooper::kShader_Bit; - SkPaint *layerPaint = dl->addLayer(info); - layerPaint->setShader(shader); - - info.fPaintBits = 0; - info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit; - info.fPaintBits |= SkLayerDrawLooper::kColorFilter_Bit; - info.fColorMode = SkXfermode::kDst_Mode; - info.fOffset.set(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y)); - info.fPostTranslate = true; - - SkMaskFilter* mf = SkBlurMaskFilter::Create(aSigma, SkBlurMaskFilter::kNormal_BlurStyle, blurFlags); - SkColor color = ColorToSkColor(aColor, 1); - SkColorFilter* cf = SkColorFilter::CreateModeFilter(color, SkXfermode::kSrcIn_Mode); - - - layerPaint = dl->addLayer(info); - SkSafeUnref(layerPaint->setMaskFilter(mf)); - SkSafeUnref(layerPaint->setColorFilter(cf)); - layerPaint->setColor(color); - - // TODO: This is using the rasterizer to calculate an alpha mask - // on both the shadow and normal layers. We should fix this - // properly so it only happens for the shadow layer - SkLayerRasterizer *raster = new SkLayerRasterizer(); - SkPaint maskPaint; - SkSafeUnref(maskPaint.setShader(shader)); - raster->addLayer(maskPaint, 0, 0); - + TempBitmap bitmap = GetBitmapForSurface(aSurface); + SkPaint paint; - paint.setAntiAlias(true); - SkSafeUnref(paint.setRasterizer(raster)); + + SkImageFilter* filter = SkDropShadowImageFilter::Create(aOffset.x, aOffset.y, + aSigma, aSigma, + ColorToSkColor(aColor, 1.0)); + + paint.setImageFilter(filter); paint.setXfermodeMode(GfxOpToSkiaOp(aOperator)); - SkSafeUnref(paint.setLooper(dl)); - SkRect rect = RectToSkRect(Rect(Float(aDest.x), Float(aDest.y), - Float(bitmap.width()), Float(bitmap.height()))); - mCanvas->drawRect(rect, paint); + mCanvas->drawBitmap(bitmap.mBitmap, aDest.x, aDest.y, &paint); mCanvas->restore(); } @@ -363,7 +419,7 @@ DrawTargetSkia::FillRect(const Rect &aRect, { MarkChanged(); SkRect rect = RectToSkRect(aRect); - AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern); + AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern, &aRect); mCanvas->drawRect(rect, paint.mPaint); } @@ -376,7 +432,7 @@ DrawTargetSkia::Stroke(const Path *aPath, { MarkChanged(); MOZ_ASSERT(aPath, "Null path"); - if (aPath->GetBackendType() != BACKEND_SKIA) { + if (aPath->GetBackendType() != BackendType::SKIA) { return; } @@ -406,7 +462,7 @@ DrawTargetSkia::StrokeRect(const Rect &aRect, mCanvas->drawRect(RectToSkRect(aRect), paint.mPaint); } -void +void DrawTargetSkia::StrokeLine(const Point &aStart, const Point &aEnd, const Pattern &aPattern, @@ -419,8 +475,8 @@ DrawTargetSkia::StrokeLine(const Point &aStart, return; } - mCanvas->drawLine(SkFloatToScalar(aStart.x), SkFloatToScalar(aStart.y), - SkFloatToScalar(aEnd.x), SkFloatToScalar(aEnd.y), + mCanvas->drawLine(SkFloatToScalar(aStart.x), SkFloatToScalar(aStart.y), + SkFloatToScalar(aEnd.x), SkFloatToScalar(aEnd.y), paint.mPaint); } @@ -430,7 +486,7 @@ DrawTargetSkia::Fill(const Path *aPath, const DrawOptions &aOptions) { MarkChanged(); - if (aPath->GetBackendType() != BACKEND_SKIA) { + if (aPath->GetBackendType() != BackendType::SKIA) { return; } @@ -446,11 +502,11 @@ DrawTargetSkia::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aOptions, - const GlyphRenderingOptions*) + const GlyphRenderingOptions *aRenderingOptions) { - if (aFont->GetType() != FONT_MAC && - aFont->GetType() != FONT_SKIA && - aFont->GetType() != FONT_GDI) { + if (aFont->GetType() != FontType::MAC && + aFont->GetType() != FontType::SKIA && + aFont->GetType() != FontType::GDI) { return; } @@ -462,7 +518,30 @@ DrawTargetSkia::FillGlyphs(ScaledFont *aFont, paint.mPaint.setTypeface(skiaFont->GetSkTypeface()); paint.mPaint.setTextSize(SkFloatToScalar(skiaFont->mSize)); paint.mPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - + + if (aRenderingOptions && aRenderingOptions->GetType() == FontType::CAIRO) { + switch (static_cast<const GlyphRenderingOptionsCairo*>(aRenderingOptions)->GetHinting()) { + case FontHinting::NONE: + paint.mPaint.setHinting(SkPaint::kNo_Hinting); + break; + case FontHinting::LIGHT: + paint.mPaint.setHinting(SkPaint::kSlight_Hinting); + break; + case FontHinting::NORMAL: + paint.mPaint.setHinting(SkPaint::kNormal_Hinting); + break; + case FontHinting::FULL: + paint.mPaint.setHinting(SkPaint::kFull_Hinting); + break; + } + + if (static_cast<const GlyphRenderingOptionsCairo*>(aRenderingOptions)->GetAutoHinting()) { + paint.mPaint.setAutohinted(true); + } + } else { + paint.mPaint.setHinting(SkPaint::kNormal_Hinting); + } + std::vector<uint16_t> indices; std::vector<SkPoint> offsets; indices.resize(aBuffer.mNumGlyphs); @@ -486,32 +565,55 @@ DrawTargetSkia::Mask(const Pattern &aSource, AutoPaintSetup paint(mCanvas.get(), aOptions, aSource); SkPaint maskPaint; - SetPaintPattern(maskPaint, aMask); - - SkLayerRasterizer *raster = new SkLayerRasterizer(); - raster->addLayer(maskPaint); - SkSafeUnref(paint.mPaint.setRasterizer(raster)); - - // Skia only uses the mask rasterizer when we are drawing a path/rect. - // Take our destination bounds and convert them into user space to use - // as the path to draw. - SkPath path; - path.addRect(SkRect::MakeWH(SkScalar(mSize.width), SkScalar(mSize.height))); - - Matrix temp = mTransform; - temp.Invert(); - SkMatrix mat; - GfxMatrixToSkiaMatrix(temp, mat); - path.transform(mat); + TempBitmap tmpBitmap; + SetPaintPattern(maskPaint, aMask, tmpBitmap); + + SkLayerRasterizer::Builder builder; + builder.addLayer(maskPaint); + SkAutoTUnref<SkRasterizer> raster(builder.detachRasterizer()); + paint.mPaint.setRasterizer(raster.get()); + + mCanvas->drawRect(SkRectCoveringWholeSurface(), paint.mPaint); +} - mCanvas->drawPath(path, paint.mPaint); +void +DrawTargetSkia::MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, + const DrawOptions &aOptions) +{ + MarkChanged(); + AutoPaintSetup paint(mCanvas.get(), aOptions, aSource); + + TempBitmap bitmap = GetBitmapForSurface(aMask); + if (bitmap.mBitmap.colorType() == kAlpha_8_SkColorType) { + mCanvas->drawBitmap(bitmap.mBitmap, aOffset.x, aOffset.y, &paint.mPaint); + } else { + SkPaint maskPaint; + TempBitmap tmpBitmap; + SetPaintPattern(maskPaint, SurfacePattern(aMask, ExtendMode::CLAMP), tmpBitmap); + + SkMatrix transform = maskPaint.getShader()->getLocalMatrix(); + transform.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y)); + SkShader* matrixShader = SkShader::CreateLocalMatrixShader(maskPaint.getShader(), transform); + SkSafeUnref(maskPaint.setShader(matrixShader)); + + SkLayerRasterizer::Builder builder; + builder.addLayer(maskPaint); + SkAutoTUnref<SkRasterizer> raster(builder.detachRasterizer()); + paint.mPaint.setRasterizer(raster.get()); + + IntSize size = aMask->GetSize(); + Rect rect = Rect(aOffset.x, aOffset.y, size.width, size.height); + mCanvas->drawRect(RectToSkRect(rect), paint.mPaint); + } } TemporaryRef<SourceSurface> DrawTargetSkia::CreateSourceSurfaceFromData(unsigned char *aData, - const IntSize &aSize, - int32_t aStride, - SurfaceFormat aFormat) const + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat) const { RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia(); @@ -519,8 +621,8 @@ DrawTargetSkia::CreateSourceSurfaceFromData(unsigned char *aData, gfxDebug() << *this << ": Failure to create source surface from data. Size: " << aSize; return nullptr; } - - return newSurf; + + return newSurf.forget(); } TemporaryRef<DrawTarget> @@ -530,18 +632,72 @@ DrawTargetSkia::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFor if (!target->Init(aSize, aFormat)) { return nullptr; } - return target; + return target.forget(); +} + +bool +DrawTargetSkia::UsingSkiaGPU() const +{ +#ifdef USE_SKIA_GPU + return !!mTexture; +#else + return false; +#endif } TemporaryRef<SourceSurface> DrawTargetSkia::OptimizeSourceSurface(SourceSurface *aSurface) const { - return nullptr; + if (aSurface->GetType() == SurfaceType::SKIA) { + return aSurface; + } + + if (!UsingSkiaGPU()) { + // If we're not using skia-gl then drawing doesn't require any + // uploading, so any data surface is fine. Call GetDataSurface + // to trigger any required readback so that it only happens + // once. + return aSurface->GetDataSurface(); + } + + // If we are using skia-gl then we want to copy into a surface that + // will cache the uploaded gl texture. + RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface(); + DataSourceSurface::MappedSurface map; + if (!dataSurf->Map(DataSourceSurface::READ, &map)) { + return nullptr; + } + + RefPtr<SourceSurface> result = CreateSourceSurfaceFromData(map.mData, + dataSurf->GetSize(), + map.mStride, + dataSurf->GetFormat()); + dataSurf->Unmap(); + return result.forget(); } TemporaryRef<SourceSurface> DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const { + if (aSurface.mType == NativeSurfaceType::CAIRO_SURFACE) { + if (aSurface.mSize.width <= 0 || + aSurface.mSize.height <= 0) { + gfxWarning() << "Can't create a SourceSurface without a valid size"; + return nullptr; + } + cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface); + return new SourceSurfaceCairo(surf, aSurface.mSize, aSurface.mFormat); +#if USE_SKIA_GPU + } else if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE) { + RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia(); + unsigned int texture = (unsigned int)((uintptr_t)aSurface.mSurface); + if (UsingSkiaGPU() && newSurf->InitFromTexture((DrawTargetSkia*)this, texture, aSurface.mSize, aSurface.mFormat)) { + return newSurf; + } + return nullptr; +#endif + } + return nullptr; } @@ -551,23 +707,39 @@ DrawTargetSkia::CopySurface(SourceSurface *aSurface, const IntPoint &aDestination) { //TODO: We could just use writePixels() here if the sourceRect is the entire source - - if (aSurface->GetType() != SURFACE_SKIA) { + + if (aSurface->GetType() != SurfaceType::SKIA && aSurface->GetType() != SurfaceType::DATA) { return; } MarkChanged(); - - const SkBitmap& bitmap = static_cast<SourceSurfaceSkia*>(aSurface)->GetBitmap(); + + TempBitmap bitmap = GetBitmapForSurface(aSurface); + + // This is a fast path that is disabled for now to mimimize risk + if (false && !bitmap.mBitmap.getTexture() && mCanvas->imageInfo() == bitmap.mBitmap.info()) { + SkBitmap bm(bitmap.mBitmap); + bm.lockPixels(); + if (bm.getPixels()) { + SkImageInfo info = bm.info(); + info.fWidth = aSourceRect.width; + info.fHeight = aSourceRect.height; + uint8_t* pixels = static_cast<uint8_t*>(bm.getPixels()); + // adjust pixels for the source offset + pixels += aSourceRect.x + aSourceRect.y*bm.rowBytes(); + mCanvas->writePixels(info, pixels, bm.rowBytes(), aDestination.x, aDestination.y); + return; + } + } mCanvas->save(); mCanvas->resetMatrix(); - SkRect dest = IntRectToSkRect(IntRect(aDestination.x, aDestination.y, aSourceRect.width, aSourceRect.height)); + SkRect dest = IntRectToSkRect(IntRect(aDestination.x, aDestination.y, aSourceRect.width, aSourceRect.height)); SkIRect source = IntRectToSkIRect(aSourceRect); mCanvas->clipRect(dest, SkRegion::kReplace_Op); SkPaint paint; - if (mCanvas->getDevice()->config() == SkBitmap::kRGB_565_Config) { + if (mCanvas->imageInfo().colorType() == kRGB_565_SkColorType) { // Set the xfermode to SOURCE_OVER to workaround // http://code.google.com/p/skia/issues/detail?id=628 // RGB565 is opaque so they're equivalent anyway @@ -575,15 +747,33 @@ DrawTargetSkia::CopySurface(SourceSurface *aSurface, } else { paint.setXfermodeMode(SkXfermode::kSrc_Mode); } - - mCanvas->drawBitmapRect(bitmap, &source, dest, &paint); + // drawBitmapRect with A8 bitmaps ends up doing a mask operation + // so we need to clear before + if (bitmap.mBitmap.colorType() == kAlpha_8_SkColorType) { + SkPaint clearPaint; + clearPaint.setColor(SkColorSetARGB(0, 0, 0, 0)); + clearPaint.setXfermodeMode(SkXfermode::kSrc_Mode); + mCanvas->drawPaint(clearPaint); + } + mCanvas->drawBitmapRect(bitmap.mBitmap, &source, dest, &paint); mCanvas->restore(); } bool DrawTargetSkia::Init(const IntSize &aSize, SurfaceFormat aFormat) { - SkAutoTUnref<SkDevice> device(new SkDevice(GfxFormatToSkiaConfig(aFormat), aSize.width, aSize.height)); + SkAlphaType alphaType = (aFormat == SurfaceFormat::B8G8R8X8) ? + kOpaque_SkAlphaType : kPremul_SkAlphaType; + + SkImageInfo skiInfo = SkImageInfo::Make( + aSize.width, aSize.height, + GfxFormatToSkiaColorType(aFormat), + alphaType); + + SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(skiInfo)); + if (!device) { + return false; + } SkBitmap bitmap = device->accessBitmap(true); if (!bitmap.allocPixels()) { @@ -592,60 +782,70 @@ DrawTargetSkia::Init(const IntSize &aSize, SurfaceFormat aFormat) bitmap.eraseARGB(0, 0, 0, 0); - SkAutoTUnref<SkCanvas> canvas(new SkCanvas(device.get())); + mCanvas.adopt(new SkCanvas(device.get())); mSize = aSize; - mCanvas = canvas.get(); mFormat = aFormat; return true; } #ifdef USE_SKIA_GPU -void -DrawTargetSkia::InitWithFBO(unsigned int aFBOID, GrContext* aGrContext, const IntSize &aSize, SurfaceFormat aFormat) +bool +DrawTargetSkia::InitWithGrContext(GrContext* aGrContext, + const IntSize &aSize, + SurfaceFormat aFormat) { - GrPlatformRenderTargetDesc targetDescriptor; + MOZ_ASSERT(aGrContext, "null GrContext"); - targetDescriptor.fWidth = aSize.width; - targetDescriptor.fHeight = aSize.height; - targetDescriptor.fConfig = GfxFormatToGrConfig(aFormat); + mGrContext = aGrContext; + mSize = aSize; + mFormat = aFormat; + + GrTextureDesc targetDescriptor; + + targetDescriptor.fFlags = kRenderTarget_GrTextureFlagBit; + targetDescriptor.fWidth = mSize.width; + targetDescriptor.fHeight = mSize.height; + targetDescriptor.fConfig = GfxFormatToGrConfig(mFormat); + targetDescriptor.fOrigin = kBottomLeft_GrSurfaceOrigin; targetDescriptor.fSampleCnt = 0; - targetDescriptor.fRenderTargetHandle = aFBOID; - SkAutoTUnref<GrRenderTarget> target(aGrContext->createPlatformRenderTarget(targetDescriptor)); + SkAutoTUnref<GrTexture> skiaTexture(mGrContext->createUncachedTexture(targetDescriptor, NULL, 0)); + if (!skiaTexture) { + return false; + } - SkAutoTUnref<SkDevice> device(new SkGpuDevice(aGrContext, target.get())); - SkAutoTUnref<SkCanvas> canvas(new SkCanvas(device.get())); - mSize = aSize; + mTexture = (uint32_t)skiaTexture->getTextureHandle(); - mCanvas = canvas.get(); - mFormat = aFormat; + SkAutoTUnref<SkBaseDevice> device(new SkGpuDevice(mGrContext.get(), skiaTexture->asRenderTarget())); + mCanvas.adopt(new SkCanvas(device.get())); + + return true; } + #endif void DrawTargetSkia::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat) { - bool isOpaque = false; - if (aFormat == FORMAT_B8G8R8X8) { + SkAlphaType alphaType = kPremul_SkAlphaType; + if (aFormat == SurfaceFormat::B8G8R8X8) { // We have to manually set the A channel to be 255 as Skia doesn't understand BGRX ConvertBGRXToBGRA(aData, aSize, aStride); - isOpaque = true; + alphaType = kOpaque_SkAlphaType; } - - SkAutoTUnref<SkDevice> device(new SkDevice(GfxFormatToSkiaConfig(aFormat), aSize.width, aSize.height, isOpaque)); - SkBitmap bitmap = (SkBitmap)device->accessBitmap(true); - bitmap.lockPixels(); + SkBitmap bitmap; + + SkImageInfo info = SkImageInfo::Make(aSize.width, + aSize.height, + GfxFormatToSkiaColorType(aFormat), + alphaType); + bitmap.setInfo(info, aStride); bitmap.setPixels(aData); - bitmap.setConfig(GfxFormatToSkiaConfig(aFormat), aSize.width, aSize.height, aStride); - bitmap.unlockPixels(); - bitmap.notifyPixelsChanged(); + mCanvas.adopt(new SkCanvas(bitmap)); - SkAutoTUnref<SkCanvas> canvas(new SkCanvas(device.get())); mSize = aSize; - - mCanvas = canvas.get(); mFormat = aFormat; } @@ -658,11 +858,22 @@ DrawTargetSkia::SetTransform(const Matrix& aTransform) mTransform = aTransform; } -TemporaryRef<PathBuilder> +void* +DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType) +{ +#ifdef USE_SKIA_GPU + if (aType == NativeSurfaceType::OPENGL_TEXTURE) { + return (void*)((uintptr_t)mTexture); + } +#endif + return nullptr; +} + + +TemporaryRef<PathBuilder> DrawTargetSkia::CreatePathBuilder(FillRule aFillRule) const { - RefPtr<PathBuilderSkia> pb = new PathBuilderSkia(aFillRule); - return pb; + return new PathBuilderSkia(aFillRule); } void @@ -671,7 +882,7 @@ DrawTargetSkia::ClearRect(const Rect &aRect) MarkChanged(); SkPaint paint; mCanvas->save(); - mCanvas->clipRect(RectToSkRect(aRect), SkRegion::kIntersect_Op, USE_SOFT_CLIPPING); + mCanvas->clipRect(RectToSkRect(aRect), SkRegion::kIntersect_Op, true); paint.setColor(SkColorSetARGB(0, 0, 0, 0)); paint.setXfermodeMode(SkXfermode::kSrc_Mode); mCanvas->drawPaint(paint); @@ -681,13 +892,13 @@ DrawTargetSkia::ClearRect(const Rect &aRect) void DrawTargetSkia::PushClip(const Path *aPath) { - if (aPath->GetBackendType() != BACKEND_SKIA) { + if (aPath->GetBackendType() != BackendType::SKIA) { return; } const PathSkia *skiaPath = static_cast<const PathSkia*>(aPath); - mCanvas->save(SkCanvas::kClip_SaveFlag); - mCanvas->clipPath(skiaPath->GetPath(), SkRegion::kIntersect_Op, USE_SOFT_CLIPPING); + mCanvas->save(); + mCanvas->clipPath(skiaPath->GetPath(), SkRegion::kIntersect_Op, true); } void @@ -695,8 +906,8 @@ DrawTargetSkia::PushClipRect(const Rect& aRect) { SkRect rect = RectToSkRect(aRect); - mCanvas->save(SkCanvas::kClip_SaveFlag); - mCanvas->clipRect(rect, SkRegion::kIntersect_Op, USE_SOFT_CLIPPING); + mCanvas->save(); + mCanvas->clipRect(rect, SkRegion::kIntersect_Op, true); } void @@ -714,36 +925,37 @@ DrawTargetSkia::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, Ex stops[i] = aStops[i]; } std::stable_sort(stops.begin(), stops.end()); - + return new GradientStopsSkia(stops, aNumStops, aExtendMode); } -void -DrawTargetSkia::AppendSnapshot(SourceSurfaceSkia* aSnapshot) +TemporaryRef<FilterNode> +DrawTargetSkia::CreateFilter(FilterType aType) { - mSnapshots.push_back(aSnapshot); + return FilterNodeSoftware::Create(aType); } void -DrawTargetSkia::RemoveSnapshot(SourceSurfaceSkia* aSnapshot) +DrawTargetSkia::MarkChanged() { - std::vector<SourceSurfaceSkia*>::iterator iter = std::find(mSnapshots.begin(), mSnapshots.end(), aSnapshot); - if (iter != mSnapshots.end()) { - mSnapshots.erase(iter); + if (mSnapshot) { + mSnapshot->DrawTargetWillChange(); + mSnapshot = nullptr; } } +// Return a rect (in user space) that covers the entire surface by applying +// the inverse of GetTransform() to (0, 0, mSize.width, mSize.height). +SkRect +DrawTargetSkia::SkRectCoveringWholeSurface() const +{ + return RectToSkRect(mTransform.TransformBounds(Rect(0, 0, mSize.width, mSize.height))); +} + void -DrawTargetSkia::MarkChanged() +DrawTargetSkia::SnapshotDestroyed() { - if (mSnapshots.size()) { - for (std::vector<SourceSurfaceSkia*>::iterator iter = mSnapshots.begin(); - iter != mSnapshots.end(); iter++) { - (*iter)->DrawTargetWillChange(); - } - // All snapshots will now have copied data. - mSnapshots.clear(); - } + mSnapshot = nullptr; } } diff --git a/gfx/2d/DrawTargetSkia.h b/gfx/2d/DrawTargetSkia.h index 3f8c05e32..27a72748a 100644 --- a/gfx/2d/DrawTargetSkia.h +++ b/gfx/2d/DrawTargetSkia.h @@ -3,7 +3,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#pragma once +#ifndef _MOZILLA_GFX_SOURCESURFACESKIA_H +#define _MOZILLA_GFX_SOURCESURFACESKIA_H #ifdef USE_SKIA_GPU #include "skia/GrContext.h" @@ -13,6 +14,7 @@ #include "skia/SkCanvas.h" #include "2D.h" +#include "HelpersSkia.h" #include "Rect.h" #include "PathSkia.h" #include <sstream> @@ -26,81 +28,92 @@ class SourceSurfaceSkia; class DrawTargetSkia : public DrawTarget { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetSkia, override) DrawTargetSkia(); virtual ~DrawTargetSkia(); - virtual BackendType GetType() const { return BACKEND_SKIA; } - virtual TemporaryRef<SourceSurface> Snapshot(); - virtual IntSize GetSize() { return mSize; } - virtual void Flush(); + virtual DrawTargetType GetType() const override; + virtual BackendType GetBackendType() const override { return BackendType::SKIA; } + virtual TemporaryRef<SourceSurface> Snapshot() override; + virtual IntSize GetSize() override { return mSize; } + virtual void Flush() override; virtual void DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource, const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; + virtual void DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions = DrawOptions()) override; virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, - CompositionOp aOperator); - virtual void ClearRect(const Rect &aRect); + CompositionOp aOperator) override; + virtual void ClearRect(const Rect &aRect) override; virtual void CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect, - const IntPoint &aDestination); + const IntPoint &aDestination) override; virtual void FillRect(const Rect &aRect, const Pattern &aPattern, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void StrokeRect(const Rect &aRect, const Pattern &aPattern, const StrokeOptions &aStrokeOptions = StrokeOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void StrokeLine(const Point &aStart, const Point &aEnd, const Pattern &aPattern, const StrokeOptions &aStrokeOptions = StrokeOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions = StrokeOptions(), - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void Fill(const Path *aPath, const Pattern &aPattern, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aOptions = DrawOptions(), - const GlyphRenderingOptions *aRenderingOptions = nullptr); + const GlyphRenderingOptions *aRenderingOptions = nullptr) override; virtual void Mask(const Pattern &aSource, const Pattern &aMask, - const DrawOptions &aOptions = DrawOptions()); + const DrawOptions &aOptions = DrawOptions()) override; virtual void MaskSurface(const Pattern &aSource, SourceSurface *aMask, Point aOffset, - const DrawOptions &aOptions = DrawOptions()) { MOZ_ASSERT(0); }; - virtual void PushClip(const Path *aPath); - virtual void PushClipRect(const Rect& aRect); - virtual void PopClip(); + const DrawOptions &aOptions = DrawOptions()) override; + virtual void PushClip(const Path *aPath) override; + virtual void PushClipRect(const Rect& aRect) override; + virtual void PopClip() override; virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, int32_t aStride, - SurfaceFormat aFormat) const; - virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const; + SurfaceFormat aFormat) const override; + virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override; virtual TemporaryRef<SourceSurface> - CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const; + CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override; virtual TemporaryRef<DrawTarget> - CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const; - virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const; - virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = EXTEND_CLAMP) const; - virtual void SetTransform(const Matrix &aTransform); + CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override; + virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override; + virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = ExtendMode::CLAMP) const override; + virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override; + virtual void SetTransform(const Matrix &aTransform) override; + virtual void *GetNativeSurface(NativeSurfaceType aType) override; bool Init(const IntSize &aSize, SurfaceFormat aFormat); void Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat); + #ifdef USE_SKIA_GPU - void InitWithFBO(unsigned int aFBOID, GrContext* aGrContext, const IntSize &aSize, SurfaceFormat aFormat); + bool InitWithGrContext(GrContext* aGrContext, + const IntSize &aSize, + SurfaceFormat aFormat) override; #endif - + operator std::string() const { std::stringstream stream; stream << "DrawTargetSkia(" << this << ")"; @@ -109,15 +122,25 @@ public: private: friend class SourceSurfaceSkia; - void AppendSnapshot(SourceSurfaceSkia* aSnapshot); - void RemoveSnapshot(SourceSurfaceSkia* aSnapshot); + void SnapshotDestroyed(); void MarkChanged(); + SkRect SkRectCoveringWholeSurface() const; + + bool UsingSkiaGPU() const; + +#ifdef USE_SKIA_GPU + RefPtrSkia<GrContext> mGrContext; + uint32_t mTexture; +#endif + IntSize mSize; - SkRefPtr<SkCanvas> mCanvas; - std::vector<SourceSurfaceSkia*> mSnapshots; + RefPtrSkia<SkCanvas> mCanvas; + SourceSurfaceSkia* mSnapshot; }; } } + +#endif // _MOZILLA_GFX_SOURCESURFACESKIA_H diff --git a/gfx/2d/DrawTargetTiled.cpp b/gfx/2d/DrawTargetTiled.cpp new file mode 100644 index 000000000..641b3cef7 --- /dev/null +++ b/gfx/2d/DrawTargetTiled.cpp @@ -0,0 +1,296 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define _USE_MATH_DEFINES +#include <cmath> + +#include "DrawTargetTiled.h" +#include "Logging.h" + +using namespace std; + +namespace mozilla { +namespace gfx { + +DrawTargetTiled::DrawTargetTiled() +{ +} + +bool +DrawTargetTiled::Init(const TileSet& aTiles) +{ + if (!aTiles.mTileCount) { + return false; + } + + mTiles.reserve(aTiles.mTileCount); + for (size_t i = 0; i < aTiles.mTileCount; ++i) { + mTiles.push_back(TileInternal(aTiles.mTiles[i])); + if (!aTiles.mTiles[i].mDrawTarget) { + return false; + } + if (mTiles[0].mDrawTarget->GetFormat() != mTiles.back().mDrawTarget->GetFormat() || + mTiles[0].mDrawTarget->GetBackendType() != mTiles.back().mDrawTarget->GetBackendType()) { + return false; + } + uint32_t newXMost = max(mRect.XMost(), + mTiles[i].mTileOrigin.x + mTiles[i].mDrawTarget->GetSize().width); + uint32_t newYMost = max(mRect.YMost(), + mTiles[i].mTileOrigin.y + mTiles[i].mDrawTarget->GetSize().height); + mRect.x = min(mRect.x, mTiles[i].mTileOrigin.x); + mRect.y = min(mRect.y, mTiles[i].mTileOrigin.y); + mRect.width = newXMost - mRect.x; + mRect.height = newYMost - mRect.y; + mTiles[i].mDrawTarget->SetTransform(Matrix::Translation(mTiles[i].mTileOrigin.x, + mTiles[i].mTileOrigin.y)); + } + mFormat = mTiles[0].mDrawTarget->GetFormat(); + return true; +} + +TemporaryRef<SourceSurface> +DrawTargetTiled::Snapshot() +{ + return new SnapshotTiled(mTiles, mRect); +} + +// Skip the mClippedOut check since this is only used for Flush() which +// should happen even if we're clipped. +#define TILED_COMMAND(command) \ + void \ + DrawTargetTiled::command() \ + { \ + for (size_t i = 0; i < mTiles.size(); i++) { \ + mTiles[i].mDrawTarget->command(); \ + } \ + } +#define TILED_COMMAND1(command, type1) \ + void \ + DrawTargetTiled::command(type1 arg1) \ + { \ + for (size_t i = 0; i < mTiles.size(); i++) { \ + if (!mTiles[i].mClippedOut) \ + mTiles[i].mDrawTarget->command(arg1); \ + } \ + } +#define TILED_COMMAND3(command, type1, type2, type3) \ + void \ + DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3) \ + { \ + for (size_t i = 0; i < mTiles.size(); i++) { \ + if (!mTiles[i].mClippedOut) \ + mTiles[i].mDrawTarget->command(arg1, arg2, arg3); \ + } \ + } +#define TILED_COMMAND4(command, type1, type2, type3, type4) \ + void \ + DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ + { \ + for (size_t i = 0; i < mTiles.size(); i++) { \ + if (!mTiles[i].mClippedOut) \ + mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4); \ + } \ + } +#define TILED_COMMAND5(command, type1, type2, type3, type4, type5) \ + void \ + DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ + { \ + for (size_t i = 0; i < mTiles.size(); i++) { \ + if (!mTiles[i].mClippedOut) \ + mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4, arg5); \ + } \ + } + +TILED_COMMAND(Flush) +TILED_COMMAND4(DrawFilter, FilterNode*, const Rect&, const Point&, const DrawOptions&) +TILED_COMMAND1(ClearRect, const Rect&) +TILED_COMMAND4(MaskSurface, const Pattern&, SourceSurface*, Point, const DrawOptions&) +TILED_COMMAND4(StrokeRect, const Rect&, const Pattern&, const StrokeOptions&, const DrawOptions&) +TILED_COMMAND5(StrokeLine, const Point&, const Point&, const Pattern&, const StrokeOptions&, const DrawOptions&) +TILED_COMMAND5(FillGlyphs, ScaledFont*, const GlyphBuffer&, const Pattern&, const DrawOptions&, const GlyphRenderingOptions*) +TILED_COMMAND3(Mask, const Pattern&, const Pattern&, const DrawOptions&) + +void +DrawTargetTiled::PushClip(const Path* aPath) +{ + mClippedOutTilesStack.push_back(std::vector<uint32_t>()); + std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back(); + + Rect deviceRect = aPath->GetBounds(mTransform); + + for (size_t i = 0; i < mTiles.size(); i++) { + if (!mTiles[i].mClippedOut) { + if (deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x, + mTiles[i].mTileOrigin.y, + mTiles[i].mDrawTarget->GetSize().width, + mTiles[i].mDrawTarget->GetSize().height))) { + mTiles[i].mDrawTarget->PushClip(aPath); + } else { + mTiles[i].mClippedOut = true; + clippedTiles.push_back(i); + } + } + } +} + +void +DrawTargetTiled::PushClipRect(const Rect& aRect) +{ + mClippedOutTilesStack.push_back(std::vector<uint32_t>()); + std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back(); + + Rect deviceRect = mTransform.TransformBounds(aRect); + + for (size_t i = 0; i < mTiles.size(); i++) { + if (!mTiles[i].mClippedOut) { + if (deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x, + mTiles[i].mTileOrigin.y, + mTiles[i].mDrawTarget->GetSize().width, + mTiles[i].mDrawTarget->GetSize().height))) { + mTiles[i].mDrawTarget->PushClipRect(aRect); + } else { + mTiles[i].mClippedOut = true; + clippedTiles.push_back(i); + } + } + } +} + +void +DrawTargetTiled::PopClip() +{ + for (size_t i = 0; i < mTiles.size(); i++) { + if (!mTiles[i].mClippedOut) { + mTiles[i].mDrawTarget->PopClip(); + } + } + + std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back(); + for (size_t i = 0; i < clippedTiles.size(); i++) { + mTiles[clippedTiles[i]].mClippedOut = false; + } + + mClippedOutTilesStack.pop_back(); +} + +void +DrawTargetTiled::CopySurface(SourceSurface *aSurface, + const IntRect &aSourceRect, + const IntPoint &aDestination) +{ + for (size_t i = 0; i < mTiles.size(); i++) { + IntPoint tileOrigin = mTiles[i].mTileOrigin; + IntSize tileSize = mTiles[i].mDrawTarget->GetSize(); + if (!IntRect(aDestination, aSourceRect.Size()).Intersects(IntRect(tileOrigin, tileSize))) { + continue; + } + // CopySurface ignores the transform, account for that here. + mTiles[i].mDrawTarget->CopySurface(aSurface, aSourceRect, aDestination - tileOrigin); + } +} + +void +DrawTargetTiled::SetTransform(const Matrix& aTransform) +{ + for (size_t i = 0; i < mTiles.size(); i++) { + Matrix mat = aTransform; + mat.PostTranslate(Float(-mTiles[i].mTileOrigin.x), Float(-mTiles[i].mTileOrigin.y)); + mTiles[i].mDrawTarget->SetTransform(mat); + } + DrawTarget::SetTransform(aTransform); +} + +void +DrawTargetTiled::DrawSurface(SourceSurface* aSurface, const Rect& aDest, const Rect& aSource, const DrawSurfaceOptions& aSurfaceOptions, const DrawOptions& aDrawOptions) +{ + Rect deviceRect = mTransform.TransformBounds(aDest); + for (size_t i = 0; i < mTiles.size(); i++) { + if (!mTiles[i].mClippedOut && + deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x, + mTiles[i].mTileOrigin.y, + mTiles[i].mDrawTarget->GetSize().width, + mTiles[i].mDrawTarget->GetSize().height))) { + mTiles[i].mDrawTarget->DrawSurface(aSurface, aDest, aSource, aSurfaceOptions, aDrawOptions); + } + } +} + +void +DrawTargetTiled::FillRect(const Rect& aRect, const Pattern& aPattern, const DrawOptions& aDrawOptions) +{ + Rect deviceRect = mTransform.TransformBounds(aRect); + for (size_t i = 0; i < mTiles.size(); i++) { + if (!mTiles[i].mClippedOut && + deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x, + mTiles[i].mTileOrigin.y, + mTiles[i].mDrawTarget->GetSize().width, + mTiles[i].mDrawTarget->GetSize().height))) { + mTiles[i].mDrawTarget->FillRect(aRect, aPattern, aDrawOptions); + } + } +} + +// The logic for this comes from _cairo_stroke_style_max_distance_from_path +static Rect +PathExtentsToMaxStrokeExtents(const StrokeOptions &aStrokeOptions, + const Rect &aRect, + const Matrix &aTransform) +{ + double styleExpansionFactor = 0.5f; + + if (aStrokeOptions.mLineCap == CapStyle::SQUARE) { + styleExpansionFactor = M_SQRT1_2; + } + + if (aStrokeOptions.mLineJoin == JoinStyle::MITER && + styleExpansionFactor < M_SQRT2 * aStrokeOptions.mMiterLimit) { + styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit; + } + + styleExpansionFactor *= aStrokeOptions.mLineWidth; + + double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21); + double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12); + + Rect result = aRect; + result.Inflate(dx, dy); + return result; +} + +void +DrawTargetTiled::Stroke(const Path* aPath, const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aDrawOptions) +{ + // Approximate the stroke extents, since Path::GetStrokeExtents can be slow + Rect deviceRect = PathExtentsToMaxStrokeExtents(aStrokeOptions, + aPath->GetBounds(mTransform), + mTransform); + for (size_t i = 0; i < mTiles.size(); i++) { + if (!mTiles[i].mClippedOut && + deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x, + mTiles[i].mTileOrigin.y, + mTiles[i].mDrawTarget->GetSize().width, + mTiles[i].mDrawTarget->GetSize().height))) { + mTiles[i].mDrawTarget->Stroke(aPath, aPattern, aStrokeOptions, aDrawOptions); + } + } +} + +void +DrawTargetTiled::Fill(const Path* aPath, const Pattern& aPattern, const DrawOptions& aDrawOptions) +{ + Rect deviceRect = aPath->GetBounds(mTransform); + for (size_t i = 0; i < mTiles.size(); i++) { + if (!mTiles[i].mClippedOut && + deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x, + mTiles[i].mTileOrigin.y, + mTiles[i].mDrawTarget->GetSize().width, + mTiles[i].mDrawTarget->GetSize().height))) { + mTiles[i].mDrawTarget->Fill(aPath, aPattern, aDrawOptions); + } + } +} + +} +} diff --git a/gfx/2d/DrawTargetTiled.h b/gfx/2d/DrawTargetTiled.h new file mode 100644 index 000000000..75fa6a8bc --- /dev/null +++ b/gfx/2d/DrawTargetTiled.h @@ -0,0 +1,208 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_DRAWTARGETTILED_H_ +#define MOZILLA_GFX_DRAWTARGETTILED_H_ + +#include "2D.h" +#include "Filters.h" +#include "Logging.h" + +#include <vector> + +namespace mozilla { +namespace gfx { + +struct TileInternal : public Tile { + TileInternal() + : mClippedOut(false) + {} + + explicit TileInternal(const Tile& aOther) + : Tile(aOther) + , mClippedOut(false) + {} + + bool mClippedOut; +}; + + +class DrawTargetTiled : public DrawTarget +{ +public: + DrawTargetTiled(); + + bool Init(const TileSet& mTiles); + + virtual bool IsTiledDrawTarget() const override { return true; } + + virtual DrawTargetType GetType() const override { return mTiles[0].mDrawTarget->GetType(); } + virtual BackendType GetBackendType() const override { return mTiles[0].mDrawTarget->GetBackendType(); } + virtual TemporaryRef<SourceSurface> Snapshot() override; + virtual IntSize GetSize() override { + MOZ_ASSERT(mRect.width > 0 && mRect.height > 0); + return IntSize(mRect.XMost(), mRect.YMost()); + } + + virtual void Flush() override; + virtual void DrawSurface(SourceSurface *aSurface, + const Rect &aDest, + const Rect &aSource, + const DrawSurfaceOptions &aSurfOptions, + const DrawOptions &aOptions) override; + virtual void DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions = DrawOptions()) override; + virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, + const Point &aDest, + const Color &aColor, + const Point &aOffset, + Float aSigma, + CompositionOp aOperator) override { /* Not implemented */ MOZ_CRASH(); } + + virtual void ClearRect(const Rect &aRect) override; + virtual void MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, + const DrawOptions &aOptions = DrawOptions()) override; + + virtual void CopySurface(SourceSurface *aSurface, + const IntRect &aSourceRect, + const IntPoint &aDestination) override; + + virtual void FillRect(const Rect &aRect, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions()) override; + virtual void StrokeRect(const Rect &aRect, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()) override; + virtual void StrokeLine(const Point &aStart, + const Point &aEnd, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()) override; + virtual void Stroke(const Path *aPath, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()) override; + virtual void Fill(const Path *aPath, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions()) override; + virtual void FillGlyphs(ScaledFont *aFont, + const GlyphBuffer &aBuffer, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions(), + const GlyphRenderingOptions *aRenderingOptions = nullptr) override; + virtual void Mask(const Pattern &aSource, + const Pattern &aMask, + const DrawOptions &aOptions = DrawOptions()) override; + virtual void PushClip(const Path *aPath) override; + virtual void PushClipRect(const Rect &aRect) override; + virtual void PopClip() override; + + virtual void SetTransform(const Matrix &aTransform) override; + + virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat) const override + { + return mTiles[0].mDrawTarget->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat); + } + virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override + { + return mTiles[0].mDrawTarget->OptimizeSourceSurface(aSurface); + } + + virtual TemporaryRef<SourceSurface> + CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override + { + return mTiles[0].mDrawTarget->CreateSourceSurfaceFromNativeSurface(aSurface); + } + + virtual TemporaryRef<DrawTarget> + CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override + { + return mTiles[0].mDrawTarget->CreateSimilarDrawTarget(aSize, aFormat); + } + + virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override + { + return mTiles[0].mDrawTarget->CreatePathBuilder(aFillRule); + } + + virtual TemporaryRef<GradientStops> + CreateGradientStops(GradientStop *aStops, + uint32_t aNumStops, + ExtendMode aExtendMode = ExtendMode::CLAMP) const override + { + return mTiles[0].mDrawTarget->CreateGradientStops(aStops, aNumStops, aExtendMode); + } + virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override + { + return mTiles[0].mDrawTarget->CreateFilter(aType); + } + +private: + std::vector<TileInternal> mTiles; + std::vector<std::vector<uint32_t> > mClippedOutTilesStack; + IntRect mRect; +}; + +class SnapshotTiled : public SourceSurface +{ +public: + SnapshotTiled(const std::vector<TileInternal>& aTiles, const IntRect& aRect) + : mRect(aRect) + { + for (size_t i = 0; i < aTiles.size(); i++) { + mSnapshots.push_back(aTiles[i].mDrawTarget->Snapshot()); + mOrigins.push_back(aTiles[i].mTileOrigin); + } + } + + virtual SurfaceType GetType() const { return SurfaceType::TILED; } + virtual IntSize GetSize() const { + MOZ_ASSERT(mRect.width > 0 && mRect.height > 0); + return IntSize(mRect.XMost(), mRect.YMost()); + } + virtual SurfaceFormat GetFormat() const { return mSnapshots[0]->GetFormat(); } + + virtual TemporaryRef<DataSourceSurface> GetDataSurface() + { + RefPtr<DataSourceSurface> surf = Factory::CreateDataSourceSurface(GetSize(), GetFormat()); + + DataSourceSurface::MappedSurface mappedSurf; + if (!surf->Map(DataSourceSurface::MapType::WRITE, &mappedSurf)) { + gfxCriticalError() << "DrawTargetTiled::GetDataSurface failed to map surface"; + return nullptr; + } + + { + RefPtr<DrawTarget> dt = + Factory::CreateDrawTargetForData(BackendType::CAIRO, mappedSurf.mData, + GetSize(), mappedSurf.mStride, GetFormat()); + + for (size_t i = 0; i < mSnapshots.size(); i++) { + RefPtr<DataSourceSurface> dataSurf = mSnapshots[i]->GetDataSurface(); + dt->CopySurface(dataSurf, IntRect(IntPoint(0, 0), mSnapshots[i]->GetSize()), mOrigins[i]); + } + } + surf->Unmap(); + + return surf.forget(); + } + + std::vector<RefPtr<SourceSurface>> mSnapshots; + std::vector<IntPoint> mOrigins; + IntRect mRect; +}; + +} +} + +#endif diff --git a/gfx/2d/ExtendInputEffectD2D1.cpp b/gfx/2d/ExtendInputEffectD2D1.cpp new file mode 100644 index 000000000..b11c36eca --- /dev/null +++ b/gfx/2d/ExtendInputEffectD2D1.cpp @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ExtendInputEffectD2D1.h" + +#include "Logging.h" + +#include "ShadersD2D1.h" +#include "HelpersD2D.h" + +#include <vector> + +#define TEXTW(x) L##x +#define XML(X) TEXTW(#X) // This macro creates a single string from multiple lines of text. + +static const PCWSTR kXmlDescription = + XML( + <?xml version='1.0'?> + <Effect> + <!-- System Properties --> + <Property name='DisplayName' type='string' value='ExtendInputEffect'/> + <Property name='Author' type='string' value='Mozilla'/> + <Property name='Category' type='string' value='Utility Effects'/> + <Property name='Description' type='string' value='This effect is used to extend the output rect of any input effect to a specified rect.'/> + <Inputs> + <Input name='InputEffect'/> + </Inputs> + <Property name='OutputRect' type='vector4'> + <Property name='DisplayName' type='string' value='Output Rect'/> + </Property> + </Effect> + ); + +// {FB947CDA-718E-40CC-AE7B-D255830D7D14} +static const GUID GUID_SampleExtendInputPS = + {0xfb947cda, 0x718e, 0x40cc, {0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14}}; +// {2C468128-6546-453C-8E25-F2DF0DE10A0F} +static const GUID GUID_SampleExtendInputA0PS = + {0x2c468128, 0x6546, 0x453c, {0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf}}; + +namespace mozilla { +namespace gfx { + +ExtendInputEffectD2D1::ExtendInputEffectD2D1() + : mRefCount(0) + , mOutputRect(D2D1::Vector4F(-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX)) +{ +} + +IFACEMETHODIMP +ExtendInputEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph) +{ + HRESULT hr; + hr = pTransformGraph->SetSingleTransformNode(this); + + if (FAILED(hr)) { + return hr; + } + + return S_OK; +} + +IFACEMETHODIMP +ExtendInputEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType) +{ + return S_OK; +} + +IFACEMETHODIMP +ExtendInputEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph) +{ + return E_NOTIMPL; +} + +IFACEMETHODIMP_(ULONG) +ExtendInputEffectD2D1::AddRef() +{ + return ++mRefCount; +} + +IFACEMETHODIMP_(ULONG) +ExtendInputEffectD2D1::Release() +{ + if (!--mRefCount) { + delete this; + return 0; + } + return mRefCount; +} + +IFACEMETHODIMP +ExtendInputEffectD2D1::QueryInterface(const IID &aIID, void **aPtr) +{ + if (!aPtr) { + return E_POINTER; + } + + if (aIID == IID_IUnknown) { + *aPtr = static_cast<IUnknown*>(static_cast<ID2D1EffectImpl*>(this)); + } else if (aIID == IID_ID2D1EffectImpl) { + *aPtr = static_cast<ID2D1EffectImpl*>(this); + } else if (aIID == IID_ID2D1DrawTransform) { + *aPtr = static_cast<ID2D1DrawTransform*>(this); + } else if (aIID == IID_ID2D1Transform) { + *aPtr = static_cast<ID2D1Transform*>(this); + } else if (aIID == IID_ID2D1TransformNode) { + *aPtr = static_cast<ID2D1TransformNode*>(this); + } else { + return E_NOINTERFACE; + } + + static_cast<IUnknown*>(*aPtr)->AddRef(); + return S_OK; +} + +static D2D1_RECT_L +ConvertFloatToLongRect(const D2D1_VECTOR_4F& aRect) +{ + // Clamp values to LONG range. We can't use std::min/max here because we want + // the comparison to operate on a type that's different from the type of the + // result. + return D2D1::RectL(aRect.x <= LONG_MIN ? LONG_MIN : LONG(aRect.x), + aRect.y <= LONG_MIN ? LONG_MIN : LONG(aRect.y), + aRect.z >= LONG_MAX ? LONG_MAX : LONG(aRect.z), + aRect.w >= LONG_MAX ? LONG_MAX : LONG(aRect.w)); +} + +static D2D1_RECT_L +IntersectRect(const D2D1_RECT_L& aRect1, const D2D1_RECT_L& aRect2) +{ + return D2D1::RectL(std::max(aRect1.left, aRect2.left), + std::max(aRect1.top, aRect2.top), + std::min(aRect1.right, aRect2.right), + std::min(aRect1.bottom, aRect2.bottom)); +} + +IFACEMETHODIMP +ExtendInputEffectD2D1::MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects, + const D2D1_RECT_L* pInputOpaqueSubRects, + UINT32 inputRectCount, + D2D1_RECT_L* pOutputRect, + D2D1_RECT_L* pOutputOpaqueSubRect) +{ + // This transform only accepts one input, so there will only be one input rect. + if (inputRectCount != 1) { + return E_INVALIDARG; + } + + // Set the output rect to the specified rect. This is the whole purpose of this effect. + *pOutputRect = ConvertFloatToLongRect(mOutputRect); + *pOutputOpaqueSubRect = IntersectRect(*pOutputRect, pInputOpaqueSubRects[0]); + return S_OK; +} + +IFACEMETHODIMP +ExtendInputEffectD2D1::MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect, + D2D1_RECT_L* pInputRects, + UINT32 inputRectCount) const +{ + if (inputRectCount != 1) { + return E_INVALIDARG; + } + + *pInputRects = *pOutputRect; + return S_OK; +} + +IFACEMETHODIMP +ExtendInputEffectD2D1::MapInvalidRect(UINT32 inputIndex, + D2D1_RECT_L invalidInputRect, + D2D1_RECT_L* pInvalidOutputRect) const +{ + MOZ_ASSERT(inputIndex = 0); + + *pInvalidOutputRect = invalidInputRect; + return S_OK; +} + +HRESULT +ExtendInputEffectD2D1::Register(ID2D1Factory1 *aFactory) +{ + D2D1_PROPERTY_BINDING bindings[] = { + D2D1_VALUE_TYPE_BINDING(L"OutputRect", &ExtendInputEffectD2D1::SetOutputRect, &ExtendInputEffectD2D1::GetOutputRect), + }; + HRESULT hr = aFactory->RegisterEffectFromString(CLSID_ExtendInputEffect, kXmlDescription, bindings, 1, CreateEffect); + + if (FAILED(hr)) { + gfxWarning() << "Failed to register extend input effect."; + } + return hr; +} + +void +ExtendInputEffectD2D1::Unregister(ID2D1Factory1 *aFactory) +{ + aFactory->UnregisterEffect(CLSID_ExtendInputEffect); +} + +HRESULT __stdcall +ExtendInputEffectD2D1::CreateEffect(IUnknown **aEffectImpl) +{ + *aEffectImpl = static_cast<ID2D1EffectImpl*>(new ExtendInputEffectD2D1()); + (*aEffectImpl)->AddRef(); + + return S_OK; +} + +} +} diff --git a/gfx/2d/ExtendInputEffectD2D1.h b/gfx/2d/ExtendInputEffectD2D1.h new file mode 100644 index 000000000..093063897 --- /dev/null +++ b/gfx/2d/ExtendInputEffectD2D1.h @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_EXTENDINPUTEFFECTD2D1_H_ +#define MOZILLA_GFX_EXTENDINPUTEFFECTD2D1_H_ + +#include <d2d1_1.h> +#include <d2d1effectauthor.h> +#include <d2d1effecthelpers.h> + +#include "2D.h" +#include "mozilla/Attributes.h" + +// {97143DC6-CBC4-4DD4-A8BA-13342B0BA46D} +DEFINE_GUID(CLSID_ExtendInputEffect, +0x5fb55c7c, 0xd795, 0x4ba3, 0xa9, 0x5c, 0x22, 0x82, 0x5d, 0x0c, 0x4d, 0xf7); + +namespace mozilla { +namespace gfx { + +enum { + EXTENDINPUT_PROP_OUTPUT_RECT = 0 +}; + +// An effect type that passes through its input unchanged but sets the effect's +// output rect to a specified rect. Unlike the built-in Crop effect, the +// ExtendInput effect can extend the input rect, and not just make it smaller. +// The added margins are filled with transparent black. +// Some effects have different output depending on their input effect's output +// rect, for example the Border effect (which repeats the edges of its input +// effect's output rect) or the component transfer and color matrix effects +// (which can transform transparent pixels into non-transparent ones, but only +// inside their input effect's output rect). +class ExtendInputEffectD2D1 final : public ID2D1EffectImpl + , public ID2D1DrawTransform +{ +public: + // ID2D1EffectImpl + IFACEMETHODIMP Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph); + IFACEMETHODIMP PrepareForRender(D2D1_CHANGE_TYPE changeType); + IFACEMETHODIMP SetGraph(ID2D1TransformGraph* pGraph); + + // IUnknown + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppOutput); + + // ID2D1Transform + IFACEMETHODIMP MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects, + const D2D1_RECT_L* pInputOpaqueSubRects, + UINT32 inputRectCount, + D2D1_RECT_L* pOutputRect, + D2D1_RECT_L* pOutputOpaqueSubRect); + IFACEMETHODIMP MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect, + D2D1_RECT_L* pInputRects, + UINT32 inputRectCount) const; + IFACEMETHODIMP MapInvalidRect(UINT32 inputIndex, + D2D1_RECT_L invalidInputRect, + D2D1_RECT_L* pInvalidOutputRect) const; + + // ID2D1TransformNode + IFACEMETHODIMP_(UINT32) GetInputCount() const { return 1; } + + // ID2D1DrawTransform + IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo *pDrawInfo) { return S_OK; } + + static HRESULT Register(ID2D1Factory1* aFactory); + static void Unregister(ID2D1Factory1* aFactory); + static HRESULT __stdcall CreateEffect(IUnknown** aEffectImpl); + + HRESULT SetOutputRect(D2D1_VECTOR_4F aOutputRect) + { mOutputRect = aOutputRect; return S_OK; } + D2D1_VECTOR_4F GetOutputRect() const { return mOutputRect; } + +private: + ExtendInputEffectD2D1(); + + uint32_t mRefCount; + D2D1_VECTOR_4F mOutputRect; +}; + +} +} +#undef SIMPLE_PROP + +#endif diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp index e98df9d74..6750c28f8 100644 --- a/gfx/2d/Factory.cpp +++ b/gfx/2d/Factory.cpp @@ -7,18 +7,19 @@ #ifdef USE_CAIRO #include "DrawTargetCairo.h" -#include "ScaledFontBase.h" +#include "ScaledFontCairo.h" #endif #ifdef USE_SKIA #include "DrawTargetSkia.h" #include "ScaledFontBase.h" #ifdef MOZ_ENABLE_FREETYPE -#include "ScaledFontFreetype.h" +#define USE_SKIA_FREETYPE +#include "ScaledFontCairo.h" #endif #endif -#if defined(WIN32) && defined(USE_SKIA) +#if defined(WIN32) #include "ScaledFontWin.h" #endif @@ -33,11 +34,14 @@ #ifdef WIN32 #include "DrawTargetD2D.h" +#include "DrawTargetD2D1.h" #include "ScaledFontDWrite.h" #include <d3d10_1.h> +#include "HelpersD2D.h" #endif #include "DrawTargetDual.h" +#include "DrawTargetTiled.h" #include "DrawTargetRecording.h" #include "SourceSurfaceRawData.h" @@ -46,8 +50,10 @@ #include "Logging.h" -#ifdef PR_LOGGING -PRLogModuleInfo * +#include "mozilla/CheckedInt.h" + +#if defined(PR_LOGGING) +GFX2D_API PRLogModuleInfo * GetGFX2DLog() { static PRLogModuleInfo *sLog; @@ -63,6 +69,9 @@ enum CPUIDRegister { eax = 0, ebx = 1, ecx = 2, edx = 3 }; #ifdef HAVE_CPUID_H +#if !(defined(__SSE2__) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP >= 2)) +// cpuid.h is available on gcc 4.3 and higher on i386 and x86_64 #include <cpuid.h> static inline bool @@ -72,6 +81,7 @@ HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit) return __get_cpuid(level, ®s[0], ®s[1], ®s[2], ®s[3]) && (regs[reg] & bit); } +#endif #define HAVE_CPU_DETECTION #else @@ -144,15 +154,69 @@ HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit) namespace mozilla { namespace gfx { -// XXX - Need to define an API to set this. -int sGfxLogLevel = LOG_DEBUG; +// These values we initialize with should match those in +// PreferenceAccess::RegisterAll method. +int32_t PreferenceAccess::sGfxLogLevel = LOG_DEFAULT; + +PreferenceAccess* PreferenceAccess::sAccess = nullptr; +PreferenceAccess::~PreferenceAccess() +{ +} + +// Just a placeholder, the derived class will set the variable to default +// if the preference doesn't exist. +void PreferenceAccess::LivePref(const char* aName, int32_t* aVar, int32_t aDef) +{ + *aVar = aDef; +} + +// This will be called with the derived class, so we will want to register +// the callbacks with it. +void PreferenceAccess::SetAccess(PreferenceAccess* aAccess) { + sAccess = aAccess; + if (sAccess) { + RegisterAll(); + } +} + #ifdef WIN32 ID3D10Device1 *Factory::mD3D10Device; +ID3D11Device *Factory::mD3D11Device; +ID2D1Device *Factory::mD2D1Device; #endif DrawEventRecorder *Factory::mRecorder; +mozilla::gfx::Config* Factory::sConfig = nullptr; + +void +Factory::Init(const Config& aConfig) +{ + MOZ_ASSERT(!sConfig); + sConfig = new Config(aConfig); + + // Make sure we don't completely break rendering because of a typo in the + // pref or whatnot. + const int32_t kMinAllocPref = 10000000; + const int32_t kMinSizePref = 2048; + if (sConfig->mMaxAllocSize < kMinAllocPref) { + sConfig->mMaxAllocSize = kMinAllocPref; + } + if (sConfig->mMaxTextureSize < kMinSizePref) { + sConfig->mMaxTextureSize = kMinSizePref; + } +} + +void +Factory::ShutDown() +{ + if (sConfig) { + delete sConfig; + sConfig = nullptr; + } +} + bool Factory::HasSSE2() { @@ -162,19 +226,110 @@ Factory::HasSSE2() // cl.exe with -arch:SSE2 (default on x64 compiler) return true; #elif defined(HAVE_CPU_DETECTION) - return HasCPUIDBit(1u, edx, (1u<<26)); + static enum { + UNINITIALIZED, + NO_SSE2, + HAS_SSE2 + } sDetectionState = UNINITIALIZED; + + if (sDetectionState == UNINITIALIZED) { + sDetectionState = HasCPUIDBit(1u, edx, (1u<<26)) ? HAS_SSE2 : NO_SSE2; + } + return sDetectionState == HAS_SSE2; #else return false; #endif } +// If the size is "reasonable", we want gfxCriticalError to assert, so +// this is the option set up for it. +inline int LoggerOptionsBasedOnSize(const IntSize& aSize) +{ + return CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize)); +} + +bool +Factory::ReasonableSurfaceSize(const IntSize &aSize) +{ + return Factory::CheckSurfaceSize(aSize, 8192); +} + +bool +Factory::AllowedSurfaceSize(const IntSize &aSize) +{ + if (sConfig) { + return Factory::CheckSurfaceSize(aSize, + sConfig->mMaxTextureSize, + sConfig->mMaxAllocSize); + } + + return CheckSurfaceSize(aSize); +} + +bool +Factory::CheckSurfaceSize(const IntSize &sz, + int32_t extentLimit, + int32_t allocLimit) +{ + if (sz.width <= 0 || sz.height <= 0) { + gfxDebug() << "Surface width or height <= 0!"; + return false; + } + + // reject images with sides bigger than limit + if (extentLimit && (sz.width > extentLimit || sz.height > extentLimit)) { + gfxDebug() << "Surface size too large (exceeds extent limit)!"; + return false; + } + + // make sure the surface area doesn't overflow a int32_t + CheckedInt<int32_t> tmp = sz.width; + tmp *= sz.height; + if (!tmp.isValid()) { + gfxDebug() << "Surface size too large (would overflow)!"; + return false; + } + + // assuming 4 bytes per pixel, make sure the allocation size + // doesn't overflow a int32_t either + CheckedInt<int32_t> stride = sz.width; + stride *= 4; + + // When aligning the stride to 16 bytes, it can grow by up to 15 bytes. + stride += 16 - 1; + + if (!stride.isValid()) { + gfxDebug() << "Surface size too large (stride overflows int32_t)!"; + return false; + } + + CheckedInt<int32_t> numBytes = GetAlignedStride<16>(sz.width * 4); + numBytes *= sz.height; + if (!numBytes.isValid()) { + gfxDebug() << "Surface size too large (allocation size would overflow int32_t)!"; + return false; + } + + if (allocLimit && allocLimit < numBytes.value()) { + gfxDebug() << "Surface size too large (exceeds allocation limit)!"; + return false; + } + + return true; +} + TemporaryRef<DrawTarget> Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat) { + if (!AllowedSurfaceSize(aSize)) { + gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size " << aSize; + return nullptr; + } + RefPtr<DrawTarget> retVal; switch (aBackend) { #ifdef WIN32 - case BACKEND_DIRECT2D: + case BackendType::DIRECT2D: { RefPtr<DrawTargetD2D> newTarget; newTarget = new DrawTargetD2D(); @@ -183,9 +338,18 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor } break; } + case BackendType::DIRECT2D1_1: + { + RefPtr<DrawTargetD2D1> newTarget; + newTarget = new DrawTargetD2D1(); + if (newTarget->Init(aSize, aFormat)) { + retVal = newTarget; + } + break; + } #elif defined XP_MACOSX - case BACKEND_COREGRAPHICS: - case BACKEND_COREGRAPHICS_ACCELERATED: + case BackendType::COREGRAPHICS: + case BackendType::COREGRAPHICS_ACCELERATED: { RefPtr<DrawTargetCG> newTarget; newTarget = new DrawTargetCG(); @@ -196,7 +360,7 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor } #endif #ifdef USE_SKIA - case BACKEND_SKIA: + case BackendType::SKIA: { RefPtr<DrawTargetSkia> newTarget; newTarget = new DrawTargetSkia(); @@ -206,23 +370,32 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor break; } #endif +#ifdef USE_CAIRO + case BackendType::CAIRO: + { + RefPtr<DrawTargetCairo> newTarget; + newTarget = new DrawTargetCairo(); + if (newTarget->Init(aSize, aFormat)) { + retVal = newTarget; + } + break; + } +#endif default: gfxDebug() << "Invalid draw target type specified."; return nullptr; } if (mRecorder && retVal) { - RefPtr<DrawTarget> recordDT; - recordDT = new DrawTargetRecording(mRecorder, retVal); - return recordDT; + return new DrawTargetRecording(mRecorder, retVal); } if (!retVal) { // Failed - gfxDebug() << "Failed to create DrawTarget, Type: " << aBackend << " Size: " << aSize; + gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to create DrawTarget, Type: " << int(aBackend) << " Size: " << aSize; } - - return retVal; + + return retVal.forget(); } TemporaryRef<DrawTarget> @@ -232,30 +405,48 @@ Factory::CreateRecordingDrawTarget(DrawEventRecorder *aRecorder, DrawTarget *aDT } TemporaryRef<DrawTarget> -Factory::CreateDrawTargetForData(BackendType aBackend, - unsigned char *aData, - const IntSize &aSize, - int32_t aStride, +Factory::CreateDrawTargetForData(BackendType aBackend, + unsigned char *aData, + const IntSize &aSize, + int32_t aStride, SurfaceFormat aFormat) { + MOZ_ASSERT(aData); + if (!AllowedSurfaceSize(aSize)) { + gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size " << aSize; + return nullptr; + } + RefPtr<DrawTarget> retVal; switch (aBackend) { #ifdef USE_SKIA - case BACKEND_SKIA: + case BackendType::SKIA: { RefPtr<DrawTargetSkia> newTarget; newTarget = new DrawTargetSkia(); newTarget->Init(aData, aSize, aStride, aFormat); retVal = newTarget; + break; } #endif #ifdef XP_MACOSX - case BACKEND_COREGRAPHICS: + case BackendType::COREGRAPHICS: { RefPtr<DrawTargetCG> newTarget = new DrawTargetCG(); if (newTarget->Init(aBackend, aData, aSize, aStride, aFormat)) - return newTarget; + return newTarget.forget(); + break; + } +#endif +#ifdef USE_CAIRO + case BackendType::CAIRO: + { + RefPtr<DrawTargetCairo> newTarget; + newTarget = new DrawTargetCairo(); + if (newTarget->Init(aData, aSize, aStride, aFormat)) { + retVal = newTarget.forget(); + } break; } #endif @@ -265,13 +456,69 @@ Factory::CreateDrawTargetForData(BackendType aBackend, } if (mRecorder && retVal) { - RefPtr<DrawTarget> recordDT = new DrawTargetRecording(mRecorder, retVal); - return recordDT; + return new DrawTargetRecording(mRecorder, retVal, true); } - gfxDebug() << "Failed to create DrawTarget, Type: " << aBackend << " Size: " << aSize; - // Failed - return nullptr; + if (!retVal) { + gfxDebug() << "Failed to create DrawTarget, Type: " << int(aBackend) << " Size: " << aSize; + } + + return retVal.forget(); +} + +TemporaryRef<DrawTarget> +Factory::CreateTiledDrawTarget(const TileSet& aTileSet) +{ + RefPtr<DrawTargetTiled> dt = new DrawTargetTiled(); + + if (!dt->Init(aTileSet)) { + return nullptr; + } + + return dt.forget(); +} + +bool +Factory::DoesBackendSupportDataDrawtarget(BackendType aType) +{ + switch (aType) { + case BackendType::DIRECT2D: + case BackendType::DIRECT2D1_1: + case BackendType::RECORDING: + case BackendType::NONE: + case BackendType::COREGRAPHICS_ACCELERATED: + return false; + case BackendType::CAIRO: + case BackendType::COREGRAPHICS: + case BackendType::SKIA: + return true; + } + + return false; +} + +uint32_t +Factory::GetMaxSurfaceSize(BackendType aType) +{ + switch (aType) { + case BackendType::CAIRO: + case BackendType::COREGRAPHICS: + return DrawTargetCairo::GetMaxSurfaceSize(); +#ifdef XP_MACOSX + case BackendType::COREGRAPHICS_ACCELERATED: + return DrawTargetCG::GetMaxSurfaceSize(); +#endif + case BackendType::SKIA: + return INT_MAX; +#ifdef WIN32 + case BackendType::DIRECT2D: + return DrawTargetD2D::GetMaxSurfaceSize(); + case BackendType::DIRECT2D1_1: + return DrawTargetD2D1::GetMaxSurfaceSize(); +#endif + default: + return 0; + } } TemporaryRef<ScaledFont> @@ -279,37 +526,27 @@ Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSiz { switch (aNativeFont.mType) { #ifdef WIN32 - case NATIVE_FONT_DWRITE_FONT_FACE: + case NativeFontType::DWRITE_FONT_FACE: { return new ScaledFontDWrite(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aSize); } #if defined(USE_CAIRO) || defined(USE_SKIA) - case NATIVE_FONT_GDI_FONT_FACE: + case NativeFontType::GDI_FONT_FACE: { return new ScaledFontWin(static_cast<LOGFONT*>(aNativeFont.mFont), aSize); } #endif #endif #ifdef XP_MACOSX - case NATIVE_FONT_MAC_FONT_FACE: + case NativeFontType::MAC_FONT_FACE: { return new ScaledFontMac(static_cast<CGFontRef>(aNativeFont.mFont), aSize); } #endif -#ifdef USE_SKIA -#ifdef MOZ_ENABLE_FREETYPE - case NATIVE_FONT_SKIA_FONT_FACE: +#if defined(USE_CAIRO) || defined(USE_SKIA_FREETYPE) + case NativeFontType::CAIRO_FONT_FACE: { - return new ScaledFontFreetype(static_cast<FontOptions*>(aNativeFont.mFont), aSize); - } -#endif -#endif -#ifdef USE_CAIRO - case NATIVE_FONT_CAIRO_FONT_FACE: - { - ScaledFontBase* fontBase = new ScaledFontBase(aSize); - fontBase->SetCairoScaledFont(static_cast<cairo_scaled_font_t*>(aNativeFont.mFont)); - return fontBase; + return new ScaledFontCairo(static_cast<cairo_scaled_font_t*>(aNativeFont.mFont), aSize); } #endif default: @@ -325,7 +562,7 @@ Factory::CreateScaledFontForTrueTypeData(uint8_t *aData, uint32_t aSize, { switch (aType) { #ifdef WIN32 - case FONT_DWRITE: + case FontType::DWRITE: { return new ScaledFontDWrite(aData, aSize, aFaceIndex, aGlyphSize); } @@ -346,16 +583,36 @@ Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont, Float aSize, c // Therefore, we just reuse CreateScaledFontForNativeFont's implementation. RefPtr<ScaledFont> font = CreateScaledFontForNativeFont(aNativeFont, aSize); static_cast<ScaledFontBase*>(font.get())->SetCairoScaledFont(aScaledFont); - return font; + return font.forget(); #else return nullptr; #endif } +TemporaryRef<DrawTarget> +Factory::CreateDualDrawTarget(DrawTarget *targetA, DrawTarget *targetB) +{ + MOZ_ASSERT(targetA && targetB); + + RefPtr<DrawTarget> newTarget = + new DrawTargetDual(targetA, targetB); + + RefPtr<DrawTarget> retVal = newTarget; + + if (mRecorder) { + retVal = new DrawTargetRecording(mRecorder, retVal); + } + + return retVal.forget(); +} + + #ifdef WIN32 TemporaryRef<DrawTarget> Factory::CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat) { + MOZ_ASSERT(aTexture); + RefPtr<DrawTargetD2D> newTarget; newTarget = new DrawTargetD2D(); @@ -363,10 +620,10 @@ Factory::CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceForma RefPtr<DrawTarget> retVal = newTarget; if (mRecorder) { - retVal = new DrawTargetRecording(mRecorder, retVal); + retVal = new DrawTargetRecording(mRecorder, retVal, true); } - return retVal; + return retVal.forget(); } gfxWarning() << "Failed to create draw target for D3D10 texture."; @@ -380,18 +637,19 @@ Factory::CreateDualDrawTargetForD3D10Textures(ID3D10Texture2D *aTextureA, ID3D10Texture2D *aTextureB, SurfaceFormat aFormat) { + MOZ_ASSERT(aTextureA && aTextureB); RefPtr<DrawTargetD2D> newTargetA; RefPtr<DrawTargetD2D> newTargetB; newTargetA = new DrawTargetD2D(); if (!newTargetA->Init(aTextureA, aFormat)) { - gfxWarning() << "Failed to create draw target for D3D10 texture."; + gfxWarning() << "Failed to create dual draw target for D3D10 texture."; return nullptr; } newTargetB = new DrawTargetD2D(); if (!newTargetB->Init(aTextureB, aFormat)) { - gfxWarning() << "Failed to create draw target for D3D10 texture."; + gfxWarning() << "Failed to create new draw target for D3D10 texture."; return nullptr; } @@ -404,28 +662,98 @@ Factory::CreateDualDrawTargetForD3D10Textures(ID3D10Texture2D *aTextureA, retVal = new DrawTargetRecording(mRecorder, retVal); } - return retVal; + return retVal.forget(); } void Factory::SetDirect3D10Device(ID3D10Device1 *aDevice) { + // do not throw on failure; return error codes and disconnect the device + // On Windows 8 error codes are the default, but on Windows 7 the + // default is to throw (or perhaps only with some drivers?) + aDevice->SetExceptionMode(0); mD3D10Device = aDevice; } ID3D10Device1* Factory::GetDirect3D10Device() { +#ifdef DEBUG + if (mD3D10Device) { + UINT mode = mD3D10Device->GetExceptionMode(); + MOZ_ASSERT(0 == mode); + } +#endif return mD3D10Device; } +TemporaryRef<DrawTarget> +Factory::CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat) +{ + MOZ_ASSERT(aTexture); + + RefPtr<DrawTargetD2D1> newTarget; + + newTarget = new DrawTargetD2D1(); + if (newTarget->Init(aTexture, aFormat)) { + RefPtr<DrawTarget> retVal = newTarget; + + if (mRecorder) { + retVal = new DrawTargetRecording(mRecorder, retVal, true); + } + + return retVal; + } + + gfxWarning() << "Failed to create draw target for D3D11 texture."; + + // Failed + return nullptr; +} + +void +Factory::SetDirect3D11Device(ID3D11Device *aDevice) +{ + mD3D11Device = aDevice; + + if (mD2D1Device) { + mD2D1Device->Release(); + mD2D1Device = nullptr; + } + + if (!aDevice) { + return; + } + + RefPtr<ID2D1Factory1> factory = D2DFactory1(); + + RefPtr<IDXGIDevice> device; + aDevice->QueryInterface((IDXGIDevice**)byRef(device)); + factory->CreateDevice(device, &mD2D1Device); +} + +ID3D11Device* +Factory::GetDirect3D11Device() +{ + return mD3D11Device; +} + +ID2D1Device* +Factory::GetD2D1Device() +{ + return mD2D1Device; +} + +bool +Factory::SupportsD2D1() +{ + return !!D2DFactory1(); +} + TemporaryRef<GlyphRenderingOptions> Factory::CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams) { - RefPtr<GlyphRenderingOptions> options = - new GlyphRenderingOptionsDWrite(aParams); - - return options; + return new GlyphRenderingOptionsDWrite(aParams); } uint64_t @@ -443,6 +771,11 @@ Factory::GetD2DVRAMUsageSourceSurface() void Factory::D2DCleanup() { + if (mD2D1Device) { + mD2D1Device->Release(); + mD2D1Device = nullptr; + } + DrawTargetD2D1::CleanupD2D(); DrawTargetD2D::CleanupD2D(); } @@ -450,45 +783,137 @@ Factory::D2DCleanup() #ifdef USE_SKIA_GPU TemporaryRef<DrawTarget> -Factory::CreateSkiaDrawTargetForFBO(unsigned int aFBOID, GrContext *aGrContext, const IntSize &aSize, SurfaceFormat aFormat) +Factory::CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext, + const IntSize &aSize, + SurfaceFormat aFormat) { - RefPtr<DrawTargetSkia> newTarget = new DrawTargetSkia(); - newTarget->InitWithFBO(aFBOID, aGrContext, aSize, aFormat); - return newTarget; + RefPtr<DrawTarget> newTarget = new DrawTargetSkia(); + if (!newTarget->InitWithGrContext(aGrContext, aSize, aFormat)) { + return nullptr; + } + return newTarget.forget(); } + #endif // USE_SKIA_GPU +void +Factory::PurgeAllCaches() +{ +} + +#ifdef USE_SKIA_FREETYPE +TemporaryRef<GlyphRenderingOptions> +Factory::CreateCairoGlyphRenderingOptions(FontHinting aHinting, bool aAutoHinting) +{ + RefPtr<GlyphRenderingOptionsCairo> options = + new GlyphRenderingOptionsCairo(); + + options->SetHinting(aHinting); + options->SetAutoHinting(aAutoHinting); + return options.forget(); +} +#endif + TemporaryRef<DrawTarget> -Factory::CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize) +Factory::CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat) { RefPtr<DrawTarget> retVal; #ifdef USE_CAIRO RefPtr<DrawTargetCairo> newTarget = new DrawTargetCairo(); - if (newTarget->Init(aSurface, aSize)) { + if (newTarget->Init(aSurface, aSize, aFormat)) { retVal = newTarget; } if (mRecorder && retVal) { - RefPtr<DrawTarget> recordDT = new DrawTargetRecording(mRecorder, retVal); - return recordDT; + RefPtr<DrawTarget> recordDT = new DrawTargetRecording(mRecorder, retVal, true); + return recordDT.forget(); } #endif - return retVal; + return retVal.forget(); +} + +#ifdef XP_MACOSX +TemporaryRef<DrawTarget> +Factory::CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize) +{ + RefPtr<DrawTarget> retVal; + + RefPtr<DrawTargetCG> newTarget = new DrawTargetCG(); + + if (newTarget->Init(cg, aSize)) { + retVal = newTarget; + } + + if (mRecorder && retVal) { + return new DrawTargetRecording(mRecorder, retVal); + } + return retVal.forget(); +} + +TemporaryRef<GlyphRenderingOptions> +Factory::CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor) +{ + return new GlyphRenderingOptionsCG(aFontSmoothingBackgroundColor); } +#endif TemporaryRef<DataSourceSurface> Factory::CreateWrappingDataSourceSurface(uint8_t *aData, int32_t aStride, const IntSize &aSize, SurfaceFormat aFormat) { + MOZ_ASSERT(aData); + if (aSize.width <= 0 || aSize.height <= 0) { + return nullptr; + } + RefPtr<SourceSurfaceRawData> newSurf = new SourceSurfaceRawData(); if (newSurf->InitWrappingData(aData, aSize, aStride, aFormat, false)) { - return newSurf; + return newSurf.forget(); + } + + return nullptr; +} + +TemporaryRef<DataSourceSurface> +Factory::CreateDataSourceSurface(const IntSize &aSize, + SurfaceFormat aFormat, + bool aZero) +{ + if (!AllowedSurfaceSize(aSize)) { + gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size " << aSize; + return nullptr; + } + + RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData(); + if (newSurf->Init(aSize, aFormat, aZero)) { + return newSurf.forget(); } + gfxWarning() << "CreateDataSourceSurface failed in init"; + return nullptr; +} + +TemporaryRef<DataSourceSurface> +Factory::CreateDataSourceSurfaceWithStride(const IntSize &aSize, + SurfaceFormat aFormat, + int32_t aStride, + bool aZero) +{ + if (aStride < aSize.width * BytesPerPixel(aFormat)) { + gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed with bad stride " << aStride << ", " << aSize << ", " << aFormat; + return nullptr; + } + + RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData(); + if (newSurf->InitWithStride(aSize, aFormat, aStride, aZero)) { + return newSurf.forget(); + } + + gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed to initialize " << aSize << ", " << aFormat << ", " << aStride << ", " << aZero; return nullptr; } @@ -504,5 +929,24 @@ Factory::SetGlobalEventRecorder(DrawEventRecorder *aRecorder) mRecorder = aRecorder; } +// static +void +Factory::SetLogForwarder(LogForwarder* aLogFwd) { + sConfig->mLogForwarder = aLogFwd; +} + + +// static +void +CriticalLogger::OutputMessage(const std::string &aString, + int aLevel, bool aNoNewline) +{ + if (Factory::GetLogForwarder()) { + Factory::GetLogForwarder()->Log(aString); + } + + BasicLogger::OutputMessage(aString, aLevel, aNoNewline); +} + } } diff --git a/gfx/2d/FilterNodeD2D1.cpp b/gfx/2d/FilterNodeD2D1.cpp new file mode 100644 index 000000000..37992546a --- /dev/null +++ b/gfx/2d/FilterNodeD2D1.cpp @@ -0,0 +1,1082 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FilterNodeD2D1.h" + +#include "Logging.h" + +#include "SourceSurfaceD2D1.h" +#include "SourceSurfaceD2D.h" +#include "SourceSurfaceD2DTarget.h" +#include "DrawTargetD2D.h" +#include "DrawTargetD2D1.h" +#include "ExtendInputEffectD2D1.h" + +namespace mozilla { +namespace gfx { + +D2D1_COLORMATRIX_ALPHA_MODE D2DAlphaMode(uint32_t aMode) +{ + switch (aMode) { + case ALPHA_MODE_PREMULTIPLIED: + return D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED; + case ALPHA_MODE_STRAIGHT: + return D2D1_COLORMATRIX_ALPHA_MODE_STRAIGHT; + default: + MOZ_CRASH("Unknown enum value!"); + } + + return D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED; +} + +D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE D2DAffineTransformInterpolationMode(Filter aFilter) +{ + switch (aFilter) { + case Filter::GOOD: + return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR; + case Filter::LINEAR: + return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR; + case Filter::POINT: + return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_NEAREST_NEIGHBOR; + default: + MOZ_CRASH("Unknown enum value!"); + } + + return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR; +} + +D2D1_BLEND_MODE D2DBlendMode(uint32_t aMode) +{ + switch (aMode) { + case BLEND_MODE_DARKEN: + return D2D1_BLEND_MODE_DARKEN; + case BLEND_MODE_LIGHTEN: + return D2D1_BLEND_MODE_LIGHTEN; + case BLEND_MODE_MULTIPLY: + return D2D1_BLEND_MODE_MULTIPLY; + case BLEND_MODE_SCREEN: + return D2D1_BLEND_MODE_SCREEN; + case BLEND_MODE_OVERLAY: + return D2D1_BLEND_MODE_OVERLAY; + case BLEND_MODE_COLOR_DODGE: + return D2D1_BLEND_MODE_COLOR_DODGE; + case BLEND_MODE_COLOR_BURN: + return D2D1_BLEND_MODE_COLOR_BURN; + case BLEND_MODE_HARD_LIGHT: + return D2D1_BLEND_MODE_HARD_LIGHT; + case BLEND_MODE_SOFT_LIGHT: + return D2D1_BLEND_MODE_SOFT_LIGHT; + case BLEND_MODE_DIFFERENCE: + return D2D1_BLEND_MODE_DIFFERENCE; + case BLEND_MODE_EXCLUSION: + return D2D1_BLEND_MODE_EXCLUSION; + case BLEND_MODE_HUE: + return D2D1_BLEND_MODE_HUE; + case BLEND_MODE_SATURATION: + return D2D1_BLEND_MODE_SATURATION; + case BLEND_MODE_COLOR: + return D2D1_BLEND_MODE_COLOR; + case BLEND_MODE_LUMINOSITY: + return D2D1_BLEND_MODE_LUMINOSITY; + + default: + MOZ_CRASH("Unknown enum value!"); + } + + return D2D1_BLEND_MODE_DARKEN; +} + +D2D1_MORPHOLOGY_MODE D2DMorphologyMode(uint32_t aMode) +{ + switch (aMode) { + case MORPHOLOGY_OPERATOR_DILATE: + return D2D1_MORPHOLOGY_MODE_DILATE; + case MORPHOLOGY_OPERATOR_ERODE: + return D2D1_MORPHOLOGY_MODE_ERODE; + } + + MOZ_CRASH("Unknown enum value!"); + return D2D1_MORPHOLOGY_MODE_DILATE; +} + +D2D1_TURBULENCE_NOISE D2DTurbulenceNoise(uint32_t aMode) +{ + switch (aMode) { + case TURBULENCE_TYPE_FRACTAL_NOISE: + return D2D1_TURBULENCE_NOISE_FRACTAL_SUM; + case TURBULENCE_TYPE_TURBULENCE: + return D2D1_TURBULENCE_NOISE_TURBULENCE; + } + + MOZ_CRASH("Unknown enum value!"); + return D2D1_TURBULENCE_NOISE_TURBULENCE; +} + +D2D1_COMPOSITE_MODE D2DFilterCompositionMode(uint32_t aMode) +{ + switch (aMode) { + case COMPOSITE_OPERATOR_OVER: + return D2D1_COMPOSITE_MODE_SOURCE_OVER; + case COMPOSITE_OPERATOR_IN: + return D2D1_COMPOSITE_MODE_SOURCE_IN; + case COMPOSITE_OPERATOR_OUT: + return D2D1_COMPOSITE_MODE_SOURCE_OUT; + case COMPOSITE_OPERATOR_ATOP: + return D2D1_COMPOSITE_MODE_SOURCE_ATOP; + case COMPOSITE_OPERATOR_XOR: + return D2D1_COMPOSITE_MODE_XOR; + } + + MOZ_CRASH("Unknown enum value!"); + return D2D1_COMPOSITE_MODE_SOURCE_OVER; +} + +D2D1_CHANNEL_SELECTOR D2DChannelSelector(uint32_t aMode) +{ + switch (aMode) { + case COLOR_CHANNEL_R: + return D2D1_CHANNEL_SELECTOR_R; + case COLOR_CHANNEL_G: + return D2D1_CHANNEL_SELECTOR_G; + case COLOR_CHANNEL_B: + return D2D1_CHANNEL_SELECTOR_B; + case COLOR_CHANNEL_A: + return D2D1_CHANNEL_SELECTOR_A; + } + + MOZ_CRASH("Unknown enum value!"); + return D2D1_CHANNEL_SELECTOR_R; +} + +TemporaryRef<ID2D1Image> GetImageForSourceSurface(DrawTarget *aDT, SourceSurface *aSurface) +{ + if (aDT->IsTiledDrawTarget() || aDT->IsDualDrawTarget()) { + MOZ_CRASH("Incompatible draw target type!"); + return nullptr; + } + switch (aDT->GetBackendType()) { + case BackendType::DIRECT2D1_1: + return static_cast<DrawTargetD2D1*>(aDT)->GetImageForSurface(aSurface, ExtendMode::CLAMP); + case BackendType::DIRECT2D: + return static_cast<DrawTargetD2D*>(aDT)->GetImageForSurface(aSurface); + default: + MOZ_CRASH("Unknown draw target type!"); + return nullptr; + } +} + +uint32_t ConvertValue(FilterType aType, uint32_t aAttribute, uint32_t aValue) +{ + switch (aType) { + case FilterType::COLOR_MATRIX: + if (aAttribute == ATT_COLOR_MATRIX_ALPHA_MODE) { + aValue = D2DAlphaMode(aValue); + } + break; + case FilterType::TRANSFORM: + if (aAttribute == ATT_TRANSFORM_FILTER) { + aValue = D2DAffineTransformInterpolationMode(Filter(aValue)); + } + break; + case FilterType::BLEND: + if (aAttribute == ATT_BLEND_BLENDMODE) { + aValue = D2DBlendMode(aValue); + } + break; + case FilterType::MORPHOLOGY: + if (aAttribute == ATT_MORPHOLOGY_OPERATOR) { + aValue = D2DMorphologyMode(aValue); + } + break; + case FilterType::DISPLACEMENT_MAP: + if (aAttribute == ATT_DISPLACEMENT_MAP_X_CHANNEL || + aAttribute == ATT_DISPLACEMENT_MAP_Y_CHANNEL) { + aValue = D2DChannelSelector(aValue); + } + break; + case FilterType::TURBULENCE: + if (aAttribute == ATT_TURBULENCE_TYPE) { + aValue = D2DTurbulenceNoise(aValue); + } + break; + case FilterType::COMPOSITE: + if (aAttribute == ATT_COMPOSITE_OPERATOR) { + aValue = D2DFilterCompositionMode(aValue); + } + break; + } + + return aValue; +} + +void ConvertValue(FilterType aType, uint32_t aAttribute, IntSize &aValue) +{ + switch (aType) { + case FilterType::MORPHOLOGY: + if (aAttribute == ATT_MORPHOLOGY_RADII) { + aValue.width *= 2; + aValue.width += 1; + aValue.height *= 2; + aValue.height += 1; + } + break; + } +} + +UINT32 +GetD2D1InputForInput(FilterType aType, uint32_t aIndex) +{ + return aIndex; +} + +#define CONVERT_PROP(moz2dname, d2dname) \ + case ATT_##moz2dname: \ + return D2D1_##d2dname + +UINT32 +GetD2D1PropForAttribute(FilterType aType, uint32_t aIndex) +{ + switch (aType) { + case FilterType::COLOR_MATRIX: + switch (aIndex) { + CONVERT_PROP(COLOR_MATRIX_MATRIX, COLORMATRIX_PROP_COLOR_MATRIX); + CONVERT_PROP(COLOR_MATRIX_ALPHA_MODE, COLORMATRIX_PROP_ALPHA_MODE); + } + break; + case FilterType::TRANSFORM: + switch (aIndex) { + CONVERT_PROP(TRANSFORM_MATRIX, 2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX); + CONVERT_PROP(TRANSFORM_FILTER, 2DAFFINETRANSFORM_PROP_INTERPOLATION_MODE); + } + case FilterType::BLEND: + switch (aIndex) { + CONVERT_PROP(BLEND_BLENDMODE, BLEND_PROP_MODE); + } + break; + case FilterType::MORPHOLOGY: + switch (aIndex) { + CONVERT_PROP(MORPHOLOGY_OPERATOR, MORPHOLOGY_PROP_MODE); + } + break; + case FilterType::FLOOD: + switch (aIndex) { + CONVERT_PROP(FLOOD_COLOR, FLOOD_PROP_COLOR); + } + break; + case FilterType::TILE: + switch (aIndex) { + CONVERT_PROP(TILE_SOURCE_RECT, TILE_PROP_RECT); + } + break; + case FilterType::TABLE_TRANSFER: + switch (aIndex) { + CONVERT_PROP(TABLE_TRANSFER_DISABLE_R, TABLETRANSFER_PROP_RED_DISABLE); + CONVERT_PROP(TABLE_TRANSFER_DISABLE_G, TABLETRANSFER_PROP_GREEN_DISABLE); + CONVERT_PROP(TABLE_TRANSFER_DISABLE_B, TABLETRANSFER_PROP_BLUE_DISABLE); + CONVERT_PROP(TABLE_TRANSFER_DISABLE_A, TABLETRANSFER_PROP_ALPHA_DISABLE); + CONVERT_PROP(TABLE_TRANSFER_TABLE_R, TABLETRANSFER_PROP_RED_TABLE); + CONVERT_PROP(TABLE_TRANSFER_TABLE_G, TABLETRANSFER_PROP_GREEN_TABLE); + CONVERT_PROP(TABLE_TRANSFER_TABLE_B, TABLETRANSFER_PROP_BLUE_TABLE); + CONVERT_PROP(TABLE_TRANSFER_TABLE_A, TABLETRANSFER_PROP_ALPHA_TABLE); + } + break; + case FilterType::DISCRETE_TRANSFER: + switch (aIndex) { + CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_R, DISCRETETRANSFER_PROP_RED_DISABLE); + CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_G, DISCRETETRANSFER_PROP_GREEN_DISABLE); + CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_B, DISCRETETRANSFER_PROP_BLUE_DISABLE); + CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_A, DISCRETETRANSFER_PROP_ALPHA_DISABLE); + CONVERT_PROP(DISCRETE_TRANSFER_TABLE_R, DISCRETETRANSFER_PROP_RED_TABLE); + CONVERT_PROP(DISCRETE_TRANSFER_TABLE_G, DISCRETETRANSFER_PROP_GREEN_TABLE); + CONVERT_PROP(DISCRETE_TRANSFER_TABLE_B, DISCRETETRANSFER_PROP_BLUE_TABLE); + CONVERT_PROP(DISCRETE_TRANSFER_TABLE_A, DISCRETETRANSFER_PROP_ALPHA_TABLE); + } + break; + case FilterType::LINEAR_TRANSFER: + switch (aIndex) { + CONVERT_PROP(LINEAR_TRANSFER_DISABLE_R, LINEARTRANSFER_PROP_RED_DISABLE); + CONVERT_PROP(LINEAR_TRANSFER_DISABLE_G, LINEARTRANSFER_PROP_GREEN_DISABLE); + CONVERT_PROP(LINEAR_TRANSFER_DISABLE_B, LINEARTRANSFER_PROP_BLUE_DISABLE); + CONVERT_PROP(LINEAR_TRANSFER_DISABLE_A, LINEARTRANSFER_PROP_ALPHA_DISABLE); + CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_R, LINEARTRANSFER_PROP_RED_Y_INTERCEPT); + CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_G, LINEARTRANSFER_PROP_GREEN_Y_INTERCEPT); + CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_B, LINEARTRANSFER_PROP_BLUE_Y_INTERCEPT); + CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_A, LINEARTRANSFER_PROP_ALPHA_Y_INTERCEPT); + CONVERT_PROP(LINEAR_TRANSFER_SLOPE_R, LINEARTRANSFER_PROP_RED_SLOPE); + CONVERT_PROP(LINEAR_TRANSFER_SLOPE_G, LINEARTRANSFER_PROP_GREEN_SLOPE); + CONVERT_PROP(LINEAR_TRANSFER_SLOPE_B, LINEARTRANSFER_PROP_BLUE_SLOPE); + CONVERT_PROP(LINEAR_TRANSFER_SLOPE_A, LINEARTRANSFER_PROP_ALPHA_SLOPE); + } + break; + case FilterType::GAMMA_TRANSFER: + switch (aIndex) { + CONVERT_PROP(GAMMA_TRANSFER_DISABLE_R, GAMMATRANSFER_PROP_RED_DISABLE); + CONVERT_PROP(GAMMA_TRANSFER_DISABLE_G, GAMMATRANSFER_PROP_GREEN_DISABLE); + CONVERT_PROP(GAMMA_TRANSFER_DISABLE_B, GAMMATRANSFER_PROP_BLUE_DISABLE); + CONVERT_PROP(GAMMA_TRANSFER_DISABLE_A, GAMMATRANSFER_PROP_ALPHA_DISABLE); + CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_R, GAMMATRANSFER_PROP_RED_AMPLITUDE); + CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_G, GAMMATRANSFER_PROP_GREEN_AMPLITUDE); + CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_B, GAMMATRANSFER_PROP_BLUE_AMPLITUDE); + CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_A, GAMMATRANSFER_PROP_ALPHA_AMPLITUDE); + CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_R, GAMMATRANSFER_PROP_RED_EXPONENT); + CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_G, GAMMATRANSFER_PROP_GREEN_EXPONENT); + CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_B, GAMMATRANSFER_PROP_BLUE_EXPONENT); + CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_A, GAMMATRANSFER_PROP_ALPHA_EXPONENT); + CONVERT_PROP(GAMMA_TRANSFER_OFFSET_R, GAMMATRANSFER_PROP_RED_OFFSET); + CONVERT_PROP(GAMMA_TRANSFER_OFFSET_G, GAMMATRANSFER_PROP_GREEN_OFFSET); + CONVERT_PROP(GAMMA_TRANSFER_OFFSET_B, GAMMATRANSFER_PROP_BLUE_OFFSET); + CONVERT_PROP(GAMMA_TRANSFER_OFFSET_A, GAMMATRANSFER_PROP_ALPHA_OFFSET); + } + break; + case FilterType::CONVOLVE_MATRIX: + switch (aIndex) { + CONVERT_PROP(CONVOLVE_MATRIX_BIAS, CONVOLVEMATRIX_PROP_BIAS); + CONVERT_PROP(CONVOLVE_MATRIX_KERNEL_MATRIX, CONVOLVEMATRIX_PROP_KERNEL_MATRIX); + CONVERT_PROP(CONVOLVE_MATRIX_DIVISOR, CONVOLVEMATRIX_PROP_DIVISOR); + CONVERT_PROP(CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH, CONVOLVEMATRIX_PROP_KERNEL_UNIT_LENGTH); + CONVERT_PROP(CONVOLVE_MATRIX_PRESERVE_ALPHA, CONVOLVEMATRIX_PROP_PRESERVE_ALPHA); + } + case FilterType::DISPLACEMENT_MAP: + switch (aIndex) { + CONVERT_PROP(DISPLACEMENT_MAP_SCALE, DISPLACEMENTMAP_PROP_SCALE); + CONVERT_PROP(DISPLACEMENT_MAP_X_CHANNEL, DISPLACEMENTMAP_PROP_X_CHANNEL_SELECT); + CONVERT_PROP(DISPLACEMENT_MAP_Y_CHANNEL, DISPLACEMENTMAP_PROP_Y_CHANNEL_SELECT); + } + break; + case FilterType::TURBULENCE: + switch (aIndex) { + CONVERT_PROP(TURBULENCE_BASE_FREQUENCY, TURBULENCE_PROP_BASE_FREQUENCY); + CONVERT_PROP(TURBULENCE_NUM_OCTAVES, TURBULENCE_PROP_NUM_OCTAVES); + CONVERT_PROP(TURBULENCE_SEED, TURBULENCE_PROP_SEED); + CONVERT_PROP(TURBULENCE_STITCHABLE, TURBULENCE_PROP_STITCHABLE); + CONVERT_PROP(TURBULENCE_TYPE, TURBULENCE_PROP_NOISE); + } + break; + case FilterType::ARITHMETIC_COMBINE: + switch (aIndex) { + CONVERT_PROP(ARITHMETIC_COMBINE_COEFFICIENTS, ARITHMETICCOMPOSITE_PROP_COEFFICIENTS); + } + break; + case FilterType::COMPOSITE: + switch (aIndex) { + CONVERT_PROP(COMPOSITE_OPERATOR, COMPOSITE_PROP_MODE); + } + break; + case FilterType::GAUSSIAN_BLUR: + switch (aIndex) { + CONVERT_PROP(GAUSSIAN_BLUR_STD_DEVIATION, GAUSSIANBLUR_PROP_STANDARD_DEVIATION); + } + break; + case FilterType::DIRECTIONAL_BLUR: + switch (aIndex) { + CONVERT_PROP(DIRECTIONAL_BLUR_STD_DEVIATION, DIRECTIONALBLUR_PROP_STANDARD_DEVIATION); + CONVERT_PROP(DIRECTIONAL_BLUR_DIRECTION, DIRECTIONALBLUR_PROP_ANGLE); + } + break; + case FilterType::POINT_DIFFUSE: + switch (aIndex) { + CONVERT_PROP(POINT_DIFFUSE_DIFFUSE_CONSTANT, POINTDIFFUSE_PROP_DIFFUSE_CONSTANT); + CONVERT_PROP(POINT_DIFFUSE_POSITION, POINTDIFFUSE_PROP_LIGHT_POSITION); + CONVERT_PROP(POINT_DIFFUSE_COLOR, POINTDIFFUSE_PROP_COLOR); + CONVERT_PROP(POINT_DIFFUSE_SURFACE_SCALE, POINTDIFFUSE_PROP_SURFACE_SCALE); + CONVERT_PROP(POINT_DIFFUSE_KERNEL_UNIT_LENGTH, POINTDIFFUSE_PROP_KERNEL_UNIT_LENGTH); + } + break; + case FilterType::SPOT_DIFFUSE: + switch (aIndex) { + CONVERT_PROP(SPOT_DIFFUSE_DIFFUSE_CONSTANT, SPOTDIFFUSE_PROP_DIFFUSE_CONSTANT); + CONVERT_PROP(SPOT_DIFFUSE_POINTS_AT, SPOTDIFFUSE_PROP_POINTS_AT); + CONVERT_PROP(SPOT_DIFFUSE_FOCUS, SPOTDIFFUSE_PROP_FOCUS); + CONVERT_PROP(SPOT_DIFFUSE_LIMITING_CONE_ANGLE, SPOTDIFFUSE_PROP_LIMITING_CONE_ANGLE); + CONVERT_PROP(SPOT_DIFFUSE_POSITION, SPOTDIFFUSE_PROP_LIGHT_POSITION); + CONVERT_PROP(SPOT_DIFFUSE_COLOR, SPOTDIFFUSE_PROP_COLOR); + CONVERT_PROP(SPOT_DIFFUSE_SURFACE_SCALE, SPOTDIFFUSE_PROP_SURFACE_SCALE); + CONVERT_PROP(SPOT_DIFFUSE_KERNEL_UNIT_LENGTH, SPOTDIFFUSE_PROP_KERNEL_UNIT_LENGTH); + } + break; + case FilterType::DISTANT_DIFFUSE: + switch (aIndex) { + CONVERT_PROP(DISTANT_DIFFUSE_DIFFUSE_CONSTANT, DISTANTDIFFUSE_PROP_DIFFUSE_CONSTANT); + CONVERT_PROP(DISTANT_DIFFUSE_AZIMUTH, DISTANTDIFFUSE_PROP_AZIMUTH); + CONVERT_PROP(DISTANT_DIFFUSE_ELEVATION, DISTANTDIFFUSE_PROP_ELEVATION); + CONVERT_PROP(DISTANT_DIFFUSE_COLOR, DISTANTDIFFUSE_PROP_COLOR); + CONVERT_PROP(DISTANT_DIFFUSE_SURFACE_SCALE, DISTANTDIFFUSE_PROP_SURFACE_SCALE); + CONVERT_PROP(DISTANT_DIFFUSE_KERNEL_UNIT_LENGTH, DISTANTDIFFUSE_PROP_KERNEL_UNIT_LENGTH); + } + break; + case FilterType::POINT_SPECULAR: + switch (aIndex) { + CONVERT_PROP(POINT_SPECULAR_SPECULAR_CONSTANT, POINTSPECULAR_PROP_SPECULAR_CONSTANT); + CONVERT_PROP(POINT_SPECULAR_SPECULAR_EXPONENT, POINTSPECULAR_PROP_SPECULAR_EXPONENT); + CONVERT_PROP(POINT_SPECULAR_POSITION, POINTSPECULAR_PROP_LIGHT_POSITION); + CONVERT_PROP(POINT_SPECULAR_COLOR, POINTSPECULAR_PROP_COLOR); + CONVERT_PROP(POINT_SPECULAR_SURFACE_SCALE, POINTSPECULAR_PROP_SURFACE_SCALE); + CONVERT_PROP(POINT_SPECULAR_KERNEL_UNIT_LENGTH, POINTSPECULAR_PROP_KERNEL_UNIT_LENGTH); + } + break; + case FilterType::SPOT_SPECULAR: + switch (aIndex) { + CONVERT_PROP(SPOT_SPECULAR_SPECULAR_CONSTANT, SPOTSPECULAR_PROP_SPECULAR_CONSTANT); + CONVERT_PROP(SPOT_SPECULAR_SPECULAR_EXPONENT, SPOTSPECULAR_PROP_SPECULAR_EXPONENT); + CONVERT_PROP(SPOT_SPECULAR_POINTS_AT, SPOTSPECULAR_PROP_POINTS_AT); + CONVERT_PROP(SPOT_SPECULAR_FOCUS, SPOTSPECULAR_PROP_FOCUS); + CONVERT_PROP(SPOT_SPECULAR_LIMITING_CONE_ANGLE, SPOTSPECULAR_PROP_LIMITING_CONE_ANGLE); + CONVERT_PROP(SPOT_SPECULAR_POSITION, SPOTSPECULAR_PROP_LIGHT_POSITION); + CONVERT_PROP(SPOT_SPECULAR_COLOR, SPOTSPECULAR_PROP_COLOR); + CONVERT_PROP(SPOT_SPECULAR_SURFACE_SCALE, SPOTSPECULAR_PROP_SURFACE_SCALE); + CONVERT_PROP(SPOT_SPECULAR_KERNEL_UNIT_LENGTH, SPOTSPECULAR_PROP_KERNEL_UNIT_LENGTH); + } + break; + case FilterType::DISTANT_SPECULAR: + switch (aIndex) { + CONVERT_PROP(DISTANT_SPECULAR_SPECULAR_CONSTANT, DISTANTSPECULAR_PROP_SPECULAR_CONSTANT); + CONVERT_PROP(DISTANT_SPECULAR_SPECULAR_EXPONENT, DISTANTSPECULAR_PROP_SPECULAR_EXPONENT); + CONVERT_PROP(DISTANT_SPECULAR_AZIMUTH, DISTANTSPECULAR_PROP_AZIMUTH); + CONVERT_PROP(DISTANT_SPECULAR_ELEVATION, DISTANTSPECULAR_PROP_ELEVATION); + CONVERT_PROP(DISTANT_SPECULAR_COLOR, DISTANTSPECULAR_PROP_COLOR); + CONVERT_PROP(DISTANT_SPECULAR_SURFACE_SCALE, DISTANTSPECULAR_PROP_SURFACE_SCALE); + CONVERT_PROP(DISTANT_SPECULAR_KERNEL_UNIT_LENGTH, DISTANTSPECULAR_PROP_KERNEL_UNIT_LENGTH); + } + break; + case FilterType::CROP: + switch (aIndex) { + CONVERT_PROP(CROP_RECT, CROP_PROP_RECT); + } + break; + } + + return UINT32_MAX; +} + +bool +GetD2D1PropsForIntSize(FilterType aType, uint32_t aIndex, UINT32 *aPropWidth, UINT32 *aPropHeight) +{ + switch (aType) { + case FilterType::MORPHOLOGY: + if (aIndex == ATT_MORPHOLOGY_RADII) { + *aPropWidth = D2D1_MORPHOLOGY_PROP_WIDTH; + *aPropHeight = D2D1_MORPHOLOGY_PROP_HEIGHT; + return true; + } + break; + } + return false; +} + +static inline REFCLSID GetCLDIDForFilterType(FilterType aType) +{ + switch (aType) { + case FilterType::COLOR_MATRIX: + return CLSID_D2D1ColorMatrix; + case FilterType::TRANSFORM: + return CLSID_D2D12DAffineTransform; + case FilterType::BLEND: + return CLSID_D2D1Blend; + case FilterType::MORPHOLOGY: + return CLSID_D2D1Morphology; + case FilterType::FLOOD: + return CLSID_D2D1Flood; + case FilterType::TILE: + return CLSID_D2D1Tile; + case FilterType::TABLE_TRANSFER: + return CLSID_D2D1TableTransfer; + case FilterType::LINEAR_TRANSFER: + return CLSID_D2D1LinearTransfer; + case FilterType::DISCRETE_TRANSFER: + return CLSID_D2D1DiscreteTransfer; + case FilterType::GAMMA_TRANSFER: + return CLSID_D2D1GammaTransfer; + case FilterType::DISPLACEMENT_MAP: + return CLSID_D2D1DisplacementMap; + case FilterType::TURBULENCE: + return CLSID_D2D1Turbulence; + case FilterType::ARITHMETIC_COMBINE: + return CLSID_D2D1ArithmeticComposite; + case FilterType::COMPOSITE: + return CLSID_D2D1Composite; + case FilterType::GAUSSIAN_BLUR: + return CLSID_D2D1GaussianBlur; + case FilterType::DIRECTIONAL_BLUR: + return CLSID_D2D1DirectionalBlur; + case FilterType::POINT_DIFFUSE: + return CLSID_D2D1PointDiffuse; + case FilterType::POINT_SPECULAR: + return CLSID_D2D1PointSpecular; + case FilterType::SPOT_DIFFUSE: + return CLSID_D2D1SpotDiffuse; + case FilterType::SPOT_SPECULAR: + return CLSID_D2D1SpotSpecular; + case FilterType::DISTANT_DIFFUSE: + return CLSID_D2D1DistantDiffuse; + case FilterType::DISTANT_SPECULAR: + return CLSID_D2D1DistantSpecular; + case FilterType::CROP: + return CLSID_D2D1Crop; + case FilterType::PREMULTIPLY: + return CLSID_D2D1Premultiply; + case FilterType::UNPREMULTIPLY: + return CLSID_D2D1UnPremultiply; + } + return GUID_NULL; +} + +static bool +IsTransferFilterType(FilterType aType) +{ + switch (aType) { + case FilterType::LINEAR_TRANSFER: + case FilterType::GAMMA_TRANSFER: + case FilterType::TABLE_TRANSFER: + case FilterType::DISCRETE_TRANSFER: + return true; + default: + return false; + } +} + +/* static */ +TemporaryRef<FilterNode> +FilterNodeD2D1::Create(ID2D1DeviceContext *aDC, FilterType aType) +{ + if (aType == FilterType::CONVOLVE_MATRIX) { + return new FilterNodeConvolveD2D1(aDC); + } + + RefPtr<ID2D1Effect> effect; + HRESULT hr; + + hr = aDC->CreateEffect(GetCLDIDForFilterType(aType), byRef(effect)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create effect for FilterType: " << hexa(hr); + return nullptr; + } + + RefPtr<FilterNodeD2D1> filter = new FilterNodeD2D1(effect, aType); + + if (IsTransferFilterType(aType) || aType == FilterType::COLOR_MATRIX) { + // These filters can produce non-transparent output from transparent + // input pixels, and we want them to have an unbounded output region. + filter = new FilterNodeExtendInputAdapterD2D1(aDC, filter, aType); + } + + if (IsTransferFilterType(aType)) { + // Component transfer filters should appear to apply on unpremultiplied + // colors, but the D2D1 effects apply on premultiplied colors. + filter = new FilterNodePremultiplyAdapterD2D1(aDC, filter, aType); + } + + return filter.forget(); +} + +void +FilterNodeD2D1::InitUnmappedProperties() +{ + switch (mType) { + case FilterType::TRANSFORM: + mEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_BORDER_MODE, D2D1_BORDER_MODE_HARD); + break; + default: + break; + } +} + +void +FilterNodeD2D1::SetInput(uint32_t aIndex, SourceSurface *aSurface) +{ + UINT32 input = GetD2D1InputForInput(mType, aIndex); + ID2D1Effect* effect = InputEffect(); + MOZ_ASSERT(input < effect->GetInputCount()); + + if (mType == FilterType::COMPOSITE) { + UINT32 inputCount = effect->GetInputCount(); + + if (aIndex == inputCount - 1 && aSurface == nullptr) { + effect->SetInputCount(inputCount - 1); + } else if (aIndex >= inputCount && aSurface) { + effect->SetInputCount(aIndex + 1); + } + } + + MOZ_ASSERT(input < effect->GetInputCount()); + + mInputSurfaces.resize(effect->GetInputCount()); + mInputFilters.resize(effect->GetInputCount()); + + // In order to convert aSurface into an ID2D1Image, we need to know what + // DrawTarget we paint into. However, the same FilterNode object can be + // used on different DrawTargets, so we need to hold on to the SourceSurface + // objects and delay the conversion until we're actually painted and know + // our target DrawTarget. + // The conversion happens in WillDraw(). + + mInputSurfaces[input] = aSurface; + mInputFilters[input] = nullptr; + + // Clear the existing image from the effect. + effect->SetInput(input, nullptr); +} + +void +FilterNodeD2D1::SetInput(uint32_t aIndex, FilterNode *aFilter) +{ + UINT32 input = GetD2D1InputForInput(mType, aIndex); + ID2D1Effect* effect = InputEffect(); + + if (mType == FilterType::COMPOSITE) { + UINT32 inputCount = effect->GetInputCount(); + + if (aIndex == inputCount - 1 && aFilter == nullptr) { + effect->SetInputCount(inputCount - 1); + } else if (aIndex >= inputCount && aFilter) { + effect->SetInputCount(aIndex + 1); + } + } + + MOZ_ASSERT(input < effect->GetInputCount()); + + if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) { + gfxWarning() << "Unknown input FilterNode set on effect."; + MOZ_ASSERT(0); + return; + } + + FilterNodeD2D1* filter = static_cast<FilterNodeD2D1*>(aFilter); + + mInputSurfaces.resize(effect->GetInputCount()); + mInputFilters.resize(effect->GetInputCount()); + + // We hold on to the FilterNode object so that we can call WillDraw() on it. + mInputSurfaces[input] = nullptr; + mInputFilters[input] = filter; + + if (filter) { + effect->SetInputEffect(input, filter->OutputEffect()); + } +} + +void +FilterNodeD2D1::WillDraw(DrawTarget *aDT) +{ + // Convert input SourceSurfaces into ID2D1Images and set them on the effect. + for (size_t inputIndex = 0; inputIndex < mInputSurfaces.size(); inputIndex++) { + if (mInputSurfaces[inputIndex]) { + ID2D1Effect* effect = InputEffect(); + RefPtr<ID2D1Image> image = GetImageForSourceSurface(aDT, mInputSurfaces[inputIndex]); + effect->SetInput(inputIndex, image); + } + } + + // Call WillDraw() on our input filters. + for (std::vector<RefPtr<FilterNodeD2D1>>::iterator it = mInputFilters.begin(); + it != mInputFilters.end(); it++) { + if (*it) { + (*it)->WillDraw(aDT); + } + } +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, uint32_t aValue) +{ + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + if (mType == FilterType::TURBULENCE && aIndex == ATT_TURBULENCE_BASE_FREQUENCY) { + mEffect->SetValue(input, D2D1::Vector2F(FLOAT(aValue), FLOAT(aValue))); + return; + } else if (mType == FilterType::DIRECTIONAL_BLUR && aIndex == ATT_DIRECTIONAL_BLUR_DIRECTION) { + mEffect->SetValue(input, aValue == BLUR_DIRECTION_X ? 0 : 90.0f); + return; + } + + mEffect->SetValue(input, ConvertValue(mType, aIndex, aValue)); +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, Float aValue) +{ + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + mEffect->SetValue(input, aValue); +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Point &aValue) +{ + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + mEffect->SetValue(input, D2DPoint(aValue)); +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Matrix5x4 &aValue) +{ + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + mEffect->SetValue(input, D2DMatrix5x4(aValue)); +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Point3D &aValue) +{ + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + mEffect->SetValue(input, D2DVector3D(aValue)); +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Size &aValue) +{ + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + mEffect->SetValue(input, D2D1::Vector2F(aValue.width, aValue.height)); +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntSize &aValue) +{ + UINT32 widthProp, heightProp; + + if (!GetD2D1PropsForIntSize(mType, aIndex, &widthProp, &heightProp)) { + return; + } + + IntSize value = aValue; + ConvertValue(mType, aIndex, value); + + mEffect->SetValue(widthProp, (UINT)value.width); + mEffect->SetValue(heightProp, (UINT)value.height); +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Color &aValue) +{ + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + switch (mType) { + case FilterType::POINT_DIFFUSE: + case FilterType::SPOT_DIFFUSE: + case FilterType::DISTANT_DIFFUSE: + case FilterType::POINT_SPECULAR: + case FilterType::SPOT_SPECULAR: + case FilterType::DISTANT_SPECULAR: + mEffect->SetValue(input, D2D1::Vector3F(aValue.r, aValue.g, aValue.b)); + break; + default: + mEffect->SetValue(input, D2D1::Vector4F(aValue.r * aValue.a, aValue.g * aValue.a, aValue.b * aValue.a, aValue.a)); + } +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Rect &aValue) +{ + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + mEffect->SetValue(input, D2DRect(aValue)); +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntRect &aValue) +{ + if (mType == FilterType::TURBULENCE) { + MOZ_ASSERT(aIndex == ATT_TURBULENCE_RECT); + + mEffect->SetValue(D2D1_TURBULENCE_PROP_OFFSET, D2D1::Vector2F(Float(aValue.x), Float(aValue.y))); + mEffect->SetValue(D2D1_TURBULENCE_PROP_SIZE, D2D1::Vector2F(Float(aValue.width), Float(aValue.height))); + return; + } + + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + mEffect->SetValue(input, D2D1::RectF(Float(aValue.x), Float(aValue.y), + Float(aValue.XMost()), Float(aValue.YMost()))); +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, bool aValue) +{ + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + mEffect->SetValue(input, (BOOL)aValue); +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Float *aValues, uint32_t aSize) +{ + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + mEffect->SetValue(input, (BYTE*)aValues, sizeof(Float) * aSize); +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntPoint &aValue) +{ + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + mEffect->SetValue(input, D2DPoint(aValue)); +} + +void +FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Matrix &aMatrix) +{ + UINT32 input = GetD2D1PropForAttribute(mType, aIndex); + MOZ_ASSERT(input < mEffect->GetPropertyCount()); + + mEffect->SetValue(input, D2DMatrix(aMatrix)); +} + +FilterNodeConvolveD2D1::FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC) + : FilterNodeD2D1(nullptr, FilterType::CONVOLVE_MATRIX) + , mEdgeMode(EDGE_MODE_DUPLICATE) +{ + // Correctly handling the interaction of edge mode and source rect is a bit + // tricky with D2D1 effects. We want the edge mode to only apply outside of + // the source rect (as specified by the ATT_CONVOLVE_MATRIX_SOURCE_RECT + // attribute). So if our input surface or filter is smaller than the source + // rect, we need to add transparency around it until we reach the edges of + // the source rect, and only then do any repeating or edge duplicating. + // Unfortunately, the border effect does not have a source rect attribute - + // it only looks at the output rect of its input filter or surface. So we use + // our custom ExtendInput effect to adjust the output rect of our input. + // All of this is only necessary when our edge mode is not EDGE_MODE_NONE, so + // we update the filter chain dynamically in UpdateChain(). + + HRESULT hr; + + hr = aDC->CreateEffect(CLSID_D2D1ConvolveMatrix, byRef(mEffect)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create ConvolveMatrix filter!"; + return; + } + + mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_BORDER_MODE, D2D1_BORDER_MODE_SOFT); + + hr = aDC->CreateEffect(CLSID_ExtendInputEffect, byRef(mExtendInputEffect)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create ConvolveMatrix filter!"; + return; + } + + hr = aDC->CreateEffect(CLSID_D2D1Border, byRef(mBorderEffect)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create ConvolveMatrix filter!"; + return; + } + + mBorderEffect->SetInputEffect(0, mExtendInputEffect.get()); + + UpdateChain(); + UpdateSourceRect(); +} + +void +FilterNodeConvolveD2D1::SetInput(uint32_t aIndex, FilterNode *aFilter) +{ + FilterNodeD2D1::SetInput(aIndex, aFilter); + + UpdateChain(); +} + +void +FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, uint32_t aValue) +{ + if (aIndex != ATT_CONVOLVE_MATRIX_EDGE_MODE) { + return FilterNodeD2D1::SetAttribute(aIndex, aValue); + } + + mEdgeMode = (ConvolveMatrixEdgeMode)aValue; + + UpdateChain(); +} + +ID2D1Effect* +FilterNodeConvolveD2D1::InputEffect() +{ + return mEdgeMode == EDGE_MODE_NONE ? mEffect.get() : mExtendInputEffect.get(); +} + +void +FilterNodeConvolveD2D1::UpdateChain() +{ + // The shape of the filter graph: + // + // EDGE_MODE_NONE: + // input --> convolvematrix + // + // EDGE_MODE_DUPLICATE or EDGE_MODE_WRAP: + // input --> extendinput --> border --> convolvematrix + // + // mEffect is convolvematrix. + + if (mEdgeMode != EDGE_MODE_NONE) { + mEffect->SetInputEffect(0, mBorderEffect.get()); + } + + RefPtr<ID2D1Effect> inputEffect; + if (mInputFilters.size() > 0 && mInputFilters[0]) { + inputEffect = mInputFilters[0]->OutputEffect(); + } + InputEffect()->SetInputEffect(0, inputEffect); + + if (mEdgeMode == EDGE_MODE_DUPLICATE) { + mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X, D2D1_BORDER_EDGE_MODE_CLAMP); + mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_CLAMP); + } else if (mEdgeMode == EDGE_MODE_WRAP) { + mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X, D2D1_BORDER_EDGE_MODE_WRAP); + mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_WRAP); + } +} + +void +FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, const IntSize &aValue) +{ + if (aIndex != ATT_CONVOLVE_MATRIX_KERNEL_SIZE) { + MOZ_ASSERT(false); + return; + } + + mKernelSize = aValue; + + mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_KERNEL_SIZE_X, aValue.width); + mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_KERNEL_SIZE_Y, aValue.height); + + UpdateOffset(); +} + +void +FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, const IntPoint &aValue) +{ + if (aIndex != ATT_CONVOLVE_MATRIX_TARGET) { + MOZ_ASSERT(false); + return; + } + + mTarget = aValue; + + UpdateOffset(); +} + +void +FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, const IntRect &aValue) +{ + if (aIndex != ATT_CONVOLVE_MATRIX_SOURCE_RECT) { + MOZ_ASSERT(false); + return; + } + + mSourceRect = aValue; + + UpdateSourceRect(); +} + +void +FilterNodeConvolveD2D1::UpdateOffset() +{ + D2D1_VECTOR_2F vector = + D2D1::Vector2F((Float(mKernelSize.width) - 1.0f) / 2.0f - Float(mTarget.x), + (Float(mKernelSize.height) - 1.0f) / 2.0f - Float(mTarget.y)); + + mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_KERNEL_OFFSET, vector); +} + +void +FilterNodeConvolveD2D1::UpdateSourceRect() +{ + mExtendInputEffect->SetValue(EXTENDINPUT_PROP_OUTPUT_RECT, + D2D1::Vector4F(Float(mSourceRect.x), Float(mSourceRect.y), + Float(mSourceRect.XMost()), Float(mSourceRect.YMost()))); +} + +FilterNodeExtendInputAdapterD2D1::FilterNodeExtendInputAdapterD2D1(ID2D1DeviceContext *aDC, + FilterNodeD2D1 *aFilterNode, FilterType aType) + : FilterNodeD2D1(aFilterNode->MainEffect(), aType) + , mWrappedFilterNode(aFilterNode) +{ + // We have an mEffect that looks at the bounds of the input effect, and we + // want mEffect to regard its input as unbounded. So we take the input, + // pipe it through an ExtendInput effect (which has an infinite output rect + // by default), and feed the resulting unbounded composition into mEffect. + + HRESULT hr; + + hr = aDC->CreateEffect(CLSID_ExtendInputEffect, byRef(mExtendInputEffect)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create extend input effect for filter: " << hexa(hr); + return; + } + + aFilterNode->InputEffect()->SetInputEffect(0, mExtendInputEffect.get()); +} + +FilterNodePremultiplyAdapterD2D1::FilterNodePremultiplyAdapterD2D1(ID2D1DeviceContext *aDC, + FilterNodeD2D1 *aFilterNode, FilterType aType) + : FilterNodeD2D1(aFilterNode->MainEffect(), aType) +{ + // D2D1 component transfer effects do strange things when it comes to + // premultiplication. + // For our purposes we only need the transfer filters to apply straight to + // unpremultiplied source channels and output unpremultiplied results. + // However, the D2D1 effects are designed differently: They can apply to both + // premultiplied and unpremultiplied inputs, and they always premultiply + // their result - at least in those color channels that have not been + // disabled. + // In order to determine whether the input needs to be unpremultiplied as + // part of the transfer, the effect consults the alpha mode metadata of the + // input surface or the input effect. We don't have such a concept in Moz2D, + // and giving Moz2D users different results based on something that cannot be + // influenced through Moz2D APIs seems like a bad idea. + // We solve this by applying a premultiply effect to the input before feeding + // it into the transfer effect. The premultiply effect always premultiplies + // regardless of any alpha mode metadata on inputs, and it always marks its + // output as premultiplied so that the transfer effect will unpremultiply + // consistently. Feeding always-premultiplied input into the transfer effect + // also avoids another problem that would appear when individual color + // channels disable the transfer: In that case, the disabled channels would + // pass through unchanged in their unpremultiplied form and the other + // channels would be premultiplied, giving a mixed result. + // But since we now ensure that the input is premultiplied, disabled channels + // will pass premultiplied values through to the result, which is consistent + // with the enabled channels. + // We also add an unpremultiply effect that postprocesses the result of the + // transfer effect because getting unpremultiplied results from the transfer + // filters is part of the FilterNode API. + HRESULT hr; + + hr = aDC->CreateEffect(CLSID_D2D1Premultiply, byRef(mPrePremultiplyEffect)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create ComponentTransfer filter!"; + return; + } + + hr = aDC->CreateEffect(CLSID_D2D1UnPremultiply, byRef(mPostUnpremultiplyEffect)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create ComponentTransfer filter!"; + return; + } + + aFilterNode->InputEffect()->SetInputEffect(0, mPrePremultiplyEffect.get()); + mPostUnpremultiplyEffect->SetInputEffect(0, aFilterNode->OutputEffect()); +} + +} +} diff --git a/gfx/2d/FilterNodeD2D1.h b/gfx/2d/FilterNodeD2D1.h new file mode 100644 index 000000000..612dbfbef --- /dev/null +++ b/gfx/2d/FilterNodeD2D1.h @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_FILTERNODED2D1_H_ +#define MOZILLA_GFX_FILTERNODED2D1_H_ + +#include "2D.h" +#include "Filters.h" +#include <vector> +#include <d2d1_1.h> +#include <cguid.h> + +namespace mozilla { +namespace gfx { + +class FilterNodeD2D1 : public FilterNode +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeD2D1) + static TemporaryRef<FilterNode> Create(ID2D1DeviceContext *aDC, FilterType aType); + + FilterNodeD2D1(ID2D1Effect *aEffect, FilterType aType) + : mEffect(aEffect) + , mType(aType) + { + InitUnmappedProperties(); + } + + virtual FilterBackend GetBackendType() { return FILTER_BACKEND_DIRECT2D1_1; } + + virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface); + virtual void SetInput(uint32_t aIndex, FilterNode *aFilter); + + virtual void SetAttribute(uint32_t aIndex, uint32_t aValue); + virtual void SetAttribute(uint32_t aIndex, Float aValue); + virtual void SetAttribute(uint32_t aIndex, const Point &aValue); + virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &aValue); + virtual void SetAttribute(uint32_t aIndex, const Point3D &aValue); + virtual void SetAttribute(uint32_t aIndex, const Size &aValue); + virtual void SetAttribute(uint32_t aIndex, const IntSize &aValue); + virtual void SetAttribute(uint32_t aIndex, const Color &aValue); + virtual void SetAttribute(uint32_t aIndex, const Rect &aValue); + virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue); + virtual void SetAttribute(uint32_t aIndex, bool aValue); + virtual void SetAttribute(uint32_t aIndex, const Float *aValues, uint32_t aSize); + virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue); + virtual void SetAttribute(uint32_t aIndex, const Matrix &aValue); + + // Called by DrawTarget before it draws our OutputEffect, and recursively + // by the filter nodes that have this filter as one of their inputs. This + // gives us a chance to convert any input surfaces to the target format for + // the DrawTarget that we will draw to. + virtual void WillDraw(DrawTarget *aDT); + + virtual ID2D1Effect* MainEffect() { return mEffect.get(); } + virtual ID2D1Effect* InputEffect() { return mEffect.get(); } + virtual ID2D1Effect* OutputEffect() { return mEffect.get(); } + +protected: + friend class DrawTargetD2D1; + friend class DrawTargetD2D; + friend class FilterNodeConvolveD2D1; + + void InitUnmappedProperties(); + + RefPtr<ID2D1Effect> mEffect; + std::vector<RefPtr<FilterNodeD2D1>> mInputFilters; + std::vector<RefPtr<SourceSurface>> mInputSurfaces; + FilterType mType; +}; + +class FilterNodeConvolveD2D1 : public FilterNodeD2D1 +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeConvolveD2D1) + FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC); + + virtual void SetInput(uint32_t aIndex, FilterNode *aFilter); + + virtual void SetAttribute(uint32_t aIndex, uint32_t aValue); + virtual void SetAttribute(uint32_t aIndex, const IntSize &aValue); + virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue); + virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue); + + virtual ID2D1Effect* InputEffect() override; + +private: + void UpdateChain(); + void UpdateOffset(); + void UpdateSourceRect(); + + RefPtr<ID2D1Effect> mExtendInputEffect; + RefPtr<ID2D1Effect> mBorderEffect; + ConvolveMatrixEdgeMode mEdgeMode; + IntPoint mTarget; + IntSize mKernelSize; + IntRect mSourceRect; +}; + +class FilterNodeExtendInputAdapterD2D1 : public FilterNodeD2D1 +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeExtendInputAdapterD2D1) + FilterNodeExtendInputAdapterD2D1(ID2D1DeviceContext *aDC, FilterNodeD2D1 *aFilterNode, FilterType aType); + + virtual ID2D1Effect* InputEffect() override { return mExtendInputEffect.get(); } + virtual ID2D1Effect* OutputEffect() override { return mWrappedFilterNode->OutputEffect(); } + +private: + RefPtr<FilterNodeD2D1> mWrappedFilterNode; + RefPtr<ID2D1Effect> mExtendInputEffect; +}; + +class FilterNodePremultiplyAdapterD2D1 : public FilterNodeD2D1 +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodePremultiplyAdapterD2D1) + FilterNodePremultiplyAdapterD2D1(ID2D1DeviceContext *aDC, FilterNodeD2D1 *aFilterNode, FilterType aType); + + virtual ID2D1Effect* InputEffect() override { return mPrePremultiplyEffect.get(); } + virtual ID2D1Effect* OutputEffect() override { return mPostUnpremultiplyEffect.get(); } + +private: + RefPtr<ID2D1Effect> mPrePremultiplyEffect; + RefPtr<ID2D1Effect> mPostUnpremultiplyEffect; +}; + +} +} + +#endif diff --git a/gfx/2d/FilterNodeSoftware.cpp b/gfx/2d/FilterNodeSoftware.cpp new file mode 100644 index 000000000..64e4026d2 --- /dev/null +++ b/gfx/2d/FilterNodeSoftware.cpp @@ -0,0 +1,3545 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define _USE_MATH_DEFINES + +#include <cmath> +#include "DataSurfaceHelpers.h" +#include "FilterNodeSoftware.h" +#include "2D.h" +#include "Tools.h" +#include "Blur.h" +#include <map> +#include "FilterProcessing.h" +#include "Logging.h" +#include "mozilla/PodOperations.h" +#include "mozilla/DebugOnly.h" + +// #define DEBUG_DUMP_SURFACES + +#ifdef DEBUG_DUMP_SURFACES +#include "gfxUtils.h" // not part of Moz2D +#endif + +namespace mozilla { +namespace gfx { + +namespace { + +/** + * This class provides a way to get a pow() results in constant-time. It works + * by caching 256 values for bases between 0 and 1 and a fixed exponent. + **/ +class PowCache +{ +public: + PowCache() + { + CacheForExponent(0.0f); + } + + void CacheForExponent(Float aExponent) + { + mExponent = aExponent; + int numPreSquares = 0; + while (numPreSquares < 5 && mExponent > (1 << (numPreSquares + 2))) { + numPreSquares++; + } + mNumPowTablePreSquares = numPreSquares; + for (size_t i = 0; i < sCacheSize; i++) { + // sCacheSize is chosen in such a way that a takes values + // from 0.0 to 1.0 inclusive. + Float a = i / Float(1 << sCacheIndexPrecisionBits); + MOZ_ASSERT(0.0f <= a && a <= 1.0f, "We only want to cache for bases between 0 and 1."); + + for (int j = 0; j < mNumPowTablePreSquares; j++) { + a = sqrt(a); + } + uint32_t cachedInt = pow(a, mExponent) * (1 << sOutputIntPrecisionBits); + MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)), "mPowCache integer type too small"); + + mPowTable[i] = cachedInt; + } + } + + uint16_t Pow(uint16_t aBase) + { + // Results should be similar to what the following code would produce: + // Float x = Float(aBase) / (1 << sInputIntPrecisionBits); + // return uint16_t(pow(x, mExponent) * (1 << sOutputIntPrecisionBits)); + + MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits), "aBase needs to be between 0 and 1!"); + + uint32_t a = aBase; + for (int j = 0; j < mNumPowTablePreSquares; j++) { + a = a * a >> sInputIntPrecisionBits; + } + uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits); + MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access"); + return mPowTable[i]; + } + + static const int sInputIntPrecisionBits = 15; + static const int sOutputIntPrecisionBits = 15; + static const int sCacheIndexPrecisionBits = 7; + +private: + static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1; + + Float mExponent; + int mNumPowTablePreSquares; + uint16_t mPowTable[sCacheSize]; +}; + +class PointLightSoftware +{ +public: + bool SetAttribute(uint32_t aIndex, Float) { return false; } + bool SetAttribute(uint32_t aIndex, const Point3D &); + void Prepare() {} + Point3D GetVectorToLight(const Point3D &aTargetPoint); + uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight); + +private: + Point3D mPosition; +}; + +class SpotLightSoftware +{ +public: + SpotLightSoftware(); + bool SetAttribute(uint32_t aIndex, Float); + bool SetAttribute(uint32_t aIndex, const Point3D &); + void Prepare(); + Point3D GetVectorToLight(const Point3D &aTargetPoint); + uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight); + +private: + Point3D mPosition; + Point3D mPointsAt; + Point3D mVectorFromFocusPointToLight; + Float mSpecularFocus; + Float mLimitingConeAngle; + Float mLimitingConeCos; + PowCache mPowCache; +}; + +class DistantLightSoftware +{ +public: + DistantLightSoftware(); + bool SetAttribute(uint32_t aIndex, Float); + bool SetAttribute(uint32_t aIndex, const Point3D &) { return false; } + void Prepare(); + Point3D GetVectorToLight(const Point3D &aTargetPoint); + uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight); + +private: + Float mAzimuth; + Float mElevation; + Point3D mVectorToLight; +}; + +class DiffuseLightingSoftware +{ +public: + DiffuseLightingSoftware(); + bool SetAttribute(uint32_t aIndex, Float); + void Prepare() {} + uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight, + uint32_t aColor); + +private: + Float mDiffuseConstant; +}; + +class SpecularLightingSoftware +{ +public: + SpecularLightingSoftware(); + bool SetAttribute(uint32_t aIndex, Float); + void Prepare(); + uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight, + uint32_t aColor); + +private: + Float mSpecularConstant; + Float mSpecularExponent; + uint32_t mSpecularConstantInt; + PowCache mPowCache; +}; + +} // unnamed namespace + +// from xpcom/ds/nsMathUtils.h +static int32_t +NS_lround(double x) +{ + return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5); +} + +TemporaryRef<DataSourceSurface> +CloneAligned(DataSourceSurface* aSource) +{ + return CreateDataSourceSurfaceByCloning(aSource); +} + +static void +FillRectWithPixel(DataSourceSurface *aSurface, const IntRect &aFillRect, IntPoint aPixelPos) +{ + MOZ_ASSERT(!aFillRect.Overflows()); + MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect), + "aFillRect needs to be completely inside the surface"); + MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos), + "aPixelPos needs to be inside the surface"); + + int32_t stride = aSurface->Stride(); + uint8_t* sourcePixelData = DataAtOffset(aSurface, aPixelPos); + uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft()); + int bpp = BytesPerPixel(aSurface->GetFormat()); + + // Fill the first row by hand. + if (bpp == 4) { + uint32_t sourcePixel = *(uint32_t*)sourcePixelData; + for (int32_t x = 0; x < aFillRect.width; x++) { + *((uint32_t*)data + x) = sourcePixel; + } + } else if (BytesPerPixel(aSurface->GetFormat()) == 1) { + uint8_t sourcePixel = *sourcePixelData; + memset(data, sourcePixel, aFillRect.width); + } + + // Copy the first row into the other rows. + for (int32_t y = 1; y < aFillRect.height; y++) { + PodCopy(data + y * stride, data, aFillRect.width * bpp); + } +} + +static void +FillRectWithVerticallyRepeatingHorizontalStrip(DataSourceSurface *aSurface, + const IntRect &aFillRect, + const IntRect &aSampleRect) +{ + MOZ_ASSERT(!aFillRect.Overflows()); + MOZ_ASSERT(!aSampleRect.Overflows()); + MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect), + "aFillRect needs to be completely inside the surface"); + MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect), + "aSampleRect needs to be completely inside the surface"); + + int32_t stride = aSurface->Stride(); + uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft()); + uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft()); + if (BytesPerPixel(aSurface->GetFormat()) == 4) { + for (int32_t y = 0; y < aFillRect.height; y++) { + PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.width); + data += stride; + } + } else if (BytesPerPixel(aSurface->GetFormat()) == 1) { + for (int32_t y = 0; y < aFillRect.height; y++) { + PodCopy(data, sampleData, aFillRect.width); + data += stride; + } + } +} + +static void +FillRectWithHorizontallyRepeatingVerticalStrip(DataSourceSurface *aSurface, + const IntRect &aFillRect, + const IntRect &aSampleRect) +{ + MOZ_ASSERT(!aFillRect.Overflows()); + MOZ_ASSERT(!aSampleRect.Overflows()); + MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect), + "aFillRect needs to be completely inside the surface"); + MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect), + "aSampleRect needs to be completely inside the surface"); + + int32_t stride = aSurface->Stride(); + uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft()); + uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft()); + if (BytesPerPixel(aSurface->GetFormat()) == 4) { + for (int32_t y = 0; y < aFillRect.height; y++) { + int32_t sampleColor = *((uint32_t*)sampleData); + for (int32_t x = 0; x < aFillRect.width; x++) { + *((uint32_t*)data + x) = sampleColor; + } + data += stride; + sampleData += stride; + } + } else if (BytesPerPixel(aSurface->GetFormat()) == 1) { + for (int32_t y = 0; y < aFillRect.height; y++) { + uint8_t sampleColor = *sampleData; + memset(data, sampleColor, aFillRect.width); + data += stride; + sampleData += stride; + } + } +} + +static void +DuplicateEdges(DataSourceSurface* aSurface, const IntRect &aFromRect) +{ + MOZ_ASSERT(!aFromRect.Overflows()); + MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect), + "aFromRect needs to be completely inside the surface"); + + IntSize size = aSurface->GetSize(); + IntRect fill; + IntRect sampleRect; + for (int32_t ix = 0; ix < 3; ix++) { + switch (ix) { + case 0: + fill.x = 0; + fill.width = aFromRect.x; + sampleRect.x = fill.XMost(); + sampleRect.width = 1; + break; + case 1: + fill.x = aFromRect.x; + fill.width = aFromRect.width; + sampleRect.x = fill.x; + sampleRect.width = fill.width; + break; + case 2: + fill.x = aFromRect.XMost(); + fill.width = size.width - fill.x; + sampleRect.x = fill.x - 1; + sampleRect.width = 1; + break; + } + if (fill.width <= 0) { + continue; + } + bool xIsMiddle = (ix == 1); + for (int32_t iy = 0; iy < 3; iy++) { + switch (iy) { + case 0: + fill.y = 0; + fill.height = aFromRect.y; + sampleRect.y = fill.YMost(); + sampleRect.height = 1; + break; + case 1: + fill.y = aFromRect.y; + fill.height = aFromRect.height; + sampleRect.y = fill.y; + sampleRect.height = fill.height; + break; + case 2: + fill.y = aFromRect.YMost(); + fill.height = size.height - fill.y; + sampleRect.y = fill.y - 1; + sampleRect.height = 1; + break; + } + if (fill.height <= 0) { + continue; + } + bool yIsMiddle = (iy == 1); + if (!xIsMiddle && !yIsMiddle) { + // Corner + FillRectWithPixel(aSurface, fill, sampleRect.TopLeft()); + } + if (xIsMiddle && !yIsMiddle) { + // Top middle or bottom middle + FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill, sampleRect); + } + if (!xIsMiddle && yIsMiddle) { + // Left middle or right middle + FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill, sampleRect); + } + } + } +} + +static IntPoint +TileIndex(const IntRect &aFirstTileRect, const IntPoint &aPoint) +{ + return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.x) / aFirstTileRect.width)), + int32_t(floor(double(aPoint.y - aFirstTileRect.y) / aFirstTileRect.height))); +} + +static void +TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget, const IntPoint &aOffset) +{ + IntRect sourceRect(aOffset, aSource->GetSize()); + IntRect targetRect(IntPoint(0, 0), aTarget->GetSize()); + IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft()); + IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight()); + + for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) { + for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) { + IntPoint destPoint(sourceRect.x + ix * sourceRect.width, + sourceRect.y + iy * sourceRect.height); + IntRect destRect(destPoint, sourceRect.Size()); + destRect = destRect.Intersect(targetRect); + IntRect srcRect = destRect - destPoint; + CopyRect(aSource, aTarget, srcRect, destRect.TopLeft()); + } + } +} + +static TemporaryRef<DataSourceSurface> +GetDataSurfaceInRect(SourceSurface *aSurface, + const IntRect &aSurfaceRect, + const IntRect &aDestRect, + ConvolveMatrixEdgeMode aEdgeMode) +{ + MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize() : aSurfaceRect.IsEmpty()); + + if (aSurfaceRect.Overflows() || aDestRect.Overflows()) { + // We can't rely on the intersection calculations below to make sense when + // XMost() or YMost() overflow. Bail out. + return nullptr; + } + + IntRect sourceRect = aSurfaceRect; + + if (sourceRect.IsEqualEdges(aDestRect)) { + return aSurface ? aSurface->GetDataSurface() : nullptr; + } + + IntRect intersect = sourceRect.Intersect(aDestRect); + IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft(); + IntRect intersectInDestSpace = intersect - aDestRect.TopLeft(); + SurfaceFormat format = aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8); + + RefPtr<DataSourceSurface> target = + Factory::CreateDataSourceSurface(aDestRect.Size(), format, true); + if (MOZ2D_WARN_IF(!target)) { + return nullptr; + } + + if (!aSurface) { + return target.forget(); + } + + RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface(); + MOZ_ASSERT(dataSource); + + if (aEdgeMode == EDGE_MODE_WRAP) { + TileSurface(dataSource, target, intersectInDestSpace.TopLeft()); + return target.forget(); + } + + CopyRect(dataSource, target, intersectInSourceSpace, + intersectInDestSpace.TopLeft()); + + if (aEdgeMode == EDGE_MODE_DUPLICATE) { + DuplicateEdges(target, intersectInDestSpace); + } + + return target.forget(); +} + +/* static */ TemporaryRef<FilterNode> +FilterNodeSoftware::Create(FilterType aType) +{ + RefPtr<FilterNodeSoftware> filter; + switch (aType) { + case FilterType::BLEND: + filter = new FilterNodeBlendSoftware(); + break; + case FilterType::TRANSFORM: + filter = new FilterNodeTransformSoftware(); + break; + case FilterType::MORPHOLOGY: + filter = new FilterNodeMorphologySoftware(); + break; + case FilterType::COLOR_MATRIX: + filter = new FilterNodeColorMatrixSoftware(); + break; + case FilterType::FLOOD: + filter = new FilterNodeFloodSoftware(); + break; + case FilterType::TILE: + filter = new FilterNodeTileSoftware(); + break; + case FilterType::TABLE_TRANSFER: + filter = new FilterNodeTableTransferSoftware(); + break; + case FilterType::DISCRETE_TRANSFER: + filter = new FilterNodeDiscreteTransferSoftware(); + break; + case FilterType::LINEAR_TRANSFER: + filter = new FilterNodeLinearTransferSoftware(); + break; + case FilterType::GAMMA_TRANSFER: + filter = new FilterNodeGammaTransferSoftware(); + break; + case FilterType::CONVOLVE_MATRIX: + filter = new FilterNodeConvolveMatrixSoftware(); + break; + case FilterType::DISPLACEMENT_MAP: + filter = new FilterNodeDisplacementMapSoftware(); + break; + case FilterType::TURBULENCE: + filter = new FilterNodeTurbulenceSoftware(); + break; + case FilterType::ARITHMETIC_COMBINE: + filter = new FilterNodeArithmeticCombineSoftware(); + break; + case FilterType::COMPOSITE: + filter = new FilterNodeCompositeSoftware(); + break; + case FilterType::GAUSSIAN_BLUR: + filter = new FilterNodeGaussianBlurSoftware(); + break; + case FilterType::DIRECTIONAL_BLUR: + filter = new FilterNodeDirectionalBlurSoftware(); + break; + case FilterType::CROP: + filter = new FilterNodeCropSoftware(); + break; + case FilterType::PREMULTIPLY: + filter = new FilterNodePremultiplySoftware(); + break; + case FilterType::UNPREMULTIPLY: + filter = new FilterNodeUnpremultiplySoftware(); + break; + case FilterType::POINT_DIFFUSE: + filter = new FilterNodeLightingSoftware<PointLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<PointLight, DiffuseLighting>"); + break; + case FilterType::POINT_SPECULAR: + filter = new FilterNodeLightingSoftware<PointLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<PointLight, SpecularLighting>"); + break; + case FilterType::SPOT_DIFFUSE: + filter = new FilterNodeLightingSoftware<SpotLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<SpotLight, DiffuseLighting>"); + break; + case FilterType::SPOT_SPECULAR: + filter = new FilterNodeLightingSoftware<SpotLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<SpotLight, SpecularLighting>"); + break; + case FilterType::DISTANT_DIFFUSE: + filter = new FilterNodeLightingSoftware<DistantLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<DistantLight, DiffuseLighting>"); + break; + case FilterType::DISTANT_SPECULAR: + filter = new FilterNodeLightingSoftware<DistantLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<DistantLight, SpecularLighting>"); + break; + } + return filter.forget(); +} + +void +FilterNodeSoftware::Draw(DrawTarget* aDrawTarget, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions) +{ +#ifdef DEBUG_DUMP_SURFACES + printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n", GetName()); +#endif + + Rect renderRect = aSourceRect; + renderRect.RoundOut(); + IntRect renderIntRect; + if (!renderRect.ToIntRect(&renderIntRect)) { +#ifdef DEBUG_DUMP_SURFACES + printf("render rect overflowed, not painting anything\n"); + printf("</pre>\n"); +#endif + return; + } + + IntRect outputRect = GetOutputRectInRect(renderIntRect); + if (outputRect.Overflows()) { +#ifdef DEBUG_DUMP_SURFACES + printf("output rect overflowed, not painting anything\n"); + printf("</pre>\n"); +#endif + return; + } + + RefPtr<DataSourceSurface> result; + if (!outputRect.IsEmpty()) { + result = GetOutput(outputRect); + } + + if (!result) { + // Null results are allowed and treated as transparent. Don't draw anything. +#ifdef DEBUG_DUMP_SURFACES + printf("output returned null\n"); + printf("</pre>\n"); +#endif + return; + } + +#ifdef DEBUG_DUMP_SURFACES + printf("output from %s:\n", GetName()); + printf("<img src='"); gfxUtils::DumpAsDataURL(result); printf("'>\n"); + printf("</pre>\n"); +#endif + + Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft(); + Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect); + Rect renderedDestRect = renderedSourceRect + sourceToDestOffset; + if (result->GetFormat() == SurfaceFormat::A8) { + // Interpret the result as having implicitly black color channels. + aDrawTarget->PushClipRect(renderedDestRect); + aDrawTarget->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)), + result, + Point(outputRect.TopLeft()) + sourceToDestOffset, + aOptions); + aDrawTarget->PopClip(); + } else { + aDrawTarget->DrawSurface(result, renderedDestRect, + renderedSourceRect - Point(outputRect.TopLeft()), + DrawSurfaceOptions(), aOptions); + } +} + +TemporaryRef<DataSourceSurface> +FilterNodeSoftware::GetOutput(const IntRect &aRect) +{ + MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect)); + + if (aRect.Overflows()) { + return nullptr; + } + + if (!mCachedRect.Contains(aRect)) { + RequestRect(aRect); + mCachedOutput = Render(mRequestedRect); + if (!mCachedOutput) { + mCachedRect = IntRect(); + mRequestedRect = IntRect(); + return nullptr; + } + mCachedRect = mRequestedRect; + mRequestedRect = IntRect(); + } else { + MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?"); + } + return GetDataSurfaceInRect(mCachedOutput, mCachedRect, aRect, EDGE_MODE_NONE); +} + +void +FilterNodeSoftware::RequestRect(const IntRect &aRect) +{ + mRequestedRect = mRequestedRect.Union(aRect); + RequestFromInputsForRect(aRect); +} + +void +FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect) +{ + if (aRect.Overflows()) { + return; + } + + int32_t inputIndex = InputIndex(aInputEnumIndex); + if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) { + MOZ_CRASH(); + } + if (mInputSurfaces[inputIndex]) { + return; + } + RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex]; + MOZ_ASSERT(filter, "missing input"); + filter->RequestRect(filter->GetOutputRectInRect(aRect)); +} + +SurfaceFormat +FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat, + FormatHint aFormatHint) +{ + if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) { + return SurfaceFormat::A8; + } + return SurfaceFormat::B8G8R8A8; +} + +TemporaryRef<DataSourceSurface> +FilterNodeSoftware::GetInputDataSourceSurface(uint32_t aInputEnumIndex, + const IntRect& aRect, + FormatHint aFormatHint, + ConvolveMatrixEdgeMode aEdgeMode, + const IntRect *aTransparencyPaddedSourceRect) +{ + if (aRect.Overflows()) { + return nullptr; + } + +#ifdef DEBUG_DUMP_SURFACES + printf("<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, %d</h1>\n", + aRect.x, aRect.y, aRect.width, aRect.height); +#endif + int32_t inputIndex = InputIndex(aInputEnumIndex); + if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) { + MOZ_CRASH(); + return nullptr; + } + + if (aRect.IsEmpty()) { + return nullptr; + } + + RefPtr<SourceSurface> surface; + IntRect surfaceRect; + + if (mInputSurfaces[inputIndex]) { + // Input from input surface + surface = mInputSurfaces[inputIndex]; +#ifdef DEBUG_DUMP_SURFACES + printf("input from input surface:\n"); +#endif + surfaceRect = IntRect(IntPoint(0, 0), surface->GetSize()); + } else { + // Input from input filter +#ifdef DEBUG_DUMP_SURFACES + printf("getting input from input filter %s...\n", mInputFilters[inputIndex]->GetName()); +#endif + RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex]; + MOZ_ASSERT(filter, "missing input"); + IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect); + if (!inputFilterOutput.IsEmpty()) { + surface = filter->GetOutput(inputFilterOutput); + } +#ifdef DEBUG_DUMP_SURFACES + printf("input from input filter %s:\n", mInputFilters[inputIndex]->GetName()); +#endif + surfaceRect = inputFilterOutput; + MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize()); + } + + if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) { +#ifdef DEBUG_DUMP_SURFACES + printf("wrong input format</section>\n\n"); +#endif + return nullptr; + } + + if (!surfaceRect.IsEmpty() && !surface) { +#ifdef DEBUG_DUMP_SURFACES + printf(" -- no input --</section>\n\n"); +#endif + return nullptr; + } + + if (aTransparencyPaddedSourceRect && !aTransparencyPaddedSourceRect->IsEmpty()) { + IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect); + surface = GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE); + surfaceRect = srcRect; + } + + RefPtr<DataSourceSurface> result = + GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode); + + if (result) { + // TODO: This isn't safe since we don't have a guarantee + // that future Maps will have the same stride + DataSourceSurface::MappedSurface map; + if (result->Map(DataSourceSurface::READ, &map)) { + // Unmap immediately since CloneAligned hasn't been updated + // to use the Map API yet. We can still read the stride/data + // values as long as we don't try to dereference them. + result->Unmap(); + if (map.mStride != GetAlignedStride<16>(map.mStride) || + reinterpret_cast<uintptr_t>(map.mData) % 16 != 0) { + // Align unaligned surface. + result = CloneAligned(result); + } + } else { + result = nullptr; + } + } + + + if (!result) { +#ifdef DEBUG_DUMP_SURFACES + printf(" -- no input --</section>\n\n"); +#endif + return nullptr; + } + + SurfaceFormat currentFormat = result->GetFormat(); + if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 && + currentFormat != SurfaceFormat::B8G8R8A8) { + result = FilterProcessing::ConvertToB8G8R8A8(result); + } + +#ifdef DEBUG_DUMP_SURFACES + printf("<img src='"); gfxUtils::DumpAsDataURL(result); printf("'></section>"); +#endif + + MOZ_ASSERT(!result || result->GetSize() == aRect.Size(), "wrong surface size"); + + return result.forget(); +} + +IntRect +FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex, + const IntRect &aInRect) +{ + if (aInRect.Overflows()) { + return IntRect(); + } + + int32_t inputIndex = InputIndex(aInputEnumIndex); + if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) { + MOZ_CRASH(); + return IntRect(); + } + if (mInputSurfaces[inputIndex]) { + return aInRect.Intersect(IntRect(IntPoint(0, 0), + mInputSurfaces[inputIndex]->GetSize())); + } + RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex]; + MOZ_ASSERT(filter, "missing input"); + return filter->GetOutputRectInRect(aInRect); +} + +size_t +FilterNodeSoftware::NumberOfSetInputs() +{ + return std::max(mInputSurfaces.size(), mInputFilters.size()); +} + +void +FilterNodeSoftware::AddInvalidationListener(FilterInvalidationListener* aListener) +{ + MOZ_ASSERT(aListener, "null listener"); + mInvalidationListeners.push_back(aListener); +} + +void +FilterNodeSoftware::RemoveInvalidationListener(FilterInvalidationListener* aListener) +{ + MOZ_ASSERT(aListener, "null listener"); + std::vector<FilterInvalidationListener*>::iterator it = + std::find(mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener); + mInvalidationListeners.erase(it); +} + +void +FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter) +{ + Invalidate(); +} + +void +FilterNodeSoftware::Invalidate() +{ + mCachedOutput = nullptr; + mCachedRect = IntRect(); + for (std::vector<FilterInvalidationListener*>::iterator it = mInvalidationListeners.begin(); + it != mInvalidationListeners.end(); it++) { + (*it)->FilterInvalidated(this); + } +} + +FilterNodeSoftware::~FilterNodeSoftware() +{ + MOZ_ASSERT(!mInvalidationListeners.size(), + "All invalidation listeners should have unsubscribed themselves by now!"); + + for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it = mInputFilters.begin(); + it != mInputFilters.end(); it++) { + if (*it) { + (*it)->RemoveInvalidationListener(this); + } + } +} + +void +FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode *aFilter) +{ + if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) { + MOZ_ASSERT(false, "can only take software filters as inputs"); + return; + } + SetInput(aIndex, nullptr, static_cast<FilterNodeSoftware*>(aFilter)); +} + +void +FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface *aSurface) +{ + SetInput(aIndex, aSurface, nullptr); +} + +void +FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex, + SourceSurface *aSurface, + FilterNodeSoftware *aFilter) +{ + int32_t inputIndex = InputIndex(aInputEnumIndex); + if (inputIndex < 0) { + MOZ_CRASH(); + return; + } + if ((uint32_t)inputIndex >= NumberOfSetInputs()) { + mInputSurfaces.resize(inputIndex + 1); + mInputFilters.resize(inputIndex + 1); + } + mInputSurfaces[inputIndex] = aSurface; + if (mInputFilters[inputIndex]) { + mInputFilters[inputIndex]->RemoveInvalidationListener(this); + } + if (aFilter) { + aFilter->AddInvalidationListener(this); + } + mInputFilters[inputIndex] = aFilter; + if (!aSurface && !aFilter && (size_t)inputIndex == NumberOfSetInputs()) { + mInputSurfaces.resize(inputIndex); + mInputFilters.resize(inputIndex); + } + Invalidate(); +} + +FilterNodeBlendSoftware::FilterNodeBlendSoftware() + : mBlendMode(BLEND_MODE_MULTIPLY) +{} + +int32_t +FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_BLEND_IN: return 0; + case IN_BLEND_IN2: return 1; + default: return -1; + } +} + +void +FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex, uint32_t aBlendMode) +{ + MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE); + mBlendMode = static_cast<BlendMode>(aBlendMode); + Invalidate(); +} + +static CompositionOp ToBlendOp(BlendMode aOp) +{ + switch (aOp) { + case BLEND_MODE_MULTIPLY: + return CompositionOp::OP_MULTIPLY; + case BLEND_MODE_SCREEN: + return CompositionOp::OP_SCREEN; + case BLEND_MODE_OVERLAY: + return CompositionOp::OP_OVERLAY; + case BLEND_MODE_DARKEN: + return CompositionOp::OP_DARKEN; + case BLEND_MODE_LIGHTEN: + return CompositionOp::OP_LIGHTEN; + case BLEND_MODE_COLOR_DODGE: + return CompositionOp::OP_COLOR_DODGE; + case BLEND_MODE_COLOR_BURN: + return CompositionOp::OP_COLOR_BURN; + case BLEND_MODE_HARD_LIGHT: + return CompositionOp::OP_HARD_LIGHT; + case BLEND_MODE_SOFT_LIGHT: + return CompositionOp::OP_SOFT_LIGHT; + case BLEND_MODE_DIFFERENCE: + return CompositionOp::OP_DIFFERENCE; + case BLEND_MODE_EXCLUSION: + return CompositionOp::OP_EXCLUSION; + case BLEND_MODE_HUE: + return CompositionOp::OP_HUE; + case BLEND_MODE_SATURATION: + return CompositionOp::OP_SATURATION; + case BLEND_MODE_COLOR: + return CompositionOp::OP_COLOR; + case BLEND_MODE_LUMINOSITY: + return CompositionOp::OP_LUMINOSITY; + default: + return CompositionOp::OP_OVER; + } + + return CompositionOp::OP_OVER; +} + +TemporaryRef<DataSourceSurface> +FilterNodeBlendSoftware::Render(const IntRect& aRect) +{ + RefPtr<DataSourceSurface> input1 = + GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS); + RefPtr<DataSourceSurface> input2 = + GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS); + + // Null inputs need to be treated as transparent. + + // First case: both are transparent. + if (!input1 && !input2) { + // Then the result is transparent, too. + return nullptr; + } + + // Second case: one of them is transparent. Return the non-transparent one. + if (!input1 || !input2) { + return input1 ? input1.forget() : input2.forget(); + } + + // Third case: both are non-transparent. + // Apply normal filtering. + RefPtr<DataSourceSurface> target = FilterProcessing::ApplyBlending(input1, input2, mBlendMode); + if (target != nullptr) { + return target.forget(); + } + + IntSize size = input1->GetSize(); + target = + Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); + if (MOZ2D_WARN_IF(!target)) { + return nullptr; + } + + CopyRect(input1, target, IntRect(IntPoint(), size), IntPoint()); + + RefPtr<DrawTarget> dt = + Factory::CreateDrawTargetForData(BackendType::CAIRO, + target->GetData(), + target->GetSize(), + target->Stride(), + target->GetFormat()); + + Rect r(0, 0, size.width, size.height); + dt->DrawSurface(input2, r, r, DrawSurfaceOptions(), DrawOptions(1.0f, ToBlendOp(mBlendMode))); + dt->Flush(); + return target.forget(); +} + +void +FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + RequestInputRect(IN_BLEND_IN, aRect); + RequestInputRect(IN_BLEND_IN2, aRect); +} + +IntRect +FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + return GetInputRectInRect(IN_BLEND_IN, aRect).Union( + GetInputRectInRect(IN_BLEND_IN2, aRect)).Intersect(aRect); +} + +FilterNodeTransformSoftware::FilterNodeTransformSoftware() + : mFilter(Filter::GOOD) +{} + +int32_t +FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_TRANSFORM_IN: return 0; + default: return -1; + } +} + +void +FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, uint32_t aFilter) +{ + MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER); + mFilter = static_cast<Filter>(aFilter); + Invalidate(); +} + +void +FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, const Matrix &aMatrix) +{ + MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX); + mMatrix = aMatrix; + Invalidate(); +} + +IntRect +FilterNodeTransformSoftware::SourceRectForOutputRect(const IntRect &aRect) +{ + if (aRect.IsEmpty()) { + return IntRect(); + } + + Matrix inverted(mMatrix); + if (!inverted.Invert()) { + return IntRect(); + } + + Rect neededRect = inverted.TransformBounds(Rect(aRect)); + neededRect.RoundOut(); + IntRect neededIntRect; + if (!neededRect.ToIntRect(&neededIntRect)) { + return IntRect(); + } + return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect); +} + +TemporaryRef<DataSourceSurface> +FilterNodeTransformSoftware::Render(const IntRect& aRect) +{ + IntRect srcRect = SourceRectForOutputRect(aRect); + + RefPtr<DataSourceSurface> input = + GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect); + + if (!input) { + return nullptr; + } + + Matrix transform = Matrix::Translation(srcRect.x, srcRect.y) * mMatrix * + Matrix::Translation(-aRect.x, -aRect.y); + if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) { + return input.forget(); + } + + RefPtr<DataSourceSurface> surf = + Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat(), true); + + if (!surf) { + return nullptr; + } + + DataSourceSurface::MappedSurface mapping; + if (!surf->Map(DataSourceSurface::MapType::WRITE, &mapping)) { + gfxCriticalError() << "FilterNodeTransformSoftware::Render failed to map surface"; + return nullptr; + } + + RefPtr<DrawTarget> dt = + Factory::CreateDrawTargetForData(BackendType::CAIRO, + mapping.mData, + surf->GetSize(), + mapping.mStride, + surf->GetFormat()); + if (!dt) { + return nullptr; + } + + Rect r(0, 0, srcRect.width, srcRect.height); + dt->SetTransform(transform); + dt->DrawSurface(input, r, r, DrawSurfaceOptions(mFilter)); + + dt->Flush(); + surf->Unmap(); + return surf.forget(); +} + +void +FilterNodeTransformSoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect)); +} + +IntRect +FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + IntRect srcRect = SourceRectForOutputRect(aRect); + if (srcRect.IsEmpty()) { + return IntRect(); + } + + Rect outRect = mMatrix.TransformBounds(Rect(srcRect)); + outRect.RoundOut(); + IntRect outIntRect; + if (!outRect.ToIntRect(&outIntRect)) { + return IntRect(); + } + return outIntRect.Intersect(aRect); +} + +FilterNodeMorphologySoftware::FilterNodeMorphologySoftware() + : mOperator(MORPHOLOGY_OPERATOR_ERODE) +{} + +int32_t +FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_MORPHOLOGY_IN: return 0; + default: return -1; + } +} + +void +FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex, + const IntSize &aRadii) +{ + MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII); + mRadii.width = std::min(std::max(aRadii.width, 0), 100000); + mRadii.height = std::min(std::max(aRadii.height, 0), 100000); + Invalidate(); +} + +void +FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex, + uint32_t aOperator) +{ + MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR); + mOperator = static_cast<MorphologyOperator>(aOperator); + Invalidate(); +} + +static TemporaryRef<DataSourceSurface> +ApplyMorphology(const IntRect& aSourceRect, DataSourceSurface* aInput, + const IntRect& aDestRect, int32_t rx, int32_t ry, + MorphologyOperator aOperator) +{ + IntRect srcRect = aSourceRect - aDestRect.TopLeft(); + IntRect destRect = aDestRect - aDestRect.TopLeft(); + IntRect tmpRect(destRect.x, srcRect.y, destRect.width, srcRect.height); +#ifdef DEBUG + IntMargin margin = srcRect - destRect; + MOZ_ASSERT(margin.top >= ry && margin.right >= rx && + margin.bottom >= ry && margin.left >= rx, "insufficient margin"); +#endif + + RefPtr<DataSourceSurface> tmp; + if (rx == 0) { + tmp = aInput; + } else { + tmp = Factory::CreateDataSourceSurface(tmpRect.Size(), SurfaceFormat::B8G8R8A8); + if (MOZ2D_WARN_IF(!tmp)) { + return nullptr; + } + + int32_t sourceStride = aInput->Stride(); + uint8_t* sourceData = DataAtOffset(aInput, destRect.TopLeft() - srcRect.TopLeft()); + + int32_t tmpStride = tmp->Stride(); + uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft()); + + FilterProcessing::ApplyMorphologyHorizontal( + sourceData, sourceStride, tmpData, tmpStride, tmpRect, rx, aOperator); + } + + RefPtr<DataSourceSurface> dest; + if (ry == 0) { + dest = tmp; + } else { + dest = Factory::CreateDataSourceSurface(destRect.Size(), SurfaceFormat::B8G8R8A8); + if (MOZ2D_WARN_IF(!dest)) { + return nullptr; + } + + int32_t tmpStride = tmp->Stride(); + uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft()); + + int32_t destStride = dest->Stride(); + uint8_t* destData = dest->GetData(); + + FilterProcessing::ApplyMorphologyVertical( + tmpData, tmpStride, destData, destStride, destRect, ry, aOperator); + } + + return dest.forget(); +} + +TemporaryRef<DataSourceSurface> +FilterNodeMorphologySoftware::Render(const IntRect& aRect) +{ + IntRect srcRect = aRect; + srcRect.Inflate(mRadii); + + RefPtr<DataSourceSurface> input = + GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS); + if (!input) { + return nullptr; + } + + int32_t rx = mRadii.width; + int32_t ry = mRadii.height; + + if (rx == 0 && ry == 0) { + return input.forget(); + } + + return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator); +} + +void +FilterNodeMorphologySoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + IntRect srcRect = aRect; + srcRect.Inflate(mRadii); + RequestInputRect(IN_MORPHOLOGY_IN, srcRect); +} + +IntRect +FilterNodeMorphologySoftware::GetOutputRectInRect(const IntRect& aRect) +{ + IntRect inflatedSourceRect = aRect; + inflatedSourceRect.Inflate(mRadii); + IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect); + if (mOperator == MORPHOLOGY_OPERATOR_ERODE) { + inputRect.Deflate(mRadii); + } else { + inputRect.Inflate(mRadii); + } + return inputRect.Intersect(aRect); +} + +int32_t +FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_COLOR_MATRIX_IN: return 0; + default: return -1; + } +} + +void +FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex, + const Matrix5x4 &aMatrix) +{ + MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX); + mMatrix = aMatrix; + Invalidate(); +} + +void +FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex, + uint32_t aAlphaMode) +{ + MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE); + mAlphaMode = (AlphaMode)aAlphaMode; + Invalidate(); +} + +static TemporaryRef<DataSourceSurface> +Premultiply(DataSourceSurface* aSurface) +{ + if (aSurface->GetFormat() == SurfaceFormat::A8) { + return aSurface; + } + + IntSize size = aSurface->GetSize(); + RefPtr<DataSourceSurface> target = + Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); + if (MOZ2D_WARN_IF(!target)) { + return nullptr; + } + + uint8_t* inputData = aSurface->GetData(); + int32_t inputStride = aSurface->Stride(); + uint8_t* targetData = target->GetData(); + int32_t targetStride = target->Stride(); + + FilterProcessing::DoPremultiplicationCalculation( + size, targetData, targetStride, inputData, inputStride); + + return target.forget(); +} + +static TemporaryRef<DataSourceSurface> +Unpremultiply(DataSourceSurface* aSurface) +{ + if (aSurface->GetFormat() == SurfaceFormat::A8) { + return aSurface; + } + + IntSize size = aSurface->GetSize(); + RefPtr<DataSourceSurface> target = + Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); + if (MOZ2D_WARN_IF(!target)) { + return nullptr; + } + + uint8_t* inputData = aSurface->GetData(); + int32_t inputStride = aSurface->Stride(); + uint8_t* targetData = target->GetData(); + int32_t targetStride = target->Stride(); + + FilterProcessing::DoUnpremultiplicationCalculation( + size, targetData, targetStride, inputData, inputStride); + + return target.forget(); +} + +TemporaryRef<DataSourceSurface> +FilterNodeColorMatrixSoftware::Render(const IntRect& aRect) +{ + RefPtr<DataSourceSurface> input = + GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS); + if (!input) { + return nullptr; + } + + if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) { + input = Unpremultiply(input); + } + + RefPtr<DataSourceSurface> result = + FilterProcessing::ApplyColorMatrix(input, mMatrix); + + if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) { + result = Premultiply(result); + } + + return result.forget(); +} + +void +FilterNodeColorMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + RequestInputRect(IN_COLOR_MATRIX_IN, aRect); +} + +IntRect +FilterNodeColorMatrixSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + if (mMatrix._54 > 0.0f) { + return aRect; + } + return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect); +} + +void +FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex, const Color &aColor) +{ + MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR); + mColor = aColor; + Invalidate(); +} + +static uint32_t +ColorToBGRA(const Color& aColor) +{ + union { + uint32_t color; + uint8_t components[4]; + }; + components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = NS_lround(aColor.r * aColor.a * 255.0f); + components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = NS_lround(aColor.g * aColor.a * 255.0f); + components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = NS_lround(aColor.b * aColor.a * 255.0f); + components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f); + return color; +} + +static SurfaceFormat +FormatForColor(Color aColor) +{ + if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) { + return SurfaceFormat::A8; + } + return SurfaceFormat::B8G8R8A8; +} + +TemporaryRef<DataSourceSurface> +FilterNodeFloodSoftware::Render(const IntRect& aRect) +{ + SurfaceFormat format = FormatForColor(mColor); + RefPtr<DataSourceSurface> target = + Factory::CreateDataSourceSurface(aRect.Size(), format); + if (MOZ2D_WARN_IF(!target)) { + return nullptr; + } + + uint8_t* targetData = target->GetData(); + uint32_t stride = target->Stride(); + + if (format == SurfaceFormat::B8G8R8A8) { + uint32_t color = ColorToBGRA(mColor); + for (int32_t y = 0; y < aRect.height; y++) { + for (int32_t x = 0; x < aRect.width; x++) { + *((uint32_t*)targetData + x) = color; + } + targetData += stride; + } + } else if (format == SurfaceFormat::A8) { + uint8_t alpha = NS_lround(mColor.a * 255.0f); + for (int32_t y = 0; y < aRect.height; y++) { + for (int32_t x = 0; x < aRect.width; x++) { + targetData[x] = alpha; + } + targetData += stride; + } + } else { + MOZ_CRASH(); + } + + return target.forget(); +} + +// Override GetOutput to get around caching. Rendering simple floods is +// comparatively fast. +TemporaryRef<DataSourceSurface> +FilterNodeFloodSoftware::GetOutput(const IntRect& aRect) +{ + return Render(aRect); +} + +IntRect +FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + if (mColor.a == 0.0f) { + return IntRect(); + } + return aRect; +} + +int32_t +FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_TILE_IN: return 0; + default: return -1; + } +} + +void +FilterNodeTileSoftware::SetAttribute(uint32_t aIndex, + const IntRect &aSourceRect) +{ + MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT); + mSourceRect = IntRect(int32_t(aSourceRect.x), int32_t(aSourceRect.y), + int32_t(aSourceRect.width), int32_t(aSourceRect.height)); + Invalidate(); +} + +namespace { +struct CompareIntRects +{ + bool operator()(const IntRect& a, const IntRect& b) const + { + if (a.x != b.x) { + return a.x < b.x; + } + if (a.y != b.y) { + return a.y < b.y; + } + if (a.width != b.width) { + return a.width < b.width; + } + return a.height < b.height; + } +}; +} + +TemporaryRef<DataSourceSurface> +FilterNodeTileSoftware::Render(const IntRect& aRect) +{ + if (mSourceRect.IsEmpty()) { + return nullptr; + } + + if (mSourceRect.Contains(aRect)) { + return GetInputDataSourceSurface(IN_TILE_IN, aRect); + } + + RefPtr<DataSourceSurface> target; + + typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects> InputMap; + InputMap inputs; + + IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft()); + IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight()); + for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) { + for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) { + IntPoint sourceToDestOffset(ix * mSourceRect.width, + iy * mSourceRect.height); + IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset); + IntRect srcRect = destRect - sourceToDestOffset; + if (srcRect.IsEmpty()) { + continue; + } + + RefPtr<DataSourceSurface> input; + InputMap::iterator it = inputs.find(srcRect); + if (it == inputs.end()) { + input = GetInputDataSourceSurface(IN_TILE_IN, srcRect); + inputs[srcRect] = input; + } else { + input = it->second; + } + if (!input) { + return nullptr; + } + if (!target) { + // We delay creating the target until now because we want to use the + // same format as our input filter, and we do not actually know the + // input format before we call GetInputDataSourceSurface. + target = Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat()); + if (MOZ2D_WARN_IF(!target)) { + return nullptr; + } + } + + if (input->GetFormat() != target->GetFormat()) { + // Different rectangles of the input can have different formats. If + // that happens, just convert everything to B8G8R8A8. + target = FilterProcessing::ConvertToB8G8R8A8(target); + input = FilterProcessing::ConvertToB8G8R8A8(input); + if (MOZ2D_WARN_IF(!target) || MOZ2D_WARN_IF(!input)) { + return nullptr; + } + } + + CopyRect(input, target, srcRect - srcRect.TopLeft(), destRect.TopLeft() - aRect.TopLeft()); + } + } + + return target.forget(); +} + +void +FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + // Do not request anything. + // Source rects for the tile filter can be discontinuous with large gaps + // between them. Requesting those from our input filter might cause it to + // render the whole bounding box of all of them, which would be wasteful. +} + +IntRect +FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + return aRect; +} + +FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware() + : mDisableR(true) + , mDisableG(true) + , mDisableB(true) + , mDisableA(true) +{} + +void +FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex, + bool aDisable) +{ + switch (aIndex) { + case ATT_TRANSFER_DISABLE_R: + mDisableR = aDisable; + break; + case ATT_TRANSFER_DISABLE_G: + mDisableG = aDisable; + break; + case ATT_TRANSFER_DISABLE_B: + mDisableB = aDisable; + break; + case ATT_TRANSFER_DISABLE_A: + mDisableA = aDisable; + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +void +FilterNodeComponentTransferSoftware::GenerateLookupTable(ptrdiff_t aComponent, + uint8_t aTables[4][256], + bool aDisabled) +{ + if (aDisabled) { + static uint8_t sIdentityLookupTable[256]; + static bool sInitializedIdentityLookupTable = false; + if (!sInitializedIdentityLookupTable) { + for (int32_t i = 0; i < 256; i++) { + sIdentityLookupTable[i] = i; + } + sInitializedIdentityLookupTable = true; + } + memcpy(aTables[aComponent], sIdentityLookupTable, 256); + } else { + FillLookupTable(aComponent, aTables[aComponent]); + } +} + +template<uint32_t BytesPerPixel> +static void TransferComponents(DataSourceSurface* aInput, + DataSourceSurface* aTarget, + const uint8_t aLookupTables[BytesPerPixel][256]) +{ + MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats"); + IntSize size = aInput->GetSize(); + + uint8_t* sourceData = aInput->GetData(); + uint8_t* targetData = aTarget->GetData(); + uint32_t sourceStride = aInput->Stride(); + uint32_t targetStride = aTarget->Stride(); + + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x++) { + uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel; + uint32_t targetIndex = y * targetStride + x * BytesPerPixel; + for (uint32_t i = 0; i < BytesPerPixel; i++) { + targetData[targetIndex + i] = aLookupTables[i][sourceData[sourceIndex + i]]; + } + } + } +} + +bool +IsAllZero(uint8_t aLookupTable[256]) +{ + for (int32_t i = 0; i < 256; i++) { + if (aLookupTable[i] != 0) { + return false; + } + } + return true; +} + +TemporaryRef<DataSourceSurface> +FilterNodeComponentTransferSoftware::Render(const IntRect& aRect) +{ + if (mDisableR && mDisableG && mDisableB && mDisableA) { + return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect); + } + + uint8_t lookupTables[4][256]; + GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR); + GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG); + GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB); + GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA); + + bool needColorChannels = + lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 || + lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 || + lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0; + + FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8; + + RefPtr<DataSourceSurface> input = + GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref); + if (!input) { + return nullptr; + } + + if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) { + bool colorChannelsBecomeBlack = + IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) && + IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) && + IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]); + + if (colorChannelsBecomeBlack) { + input = FilterProcessing::ExtractAlpha(input); + } + } + + SurfaceFormat format = input->GetFormat(); + if (format == SurfaceFormat::A8 && mDisableA) { + return input.forget(); + } + + RefPtr<DataSourceSurface> target = + Factory::CreateDataSourceSurface(aRect.Size(), format); + if (MOZ2D_WARN_IF(!target)) { + return nullptr; + } + + if (format == SurfaceFormat::A8) { + TransferComponents<1>(input, target, &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]); + } else { + TransferComponents<4>(input, target, lookupTables); + } + + return target.forget(); +} + +void +FilterNodeComponentTransferSoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + RequestInputRect(IN_TRANSFER_IN, aRect); +} + +IntRect +FilterNodeComponentTransferSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + if (mDisableA) { + return GetInputRectInRect(IN_TRANSFER_IN, aRect); + } + return aRect; +} + +int32_t +FilterNodeComponentTransferSoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_TRANSFER_IN: return 0; + default: return -1; + } +} + +void +FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex, + const Float* aFloat, + uint32_t aSize) +{ + std::vector<Float> table(aFloat, aFloat + aSize); + switch (aIndex) { + case ATT_TABLE_TRANSFER_TABLE_R: + mTableR = table; + break; + case ATT_TABLE_TRANSFER_TABLE_G: + mTableG = table; + break; + case ATT_TABLE_TRANSFER_TABLE_B: + mTableB = table; + break; + case ATT_TABLE_TRANSFER_TABLE_A: + mTableA = table; + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +void +FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent, + uint8_t aTable[256]) +{ + switch (aComponent) { + case B8G8R8A8_COMPONENT_BYTEOFFSET_R: + FillLookupTableImpl(mTableR, aTable); + break; + case B8G8R8A8_COMPONENT_BYTEOFFSET_G: + FillLookupTableImpl(mTableG, aTable); + break; + case B8G8R8A8_COMPONENT_BYTEOFFSET_B: + FillLookupTableImpl(mTableB, aTable); + break; + case B8G8R8A8_COMPONENT_BYTEOFFSET_A: + FillLookupTableImpl(mTableA, aTable); + break; + default: + MOZ_ASSERT(false, "unknown component"); + break; + } +} + +void +FilterNodeTableTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues, + uint8_t aTable[256]) +{ + uint32_t tvLength = aTableValues.size(); + if (tvLength < 2) { + return; + } + + for (size_t i = 0; i < 256; i++) { + uint32_t k = (i * (tvLength - 1)) / 255; + Float v1 = aTableValues[k]; + Float v2 = aTableValues[std::min(k + 1, tvLength - 1)]; + int32_t val = + int32_t(255 * (v1 + (i/255.0f - k/float(tvLength-1))*(tvLength - 1)*(v2 - v1))); + val = std::min(255, val); + val = std::max(0, val); + aTable[i] = val; + } +} + +void +FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex, + const Float* aFloat, + uint32_t aSize) +{ + std::vector<Float> discrete(aFloat, aFloat + aSize); + switch (aIndex) { + case ATT_DISCRETE_TRANSFER_TABLE_R: + mTableR = discrete; + break; + case ATT_DISCRETE_TRANSFER_TABLE_G: + mTableG = discrete; + break; + case ATT_DISCRETE_TRANSFER_TABLE_B: + mTableB = discrete; + break; + case ATT_DISCRETE_TRANSFER_TABLE_A: + mTableA = discrete; + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +void +FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent, + uint8_t aTable[256]) +{ + switch (aComponent) { + case B8G8R8A8_COMPONENT_BYTEOFFSET_R: + FillLookupTableImpl(mTableR, aTable); + break; + case B8G8R8A8_COMPONENT_BYTEOFFSET_G: + FillLookupTableImpl(mTableG, aTable); + break; + case B8G8R8A8_COMPONENT_BYTEOFFSET_B: + FillLookupTableImpl(mTableB, aTable); + break; + case B8G8R8A8_COMPONENT_BYTEOFFSET_A: + FillLookupTableImpl(mTableA, aTable); + break; + default: + MOZ_ASSERT(false, "unknown component"); + break; + } +} + +void +FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues, + uint8_t aTable[256]) +{ + uint32_t tvLength = aTableValues.size(); + if (tvLength < 1) { + return; + } + + for (size_t i = 0; i < 256; i++) { + uint32_t k = (i * tvLength) / 255; + k = std::min(k, tvLength - 1); + Float v = aTableValues[k]; + int32_t val = NS_lround(255 * v); + val = std::min(255, val); + val = std::max(0, val); + aTable[i] = val; + } +} + +FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware() + : mSlopeR(0) + , mSlopeG(0) + , mSlopeB(0) + , mSlopeA(0) + , mInterceptR(0) + , mInterceptG(0) + , mInterceptB(0) + , mInterceptA(0) +{} + +void +FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex, + Float aValue) +{ + switch (aIndex) { + case ATT_LINEAR_TRANSFER_SLOPE_R: + mSlopeR = aValue; + break; + case ATT_LINEAR_TRANSFER_INTERCEPT_R: + mInterceptR = aValue; + break; + case ATT_LINEAR_TRANSFER_SLOPE_G: + mSlopeG = aValue; + break; + case ATT_LINEAR_TRANSFER_INTERCEPT_G: + mInterceptG = aValue; + break; + case ATT_LINEAR_TRANSFER_SLOPE_B: + mSlopeB = aValue; + break; + case ATT_LINEAR_TRANSFER_INTERCEPT_B: + mInterceptB = aValue; + break; + case ATT_LINEAR_TRANSFER_SLOPE_A: + mSlopeA = aValue; + break; + case ATT_LINEAR_TRANSFER_INTERCEPT_A: + mInterceptA = aValue; + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +void +FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent, + uint8_t aTable[256]) +{ + switch (aComponent) { + case B8G8R8A8_COMPONENT_BYTEOFFSET_R: + FillLookupTableImpl(mSlopeR, mInterceptR, aTable); + break; + case B8G8R8A8_COMPONENT_BYTEOFFSET_G: + FillLookupTableImpl(mSlopeG, mInterceptG, aTable); + break; + case B8G8R8A8_COMPONENT_BYTEOFFSET_B: + FillLookupTableImpl(mSlopeB, mInterceptB, aTable); + break; + case B8G8R8A8_COMPONENT_BYTEOFFSET_A: + FillLookupTableImpl(mSlopeA, mInterceptA, aTable); + break; + default: + MOZ_ASSERT(false, "unknown component"); + break; + } +} + +void +FilterNodeLinearTransferSoftware::FillLookupTableImpl(Float aSlope, + Float aIntercept, + uint8_t aTable[256]) +{ + for (size_t i = 0; i < 256; i++) { + int32_t val = NS_lround(aSlope * i + 255 * aIntercept); + val = std::min(255, val); + val = std::max(0, val); + aTable[i] = val; + } +} + +FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware() + : mAmplitudeR(0) + , mAmplitudeG(0) + , mAmplitudeB(0) + , mAmplitudeA(0) + , mExponentR(0) + , mExponentG(0) + , mExponentB(0) + , mExponentA(0) +{} + +void +FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex, + Float aValue) +{ + switch (aIndex) { + case ATT_GAMMA_TRANSFER_AMPLITUDE_R: + mAmplitudeR = aValue; + break; + case ATT_GAMMA_TRANSFER_EXPONENT_R: + mExponentR = aValue; + break; + case ATT_GAMMA_TRANSFER_OFFSET_R: + mOffsetR = aValue; + break; + case ATT_GAMMA_TRANSFER_AMPLITUDE_G: + mAmplitudeG = aValue; + break; + case ATT_GAMMA_TRANSFER_EXPONENT_G: + mExponentG = aValue; + break; + case ATT_GAMMA_TRANSFER_OFFSET_G: + mOffsetG = aValue; + break; + case ATT_GAMMA_TRANSFER_AMPLITUDE_B: + mAmplitudeB = aValue; + break; + case ATT_GAMMA_TRANSFER_EXPONENT_B: + mExponentB = aValue; + break; + case ATT_GAMMA_TRANSFER_OFFSET_B: + mOffsetB = aValue; + break; + case ATT_GAMMA_TRANSFER_AMPLITUDE_A: + mAmplitudeA = aValue; + break; + case ATT_GAMMA_TRANSFER_EXPONENT_A: + mExponentA = aValue; + break; + case ATT_GAMMA_TRANSFER_OFFSET_A: + mOffsetA = aValue; + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +void +FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent, + uint8_t aTable[256]) +{ + switch (aComponent) { + case B8G8R8A8_COMPONENT_BYTEOFFSET_R: + FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable); + break; + case B8G8R8A8_COMPONENT_BYTEOFFSET_G: + FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable); + break; + case B8G8R8A8_COMPONENT_BYTEOFFSET_B: + FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable); + break; + case B8G8R8A8_COMPONENT_BYTEOFFSET_A: + FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable); + break; + default: + MOZ_ASSERT(false, "unknown component"); + break; + } +} + +void +FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude, + Float aExponent, + Float aOffset, + uint8_t aTable[256]) +{ + for (size_t i = 0; i < 256; i++) { + int32_t val = NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset)); + val = std::min(255, val); + val = std::max(0, val); + aTable[i] = val; + } +} + +FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware() + : mDivisor(0) + , mBias(0) + , mEdgeMode(EDGE_MODE_DUPLICATE) + , mPreserveAlpha(false) +{} + +int32_t +FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_CONVOLVE_MATRIX_IN: return 0; + default: return -1; + } +} + +void +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, + const IntSize &aKernelSize) +{ + MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE); + mKernelSize = aKernelSize; + Invalidate(); +} + +void +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, + const Float *aMatrix, + uint32_t aSize) +{ + MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX); + mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize); + Invalidate(); +} + +void +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, Float aValue) +{ + switch (aIndex) { + case ATT_CONVOLVE_MATRIX_DIVISOR: + mDivisor = aValue; + break; + case ATT_CONVOLVE_MATRIX_BIAS: + mBias = aValue; + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +void +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength) +{ + switch (aIndex) { + case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH: + mKernelUnitLength = aKernelUnitLength; + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +void +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, + const IntPoint &aTarget) +{ + MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET); + mTarget = aTarget; + Invalidate(); +} + +void +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, + const IntRect &aSourceRect) +{ + MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT); + mSourceRect = aSourceRect; + Invalidate(); +} + +void +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, + uint32_t aEdgeMode) +{ + MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE); + mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode); + Invalidate(); +} + +void +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, + bool aPreserveAlpha) +{ + MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA); + mPreserveAlpha = aPreserveAlpha; + Invalidate(); +} + +#ifdef DEBUG +static bool sColorSamplingAccessControlEnabled = false; +static uint8_t* sColorSamplingAccessControlStart = nullptr; +static uint8_t* sColorSamplingAccessControlEnd = nullptr; + +struct DebugOnlyAutoColorSamplingAccessControl +{ + explicit DebugOnlyAutoColorSamplingAccessControl(DataSourceSurface* aSurface) + { + sColorSamplingAccessControlStart = aSurface->GetData(); + sColorSamplingAccessControlEnd = sColorSamplingAccessControlStart + + aSurface->Stride() * aSurface->GetSize().height; + sColorSamplingAccessControlEnabled = true; + } + + ~DebugOnlyAutoColorSamplingAccessControl() + { + sColorSamplingAccessControlEnabled = false; + } +}; + +static inline void +DebugOnlyCheckColorSamplingAccess(const uint8_t* aSampleAddress) +{ + if (sColorSamplingAccessControlEnabled) { + MOZ_ASSERT(aSampleAddress >= sColorSamplingAccessControlStart, "accessing before start"); + MOZ_ASSERT(aSampleAddress < sColorSamplingAccessControlEnd, "accessing after end"); + } +} +#else +typedef DebugOnly<DataSourceSurface*> DebugOnlyAutoColorSamplingAccessControl; +#define DebugOnlyCheckColorSamplingAccess(address) +#endif + +static inline uint8_t +ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y, size_t bpp, ptrdiff_t c) +{ + DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c]); + return aData[y * aStride + bpp * x + c]; +} + +static inline int32_t +ColorAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y) +{ + DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x); + return *(uint32_t*)(aData + y * aStride + 4 * x); +} + +// Accepts fractional x & y and does bilinear interpolation. +// Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible. +static inline uint8_t +ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, Float x, Float y, size_t bpp, ptrdiff_t c) +{ + const uint32_t f = 256; + const int32_t lx = floor(x); + const int32_t ly = floor(y); + const int32_t tux = uint32_t((x - lx) * f); + const int32_t tlx = f - tux; + const int32_t tuy = uint32_t((y - ly) * f); + const int32_t tly = f - tuy; + const uint8_t &cll = ColorComponentAtPoint(aData, aStride, lx, ly, bpp, c); + const uint8_t &cul = ColorComponentAtPoint(aData, aStride, lx + 1, ly, bpp, c); + const uint8_t &clu = ColorComponentAtPoint(aData, aStride, lx, ly + 1, bpp, c); + const uint8_t &cuu = ColorComponentAtPoint(aData, aStride, lx + 1, ly + 1, bpp, c); + return ((cll * tlx + cul * tux) * tly + + (clu * tlx + cuu * tux) * tuy + f * f / 2) / (f * f); +} + +static int32_t +ClampToNonZero(int32_t a) +{ + return a * (a >= 0); +} + +template<typename CoordType> +static void +ConvolvePixel(const uint8_t *aSourceData, + uint8_t *aTargetData, + int32_t aWidth, int32_t aHeight, + int32_t aSourceStride, int32_t aTargetStride, + int32_t aX, int32_t aY, + const int32_t *aKernel, + int32_t aBias, int32_t shiftL, int32_t shiftR, + bool aPreserveAlpha, + int32_t aOrderX, int32_t aOrderY, + int32_t aTargetX, int32_t aTargetY, + CoordType aKernelUnitLengthX, + CoordType aKernelUnitLengthY) +{ + int32_t sum[4] = {0, 0, 0, 0}; + int32_t offsets[4] = { B8G8R8A8_COMPONENT_BYTEOFFSET_R, + B8G8R8A8_COMPONENT_BYTEOFFSET_G, + B8G8R8A8_COMPONENT_BYTEOFFSET_B, + B8G8R8A8_COMPONENT_BYTEOFFSET_A }; + int32_t channels = aPreserveAlpha ? 3 : 4; + int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1); + + for (int32_t y = 0; y < aOrderY; y++) { + CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY; + for (int32_t x = 0; x < aOrderX; x++) { + CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX; + for (int32_t i = 0; i < channels; i++) { + sum[i] += aKernel[aOrderX * y + x] * + ColorComponentAtPoint(aSourceData, aSourceStride, + sampleX, sampleY, 4, offsets[i]); + } + } + } + for (int32_t i = 0; i < channels; i++) { + int32_t clamped = umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR); + aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] = + (clamped + roundingAddition) << shiftR >> shiftL; + } + if (aPreserveAlpha) { + aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] = + aSourceData[aY * aSourceStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A]; + } +} + +TemporaryRef<DataSourceSurface> +FilterNodeConvolveMatrixSoftware::Render(const IntRect& aRect) +{ + if (mKernelUnitLength.width == floor(mKernelUnitLength.width) && + mKernelUnitLength.height == floor(mKernelUnitLength.height)) { + return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height); + } + return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height); +} + +static std::vector<Float> +ReversedVector(const std::vector<Float> &aVector) +{ + size_t length = aVector.size(); + std::vector<Float> result(length, 0); + for (size_t i = 0; i < length; i++) { + result[length - 1 - i] = aVector[i]; + } + return result; +} + +static std::vector<Float> +ScaledVector(const std::vector<Float> &aVector, Float aDivisor) +{ + size_t length = aVector.size(); + std::vector<Float> result(length, 0); + for (size_t i = 0; i < length; i++) { + result[i] = aVector[i] / aDivisor; + } + return result; +} + +static Float +MaxVectorSum(const std::vector<Float> &aVector) +{ + Float sum = 0; + size_t length = aVector.size(); + for (size_t i = 0; i < length; i++) { + if (aVector[i] > 0) { + sum += aVector[i]; + } + } + return sum; +} + +// Returns shiftL and shiftR in such a way that +// a << shiftL >> shiftR is roughly a * aFloat. +static void +TranslateDoubleToShifts(double aDouble, int32_t &aShiftL, int32_t &aShiftR) +{ + aShiftL = 0; + aShiftR = 0; + if (aDouble <= 0) { + MOZ_CRASH(); + } + if (aDouble < 1) { + while (1 << (aShiftR + 1) < 1 / aDouble) { + aShiftR++; + } + } else { + while (1 << (aShiftL + 1) < aDouble) { + aShiftL++; + } + } +} + +template<typename CoordType> +TemporaryRef<DataSourceSurface> +FilterNodeConvolveMatrixSoftware::DoRender(const IntRect& aRect, + CoordType aKernelUnitLengthX, + CoordType aKernelUnitLengthY) +{ + if (mKernelSize.width <= 0 || mKernelSize.height <= 0 || + mKernelMatrix.size() != uint32_t(mKernelSize.width * mKernelSize.height) || + !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) || + mDivisor == 0) { + return Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8); + } + + IntRect srcRect = InflatedSourceRect(aRect); + + // Inflate the source rect by another pixel because the bilinear filtering in + // ColorComponentAtPoint may want to access the margins. + srcRect.Inflate(1); + + RefPtr<DataSourceSurface> input = + GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect, NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect); + + if (!input) { + return nullptr; + } + + DebugOnlyAutoColorSamplingAccessControl accessControl(input); + + RefPtr<DataSourceSurface> target = + Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true); + if (MOZ2D_WARN_IF(!target)) { + return nullptr; + } + + IntPoint offset = aRect.TopLeft() - srcRect.TopLeft(); + + uint8_t* sourceData = DataAtOffset(input, offset); + int32_t sourceStride = input->Stride(); + uint8_t* targetData = target->GetData(); + int32_t targetStride = target->Stride(); + + // Why exactly are we reversing the kernel? + std::vector<Float> kernel = ReversedVector(mKernelMatrix); + kernel = ScaledVector(kernel, mDivisor); + Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias, + MaxVectorSum(ScaledVector(kernel, -1)) - mBias); + maxResultAbs = std::max(maxResultAbs, 1.0f); + + double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999; + MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0, "badly chosen float-to-int scale"); + int32_t shiftL, shiftR; + TranslateDoubleToShifts(idealFactor, shiftL, shiftR); + double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR); + MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0, "badly chosen float-to-int scale"); + + int32_t* intKernel = new int32_t[kernel.size()]; + for (size_t i = 0; i < kernel.size(); i++) { + intKernel[i] = NS_lround(kernel[i] * factorFromShifts); + } + int32_t bias = NS_lround(mBias * 255 * factorFromShifts); + + for (int32_t y = 0; y < aRect.height; y++) { + for (int32_t x = 0; x < aRect.width; x++) { + ConvolvePixel(sourceData, targetData, + aRect.width, aRect.height, sourceStride, targetStride, + x, y, intKernel, bias, shiftL, shiftR, mPreserveAlpha, + mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y, + aKernelUnitLengthX, aKernelUnitLengthY); + } + } + delete[] intKernel; + + return target.forget(); +} + +void +FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect)); +} + +IntRect +FilterNodeConvolveMatrixSoftware::InflatedSourceRect(const IntRect &aDestRect) +{ + if (aDestRect.IsEmpty()) { + return IntRect(); + } + + IntMargin margin; + margin.left = ceil(mTarget.x * mKernelUnitLength.width); + margin.top = ceil(mTarget.y * mKernelUnitLength.height); + margin.right = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width); + margin.bottom = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height); + + IntRect srcRect = aDestRect; + srcRect.Inflate(margin); + return srcRect; +} + +IntRect +FilterNodeConvolveMatrixSoftware::InflatedDestRect(const IntRect &aSourceRect) +{ + if (aSourceRect.IsEmpty()) { + return IntRect(); + } + + IntMargin margin; + margin.left = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width); + margin.top = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height); + margin.right = ceil(mTarget.x * mKernelUnitLength.width); + margin.bottom = ceil(mTarget.y * mKernelUnitLength.height); + + IntRect destRect = aSourceRect; + destRect.Inflate(margin); + return destRect; +} + +IntRect +FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + IntRect srcRequest = InflatedSourceRect(aRect); + IntRect srcOutput = GetInputRectInRect(IN_COLOR_MATRIX_IN, srcRequest); + return InflatedDestRect(srcOutput).Intersect(aRect); +} + +FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware() + : mScale(0.0f) + , mChannelX(COLOR_CHANNEL_R) + , mChannelY(COLOR_CHANNEL_G) +{} + +int32_t +FilterNodeDisplacementMapSoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_DISPLACEMENT_MAP_IN: return 0; + case IN_DISPLACEMENT_MAP_IN2: return 1; + default: return -1; + } +} + +void +FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, + Float aScale) +{ + MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE); + mScale = aScale; + Invalidate(); +} + +void +FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue) +{ + switch (aIndex) { + case ATT_DISPLACEMENT_MAP_X_CHANNEL: + mChannelX = static_cast<ColorChannel>(aValue); + break; + case ATT_DISPLACEMENT_MAP_Y_CHANNEL: + mChannelY = static_cast<ColorChannel>(aValue); + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +TemporaryRef<DataSourceSurface> +FilterNodeDisplacementMapSoftware::Render(const IntRect& aRect) +{ + IntRect srcRect = InflatedSourceOrDestRect(aRect); + RefPtr<DataSourceSurface> input = + GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS); + RefPtr<DataSourceSurface> map = + GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS); + RefPtr<DataSourceSurface> target = + Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8); + if (MOZ2D_WARN_IF(!(input && map && target))) { + return nullptr; + } + + IntPoint offset = aRect.TopLeft() - srcRect.TopLeft(); + + uint8_t* sourceData = DataAtOffset(input, offset); + int32_t sourceStride = input->Stride(); + uint8_t* mapData = map->GetData(); + int32_t mapStride = map->Stride(); + uint8_t* targetData = target->GetData(); + int32_t targetStride = target->Stride(); + + static const ptrdiff_t channelMap[4] = { + B8G8R8A8_COMPONENT_BYTEOFFSET_R, + B8G8R8A8_COMPONENT_BYTEOFFSET_G, + B8G8R8A8_COMPONENT_BYTEOFFSET_B, + B8G8R8A8_COMPONENT_BYTEOFFSET_A }; + uint16_t xChannel = channelMap[mChannelX]; + uint16_t yChannel = channelMap[mChannelY]; + + float scaleOver255 = mScale / 255.0f; + float scaleAdjustment = -0.5f * mScale; + + for (int32_t y = 0; y < aRect.height; y++) { + for (int32_t x = 0; x < aRect.width; x++) { + uint32_t mapIndex = y * mapStride + 4 * x; + uint32_t targIndex = y * targetStride + 4 * x; + int32_t sourceX = x + + scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment; + int32_t sourceY = y + + scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment; + *(uint32_t*)(targetData + targIndex) = + ColorAtPoint(sourceData, sourceStride, sourceX, sourceY); + } + } + + return target.forget(); +} + +void +FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect)); + RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect); +} + +IntRect +FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect) +{ + IntRect sourceOrDestRect = aDestOrSourceRect; + sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2)); + return sourceOrDestRect; +} + +IntRect +FilterNodeDisplacementMapSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + IntRect srcRequest = InflatedSourceOrDestRect(aRect); + IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest); + return InflatedSourceOrDestRect(srcOutput).Intersect(aRect); +} + +FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware() + : mNumOctaves(0) + , mSeed(0) + , mStitchable(false) + , mType(TURBULENCE_TYPE_TURBULENCE) +{} + +int32_t +FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex) +{ + return -1; +} + +void +FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const Size &aBaseFrequency) +{ + switch (aIndex) { + case ATT_TURBULENCE_BASE_FREQUENCY: + mBaseFrequency = aBaseFrequency; + break; + default: + MOZ_CRASH(); + break; + } + Invalidate(); +} + +void +FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const IntRect &aRect) +{ + switch (aIndex) { + case ATT_TURBULENCE_RECT: + mRenderRect = aRect; + break; + default: + MOZ_CRASH(); + break; + } + Invalidate(); +} + +void +FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, bool aStitchable) +{ + MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE); + mStitchable = aStitchable; + Invalidate(); +} + +void +FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue) +{ + switch (aIndex) { + case ATT_TURBULENCE_NUM_OCTAVES: + mNumOctaves = aValue; + break; + case ATT_TURBULENCE_SEED: + mSeed = aValue; + break; + case ATT_TURBULENCE_TYPE: + mType = static_cast<TurbulenceType>(aValue); + break; + default: + MOZ_CRASH(); + break; + } + Invalidate(); +} + +TemporaryRef<DataSourceSurface> +FilterNodeTurbulenceSoftware::Render(const IntRect& aRect) +{ + return FilterProcessing::RenderTurbulence( + aRect.Size(), aRect.TopLeft(), mBaseFrequency, + mSeed, mNumOctaves, mType, mStitchable, Rect(mRenderRect)); +} + +IntRect +FilterNodeTurbulenceSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + return aRect.Intersect(mRenderRect); +} + +FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware() + : mK1(0), mK2(0), mK3(0), mK4(0) +{ +} + +int32_t +FilterNodeArithmeticCombineSoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_ARITHMETIC_COMBINE_IN: return 0; + case IN_ARITHMETIC_COMBINE_IN2: return 1; + default: return -1; + } +} + +void +FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex, + const Float* aFloat, + uint32_t aSize) +{ + MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS); + MOZ_ASSERT(aSize == 4); + + mK1 = aFloat[0]; + mK2 = aFloat[1]; + mK3 = aFloat[2]; + mK4 = aFloat[3]; + + Invalidate(); +} + +TemporaryRef<DataSourceSurface> +FilterNodeArithmeticCombineSoftware::Render(const IntRect& aRect) +{ + RefPtr<DataSourceSurface> input1 = + GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS); + RefPtr<DataSourceSurface> input2 = + GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS); + if (!input1 && !input2) { + return nullptr; + } + + // If one input is null, treat it as transparent by adjusting the factors. + Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4; + if (!input1) { + k1 = 0.0f; + k2 = 0.0f; + input1 = input2; + } + + if (!input2) { + k1 = 0.0f; + k3 = 0.0f; + input2 = input1; + } + + return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3, k4); +} + +void +FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect); + RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect); +} + +IntRect +FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + if (mK4 > 0.0f) { + return aRect; + } + IntRect rectFrom1 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect); + IntRect rectFrom2 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect); + IntRect result; + if (mK1 > 0.0f) { + result = rectFrom1.Intersect(rectFrom2); + } + if (mK2 > 0.0f) { + result = result.Union(rectFrom1); + } + if (mK3 > 0.0f) { + result = result.Union(rectFrom2); + } + return result; +} + +FilterNodeCompositeSoftware::FilterNodeCompositeSoftware() + : mOperator(COMPOSITE_OPERATOR_OVER) +{} + +int32_t +FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex) +{ + return aInputEnumIndex - IN_COMPOSITE_IN_START; +} + +void +FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex, uint32_t aCompositeOperator) +{ + MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR); + mOperator = static_cast<CompositeOperator>(aCompositeOperator); + Invalidate(); +} + +TemporaryRef<DataSourceSurface> +FilterNodeCompositeSoftware::Render(const IntRect& aRect) +{ + RefPtr<DataSourceSurface> start = + GetInputDataSourceSurface(IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS); + RefPtr<DataSourceSurface> dest = + Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true); + if (MOZ2D_WARN_IF(!dest)) { + return nullptr; + } + + if (start) { + CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint()); + } + + for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) { + RefPtr<DataSourceSurface> input = + GetInputDataSourceSurface(IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS); + if (input) { + FilterProcessing::ApplyComposition(input, dest, mOperator); + } else { + // We need to treat input as transparent. Depending on the composite + // operator, different things happen to dest. + switch (mOperator) { + case COMPOSITE_OPERATOR_OVER: + case COMPOSITE_OPERATOR_ATOP: + case COMPOSITE_OPERATOR_XOR: + // dest is unchanged. + break; + case COMPOSITE_OPERATOR_OUT: + // dest is now transparent, but it can become non-transparent again + // when compositing additional inputs. + ClearDataSourceSurface(dest); + break; + case COMPOSITE_OPERATOR_IN: + // Transparency always wins. We're completely transparent now and + // no additional input can get rid of that transparency. + return nullptr; + } + } + } + return dest.forget(); +} + +void +FilterNodeCompositeSoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) { + RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect); + } +} + +IntRect +FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + IntRect rect; + for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) { + IntRect inputRect = GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect); + if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) { + rect = rect.Intersect(inputRect); + } else { + rect = rect.Union(inputRect); + } + } + return rect; +} + +int32_t +FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_GAUSSIAN_BLUR_IN: return 0; + default: return -1; + } +} + +TemporaryRef<DataSourceSurface> +FilterNodeBlurXYSoftware::Render(const IntRect& aRect) +{ + Size sigmaXY = StdDeviationXY(); + IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height)); + + if (d.width == 0 && d.height == 0) { + return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect); + } + + IntRect srcRect = InflatedSourceOrDestRect(aRect); + RefPtr<DataSourceSurface> input = + GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect); + if (!input) { + return nullptr; + } + + RefPtr<DataSourceSurface> target; + Rect r(0, 0, srcRect.width, srcRect.height); + + if (input->GetFormat() == SurfaceFormat::A8) { + target = Factory::CreateDataSourceSurface(srcRect.Size(), SurfaceFormat::A8); + if (MOZ2D_WARN_IF(!target)) { + return nullptr; + } + CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint()); + AlphaBoxBlur blur(r, target->Stride(), sigmaXY.width, sigmaXY.height); + blur.Blur(target->GetData()); + } else { + RefPtr<DataSourceSurface> channel0, channel1, channel2, channel3; + FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2, channel3); + if (MOZ2D_WARN_IF(!(channel0 && channel1 && channel2))) { + return nullptr; + } + AlphaBoxBlur blur(r, channel0->Stride(), sigmaXY.width, sigmaXY.height); + blur.Blur(channel0->GetData()); + blur.Blur(channel1->GetData()); + blur.Blur(channel2->GetData()); + blur.Blur(channel3->GetData()); + target = FilterProcessing::CombineColorChannels(channel0, channel1, channel2, channel3); + } + + return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE); +} + +void +FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect)); +} + +IntRect +FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(const IntRect &aDestRect) +{ + Size sigmaXY = StdDeviationXY(); + IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height)); + IntRect srcRect = aDestRect; + srcRect.Inflate(d); + return srcRect; +} + +IntRect +FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + IntRect srcRequest = InflatedSourceOrDestRect(aRect); + IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest); + return InflatedSourceOrDestRect(srcOutput).Intersect(aRect); +} + +FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware() + : mStdDeviation(0) +{} + +static float +ClampStdDeviation(float aStdDeviation) +{ + // Cap software blur radius for performance reasons. + return std::min(std::max(0.0f, aStdDeviation), 100.0f); +} + +void +FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex, + float aStdDeviation) +{ + switch (aIndex) { + case ATT_GAUSSIAN_BLUR_STD_DEVIATION: + mStdDeviation = ClampStdDeviation(aStdDeviation); + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +Size +FilterNodeGaussianBlurSoftware::StdDeviationXY() +{ + return Size(mStdDeviation, mStdDeviation); +} + +FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware() + : mBlurDirection(BLUR_DIRECTION_X) +{} + +void +FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex, + Float aStdDeviation) +{ + switch (aIndex) { + case ATT_DIRECTIONAL_BLUR_STD_DEVIATION: + mStdDeviation = ClampStdDeviation(aStdDeviation); + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +void +FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex, + uint32_t aBlurDirection) +{ + switch (aIndex) { + case ATT_DIRECTIONAL_BLUR_DIRECTION: + mBlurDirection = (BlurDirection)aBlurDirection; + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +Size +FilterNodeDirectionalBlurSoftware::StdDeviationXY() +{ + float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0; + float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0; + return Size(sigmaX, sigmaY); +} + +int32_t +FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_CROP_IN: return 0; + default: return -1; + } +} + +void +FilterNodeCropSoftware::SetAttribute(uint32_t aIndex, + const Rect &aSourceRect) +{ + MOZ_ASSERT(aIndex == ATT_CROP_RECT); + Rect srcRect = aSourceRect; + srcRect.Round(); + if (!srcRect.ToIntRect(&mCropRect)) { + mCropRect = IntRect(); + } + Invalidate(); +} + +TemporaryRef<DataSourceSurface> +FilterNodeCropSoftware::Render(const IntRect& aRect) +{ + return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect)); +} + +void +FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect)); +} + +IntRect +FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect) +{ + return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect); +} + +int32_t +FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_PREMULTIPLY_IN: return 0; + default: return -1; + } +} + +TemporaryRef<DataSourceSurface> +FilterNodePremultiplySoftware::Render(const IntRect& aRect) +{ + RefPtr<DataSourceSurface> input = + GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect); + return input ? Premultiply(input) : nullptr; +} + +void +FilterNodePremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + RequestInputRect(IN_PREMULTIPLY_IN, aRect); +} + +IntRect +FilterNodePremultiplySoftware::GetOutputRectInRect(const IntRect& aRect) +{ + return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect); +} + +int32_t +FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_UNPREMULTIPLY_IN: return 0; + default: return -1; + } +} + +TemporaryRef<DataSourceSurface> +FilterNodeUnpremultiplySoftware::Render(const IntRect& aRect) +{ + RefPtr<DataSourceSurface> input = + GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect); + return input ? Unpremultiply(input) : nullptr; +} + +void +FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect) +{ + RequestInputRect(IN_UNPREMULTIPLY_IN, aRect); +} + +IntRect +FilterNodeUnpremultiplySoftware::GetOutputRectInRect(const IntRect& aRect) +{ + return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect); +} + +bool +PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint) +{ + switch (aIndex) { + case ATT_POINT_LIGHT_POSITION: + mPosition = aPoint; + break; + default: + return false; + } + return true; +} + +SpotLightSoftware::SpotLightSoftware() + : mSpecularFocus(0) + , mLimitingConeAngle(0) + , mLimitingConeCos(1) +{ +} + +bool +SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint) +{ + switch (aIndex) { + case ATT_SPOT_LIGHT_POSITION: + mPosition = aPoint; + break; + case ATT_SPOT_LIGHT_POINTS_AT: + mPointsAt = aPoint; + break; + default: + return false; + } + return true; +} + +bool +SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) +{ + switch (aIndex) { + case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE: + mLimitingConeAngle = aValue; + break; + case ATT_SPOT_LIGHT_FOCUS: + mSpecularFocus = aValue; + break; + default: + return false; + } + return true; +} + +DistantLightSoftware::DistantLightSoftware() + : mAzimuth(0) + , mElevation(0) +{ +} + +bool +DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) +{ + switch (aIndex) { + case ATT_DISTANT_LIGHT_AZIMUTH: + mAzimuth = aValue; + break; + case ATT_DISTANT_LIGHT_ELEVATION: + mElevation = aValue; + break; + default: + return false; + } + return true; +} + +static inline Point3D Normalized(const Point3D &vec) { + Point3D copy(vec); + copy.Normalize(); + return copy; +} + +template<typename LightType, typename LightingType> +FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(const char* aTypeName) + : mSurfaceScale(0) +#if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING)) + , mTypeName(aTypeName) +#endif +{} + +template<typename LightType, typename LightingType> +int32_t +FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(uint32_t aInputEnumIndex) +{ + switch (aInputEnumIndex) { + case IN_LIGHTING_IN: return 0; + default: return -1; + } +} + +template<typename LightType, typename LightingType> +void +FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Point3D &aPoint) +{ + if (mLight.SetAttribute(aIndex, aPoint)) { + Invalidate(); + return; + } + MOZ_CRASH(); +} + +template<typename LightType, typename LightingType> +void +FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, Float aValue) +{ + if (mLight.SetAttribute(aIndex, aValue) || + mLighting.SetAttribute(aIndex, aValue)) { + Invalidate(); + return; + } + switch (aIndex) { + case ATT_LIGHTING_SURFACE_SCALE: + mSurfaceScale = aValue; + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +template<typename LightType, typename LightingType> +void +FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength) +{ + switch (aIndex) { + case ATT_LIGHTING_KERNEL_UNIT_LENGTH: + mKernelUnitLength = aKernelUnitLength; + break; + default: + MOZ_CRASH(); + } + Invalidate(); +} + +template<typename LightType, typename LightingType> +void +FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Color &aColor) +{ + MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR); + mColor = aColor; + Invalidate(); +} + +template<typename LightType, typename LightingType> +IntRect +FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(const IntRect& aRect) +{ + return GetInputRectInRect(IN_LIGHTING_IN, aRect); +} + +Point3D +PointLightSoftware::GetVectorToLight(const Point3D &aTargetPoint) +{ + return Normalized(mPosition - aTargetPoint); +} + +uint32_t +PointLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight) +{ + return aLightColor; +} + +void +SpotLightSoftware::Prepare() +{ + mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition); + mLimitingConeCos = std::max<double>(cos(mLimitingConeAngle * M_PI/180.0), 0.0); + mPowCache.CacheForExponent(mSpecularFocus); +} + +Point3D +SpotLightSoftware::GetVectorToLight(const Point3D &aTargetPoint) +{ + return Normalized(mPosition - aTargetPoint); +} + +uint32_t +SpotLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight) +{ + union { + uint32_t color; + uint8_t colorC[4]; + }; + color = aLightColor; + Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight); + uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits); + uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos); + MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits), "pow() result must not exceed 1.0"); + colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >> PowCache::sOutputIntPrecisionBits); + colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >> PowCache::sOutputIntPrecisionBits); + colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >> PowCache::sOutputIntPrecisionBits); + colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255; + return color; +} + +void +DistantLightSoftware::Prepare() +{ + const double radPerDeg = M_PI / 180.0; + mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg); + mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg); + mVectorToLight.z = sin(mElevation * radPerDeg); +} + +Point3D +DistantLightSoftware::GetVectorToLight(const Point3D &aTargetPoint) +{ + return mVectorToLight; +} + +uint32_t +DistantLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight) +{ + return aLightColor; +} + +template<typename CoordType> +static Point3D +GenerateNormal(const uint8_t *data, int32_t stride, + int32_t x, int32_t y, float surfaceScale, + CoordType dx, CoordType dy) +{ + const uint8_t *index = data + y * stride + x; + + CoordType zero = 0; + + // See this for source of constants: + // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement + int16_t normalX = + -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) + + 1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) + + -2 * ColorComponentAtPoint(index, stride, -dx, zero, 1, 0) + + 2 * ColorComponentAtPoint(index, stride, dx, zero, 1, 0) + + -1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) + + 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0); + + int16_t normalY = + -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) + + -2 * ColorComponentAtPoint(index, stride, zero, -dy, 1, 0) + + -1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) + + 1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) + + 2 * ColorComponentAtPoint(index, stride, zero, dy, 1, 0) + + 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0); + + Point3D normal; + normal.x = -surfaceScale * normalX / 4.0f; + normal.y = -surfaceScale * normalY / 4.0f; + normal.z = 255; + return Normalized(normal); +} + +template<typename LightType, typename LightingType> +TemporaryRef<DataSourceSurface> +FilterNodeLightingSoftware<LightType, LightingType>::Render(const IntRect& aRect) +{ + if (mKernelUnitLength.width == floor(mKernelUnitLength.width) && + mKernelUnitLength.height == floor(mKernelUnitLength.height)) { + return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height); + } + return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height); +} + +template<typename LightType, typename LightingType> +void +FilterNodeLightingSoftware<LightType, LightingType>::RequestFromInputsForRect(const IntRect &aRect) +{ + IntRect srcRect = aRect; + srcRect.Inflate(ceil(mKernelUnitLength.width), + ceil(mKernelUnitLength.height)); + RequestInputRect(IN_LIGHTING_IN, srcRect); +} + +template<typename LightType, typename LightingType> template<typename CoordType> +TemporaryRef<DataSourceSurface> +FilterNodeLightingSoftware<LightType, LightingType>::DoRender(const IntRect& aRect, + CoordType aKernelUnitLengthX, + CoordType aKernelUnitLengthY) +{ + IntRect srcRect = aRect; + IntSize size = aRect.Size(); + srcRect.Inflate(ceil(float(aKernelUnitLengthX)), + ceil(float(aKernelUnitLengthY))); + + // Inflate the source rect by another pixel because the bilinear filtering in + // ColorComponentAtPoint may want to access the margins. + srcRect.Inflate(1); + + RefPtr<DataSourceSurface> input = + GetInputDataSourceSurface(IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8, + EDGE_MODE_DUPLICATE); + + if (!input) { + return nullptr; + } + + if (input->GetFormat() != SurfaceFormat::A8) { + input = FilterProcessing::ExtractAlpha(input); + } + + DebugOnlyAutoColorSamplingAccessControl accessControl(input); + + RefPtr<DataSourceSurface> target = + Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); + if (MOZ2D_WARN_IF(!target)) { + return nullptr; + } + + IntPoint offset = aRect.TopLeft() - srcRect.TopLeft(); + + uint8_t* sourceData = DataAtOffset(input, offset); + int32_t sourceStride = input->Stride(); + uint8_t* targetData = target->GetData(); + int32_t targetStride = target->Stride(); + + uint32_t lightColor = ColorToBGRA(mColor); + mLight.Prepare(); + mLighting.Prepare(); + + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x++) { + int32_t sourceIndex = y * sourceStride + x; + int32_t targetIndex = y * targetStride + 4 * x; + + Point3D normal = GenerateNormal(sourceData, sourceStride, + x, y, mSurfaceScale, + aKernelUnitLengthX, aKernelUnitLengthY); + + IntPoint pointInFilterSpace(aRect.x + x, aRect.y + y); + Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f; + Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z); + Point3D rayDir = mLight.GetVectorToLight(pt); + uint32_t color = mLight.GetColor(lightColor, rayDir); + + *(uint32_t*)(targetData + targetIndex) = mLighting.LightPixel(normal, rayDir, color); + } + } + + return target.forget(); +} + +DiffuseLightingSoftware::DiffuseLightingSoftware() + : mDiffuseConstant(0) +{ +} + +bool +DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) +{ + switch (aIndex) { + case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT: + mDiffuseConstant = aValue; + break; + default: + return false; + } + return true; +} + +uint32_t +DiffuseLightingSoftware::LightPixel(const Point3D &aNormal, + const Point3D &aVectorToLight, + uint32_t aColor) +{ + Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight)); + Float diffuseNL = mDiffuseConstant * dotNL; + + union { + uint32_t bgra; + uint8_t components[4]; + } color = { aColor }; + color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = + umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]), 255U); + color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = + umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]), 255U); + color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = + umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]), 255U); + color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255; + return color.bgra; +} + +SpecularLightingSoftware::SpecularLightingSoftware() + : mSpecularConstant(0) + , mSpecularExponent(0) +{ +} + +bool +SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) +{ + switch (aIndex) { + case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT: + mSpecularConstant = std::min(std::max(aValue, 0.0f), 255.0f); + break; + case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT: + mSpecularExponent = std::min(std::max(aValue, 1.0f), 128.0f); + break; + default: + return false; + } + return true; +} + +void +SpecularLightingSoftware::Prepare() +{ + mPowCache.CacheForExponent(mSpecularExponent); + mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8)); +} + +uint32_t +SpecularLightingSoftware::LightPixel(const Point3D &aNormal, + const Point3D &aVectorToLight, + uint32_t aColor) +{ + Point3D vectorToEye(0, 0, 1); + Point3D halfwayVector = Normalized(aVectorToLight + vectorToEye); + Float dotNH = aNormal.DotProduct(halfwayVector); + uint16_t dotNHi = uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits)); + uint32_t specularNHi = uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8; + + union { + uint32_t bgra; + uint8_t components[4]; + } color = { aColor }; + color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = + umin( + (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >> PowCache::sOutputIntPrecisionBits, 255U); + color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = + umin( + (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >> PowCache::sOutputIntPrecisionBits, 255U); + color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = + umin( + (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >> PowCache::sOutputIntPrecisionBits, 255U); + + color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = + umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B], + umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G], + color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R])); + return color.bgra; +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/2d/FilterNodeSoftware.h b/gfx/2d/FilterNodeSoftware.h new file mode 100644 index 000000000..dfef8ba01 --- /dev/null +++ b/gfx/2d/FilterNodeSoftware.h @@ -0,0 +1,724 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MOZILLA_GFX_FILTERNODESOFTWARE_H_ +#define _MOZILLA_GFX_FILTERNODESOFTWARE_H_ + +#include "Filters.h" +#include <vector> + +namespace mozilla { +namespace gfx { + +class DataSourceSurface; +class DrawTarget; +struct DrawOptions; +class FilterNodeSoftware; + +/** + * Can be attached to FilterNodeSoftware instances using + * AddInvalidationListener. FilterInvalidated is called whenever the output of + * the observed filter may have changed; that is, whenever cached GetOutput() + * results (and results derived from them) need to discarded. + */ +class FilterInvalidationListener +{ +public: + virtual void FilterInvalidated(FilterNodeSoftware* aFilter) = 0; +}; + +/** + * This is the base class for the software (i.e. pure CPU, non-accelerated) + * FilterNode implementation. The software implementation is backend-agnostic, + * so it can be used as a fallback for all DrawTarget implementations. + */ +class FilterNodeSoftware : public FilterNode, + public FilterInvalidationListener +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeSoftware, override) + virtual ~FilterNodeSoftware(); + + // Factory method, intended to be called from DrawTarget*::CreateFilter. + static TemporaryRef<FilterNode> Create(FilterType aType); + + // Draw the filter, intended to be called by DrawTarget*::DrawFilter. + void Draw(DrawTarget* aDrawTarget, const Rect &aSourceRect, + const Point &aDestPoint, const DrawOptions &aOptions); + + virtual FilterBackend GetBackendType() override { return FILTER_BACKEND_SOFTWARE; } + virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override; + virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override; + + virtual const char* GetName() { return "Unknown"; } + + virtual void AddInvalidationListener(FilterInvalidationListener* aListener); + virtual void RemoveInvalidationListener(FilterInvalidationListener* aListener); + + // FilterInvalidationListener implementation + virtual void FilterInvalidated(FilterNodeSoftware* aFilter) override; + +protected: + + // The following methods are intended to be overriden by subclasses. + + /** + * Translates a *FilterInputs enum value into an index for the + * mInputFilters / mInputSurfaces arrays. Returns -1 for invalid inputs. + * If somebody calls SetInput(enumValue, input) with an enumValue for which + * InputIndex(enumValue) is -1, we abort. + */ + virtual int32_t InputIndex(uint32_t aInputEnumIndex) { return -1; } + + /** + * Every filter node has an output rect, which can also be infinite. The + * output rect can depend on the values of any set attributes and on the + * output rects of any input filters or surfaces. + * This method returns the intersection of the filter's output rect with + * aInRect. Filters with unconstrained output always return aInRect. + */ + virtual IntRect GetOutputRectInRect(const IntRect& aInRect) = 0; + + /** + * Return a surface with the rendered output which is of size aRect.Size(). + * aRect is required to be a subrect of this filter's output rect; in other + * words, aRect == GetOutputRectInRect(aRect) must always be true. + * May return nullptr in error conditions or for an empty aRect. + * Implementations are not required to allocate a new surface and may even + * pass through input surfaces unchanged. + * Callers need to treat the returned surface as immutable. + */ + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) = 0; + + /** + * Call RequestRect (see below) on any input filters with the desired input + * rect, so that the input filter knows what to cache the next time it + * renders. + */ + virtual void RequestFromInputsForRect(const IntRect &aRect) {} + + /** + * This method provides a caching default implementation but can be overriden + * by subclasses that don't want to cache their output. Those classes should + * call Render(aRect) directly from here. + */ + virtual TemporaryRef<DataSourceSurface> GetOutput(const IntRect &aRect); + + // The following methods are non-virtual helper methods. + + /** + * Format hints for GetInputDataSourceSurface. Some callers of + * GetInputDataSourceSurface can handle both B8G8R8A8 and A8 surfaces, these + * should pass CAN_HANDLE_A8 in order to avoid unnecessary conversions. + * Callers that can only handle B8G8R8A8 surfaces pass NEED_COLOR_CHANNELS. + */ + enum FormatHint { + CAN_HANDLE_A8, + NEED_COLOR_CHANNELS + }; + + /** + * Returns SurfaceFormat::B8G8R8A8 or SurfaceFormat::A8, depending on the current surface + * format and the format hint. + */ + SurfaceFormat DesiredFormat(SurfaceFormat aCurrentFormat, + FormatHint aFormatHint); + + /** + * Intended to be called by FilterNodeSoftware::Render implementations. + * Returns a surface of size aRect.Size() or nullptr in error conditions. The + * returned surface contains the output of the specified input filter or + * input surface in aRect. If aRect extends beyond the input filter's output + * rect (or the input surface's dimensions), the remaining area is filled + * according to aEdgeMode: The default, EDGE_MODE_NONE, simply pads with + * transparent black. + * If non-null, the returned surface is guaranteed to be of SurfaceFormat::A8 or + * SurfaceFormat::B8G8R8A8. If aFormatHint is NEED_COLOR_CHANNELS, the returned + * surface is guaranteed to be of SurfaceFormat::B8G8R8A8 always. + * Each pixel row of the returned surface is guaranteed to be 16-byte aligned. + */ + TemporaryRef<DataSourceSurface> + GetInputDataSourceSurface(uint32_t aInputEnumIndex, const IntRect& aRect, + FormatHint aFormatHint = CAN_HANDLE_A8, + ConvolveMatrixEdgeMode aEdgeMode = EDGE_MODE_NONE, + const IntRect *aTransparencyPaddedSourceRect = nullptr); + + /** + * Returns the intersection of the input filter's or surface's output rect + * with aInRect. + */ + IntRect GetInputRectInRect(uint32_t aInputEnumIndex, const IntRect& aInRect); + + /** + * Calls RequestRect on the specified input, if it's a filter. + */ + void RequestInputRect(uint32_t aInputEnumIndex, const IntRect& aRect); + + /** + * Returns the number of set input filters or surfaces. Needed for filters + * which can have an arbitrary number of inputs. + */ + size_t NumberOfSetInputs(); + + /** + * Discard the cached surface that was stored in the GetOutput default + * implementation. Needs to be called whenever attributes or inputs are set + * that might change the result of a Render() call. + */ + void Invalidate(); + + /** + * Called in order to let this filter know what to cache during the next + * GetOutput call. Expected to call RequestRect on this filter's input + * filters. + */ + void RequestRect(const IntRect &aRect); + + /** + * Set input filter and clear input surface for this input index, or set + * input surface and clear input filter. One of aSurface and aFilter should + * be null. + */ + void SetInput(uint32_t aIndex, SourceSurface *aSurface, + FilterNodeSoftware *aFilter); + +protected: + /** + * mInputSurfaces / mInputFilters: For each input index, either a surface or + * a filter is set, and the other is null. + */ + std::vector<RefPtr<SourceSurface> > mInputSurfaces; + std::vector<RefPtr<FilterNodeSoftware> > mInputFilters; + + /** + * Weak pointers to our invalidation listeners, i.e. to those filters who + * have this filter as an input. Invalidation listeners are required to + * unsubscribe themselves from us when they let go of their reference to us. + * This ensures that the pointers in this array are never stale. + */ + std::vector<FilterInvalidationListener*> mInvalidationListeners; + + /** + * Stores the rect which we want to render and cache on the next call to + * GetOutput. + */ + IntRect mRequestedRect; + + /** + * Stores our cached output. + */ + IntRect mCachedRect; + RefPtr<DataSourceSurface> mCachedOutput; +}; + +// Subclasses for specific filters. + +class FilterNodeTransformSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTransformSoftware, override) + FilterNodeTransformSoftware(); + virtual const char* GetName() override { return "Transform"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, uint32_t aGraphicsFilter) override; + virtual void SetAttribute(uint32_t aIndex, const Matrix &aMatrix) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + IntRect SourceRectForOutputRect(const IntRect &aRect); + +private: + Matrix mMatrix; + Filter mFilter; +}; + +class FilterNodeBlendSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeBlendSoftware, override) + FilterNodeBlendSoftware(); + virtual const char* GetName() override { return "Blend"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, uint32_t aBlendMode) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + +private: + BlendMode mBlendMode; +}; + +class FilterNodeMorphologySoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeMorphologySoftware, override) + FilterNodeMorphologySoftware(); + virtual const char* GetName() override { return "Morphology"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, const IntSize &aRadii) override; + virtual void SetAttribute(uint32_t aIndex, uint32_t aOperator) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + +private: + IntSize mRadii; + MorphologyOperator mOperator; +}; + +class FilterNodeColorMatrixSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeColorMatrixSoftware, override) + virtual const char* GetName() override { return "ColorMatrix"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &aMatrix) override; + virtual void SetAttribute(uint32_t aIndex, uint32_t aAlphaMode) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + +private: + Matrix5x4 mMatrix; + AlphaMode mAlphaMode; +}; + +class FilterNodeFloodSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeFloodSoftware, override) + virtual const char* GetName() override { return "Flood"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, const Color &aColor) override; + +protected: + virtual TemporaryRef<DataSourceSurface> GetOutput(const IntRect &aRect) override; + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + +private: + Color mColor; +}; + +class FilterNodeTileSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTileSoftware, override) + virtual const char* GetName() override { return "Tile"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, const IntRect &aSourceRect) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + +private: + IntRect mSourceRect; +}; + +/** + * Baseclass for the four different component transfer filters. + */ +class FilterNodeComponentTransferSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeComponentTransferSoftware, override) + FilterNodeComponentTransferSoftware(); + + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, bool aDisable) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + virtual void GenerateLookupTable(ptrdiff_t aComponent, uint8_t aTables[4][256], + bool aDisabled); + virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) = 0; + + bool mDisableR; + bool mDisableG; + bool mDisableB; + bool mDisableA; +}; + +class FilterNodeTableTransferSoftware : public FilterNodeComponentTransferSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTableTransferSoftware, override) + virtual const char* GetName() override { return "TableTransfer"; } + using FilterNodeComponentTransferSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override; + +protected: + virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override; + +private: + void FillLookupTableImpl(std::vector<Float>& aTableValues, uint8_t aTable[256]); + + std::vector<Float> mTableR; + std::vector<Float> mTableG; + std::vector<Float> mTableB; + std::vector<Float> mTableA; +}; + +class FilterNodeDiscreteTransferSoftware : public FilterNodeComponentTransferSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDiscreteTransferSoftware, override) + virtual const char* GetName() override { return "DiscreteTransfer"; } + using FilterNodeComponentTransferSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override; + +protected: + virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override; + +private: + void FillLookupTableImpl(std::vector<Float>& aTableValues, uint8_t aTable[256]); + + std::vector<Float> mTableR; + std::vector<Float> mTableG; + std::vector<Float> mTableB; + std::vector<Float> mTableA; +}; + +class FilterNodeLinearTransferSoftware : public FilterNodeComponentTransferSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeLinearTransformSoftware, override) + FilterNodeLinearTransferSoftware(); + virtual const char* GetName() override { return "LinearTransfer"; } + using FilterNodeComponentTransferSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, Float aValue) override; + +protected: + virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override; + +private: + void FillLookupTableImpl(Float aSlope, Float aIntercept, uint8_t aTable[256]); + + Float mSlopeR; + Float mSlopeG; + Float mSlopeB; + Float mSlopeA; + Float mInterceptR; + Float mInterceptG; + Float mInterceptB; + Float mInterceptA; +}; + +class FilterNodeGammaTransferSoftware : public FilterNodeComponentTransferSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeGammaTransferSoftware, override) + FilterNodeGammaTransferSoftware(); + virtual const char* GetName() override { return "GammaTransfer"; } + using FilterNodeComponentTransferSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, Float aValue) override; + +protected: + virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override; + +private: + void FillLookupTableImpl(Float aAmplitude, Float aExponent, Float aOffset, uint8_t aTable[256]); + + Float mAmplitudeR; + Float mAmplitudeG; + Float mAmplitudeB; + Float mAmplitudeA; + Float mExponentR; + Float mExponentG; + Float mExponentB; + Float mExponentA; + Float mOffsetR; + Float mOffsetG; + Float mOffsetB; + Float mOffsetA; +}; + +class FilterNodeConvolveMatrixSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeConvolveMatrixSoftware, override) + FilterNodeConvolveMatrixSoftware(); + virtual const char* GetName() override { return "ConvolveMatrix"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, const IntSize &aKernelSize) override; + virtual void SetAttribute(uint32_t aIndex, const Float* aMatrix, uint32_t aSize) override; + virtual void SetAttribute(uint32_t aIndex, Float aValue) override; + virtual void SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength) override; + virtual void SetAttribute(uint32_t aIndex, const IntRect &aSourceRect) override; + virtual void SetAttribute(uint32_t aIndex, const IntPoint &aTarget) override; + virtual void SetAttribute(uint32_t aIndex, uint32_t aEdgeMode) override; + virtual void SetAttribute(uint32_t aIndex, bool aPreserveAlpha) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + +private: + template<typename CoordType> + TemporaryRef<DataSourceSurface> DoRender(const IntRect& aRect, + CoordType aKernelUnitLengthX, + CoordType aKernelUnitLengthY); + + IntRect InflatedSourceRect(const IntRect &aDestRect); + IntRect InflatedDestRect(const IntRect &aSourceRect); + + IntSize mKernelSize; + std::vector<Float> mKernelMatrix; + Float mDivisor; + Float mBias; + IntPoint mTarget; + IntRect mSourceRect; + ConvolveMatrixEdgeMode mEdgeMode; + Size mKernelUnitLength; + bool mPreserveAlpha; +}; + +class FilterNodeDisplacementMapSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDisplacementMapSoftware, override) + FilterNodeDisplacementMapSoftware(); + virtual const char* GetName() override { return "DisplacementMap"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, Float aScale) override; + virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + +private: + IntRect InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect); + + Float mScale; + ColorChannel mChannelX; + ColorChannel mChannelY; +}; + +class FilterNodeTurbulenceSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTurbulenceSoftware, override) + FilterNodeTurbulenceSoftware(); + virtual const char* GetName() override { return "Turbulence"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, const Size &aSize) override; + virtual void SetAttribute(uint32_t aIndex, const IntRect &aRenderRect) override; + virtual void SetAttribute(uint32_t aIndex, bool aStitchable) override; + virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + +private: + IntRect mRenderRect; + Size mBaseFrequency; + uint32_t mNumOctaves; + uint32_t mSeed; + bool mStitchable; + TurbulenceType mType; +}; + +class FilterNodeArithmeticCombineSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeArithmeticCombineSoftware, override) + FilterNodeArithmeticCombineSoftware(); + virtual const char* GetName() override { return "ArithmeticCombine"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + +private: + Float mK1; + Float mK2; + Float mK3; + Float mK4; +}; + +class FilterNodeCompositeSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeCompositeSoftware, override) + FilterNodeCompositeSoftware(); + virtual const char* GetName() override { return "Composite"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, uint32_t aOperator) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + +private: + CompositeOperator mOperator; +}; + +// Base class for FilterNodeGaussianBlurSoftware and +// FilterNodeDirectionalBlurSoftware. +class FilterNodeBlurXYSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeBlurXYSoftware, override) +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + IntRect InflatedSourceOrDestRect(const IntRect &aDestRect); + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + + // Implemented by subclasses. + virtual Size StdDeviationXY() = 0; +}; + +class FilterNodeGaussianBlurSoftware : public FilterNodeBlurXYSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeGaussianBlurSoftware, override) + FilterNodeGaussianBlurSoftware(); + virtual const char* GetName() override { return "GaussianBlur"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, Float aStdDeviation) override; + +protected: + virtual Size StdDeviationXY() override; + +private: + Float mStdDeviation; +}; + +class FilterNodeDirectionalBlurSoftware : public FilterNodeBlurXYSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDirectionalBlurSoftware, override) + FilterNodeDirectionalBlurSoftware(); + virtual const char* GetName() override { return "DirectionalBlur"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, Float aStdDeviation) override; + virtual void SetAttribute(uint32_t aIndex, uint32_t aBlurDirection) override; + +protected: + virtual Size StdDeviationXY() override; + +private: + Float mStdDeviation; + BlurDirection mBlurDirection; +}; + +class FilterNodeCropSoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeCropSoftware, override) + virtual const char* GetName() override { return "Crop"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, const Rect &aSourceRect) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + +private: + IntRect mCropRect; +}; + +class FilterNodePremultiplySoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodePremultiplySoftware, override) + virtual const char* GetName() override { return "Premultiply"; } +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; +}; + +class FilterNodeUnpremultiplySoftware : public FilterNodeSoftware +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeUnpremultiplySoftware, override) + virtual const char* GetName() override { return "Unpremultiply"; } +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; +}; + +template<typename LightType, typename LightingType> +class FilterNodeLightingSoftware : public FilterNodeSoftware +{ +public: +#if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING)) + // Helpers for refcounted + virtual const char* typeName() const override { return mTypeName; } + virtual size_t typeSize() const override { return sizeof(*this); } +#endif + explicit FilterNodeLightingSoftware(const char* aTypeName); + virtual const char* GetName() override { return "Lighting"; } + using FilterNodeSoftware::SetAttribute; + virtual void SetAttribute(uint32_t aIndex, Float) override; + virtual void SetAttribute(uint32_t aIndex, const Size &) override; + virtual void SetAttribute(uint32_t aIndex, const Point3D &) override; + virtual void SetAttribute(uint32_t aIndex, const Color &) override; + +protected: + virtual TemporaryRef<DataSourceSurface> Render(const IntRect& aRect) override; + virtual IntRect GetOutputRectInRect(const IntRect& aRect) override; + virtual int32_t InputIndex(uint32_t aInputEnumIndex) override; + virtual void RequestFromInputsForRect(const IntRect &aRect) override; + +private: + template<typename CoordType> + TemporaryRef<DataSourceSurface> DoRender(const IntRect& aRect, + CoordType aKernelUnitLengthX, + CoordType aKernelUnitLengthY); + + LightType mLight; + LightingType mLighting; + Float mSurfaceScale; + Size mKernelUnitLength; + Color mColor; +#if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING)) + const char* mTypeName; +#endif +}; + +} +} + +#endif // _MOZILLA_GFX_FILTERNODESOFTWARE_H_ diff --git a/gfx/2d/FilterProcessing.cpp b/gfx/2d/FilterProcessing.cpp new file mode 100644 index 000000000..084f9476c --- /dev/null +++ b/gfx/2d/FilterProcessing.cpp @@ -0,0 +1,235 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FilterProcessing.h" +#include "Logging.h" + +namespace mozilla { +namespace gfx { + +TemporaryRef<DataSourceSurface> +FilterProcessing::ExtractAlpha(DataSourceSurface* aSource) +{ + IntSize size = aSource->GetSize(); + RefPtr<DataSourceSurface> alpha = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8); + if (MOZ2D_WARN_IF(!alpha)) { + return nullptr; + } + uint8_t* sourceData = aSource->GetData(); + int32_t sourceStride = aSource->Stride(); + uint8_t* alphaData = alpha->GetData(); + int32_t alphaStride = alpha->Stride(); + + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + ExtractAlpha_SSE2(size, sourceData, sourceStride, alphaData, alphaStride); +#endif + } else { + ExtractAlpha_Scalar(size, sourceData, sourceStride, alphaData, alphaStride); + } + + return alpha.forget(); +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::ConvertToB8G8R8A8(SourceSurface* aSurface) +{ + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + return ConvertToB8G8R8A8_SSE2(aSurface); +#endif + } + return ConvertToB8G8R8A8_Scalar(aSurface); +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::ApplyBlending(DataSourceSurface* aInput1, DataSourceSurface* aInput2, + BlendMode aBlendMode) +{ + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + return ApplyBlending_SSE2(aInput1, aInput2, aBlendMode); +#endif + } + return nullptr; +} + +void +FilterProcessing::ApplyMorphologyHorizontal(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOp) +{ + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + ApplyMorphologyHorizontal_SSE2( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp); +#endif + } else { + ApplyMorphologyHorizontal_Scalar( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp); + } +} + +void +FilterProcessing::ApplyMorphologyVertical(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOp) +{ + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + ApplyMorphologyVertical_SSE2( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp); +#endif + } else { + ApplyMorphologyVertical_Scalar( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp); + } +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::ApplyColorMatrix(DataSourceSurface* aInput, const Matrix5x4 &aMatrix) +{ + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + return ApplyColorMatrix_SSE2(aInput, aMatrix); +#endif + } + return ApplyColorMatrix_Scalar(aInput, aMatrix); +} + +void +FilterProcessing::ApplyComposition(DataSourceSurface* aSource, DataSourceSurface* aDest, + CompositeOperator aOperator) +{ + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + ApplyComposition_SSE2(aSource, aDest, aOperator); +#endif + } else { + ApplyComposition_Scalar(aSource, aDest, aOperator); + } +} + +void +FilterProcessing::SeparateColorChannels(DataSourceSurface* aSource, + RefPtr<DataSourceSurface>& aChannel0, + RefPtr<DataSourceSurface>& aChannel1, + RefPtr<DataSourceSurface>& aChannel2, + RefPtr<DataSourceSurface>& aChannel3) +{ + IntSize size = aSource->GetSize(); + aChannel0 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8); + aChannel1 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8); + aChannel2 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8); + aChannel3 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8); + if (MOZ2D_WARN_IF(!(aChannel0 && aChannel1 && aChannel2 && aChannel3))) { + return; + } + + uint8_t* sourceData = aSource->GetData(); + int32_t sourceStride = aSource->Stride(); + uint8_t* channel0Data = aChannel0->GetData(); + uint8_t* channel1Data = aChannel1->GetData(); + uint8_t* channel2Data = aChannel2->GetData(); + uint8_t* channel3Data = aChannel3->GetData(); + int32_t channelStride = aChannel0->Stride(); + + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + SeparateColorChannels_SSE2(size, sourceData, sourceStride, channel0Data, channel1Data, channel2Data, channel3Data, channelStride); +#endif + } else { + SeparateColorChannels_Scalar(size, sourceData, sourceStride, channel0Data, channel1Data, channel2Data, channel3Data, channelStride); + } +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::CombineColorChannels(DataSourceSurface* aChannel0, DataSourceSurface* aChannel1, + DataSourceSurface* aChannel2, DataSourceSurface* aChannel3) +{ + IntSize size = aChannel0->GetSize(); + RefPtr<DataSourceSurface> result = + Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); + if (MOZ2D_WARN_IF(!result)) { + return nullptr; + } + int32_t resultStride = result->Stride(); + uint8_t* resultData = result->GetData(); + int32_t channelStride = aChannel0->Stride(); + uint8_t* channel0Data = aChannel0->GetData(); + uint8_t* channel1Data = aChannel1->GetData(); + uint8_t* channel2Data = aChannel2->GetData(); + uint8_t* channel3Data = aChannel3->GetData(); + + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + CombineColorChannels_SSE2(size, resultStride, resultData, channelStride, channel0Data, channel1Data, channel2Data, channel3Data); +#endif + } else { + CombineColorChannels_Scalar(size, resultStride, resultData, channelStride, channel0Data, channel1Data, channel2Data, channel3Data); + } + + return result.forget(); +} + +void +FilterProcessing::DoPremultiplicationCalculation(const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride) +{ + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + DoPremultiplicationCalculation_SSE2( + aSize, aTargetData, aTargetStride, aSourceData, aSourceStride); +#endif + } else { + DoPremultiplicationCalculation_Scalar( + aSize, aTargetData, aTargetStride, aSourceData, aSourceStride); + } +} + +void +FilterProcessing::DoUnpremultiplicationCalculation(const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride) +{ + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + DoUnpremultiplicationCalculation_SSE2( + aSize, aTargetData, aTargetStride, aSourceData, aSourceStride); +#endif + } else { + DoUnpremultiplicationCalculation_Scalar( + aSize, aTargetData, aTargetStride, aSourceData, aSourceStride); + } +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::RenderTurbulence(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency, + int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect) +{ + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + return RenderTurbulence_SSE2(aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect); +#endif + } + return RenderTurbulence_Scalar(aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect); +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::ApplyArithmeticCombine(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4) +{ + if (Factory::HasSSE2()) { +#ifdef USE_SSE2 + return ApplyArithmeticCombine_SSE2(aInput1, aInput2, aK1, aK2, aK3, aK4); +#endif + } + return ApplyArithmeticCombine_Scalar(aInput1, aInput2, aK1, aK2, aK3, aK4); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/2d/FilterProcessing.h b/gfx/2d/FilterProcessing.h new file mode 100644 index 000000000..253d16543 --- /dev/null +++ b/gfx/2d/FilterProcessing.h @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MOZILLA_GFX_FILTERPROCESSING_H_ +#define _MOZILLA_GFX_FILTERPROCESSING_H_ + +#include "2D.h" +#include "Filters.h" + +namespace mozilla { +namespace gfx { + +const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_B = 0; +const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_G = 1; +const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_R = 2; +const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_A = 3; + +class FilterProcessing +{ +public: + + // Fast approximate division by 255. It has the property that + // for all 0 <= v <= 255*255, FastDivideBy255(v) == v/255. + // But it only uses two adds and two shifts instead of an + // integer division (which is expensive on many processors). + template<class B, class A> + static B FastDivideBy255(A v) + { + return ((v << 8) + v + 255) >> 16; + } + + static TemporaryRef<DataSourceSurface> ExtractAlpha(DataSourceSurface* aSource); + static TemporaryRef<DataSourceSurface> ConvertToB8G8R8A8(SourceSurface* aSurface); + static TemporaryRef<DataSourceSurface> ApplyBlending(DataSourceSurface* aInput1, DataSourceSurface* aInput2, BlendMode aBlendMode); + static void ApplyMorphologyHorizontal(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOperator); + static void ApplyMorphologyVertical(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOperator); + static TemporaryRef<DataSourceSurface> ApplyColorMatrix(DataSourceSurface* aInput, const Matrix5x4 &aMatrix); + static void ApplyComposition(DataSourceSurface* aSource, DataSourceSurface* aDest, CompositeOperator aOperator); + static void SeparateColorChannels(DataSourceSurface* aSource, + RefPtr<DataSourceSurface>& aChannel0, + RefPtr<DataSourceSurface>& aChannel1, + RefPtr<DataSourceSurface>& aChannel2, + RefPtr<DataSourceSurface>& aChannel3); + static TemporaryRef<DataSourceSurface> + CombineColorChannels(DataSourceSurface* aChannel0, DataSourceSurface* aChannel1, + DataSourceSurface* aChannel2, DataSourceSurface* aChannel3); + static void DoPremultiplicationCalculation(const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride); + static void DoUnpremultiplicationCalculation(const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride); + static TemporaryRef<DataSourceSurface> + RenderTurbulence(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency, + int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect); + static TemporaryRef<DataSourceSurface> + ApplyArithmeticCombine(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4); + +protected: + static void ExtractAlpha_Scalar(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride); + static TemporaryRef<DataSourceSurface> ConvertToB8G8R8A8_Scalar(SourceSurface* aSurface); + static void ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOperator); + static void ApplyMorphologyVertical_Scalar(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOperator); + static TemporaryRef<DataSourceSurface> ApplyColorMatrix_Scalar(DataSourceSurface* aInput, const Matrix5x4 &aMatrix); + static void ApplyComposition_Scalar(DataSourceSurface* aSource, DataSourceSurface* aDest, CompositeOperator aOperator); + + static void SeparateColorChannels_Scalar(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride); + static void CombineColorChannels_Scalar(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data); + static void DoPremultiplicationCalculation_Scalar(const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride); + static void DoUnpremultiplicationCalculation_Scalar(const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride); + static TemporaryRef<DataSourceSurface> + RenderTurbulence_Scalar(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency, + int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect); + static TemporaryRef<DataSourceSurface> + ApplyArithmeticCombine_Scalar(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4); + +#ifdef USE_SSE2 + static void ExtractAlpha_SSE2(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride); + static TemporaryRef<DataSourceSurface> ConvertToB8G8R8A8_SSE2(SourceSurface* aSurface); + static TemporaryRef<DataSourceSurface> ApplyBlending_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, BlendMode aBlendMode); + static void ApplyMorphologyHorizontal_SSE2(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOperator); + static void ApplyMorphologyVertical_SSE2(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOperator); + static TemporaryRef<DataSourceSurface> ApplyColorMatrix_SSE2(DataSourceSurface* aInput, const Matrix5x4 &aMatrix); + static void ApplyComposition_SSE2(DataSourceSurface* aSource, DataSourceSurface* aDest, CompositeOperator aOperator); + static void SeparateColorChannels_SSE2(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride); + static void CombineColorChannels_SSE2(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data); + static void DoPremultiplicationCalculation_SSE2(const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride); + static void DoUnpremultiplicationCalculation_SSE2(const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride); + static TemporaryRef<DataSourceSurface> + RenderTurbulence_SSE2(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency, + int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect); + static TemporaryRef<DataSourceSurface> + ApplyArithmeticCombine_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4); +#endif +}; + +// Constant-time max and min functions for unsigned arguments +static inline unsigned +umax(unsigned a, unsigned b) +{ + return a - ((a - b) & -(a < b)); +} + +static inline unsigned +umin(unsigned a, unsigned b) +{ + return a - ((a - b) & -(a > b)); +} + +} // namespace gfx +} // namespace mozilla + +#endif // _MOZILLA_GFX_FILTERPROCESSING_H_ diff --git a/gfx/2d/FilterProcessingSIMD-inl.h b/gfx/2d/FilterProcessingSIMD-inl.h new file mode 100644 index 000000000..8f58809ec --- /dev/null +++ b/gfx/2d/FilterProcessingSIMD-inl.h @@ -0,0 +1,1081 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FilterProcessing.h" + +#include "SIMD.h" +#include "SVGTurbulenceRenderer-inl.h" + +namespace mozilla { +namespace gfx { + +template<typename u8x16_t> +inline TemporaryRef<DataSourceSurface> +ConvertToB8G8R8A8_SIMD(SourceSurface* aSurface) +{ + IntSize size = aSurface->GetSize(); + RefPtr<DataSourceSurface> input = aSurface->GetDataSurface(); + RefPtr<DataSourceSurface> output = + Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); + uint8_t *inputData = input->GetData(); + uint8_t *outputData = output->GetData(); + int32_t inputStride = input->Stride(); + int32_t outputStride = output->Stride(); + switch (input->GetFormat()) { + case SurfaceFormat::B8G8R8A8: + output = input; + break; + case SurfaceFormat::B8G8R8X8: + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x++) { + int32_t inputIndex = y * inputStride + 4 * x; + int32_t outputIndex = y * outputStride + 4 * x; + outputData[outputIndex + 0] = inputData[inputIndex + 0]; + outputData[outputIndex + 1] = inputData[inputIndex + 1]; + outputData[outputIndex + 2] = inputData[inputIndex + 2]; + outputData[outputIndex + 3] = 255; + } + } + break; + case SurfaceFormat::R8G8B8A8: + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x++) { + int32_t inputIndex = y * inputStride + 4 * x; + int32_t outputIndex = y * outputStride + 4 * x; + outputData[outputIndex + 2] = inputData[inputIndex + 0]; + outputData[outputIndex + 1] = inputData[inputIndex + 1]; + outputData[outputIndex + 0] = inputData[inputIndex + 2]; + outputData[outputIndex + 3] = inputData[inputIndex + 3]; + } + } + break; + case SurfaceFormat::R8G8B8X8: + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x++) { + int32_t inputIndex = y * inputStride + 4 * x; + int32_t outputIndex = y * outputStride + 4 * x; + outputData[outputIndex + 2] = inputData[inputIndex + 0]; + outputData[outputIndex + 1] = inputData[inputIndex + 1]; + outputData[outputIndex + 0] = inputData[inputIndex + 2]; + outputData[outputIndex + 3] = 255; + } + } + break; + case SurfaceFormat::A8: + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x += 16) { + int32_t inputIndex = y * inputStride + x; + int32_t outputIndex = y * outputStride + 4 * x; + u8x16_t p1To16 = simd::Load8<u8x16_t>(&inputData[inputIndex]); + // Turn AAAAAAAAAAAAAAAA into four chunks of 000A000A000A000A by + // interleaving with 0000000000000000 twice. + u8x16_t zero = simd::FromZero8<u8x16_t>(); + u8x16_t p1To8 = simd::InterleaveLo8(zero, p1To16); + u8x16_t p9To16 = simd::InterleaveHi8(zero, p1To16); + u8x16_t p1To4 = simd::InterleaveLo8(zero, p1To8); + u8x16_t p5To8 = simd::InterleaveHi8(zero, p1To8); + u8x16_t p9To12 = simd::InterleaveLo8(zero, p9To16); + u8x16_t p13To16 = simd::InterleaveHi8(zero, p9To16); + simd::Store8(&outputData[outputIndex], p1To4); + if ((x + 4) * 4 < outputStride) { + simd::Store8(&outputData[outputIndex + 4 * 4], p5To8); + } + if ((x + 8) * 4 < outputStride) { + simd::Store8(&outputData[outputIndex + 4 * 8], p9To12); + } + if ((x + 12) * 4 < outputStride) { + simd::Store8(&outputData[outputIndex + 4 * 12], p13To16); + } + } + } + break; + default: + output = nullptr; + break; + } + return output; +} + +template<typename u8x16_t> +inline void +ExtractAlpha_SIMD(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride) +{ + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x += 16) { + // Process 16 pixels at a time. + // Turn up to four chunks of BGRABGRABGRABGRA into one chunk of AAAAAAAAAAAAAAAA. + int32_t sourceIndex = y * sourceStride + 4 * x; + int32_t targetIndex = y * alphaStride + x; + + u8x16_t bgrabgrabgrabgra1 = simd::FromZero8<u8x16_t>(); + u8x16_t bgrabgrabgrabgra2 = simd::FromZero8<u8x16_t>(); + u8x16_t bgrabgrabgrabgra3 = simd::FromZero8<u8x16_t>(); + u8x16_t bgrabgrabgrabgra4 = simd::FromZero8<u8x16_t>(); + + bgrabgrabgrabgra1 = simd::Load8<u8x16_t>(&sourceData[sourceIndex]); + if (4 * (x + 4) < sourceStride) { + bgrabgrabgrabgra2 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 4]); + } + if (4 * (x + 8) < sourceStride) { + bgrabgrabgrabgra3 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 8]); + } + if (4 * (x + 12) < sourceStride) { + bgrabgrabgrabgra4 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 12]); + } + + u8x16_t bbggrraabbggrraa1 = simd::InterleaveLo8(bgrabgrabgrabgra1, bgrabgrabgrabgra3); + u8x16_t bbggrraabbggrraa2 = simd::InterleaveHi8(bgrabgrabgrabgra1, bgrabgrabgrabgra3); + u8x16_t bbggrraabbggrraa3 = simd::InterleaveLo8(bgrabgrabgrabgra2, bgrabgrabgrabgra4); + u8x16_t bbggrraabbggrraa4 = simd::InterleaveHi8(bgrabgrabgrabgra2, bgrabgrabgrabgra4); + u8x16_t bbbbggggrrrraaaa1 = simd::InterleaveLo8(bbggrraabbggrraa1, bbggrraabbggrraa3); + u8x16_t bbbbggggrrrraaaa2 = simd::InterleaveHi8(bbggrraabbggrraa1, bbggrraabbggrraa3); + u8x16_t bbbbggggrrrraaaa3 = simd::InterleaveLo8(bbggrraabbggrraa2, bbggrraabbggrraa4); + u8x16_t bbbbggggrrrraaaa4 = simd::InterleaveHi8(bbggrraabbggrraa2, bbggrraabbggrraa4); + u8x16_t rrrrrrrraaaaaaaa1 = simd::InterleaveHi8(bbbbggggrrrraaaa1, bbbbggggrrrraaaa3); + u8x16_t rrrrrrrraaaaaaaa2 = simd::InterleaveHi8(bbbbggggrrrraaaa2, bbbbggggrrrraaaa4); + u8x16_t aaaaaaaaaaaaaaaa = simd::InterleaveHi8(rrrrrrrraaaaaaaa1, rrrrrrrraaaaaaaa2); + + simd::Store8(&alphaData[targetIndex], aaaaaaaaaaaaaaaa); + } + } +} + +// This function calculates the result color values for four pixels, but for +// only two color channels - either b & r or g & a. However, the a result will +// not be used. +// source and dest each contain 8 values, either bbbb gggg or rrrr aaaa. +// sourceAlpha and destAlpha are of the form aaaa aaaa, where each aaaa is the +// alpha of all four pixels (and both aaaa's are the same). +// blendendComponent1 and blendedComponent2 are the out parameters. +template<typename i16x8_t, typename i32x4_t, uint32_t aBlendMode> +inline void +BlendTwoComponentsOfFourPixels(i16x8_t source, i16x8_t sourceAlpha, + i16x8_t dest, const i16x8_t& destAlpha, + i32x4_t& blendedComponent1, i32x4_t& blendedComponent2) +{ + i16x8_t x255 = simd::FromI16<i16x8_t>(255); + + switch (aBlendMode) { + + case BLEND_MODE_MULTIPLY: + { + // val = ((255 - destAlpha) * source + (255 - sourceAlpha + source) * dest); + i16x8_t twoFiftyFiveMinusDestAlpha = simd::Sub16(x255, destAlpha); + i16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha); + i16x8_t twoFiftyFiveMinusSourceAlphaPlusSource = simd::Add16(twoFiftyFiveMinusSourceAlpha, source); + + i16x8_t sourceInterleavedWithDest1 = simd::InterleaveLo16(source, dest); + i16x8_t leftFactor1 = simd::InterleaveLo16(twoFiftyFiveMinusDestAlpha, twoFiftyFiveMinusSourceAlphaPlusSource); + blendedComponent1 = simd::MulAdd16x8x2To32x4(sourceInterleavedWithDest1, leftFactor1); + blendedComponent1 = simd::FastDivideBy255(blendedComponent1); + + i16x8_t sourceInterleavedWithDest2 = simd::InterleaveHi16(source, dest); + i16x8_t leftFactor2 = simd::InterleaveHi16(twoFiftyFiveMinusDestAlpha, twoFiftyFiveMinusSourceAlphaPlusSource); + blendedComponent2 = simd::MulAdd16x8x2To32x4(sourceInterleavedWithDest2, leftFactor2); + blendedComponent2 = simd::FastDivideBy255(blendedComponent2); + + break; + } + + case BLEND_MODE_SCREEN: + { + // val = 255 * (source + dest) + (0 - dest) * source; + i16x8_t sourcePlusDest = simd::Add16(source, dest); + i16x8_t zeroMinusDest = simd::Sub16(simd::FromI16<i16x8_t>(0), dest); + + i16x8_t twoFiftyFiveInterleavedWithZeroMinusDest1 = simd::InterleaveLo16(x255, zeroMinusDest); + i16x8_t sourcePlusDestInterleavedWithSource1 = simd::InterleaveLo16(sourcePlusDest, source); + blendedComponent1 = simd::MulAdd16x8x2To32x4(twoFiftyFiveInterleavedWithZeroMinusDest1, sourcePlusDestInterleavedWithSource1); + blendedComponent1 = simd::FastDivideBy255(blendedComponent1); + + i16x8_t twoFiftyFiveInterleavedWithZeroMinusDest2 = simd::InterleaveHi16(x255, zeroMinusDest); + i16x8_t sourcePlusDestInterleavedWithSource2 = simd::InterleaveHi16(sourcePlusDest, source); + blendedComponent2 = simd::MulAdd16x8x2To32x4(twoFiftyFiveInterleavedWithZeroMinusDest2, sourcePlusDestInterleavedWithSource2); + blendedComponent2 = simd::FastDivideBy255(blendedComponent2); + + break; + } + + case BLEND_MODE_DARKEN: + case BLEND_MODE_LIGHTEN: + { + // Darken: + // val = min((255 - destAlpha) * source + 255 * dest, + // 255 * source + (255 - sourceAlpha) * dest); + // + // Lighten: + // val = max((255 - destAlpha) * source + 255 * dest, + // 255 * source + (255 - sourceAlpha) * dest); + + i16x8_t twoFiftyFiveMinusDestAlpha = simd::Sub16(x255, destAlpha); + i16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha); + + i16x8_t twoFiftyFiveMinusDestAlphaInterleavedWithTwoFiftyFive1 = simd::InterleaveLo16(twoFiftyFiveMinusDestAlpha, x255); + i16x8_t twoFiftyFiveInterleavedWithTwoFiftyFiveMinusSourceAlpha1 = simd::InterleaveLo16(x255, twoFiftyFiveMinusSourceAlpha); + i16x8_t sourceInterleavedWithDest1 = simd::InterleaveLo16(source, dest); + i32x4_t product1_1 = simd::MulAdd16x8x2To32x4(twoFiftyFiveMinusDestAlphaInterleavedWithTwoFiftyFive1, sourceInterleavedWithDest1); + i32x4_t product1_2 = simd::MulAdd16x8x2To32x4(twoFiftyFiveInterleavedWithTwoFiftyFiveMinusSourceAlpha1, sourceInterleavedWithDest1); + blendedComponent1 = aBlendMode == BLEND_MODE_DARKEN ? simd::Min32(product1_1, product1_2) : simd::Max32(product1_1, product1_2); + blendedComponent1 = simd::FastDivideBy255(blendedComponent1); + + i16x8_t twoFiftyFiveMinusDestAlphaInterleavedWithTwoFiftyFive2 = simd::InterleaveHi16(twoFiftyFiveMinusDestAlpha, x255); + i16x8_t twoFiftyFiveInterleavedWithTwoFiftyFiveMinusSourceAlpha2 = simd::InterleaveHi16(x255, twoFiftyFiveMinusSourceAlpha); + i16x8_t sourceInterleavedWithDest2 = simd::InterleaveHi16(source, dest); + i32x4_t product2_1 = simd::MulAdd16x8x2To32x4(twoFiftyFiveMinusDestAlphaInterleavedWithTwoFiftyFive2, sourceInterleavedWithDest2); + i32x4_t product2_2 = simd::MulAdd16x8x2To32x4(twoFiftyFiveInterleavedWithTwoFiftyFiveMinusSourceAlpha2, sourceInterleavedWithDest2); + blendedComponent2 = aBlendMode == BLEND_MODE_DARKEN ? simd::Min32(product2_1, product2_2) : simd::Max32(product2_1, product2_2); + blendedComponent2 = simd::FastDivideBy255(blendedComponent2); + + break; + } + + } +} + +// The alpha channel is subject to a different calculation than the RGB +// channels, and this calculation is the same for all blend modes: +// resultAlpha * 255 = 255 * 255 - (255 - sourceAlpha) * (255 - destAlpha) +template<typename i16x8_t, typename i32x4_t> +inline i32x4_t +BlendAlphaOfFourPixels(i16x8_t s_rrrraaaa1234, i16x8_t d_rrrraaaa1234) +{ + // We're using MulAdd16x8x2To32x4, so we need to interleave our factors + // appropriately. The calculation is rewritten as follows: + // resultAlpha[0] * 255 = 255 * 255 - (255 - sourceAlpha[0]) * (255 - destAlpha[0]) + // = 255 * 255 + (255 - sourceAlpha[0]) * (destAlpha[0] - 255) + // = (255 - 0) * (510 - 255) + (255 - sourceAlpha[0]) * (destAlpha[0] - 255) + // = MulAdd(255 - IntLv(0, sourceAlpha), IntLv(510, destAlpha) - 255)[0] + i16x8_t zeroInterleavedWithSourceAlpha = simd::InterleaveHi16(simd::FromI16<i16x8_t>(0), s_rrrraaaa1234); + i16x8_t fiveTenInterleavedWithDestAlpha = simd::InterleaveHi16(simd::FromI16<i16x8_t>(510), d_rrrraaaa1234); + i16x8_t f1 = simd::Sub16(simd::FromI16<i16x8_t>(255), zeroInterleavedWithSourceAlpha); + i16x8_t f2 = simd::Sub16(fiveTenInterleavedWithDestAlpha, simd::FromI16<i16x8_t>(255)); + return simd::FastDivideBy255(simd::MulAdd16x8x2To32x4(f1, f2)); +} + +template<typename u8x16_t, typename i16x8_t> +inline void +UnpackAndShuffleComponents(u8x16_t bgrabgrabgrabgra1234, + i16x8_t& bbbbgggg1234, i16x8_t& rrrraaaa1234) +{ + // bgrabgrabgrabgra1234 -> bbbbgggg1234, rrrraaaa1234 + i16x8_t bgrabgra12 = simd::UnpackLo8x8ToI16x8(bgrabgrabgrabgra1234); + i16x8_t bgrabgra34 = simd::UnpackHi8x8ToI16x8(bgrabgrabgrabgra1234); + i16x8_t bbggrraa13 = simd::InterleaveLo16(bgrabgra12, bgrabgra34); + i16x8_t bbggrraa24 = simd::InterleaveHi16(bgrabgra12, bgrabgra34); + bbbbgggg1234 = simd::InterleaveLo16(bbggrraa13, bbggrraa24); + rrrraaaa1234 = simd::InterleaveHi16(bbggrraa13, bbggrraa24); +} + +template<typename i32x4_t, typename i16x8_t, typename u8x16_t> +inline u8x16_t +ShuffleAndPackComponents(i32x4_t bbbb1234, i32x4_t gggg1234, + i32x4_t rrrr1234, const i32x4_t& aaaa1234) +{ + // bbbb1234, gggg1234, rrrr1234, aaaa1234 -> bgrabgrabgrabgra1234 + i16x8_t bbbbgggg1234 = simd::PackAndSaturate32To16(bbbb1234, gggg1234); + i16x8_t rrrraaaa1234 = simd::PackAndSaturate32To16(rrrr1234, aaaa1234); + i16x8_t brbrbrbr1234 = simd::InterleaveLo16(bbbbgggg1234, rrrraaaa1234); + i16x8_t gagagaga1234 = simd::InterleaveHi16(bbbbgggg1234, rrrraaaa1234); + i16x8_t bgrabgra12 = simd::InterleaveLo16(brbrbrbr1234, gagagaga1234); + i16x8_t bgrabgra34 = simd::InterleaveHi16(brbrbrbr1234, gagagaga1234); + return simd::PackAndSaturate16To8(bgrabgra12, bgrabgra34); +} + +template<typename i32x4_t, typename i16x8_t, typename u8x16_t, BlendMode mode> +inline TemporaryRef<DataSourceSurface> +ApplyBlending_SIMD(DataSourceSurface* aInput1, DataSourceSurface* aInput2) +{ + IntSize size = aInput1->GetSize(); + RefPtr<DataSourceSurface> target = + Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); + if (!target) { + return nullptr; + } + + uint8_t* source1Data = aInput1->GetData(); + uint8_t* source2Data = aInput2->GetData(); + uint8_t* targetData = target->GetData(); + int32_t targetStride = target->Stride(); + int32_t source1Stride = aInput1->Stride(); + int32_t source2Stride = aInput2->Stride(); + + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x += 4) { + int32_t targetIndex = y * targetStride + 4 * x; + int32_t source1Index = y * source1Stride + 4 * x; + int32_t source2Index = y * source2Stride + 4 * x; + + u8x16_t s1234 = simd::Load8<u8x16_t>(&source2Data[source2Index]); + u8x16_t d1234 = simd::Load8<u8x16_t>(&source1Data[source1Index]); + + // The blending calculation for the RGB channels all need access to the + // alpha channel of their pixel, and the alpha calculation is different, + // so it makes sense to separate by channel. + + i16x8_t s_bbbbgggg1234, s_rrrraaaa1234; + i16x8_t d_bbbbgggg1234, d_rrrraaaa1234; + UnpackAndShuffleComponents(s1234, s_bbbbgggg1234, s_rrrraaaa1234); + UnpackAndShuffleComponents(d1234, d_bbbbgggg1234, d_rrrraaaa1234); + i16x8_t s_aaaaaaaa1234 = simd::Shuffle32<3,2,3,2>(s_rrrraaaa1234); + i16x8_t d_aaaaaaaa1234 = simd::Shuffle32<3,2,3,2>(d_rrrraaaa1234); + + // We only use blendedB, blendedG and blendedR. + i32x4_t blendedB, blendedG, blendedR, blendedA; + BlendTwoComponentsOfFourPixels<i16x8_t,i32x4_t,mode>(s_bbbbgggg1234, s_aaaaaaaa1234, d_bbbbgggg1234, d_aaaaaaaa1234, blendedB, blendedG); + BlendTwoComponentsOfFourPixels<i16x8_t,i32x4_t,mode>(s_rrrraaaa1234, s_aaaaaaaa1234, d_rrrraaaa1234, d_aaaaaaaa1234, blendedR, blendedA); + + // Throw away blendedA and overwrite it with the correct blended alpha. + blendedA = BlendAlphaOfFourPixels<i16x8_t,i32x4_t>(s_rrrraaaa1234, d_rrrraaaa1234); + + u8x16_t result1234 = ShuffleAndPackComponents<i32x4_t,i16x8_t,u8x16_t>(blendedB, blendedG, blendedR, blendedA); + simd::Store8(&targetData[targetIndex], result1234); + } + } + + return target; +} + +template<typename i32x4_t, typename i16x8_t, typename u8x16_t> +static TemporaryRef<DataSourceSurface> +ApplyBlending_SIMD(DataSourceSurface* aInput1, DataSourceSurface* aInput2, + BlendMode aBlendMode) +{ + switch (aBlendMode) { + case BLEND_MODE_MULTIPLY: + return ApplyBlending_SIMD<i32x4_t,i16x8_t,u8x16_t, BLEND_MODE_MULTIPLY>(aInput1, aInput2); + case BLEND_MODE_SCREEN: + return ApplyBlending_SIMD<i32x4_t,i16x8_t,u8x16_t, BLEND_MODE_SCREEN>(aInput1, aInput2); + case BLEND_MODE_DARKEN: + return ApplyBlending_SIMD<i32x4_t,i16x8_t,u8x16_t, BLEND_MODE_DARKEN>(aInput1, aInput2); + case BLEND_MODE_LIGHTEN: + return ApplyBlending_SIMD<i32x4_t,i16x8_t,u8x16_t, BLEND_MODE_LIGHTEN>(aInput1, aInput2); + default: + return nullptr; + } +} + +template<MorphologyOperator Operator, typename u8x16_t> +static u8x16_t +Morph8(u8x16_t a, u8x16_t b) +{ + return Operator == MORPHOLOGY_OPERATOR_ERODE ? + simd::Min8(a, b) : simd::Max8(a, b); +} + +// Set every pixel to the per-component minimum or maximum of the pixels around +// it that are up to aRadius pixels away from it (horizontally). +template<MorphologyOperator op, typename i16x8_t, typename u8x16_t> +inline void ApplyMorphologyHorizontal_SIMD(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius) +{ + static_assert(op == MORPHOLOGY_OPERATOR_ERODE || + op == MORPHOLOGY_OPERATOR_DILATE, + "unexpected morphology operator"); + + int32_t kernelSize = aRadius + 1 + aRadius; + MOZ_ASSERT(kernelSize >= 3, "don't call this with aRadius <= 0"); + MOZ_ASSERT(kernelSize % 4 == 1 || kernelSize % 4 == 3); + int32_t completeKernelSizeForFourPixels = kernelSize + 3; + MOZ_ASSERT(completeKernelSizeForFourPixels % 4 == 0 || + completeKernelSizeForFourPixels % 4 == 2); + + // aSourceData[-aRadius] and aDestData[0] are both aligned to 16 bytes, just + // the way we need them to be. + + IntRect sourceRect = aDestRect; + sourceRect.Inflate(aRadius, 0); + + for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++) { + int32_t kernelStartX = aDestRect.x - aRadius; + for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x += 4, kernelStartX += 4) { + // We process four pixels (16 color values) at a time. + // aSourceData[0] points to the pixel located at aDestRect.TopLeft(); + // source values can be read beyond that because the source is extended + // by aRadius pixels. + + int32_t sourceIndex = y * aSourceStride + 4 * kernelStartX; + u8x16_t p1234 = simd::Load8<u8x16_t>(&aSourceData[sourceIndex]); + u8x16_t m1234 = p1234; + + for (int32_t i = 4; i < completeKernelSizeForFourPixels; i += 4) { + u8x16_t p5678 = (kernelStartX + i < sourceRect.XMost()) ? + simd::Load8<u8x16_t>(&aSourceData[sourceIndex + 4 * i]) : + simd::FromZero8<u8x16_t>(); + u8x16_t p2345 = simd::Rotate8<4>(p1234, p5678); + u8x16_t p3456 = simd::Rotate8<8>(p1234, p5678); + m1234 = Morph8<op,u8x16_t>(m1234, p2345); + m1234 = Morph8<op,u8x16_t>(m1234, p3456); + if (i + 2 < completeKernelSizeForFourPixels) { + u8x16_t p4567 = simd::Rotate8<12>(p1234, p5678); + m1234 = Morph8<op,u8x16_t>(m1234, p4567); + m1234 = Morph8<op,u8x16_t>(m1234, p5678); + } + p1234 = p5678; + } + + int32_t destIndex = y * aDestStride + 4 * x; + simd::Store8(&aDestData[destIndex], m1234); + } + } +} + +template<typename i16x8_t, typename u8x16_t> +inline void ApplyMorphologyHorizontal_SIMD(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOp) +{ + if (aOp == MORPHOLOGY_OPERATOR_ERODE) { + ApplyMorphologyHorizontal_SIMD<MORPHOLOGY_OPERATOR_ERODE,i16x8_t,u8x16_t>( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius); + } else { + ApplyMorphologyHorizontal_SIMD<MORPHOLOGY_OPERATOR_DILATE,i16x8_t,u8x16_t>( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius); + } +} + +// Set every pixel to the per-component minimum or maximum of the pixels around +// it that are up to aRadius pixels away from it (vertically). +template<MorphologyOperator op, typename i16x8_t, typename u8x16_t> +static void ApplyMorphologyVertical_SIMD(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius) +{ + static_assert(op == MORPHOLOGY_OPERATOR_ERODE || + op == MORPHOLOGY_OPERATOR_DILATE, + "unexpected morphology operator"); + + int32_t startY = aDestRect.y - aRadius; + int32_t endY = aDestRect.y + aRadius; + for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++, startY++, endY++) { + for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x += 4) { + int32_t sourceIndex = startY * aSourceStride + 4 * x; + u8x16_t u = simd::Load8<u8x16_t>(&aSourceData[sourceIndex]); + sourceIndex += aSourceStride; + for (int32_t iy = startY + 1; iy <= endY; iy++, sourceIndex += aSourceStride) { + u8x16_t u2 = simd::Load8<u8x16_t>(&aSourceData[sourceIndex]); + u = Morph8<op,u8x16_t>(u, u2); + } + + int32_t destIndex = y * aDestStride + 4 * x; + simd::Store8(&aDestData[destIndex], u); + } + } +} + +template<typename i16x8_t, typename u8x16_t> +inline void ApplyMorphologyVertical_SIMD(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOp) +{ + if (aOp == MORPHOLOGY_OPERATOR_ERODE) { + ApplyMorphologyVertical_SIMD<MORPHOLOGY_OPERATOR_ERODE,i16x8_t,u8x16_t>( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius); + } else { + ApplyMorphologyVertical_SIMD<MORPHOLOGY_OPERATOR_DILATE,i16x8_t,u8x16_t>( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius); + } +} + +template<typename i32x4_t, typename i16x8_t> +static i32x4_t +ColorMatrixMultiply(i16x8_t p, i16x8_t rows_bg, i16x8_t rows_ra, const i32x4_t& bias) +{ + // int16_t p[8] == { b, g, r, a, b, g, r, a }. + // int16_t rows_bg[8] == { bB, bG, bR, bA, gB, gG, gR, gA }. + // int16_t rows_ra[8] == { rB, rG, rR, rA, aB, aG, aR, aA }. + // int32_t bias[4] == { _B, _G, _R, _A }. + + i32x4_t sum = bias; + + // int16_t bg[8] = { b, g, b, g, b, g, b, g }; + i16x8_t bg = simd::ShuffleHi16<1,0,1,0>(simd::ShuffleLo16<1,0,1,0>(p)); + // int32_t prodsum_bg[4] = { b * bB + g * gB, b * bG + g * gG, b * bR + g * gR, b * bA + g * gA } + i32x4_t prodsum_bg = simd::MulAdd16x8x2To32x4(bg, rows_bg); + sum = simd::Add32(sum, prodsum_bg); + + // uint16_t ra[8] = { r, a, r, a, r, a, r, a }; + i16x8_t ra = simd::ShuffleHi16<3,2,3,2>(simd::ShuffleLo16<3,2,3,2>(p)); + // int32_t prodsum_ra[4] = { r * rB + a * aB, r * rG + a * aG, r * rR + a * aR, r * rA + a * aA } + i32x4_t prodsum_ra = simd::MulAdd16x8x2To32x4(ra, rows_ra); + sum = simd::Add32(sum, prodsum_ra); + + // int32_t sum[4] == { b * bB + g * gB + r * rB + a * aB + _B, ... }. + return sum; +} + +template<typename i32x4_t, typename i16x8_t, typename u8x16_t> +static TemporaryRef<DataSourceSurface> +ApplyColorMatrix_SIMD(DataSourceSurface* aInput, const Matrix5x4 &aMatrix) +{ + IntSize size = aInput->GetSize(); + RefPtr<DataSourceSurface> target = + Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); + if (!target) { + return nullptr; + } + + uint8_t* sourceData = aInput->GetData(); + uint8_t* targetData = target->GetData(); + int32_t sourceStride = aInput->Stride(); + int32_t targetStride = target->Stride(); + + const int16_t factor = 128; + const Float floatElementMax = INT16_MAX / factor; // 255 + MOZ_ASSERT((floatElementMax * factor) <= INT16_MAX, "badly chosen float-to-int scale"); + + const Float *floats = &aMatrix._11; + + ptrdiff_t componentOffsets[4] = { + B8G8R8A8_COMPONENT_BYTEOFFSET_R, + B8G8R8A8_COMPONENT_BYTEOFFSET_G, + B8G8R8A8_COMPONENT_BYTEOFFSET_B, + B8G8R8A8_COMPONENT_BYTEOFFSET_A + }; + + // We store the color matrix in rows_bgra in the following format: + // { bB, bG, bR, bA, gB, gG, gR, gA }. + // { bB, gB, bG, gG, bR, gR, bA, gA } + // The way this is interleaved allows us to use the intrinsic _mm_madd_epi16 + // which works especially well for our use case. + int16_t rows_bgra[2][8]; + for (size_t rowIndex = 0; rowIndex < 4; rowIndex++) { + for (size_t colIndex = 0; colIndex < 4; colIndex++) { + const Float& floatMatrixElement = floats[rowIndex * 4 + colIndex]; + Float clampedFloatMatrixElement = std::min(std::max(floatMatrixElement, -floatElementMax), floatElementMax); + int16_t scaledIntMatrixElement = int16_t(clampedFloatMatrixElement * factor + 0.5); + int8_t bg_or_ra = componentOffsets[rowIndex] / 2; + int8_t g_or_a = componentOffsets[rowIndex] % 2; + int8_t B_or_G_or_R_or_A = componentOffsets[colIndex]; + rows_bgra[bg_or_ra][B_or_G_or_R_or_A * 2 + g_or_a] = scaledIntMatrixElement; + } + } + + int32_t rowBias[4]; + Float biasMax = (INT32_MAX - 4 * 255 * INT16_MAX) / (factor * 255); + for (size_t colIndex = 0; colIndex < 4; colIndex++) { + size_t rowIndex = 4; + const Float& floatMatrixElement = floats[rowIndex * 4 + colIndex]; + Float clampedFloatMatrixElement = std::min(std::max(floatMatrixElement, -biasMax), biasMax); + int32_t scaledIntMatrixElement = int32_t(clampedFloatMatrixElement * factor * 255 + 0.5); + rowBias[componentOffsets[colIndex]] = scaledIntMatrixElement; + } + + i16x8_t row_bg_v = simd::FromI16<i16x8_t>( + rows_bgra[0][0], rows_bgra[0][1], rows_bgra[0][2], rows_bgra[0][3], + rows_bgra[0][4], rows_bgra[0][5], rows_bgra[0][6], rows_bgra[0][7]); + + i16x8_t row_ra_v = simd::FromI16<i16x8_t>( + rows_bgra[1][0], rows_bgra[1][1], rows_bgra[1][2], rows_bgra[1][3], + rows_bgra[1][4], rows_bgra[1][5], rows_bgra[1][6], rows_bgra[1][7]); + + i32x4_t rowsBias_v = + simd::From32<i32x4_t>(rowBias[0], rowBias[1], rowBias[2], rowBias[3]); + + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x += 4) { + MOZ_ASSERT(sourceStride >= 4 * (x + 4), "need to be able to read 4 pixels at this position"); + MOZ_ASSERT(targetStride >= 4 * (x + 4), "need to be able to write 4 pixels at this position"); + int32_t sourceIndex = y * sourceStride + 4 * x; + int32_t targetIndex = y * targetStride + 4 * x; + + // We load 4 pixels, unpack them, process them 1 pixel at a time, and + // finally pack and store the 4 result pixels. + + u8x16_t p1234 = simd::Load8<u8x16_t>(&sourceData[sourceIndex]); + + // Splat needed to get each pixel twice into i16x8 + i16x8_t p11 = simd::UnpackLo8x8ToI16x8(simd::Splat32On8<0>(p1234)); + i16x8_t p22 = simd::UnpackLo8x8ToI16x8(simd::Splat32On8<1>(p1234)); + i16x8_t p33 = simd::UnpackLo8x8ToI16x8(simd::Splat32On8<2>(p1234)); + i16x8_t p44 = simd::UnpackLo8x8ToI16x8(simd::Splat32On8<3>(p1234)); + + i32x4_t result_p1 = ColorMatrixMultiply(p11, row_bg_v, row_ra_v, rowsBias_v); + i32x4_t result_p2 = ColorMatrixMultiply(p22, row_bg_v, row_ra_v, rowsBias_v); + i32x4_t result_p3 = ColorMatrixMultiply(p33, row_bg_v, row_ra_v, rowsBias_v); + i32x4_t result_p4 = ColorMatrixMultiply(p44, row_bg_v, row_ra_v, rowsBias_v); + + static_assert(factor == 1 << 7, "Please adapt the calculation in the lines below for a different factor."); + u8x16_t result_p1234 = simd::PackAndSaturate32To8(simd::ShiftRight32<7>(result_p1), + simd::ShiftRight32<7>(result_p2), + simd::ShiftRight32<7>(result_p3), + simd::ShiftRight32<7>(result_p4)); + simd::Store8(&targetData[targetIndex], result_p1234); + } + } + + return target; +} + +// source / dest: bgra bgra +// sourceAlpha / destAlpha: aaaa aaaa +// result: bgra bgra +template<typename i32x4_t, typename u16x8_t, uint32_t aCompositeOperator> +static inline u16x8_t +CompositeTwoPixels(u16x8_t source, u16x8_t sourceAlpha, u16x8_t dest, const u16x8_t& destAlpha) +{ + u16x8_t x255 = simd::FromU16<u16x8_t>(255); + + switch (aCompositeOperator) { + + case COMPOSITE_OPERATOR_OVER: + { + // val = dest * (255 - sourceAlpha) + source * 255; + u16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha); + + u16x8_t destSourceInterleaved1 = simd::InterleaveLo16(dest, source); + u16x8_t rightFactor1 = simd::InterleaveLo16(twoFiftyFiveMinusSourceAlpha, x255); + i32x4_t result1 = simd::MulAdd16x8x2To32x4(destSourceInterleaved1, rightFactor1); + + u16x8_t destSourceInterleaved2 = simd::InterleaveHi16(dest, source); + u16x8_t rightFactor2 = simd::InterleaveHi16(twoFiftyFiveMinusSourceAlpha, x255); + i32x4_t result2 = simd::MulAdd16x8x2To32x4(destSourceInterleaved2, rightFactor2); + + return simd::PackAndSaturate32ToU16(simd::FastDivideBy255(result1), + simd::FastDivideBy255(result2)); + } + + case COMPOSITE_OPERATOR_IN: + { + // val = source * destAlpha; + return simd::FastDivideBy255_16(simd::Mul16(source, destAlpha)); + } + + case COMPOSITE_OPERATOR_OUT: + { + // val = source * (255 - destAlpha); + u16x8_t prod = simd::Mul16(source, simd::Sub16(x255, destAlpha)); + return simd::FastDivideBy255_16(prod); + } + + case COMPOSITE_OPERATOR_ATOP: + { + // val = dest * (255 - sourceAlpha) + source * destAlpha; + u16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha); + + u16x8_t destSourceInterleaved1 = simd::InterleaveLo16(dest, source); + u16x8_t rightFactor1 = simd::InterleaveLo16(twoFiftyFiveMinusSourceAlpha, destAlpha); + i32x4_t result1 = simd::MulAdd16x8x2To32x4(destSourceInterleaved1, rightFactor1); + + u16x8_t destSourceInterleaved2 = simd::InterleaveHi16(dest, source); + u16x8_t rightFactor2 = simd::InterleaveHi16(twoFiftyFiveMinusSourceAlpha, destAlpha); + i32x4_t result2 = simd::MulAdd16x8x2To32x4(destSourceInterleaved2, rightFactor2); + + return simd::PackAndSaturate32ToU16(simd::FastDivideBy255(result1), + simd::FastDivideBy255(result2)); + } + + case COMPOSITE_OPERATOR_XOR: + { + // val = dest * (255 - sourceAlpha) + source * (255 - destAlpha); + u16x8_t twoFiftyFiveMinusSourceAlpha = simd::Sub16(x255, sourceAlpha); + u16x8_t twoFiftyFiveMinusDestAlpha = simd::Sub16(x255, destAlpha); + + u16x8_t destSourceInterleaved1 = simd::InterleaveLo16(dest, source); + u16x8_t rightFactor1 = simd::InterleaveLo16(twoFiftyFiveMinusSourceAlpha, + twoFiftyFiveMinusDestAlpha); + i32x4_t result1 = simd::MulAdd16x8x2To32x4(destSourceInterleaved1, rightFactor1); + + u16x8_t destSourceInterleaved2 = simd::InterleaveHi16(dest, source); + u16x8_t rightFactor2 = simd::InterleaveHi16(twoFiftyFiveMinusSourceAlpha, + twoFiftyFiveMinusDestAlpha); + i32x4_t result2 = simd::MulAdd16x8x2To32x4(destSourceInterleaved2, rightFactor2); + + return simd::PackAndSaturate32ToU16(simd::FastDivideBy255(result1), + simd::FastDivideBy255(result2)); + } + + default: + return simd::FromU16<u16x8_t>(0); + + } +} + +template<typename i32x4_t, typename u16x8_t, typename u8x16_t, uint32_t op> +static void +ApplyComposition(DataSourceSurface* aSource, DataSourceSurface* aDest) +{ + IntSize size = aDest->GetSize(); + + uint8_t* sourceData = aSource->GetData(); + uint8_t* destData = aDest->GetData(); + uint32_t sourceStride = aSource->Stride(); + uint32_t destStride = aDest->Stride(); + + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x += 4) { + uint32_t sourceIndex = y * sourceStride + 4 * x; + uint32_t destIndex = y * destStride + 4 * x; + + u8x16_t s1234 = simd::Load8<u8x16_t>(&sourceData[sourceIndex]); + u8x16_t d1234 = simd::Load8<u8x16_t>(&destData[destIndex]); + + u16x8_t s12 = simd::UnpackLo8x8ToU16x8(s1234); + u16x8_t d12 = simd::UnpackLo8x8ToU16x8(d1234); + u16x8_t sa12 = simd::Splat16<3,3>(s12); + u16x8_t da12 = simd::Splat16<3,3>(d12); + u16x8_t result12 = CompositeTwoPixels<i32x4_t,u16x8_t,op>(s12, sa12, d12, da12); + + u16x8_t s34 = simd::UnpackHi8x8ToU16x8(s1234); + u16x8_t d34 = simd::UnpackHi8x8ToU16x8(d1234); + u16x8_t sa34 = simd::Splat16<3,3>(s34); + u16x8_t da34 = simd::Splat16<3,3>(d34); + u16x8_t result34 = CompositeTwoPixels<i32x4_t,u16x8_t,op>(s34, sa34, d34, da34); + + u8x16_t result1234 = simd::PackAndSaturate16To8(result12, result34); + simd::Store8(&destData[destIndex], result1234); + } + } +} + +template<typename i32x4_t, typename i16x8_t, typename u8x16_t> +static void +ApplyComposition_SIMD(DataSourceSurface* aSource, DataSourceSurface* aDest, + CompositeOperator aOperator) +{ + switch (aOperator) { + case COMPOSITE_OPERATOR_OVER: + ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_OVER>(aSource, aDest); + break; + case COMPOSITE_OPERATOR_IN: + ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_IN>(aSource, aDest); + break; + case COMPOSITE_OPERATOR_OUT: + ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_OUT>(aSource, aDest); + break; + case COMPOSITE_OPERATOR_ATOP: + ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_ATOP>(aSource, aDest); + break; + case COMPOSITE_OPERATOR_XOR: + ApplyComposition<i32x4_t,i16x8_t,u8x16_t, COMPOSITE_OPERATOR_XOR>(aSource, aDest); + break; + default: + MOZ_CRASH(); + } +} + +template<typename u8x16_t> +static void +SeparateColorChannels_SIMD(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, + uint8_t* channel0Data, uint8_t* channel1Data, + uint8_t* channel2Data, uint8_t* channel3Data, + int32_t channelStride) +{ + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x += 16) { + // Process 16 pixels at a time. + int32_t sourceIndex = y * sourceStride + 4 * x; + int32_t targetIndex = y * channelStride + x; + + u8x16_t bgrabgrabgrabgra1 = simd::FromZero8<u8x16_t>(); + u8x16_t bgrabgrabgrabgra2 = simd::FromZero8<u8x16_t>(); + u8x16_t bgrabgrabgrabgra3 = simd::FromZero8<u8x16_t>(); + u8x16_t bgrabgrabgrabgra4 = simd::FromZero8<u8x16_t>(); + + bgrabgrabgrabgra1 = simd::Load8<u8x16_t>(&sourceData[sourceIndex]); + if (4 * (x + 4) < sourceStride) { + bgrabgrabgrabgra2 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 4]); + } + if (4 * (x + 8) < sourceStride) { + bgrabgrabgrabgra3 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 8]); + } + if (4 * (x + 12) < sourceStride) { + bgrabgrabgrabgra4 = simd::Load8<u8x16_t>(&sourceData[sourceIndex + 4 * 12]); + } + + u8x16_t bbggrraabbggrraa1 = simd::InterleaveLo8(bgrabgrabgrabgra1, bgrabgrabgrabgra3); + u8x16_t bbggrraabbggrraa2 = simd::InterleaveHi8(bgrabgrabgrabgra1, bgrabgrabgrabgra3); + u8x16_t bbggrraabbggrraa3 = simd::InterleaveLo8(bgrabgrabgrabgra2, bgrabgrabgrabgra4); + u8x16_t bbggrraabbggrraa4 = simd::InterleaveHi8(bgrabgrabgrabgra2, bgrabgrabgrabgra4); + u8x16_t bbbbggggrrrraaaa1 = simd::InterleaveLo8(bbggrraabbggrraa1, bbggrraabbggrraa3); + u8x16_t bbbbggggrrrraaaa2 = simd::InterleaveHi8(bbggrraabbggrraa1, bbggrraabbggrraa3); + u8x16_t bbbbggggrrrraaaa3 = simd::InterleaveLo8(bbggrraabbggrraa2, bbggrraabbggrraa4); + u8x16_t bbbbggggrrrraaaa4 = simd::InterleaveHi8(bbggrraabbggrraa2, bbggrraabbggrraa4); + u8x16_t bbbbbbbbgggggggg1 = simd::InterleaveLo8(bbbbggggrrrraaaa1, bbbbggggrrrraaaa3); + u8x16_t rrrrrrrraaaaaaaa1 = simd::InterleaveHi8(bbbbggggrrrraaaa1, bbbbggggrrrraaaa3); + u8x16_t bbbbbbbbgggggggg2 = simd::InterleaveLo8(bbbbggggrrrraaaa2, bbbbggggrrrraaaa4); + u8x16_t rrrrrrrraaaaaaaa2 = simd::InterleaveHi8(bbbbggggrrrraaaa2, bbbbggggrrrraaaa4); + u8x16_t bbbbbbbbbbbbbbbb = simd::InterleaveLo8(bbbbbbbbgggggggg1, bbbbbbbbgggggggg2); + u8x16_t gggggggggggggggg = simd::InterleaveHi8(bbbbbbbbgggggggg1, bbbbbbbbgggggggg2); + u8x16_t rrrrrrrrrrrrrrrr = simd::InterleaveLo8(rrrrrrrraaaaaaaa1, rrrrrrrraaaaaaaa2); + u8x16_t aaaaaaaaaaaaaaaa = simd::InterleaveHi8(rrrrrrrraaaaaaaa1, rrrrrrrraaaaaaaa2); + + simd::Store8(&channel0Data[targetIndex], bbbbbbbbbbbbbbbb); + simd::Store8(&channel1Data[targetIndex], gggggggggggggggg); + simd::Store8(&channel2Data[targetIndex], rrrrrrrrrrrrrrrr); + simd::Store8(&channel3Data[targetIndex], aaaaaaaaaaaaaaaa); + } + } +} + +template<typename u8x16_t> +static void +CombineColorChannels_SIMD(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data) +{ + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x += 16) { + // Process 16 pixels at a time. + int32_t resultIndex = y * resultStride + 4 * x; + int32_t channelIndex = y * channelStride + x; + + u8x16_t bbbbbbbbbbbbbbbb = simd::Load8<u8x16_t>(&channel0Data[channelIndex]); + u8x16_t gggggggggggggggg = simd::Load8<u8x16_t>(&channel1Data[channelIndex]); + u8x16_t rrrrrrrrrrrrrrrr = simd::Load8<u8x16_t>(&channel2Data[channelIndex]); + u8x16_t aaaaaaaaaaaaaaaa = simd::Load8<u8x16_t>(&channel3Data[channelIndex]); + + u8x16_t brbrbrbrbrbrbrbr1 = simd::InterleaveLo8(bbbbbbbbbbbbbbbb, rrrrrrrrrrrrrrrr); + u8x16_t brbrbrbrbrbrbrbr2 = simd::InterleaveHi8(bbbbbbbbbbbbbbbb, rrrrrrrrrrrrrrrr); + u8x16_t gagagagagagagaga1 = simd::InterleaveLo8(gggggggggggggggg, aaaaaaaaaaaaaaaa); + u8x16_t gagagagagagagaga2 = simd::InterleaveHi8(gggggggggggggggg, aaaaaaaaaaaaaaaa); + + u8x16_t bgrabgrabgrabgra1 = simd::InterleaveLo8(brbrbrbrbrbrbrbr1, gagagagagagagaga1); + u8x16_t bgrabgrabgrabgra2 = simd::InterleaveHi8(brbrbrbrbrbrbrbr1, gagagagagagagaga1); + u8x16_t bgrabgrabgrabgra3 = simd::InterleaveLo8(brbrbrbrbrbrbrbr2, gagagagagagagaga2); + u8x16_t bgrabgrabgrabgra4 = simd::InterleaveHi8(brbrbrbrbrbrbrbr2, gagagagagagagaga2); + + simd::Store8(&resultData[resultIndex], bgrabgrabgrabgra1); + if (4 * (x + 4) < resultStride) { + simd::Store8(&resultData[resultIndex + 4 * 4], bgrabgrabgrabgra2); + } + if (4 * (x + 8) < resultStride) { + simd::Store8(&resultData[resultIndex + 8 * 4], bgrabgrabgrabgra3); + } + if (4 * (x + 12) < resultStride) { + simd::Store8(&resultData[resultIndex + 12 * 4], bgrabgrabgrabgra4); + } + } + } +} + + +template<typename i32x4_t, typename u16x8_t, typename u8x16_t> +static void +DoPremultiplicationCalculation_SIMD(const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride) +{ + const u8x16_t alphaMask = simd::From8<u8x16_t>(0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff); + for (int32_t y = 0; y < aSize.height; y++) { + for (int32_t x = 0; x < aSize.width; x += 4) { + int32_t inputIndex = y * aSourceStride + 4 * x; + int32_t targetIndex = y * aTargetStride + 4 * x; + + u8x16_t p1234 = simd::Load8<u8x16_t>(&aSourceData[inputIndex]); + u16x8_t p12 = simd::UnpackLo8x8ToU16x8(p1234); + u16x8_t p34 = simd::UnpackHi8x8ToU16x8(p1234); + + // Multiply all components with alpha. + p12 = simd::Mul16(p12, simd::Splat16<3,3>(p12)); + p34 = simd::Mul16(p34, simd::Splat16<3,3>(p34)); + + // Divide by 255 and pack. + u8x16_t result = simd::PackAndSaturate16To8(simd::FastDivideBy255_16(p12), + simd::FastDivideBy255_16(p34)); + + // Get the original alpha channel value back from p1234. + result = simd::Pick(alphaMask, result, p1234); + + simd::Store8(&aTargetData[targetIndex], result); + } + } +} + +// We use a table of precomputed factors for unpremultiplying. +// We want to compute round(r / (alpha / 255.0f)) for arbitrary values of +// r and alpha in constant time. This table of factors has the property that +// (r * sAlphaFactors[alpha] + 128) >> 8 roughly gives the result we want (with +// a maximum deviation of 1). +// +// sAlphaFactors[alpha] == round(255.0 * (1 << 8) / alpha) +// +// This table has been created using the python code +// ", ".join("%d" % (round(255.0 * 256 / alpha) if alpha > 0 else 0) for alpha in range(256)) +static const uint16_t sAlphaFactors[256] = { + 0, 65280, 32640, 21760, 16320, 13056, 10880, 9326, 8160, 7253, 6528, 5935, + 5440, 5022, 4663, 4352, 4080, 3840, 3627, 3436, 3264, 3109, 2967, 2838, 2720, + 2611, 2511, 2418, 2331, 2251, 2176, 2106, 2040, 1978, 1920, 1865, 1813, 1764, + 1718, 1674, 1632, 1592, 1554, 1518, 1484, 1451, 1419, 1389, 1360, 1332, 1306, + 1280, 1255, 1232, 1209, 1187, 1166, 1145, 1126, 1106, 1088, 1070, 1053, 1036, + 1020, 1004, 989, 974, 960, 946, 933, 919, 907, 894, 882, 870, 859, 848, 837, + 826, 816, 806, 796, 787, 777, 768, 759, 750, 742, 733, 725, 717, 710, 702, + 694, 687, 680, 673, 666, 659, 653, 646, 640, 634, 628, 622, 616, 610, 604, + 599, 593, 588, 583, 578, 573, 568, 563, 558, 553, 549, 544, 540, 535, 531, + 526, 522, 518, 514, 510, 506, 502, 498, 495, 491, 487, 484, 480, 476, 473, + 470, 466, 463, 460, 457, 453, 450, 447, 444, 441, 438, 435, 432, 429, 427, + 424, 421, 418, 416, 413, 411, 408, 405, 403, 400, 398, 396, 393, 391, 389, + 386, 384, 382, 380, 377, 375, 373, 371, 369, 367, 365, 363, 361, 359, 357, + 355, 353, 351, 349, 347, 345, 344, 342, 340, 338, 336, 335, 333, 331, 330, + 328, 326, 325, 323, 322, 320, 318, 317, 315, 314, 312, 311, 309, 308, 306, + 305, 304, 302, 301, 299, 298, 297, 295, 294, 293, 291, 290, 289, 288, 286, + 285, 284, 283, 281, 280, 279, 278, 277, 275, 274, 273, 272, 271, 270, 269, + 268, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256 +}; + +template<typename u16x8_t, typename u8x16_t> +static void +DoUnpremultiplicationCalculation_SIMD(const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride) +{ + for (int32_t y = 0; y < aSize.height; y++) { + for (int32_t x = 0; x < aSize.width; x += 4) { + int32_t inputIndex = y * aSourceStride + 4 * x; + int32_t targetIndex = y * aTargetStride + 4 * x; + union { + u8x16_t p1234; + uint8_t u8[4][4]; + }; + p1234 = simd::Load8<u8x16_t>(&aSourceData[inputIndex]); + + // Prepare the alpha factors. + uint16_t aF1 = sAlphaFactors[u8[0][B8G8R8A8_COMPONENT_BYTEOFFSET_A]]; + uint16_t aF2 = sAlphaFactors[u8[1][B8G8R8A8_COMPONENT_BYTEOFFSET_A]]; + uint16_t aF3 = sAlphaFactors[u8[2][B8G8R8A8_COMPONENT_BYTEOFFSET_A]]; + uint16_t aF4 = sAlphaFactors[u8[3][B8G8R8A8_COMPONENT_BYTEOFFSET_A]]; + u16x8_t aF12 = simd::FromU16<u16x8_t>(aF1, aF1, aF1, 1 << 8, aF2, aF2, aF2, 1 << 8); + u16x8_t aF34 = simd::FromU16<u16x8_t>(aF3, aF3, aF3, 1 << 8, aF4, aF4, aF4, 1 << 8); + + u16x8_t p12 = simd::UnpackLo8x8ToU16x8(p1234); + u16x8_t p34 = simd::UnpackHi8x8ToU16x8(p1234); + + // Multiply with the alpha factors, add 128 for rounding, and shift right by 8 bits. + p12 = simd::ShiftRight16<8>(simd::Add16(simd::Mul16(p12, aF12), simd::FromU16<u16x8_t>(128))); + p34 = simd::ShiftRight16<8>(simd::Add16(simd::Mul16(p34, aF34), simd::FromU16<u16x8_t>(128))); + + u8x16_t result = simd::PackAndSaturate16To8(p12, p34); + simd::Store8(&aTargetData[targetIndex], result); + } + } +} + +template<typename f32x4_t, typename i32x4_t, typename u8x16_t> +static TemporaryRef<DataSourceSurface> +RenderTurbulence_SIMD(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency, + int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect) +{ +#define RETURN_TURBULENCE(Type, Stitch) \ + SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t> \ + renderer(aBaseFrequency, aSeed, aNumOctaves, aTileRect); \ + return renderer.Render(aSize, aOffset); + + switch (aType) { + case TURBULENCE_TYPE_TURBULENCE: + { + if (aStitch) { + RETURN_TURBULENCE(TURBULENCE_TYPE_TURBULENCE, true); + } + RETURN_TURBULENCE(TURBULENCE_TYPE_TURBULENCE, false); + } + case TURBULENCE_TYPE_FRACTAL_NOISE: + { + if (aStitch) { + RETURN_TURBULENCE(TURBULENCE_TYPE_FRACTAL_NOISE, true); + } + RETURN_TURBULENCE(TURBULENCE_TYPE_FRACTAL_NOISE, false); + } + } + return nullptr; +#undef RETURN_TURBULENCE +} + +// k1 * in1 * in2 + k2 * in1 + k3 * in2 + k4 +template<typename i32x4_t, typename i16x8_t> +static MOZ_ALWAYS_INLINE i16x8_t +ArithmeticCombineTwoPixels(i16x8_t in1, i16x8_t in2, + const i16x8_t &k1And4, const i16x8_t &k2And3) +{ + // Calculate input product: inProd = (in1 * in2) / 255. + i32x4_t inProd_1, inProd_2; + simd::Mul16x4x2x2To32x4x2(in1, in2, inProd_1, inProd_2); + i16x8_t inProd = simd::PackAndSaturate32To16(simd::FastDivideBy255(inProd_1), simd::FastDivideBy255(inProd_2)); + + // Calculate k1 * ((in1 * in2) / 255) + (k4/128) * 128 + i16x8_t oneTwentyEight = simd::FromI16<i16x8_t>(128); + i16x8_t inProd1AndOneTwentyEight = simd::InterleaveLo16(inProd, oneTwentyEight); + i16x8_t inProd2AndOneTwentyEight = simd::InterleaveHi16(inProd, oneTwentyEight); + i32x4_t inProdTimesK1PlusK4_1 = simd::MulAdd16x8x2To32x4(k1And4, inProd1AndOneTwentyEight); + i32x4_t inProdTimesK1PlusK4_2 = simd::MulAdd16x8x2To32x4(k1And4, inProd2AndOneTwentyEight); + + // Calculate k2 * in1 + k3 * in2 + i16x8_t in12_1 = simd::InterleaveLo16(in1, in2); + i16x8_t in12_2 = simd::InterleaveHi16(in1, in2); + i32x4_t inTimesK2K3_1 = simd::MulAdd16x8x2To32x4(k2And3, in12_1); + i32x4_t inTimesK2K3_2 = simd::MulAdd16x8x2To32x4(k2And3, in12_2); + + // Sum everything up and truncate the fractional part. + i32x4_t result_1 = simd::ShiftRight32<7>(simd::Add32(inProdTimesK1PlusK4_1, inTimesK2K3_1)); + i32x4_t result_2 = simd::ShiftRight32<7>(simd::Add32(inProdTimesK1PlusK4_2, inTimesK2K3_2)); + return simd::PackAndSaturate32To16(result_1, result_2); +} + +template<typename i32x4_t, typename i16x8_t, typename u8x16_t> +static TemporaryRef<DataSourceSurface> +ApplyArithmeticCombine_SIMD(DataSourceSurface* aInput1, DataSourceSurface* aInput2, + Float aK1, Float aK2, Float aK3, Float aK4) +{ + IntSize size = aInput1->GetSize(); + RefPtr<DataSourceSurface> target = + Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); + if (!target) { + return nullptr; + } + + uint8_t* source1Data = aInput1->GetData(); + uint8_t* source2Data = aInput2->GetData(); + uint8_t* targetData = target->GetData(); + uint32_t source1Stride = aInput1->Stride(); + uint32_t source2Stride = aInput2->Stride(); + uint32_t targetStride = target->Stride(); + + // The arithmetic combine filter does the following calculation: + // result = k1 * in1 * in2 + k2 * in1 + k3 * in2 + k4 + // + // Or, with in1/2 integers between 0 and 255: + // result = (k1 * in1 * in2) / 255 + k2 * in1 + k3 * in2 + k4 * 255 + // + // We want the whole calculation to happen in integer, with 16-bit factors. + // So we convert our factors to fixed-point with precision 1.8.7. + // K4 is premultiplied with 255, and it will be multiplied with 128 later + // during the actual calculation, because premultiplying it with 255 * 128 + // would overflow int16. + + i16x8_t k1 = simd::FromI16<i16x8_t>(int16_t(floorf(std::min(std::max(aK1, -255.0f), 255.0f) * 128 + 0.5f))); + i16x8_t k2 = simd::FromI16<i16x8_t>(int16_t(floorf(std::min(std::max(aK2, -255.0f), 255.0f) * 128 + 0.5f))); + i16x8_t k3 = simd::FromI16<i16x8_t>(int16_t(floorf(std::min(std::max(aK3, -255.0f), 255.0f) * 128 + 0.5f))); + i16x8_t k4 = simd::FromI16<i16x8_t>(int16_t(floorf(std::min(std::max(aK4, -128.0f), 128.0f) * 255 + 0.5f))); + + i16x8_t k1And4 = simd::InterleaveLo16(k1, k4); + i16x8_t k2And3 = simd::InterleaveLo16(k2, k3); + + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x += 4) { + uint32_t source1Index = y * source1Stride + 4 * x; + uint32_t source2Index = y * source2Stride + 4 * x; + uint32_t targetIndex = y * targetStride + 4 * x; + + // Load and unpack. + u8x16_t in1 = simd::Load8<u8x16_t>(&source1Data[source1Index]); + u8x16_t in2 = simd::Load8<u8x16_t>(&source2Data[source2Index]); + i16x8_t in1_12 = simd::UnpackLo8x8ToI16x8(in1); + i16x8_t in1_34 = simd::UnpackHi8x8ToI16x8(in1); + i16x8_t in2_12 = simd::UnpackLo8x8ToI16x8(in2); + i16x8_t in2_34 = simd::UnpackHi8x8ToI16x8(in2); + + // Multiply and add. + i16x8_t result_12 = ArithmeticCombineTwoPixels<i32x4_t,i16x8_t>(in1_12, in2_12, k1And4, k2And3); + i16x8_t result_34 = ArithmeticCombineTwoPixels<i32x4_t,i16x8_t>(in1_34, in2_34, k1And4, k2And3); + + // Pack and store. + simd::Store8(&targetData[targetIndex], simd::PackAndSaturate16To8(result_12, result_34)); + } + } + + return target; +} + +} // namespace mozilla +} // namespace gfx diff --git a/gfx/2d/FilterProcessingSSE2.cpp b/gfx/2d/FilterProcessingSSE2.cpp new file mode 100644 index 000000000..ca1d2dbef --- /dev/null +++ b/gfx/2d/FilterProcessingSSE2.cpp @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define SIMD_COMPILE_SSE2 + +#include "FilterProcessingSIMD-inl.h" + +#ifndef USE_SSE2 +static_assert(false, "If this file is built, FilterProcessing.h should know about it!"); +#endif + +namespace mozilla { +namespace gfx { + +void +FilterProcessing::ExtractAlpha_SSE2(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride) +{ + ExtractAlpha_SIMD<__m128i>(size, sourceData, sourceStride, alphaData, alphaStride); +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::ConvertToB8G8R8A8_SSE2(SourceSurface* aSurface) +{ + return ConvertToB8G8R8A8_SIMD<__m128i>(aSurface); +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::ApplyBlending_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, + BlendMode aBlendMode) +{ + return ApplyBlending_SIMD<__m128i,__m128i,__m128i>(aInput1, aInput2, aBlendMode); +} + +void +FilterProcessing::ApplyMorphologyHorizontal_SSE2(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOp) +{ + ApplyMorphologyHorizontal_SIMD<__m128i,__m128i>( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp); +} + +void +FilterProcessing::ApplyMorphologyVertical_SSE2(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOp) +{ + ApplyMorphologyVertical_SIMD<__m128i,__m128i>( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp); +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::ApplyColorMatrix_SSE2(DataSourceSurface* aInput, const Matrix5x4 &aMatrix) +{ + return ApplyColorMatrix_SIMD<__m128i,__m128i,__m128i>(aInput, aMatrix); +} + +void +FilterProcessing::ApplyComposition_SSE2(DataSourceSurface* aSource, DataSourceSurface* aDest, + CompositeOperator aOperator) +{ + return ApplyComposition_SIMD<__m128i,__m128i,__m128i>(aSource, aDest, aOperator); +} + +void +FilterProcessing::SeparateColorChannels_SSE2(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride) +{ + SeparateColorChannels_SIMD<__m128i>(size, sourceData, sourceStride, channel0Data, channel1Data, channel2Data, channel3Data, channelStride); +} + +void +FilterProcessing::CombineColorChannels_SSE2(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data) +{ + CombineColorChannels_SIMD<__m128i>(size, resultStride, resultData, channelStride, channel0Data, channel1Data, channel2Data, channel3Data); +} + +void +FilterProcessing::DoPremultiplicationCalculation_SSE2(const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride) +{ + DoPremultiplicationCalculation_SIMD<__m128i,__m128i,__m128i>(aSize, aTargetData, aTargetStride, aSourceData, aSourceStride); +} + +void +FilterProcessing::DoUnpremultiplicationCalculation_SSE2( + const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride) +{ + DoUnpremultiplicationCalculation_SIMD<__m128i,__m128i>(aSize, aTargetData, aTargetStride, aSourceData, aSourceStride); +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::RenderTurbulence_SSE2(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency, + int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect) +{ + return RenderTurbulence_SIMD<__m128,__m128i,__m128i>(aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect); +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::ApplyArithmeticCombine_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4) +{ + return ApplyArithmeticCombine_SIMD<__m128i,__m128i,__m128i>(aInput1, aInput2, aK1, aK2, aK3, aK4); +} + +} // namespace mozilla +} // namespace gfx diff --git a/gfx/2d/FilterProcessingScalar.cpp b/gfx/2d/FilterProcessingScalar.cpp new file mode 100644 index 000000000..3a38118ee --- /dev/null +++ b/gfx/2d/FilterProcessingScalar.cpp @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define FILTER_PROCESSING_SCALAR + +#include "FilterProcessingSIMD-inl.h" +#include "Logging.h" + +namespace mozilla { +namespace gfx { + +void +FilterProcessing::ExtractAlpha_Scalar(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride) +{ + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x++) { + int32_t sourceIndex = y * sourceStride + 4 * x; + int32_t targetIndex = y * alphaStride + x; + alphaData[targetIndex] = sourceData[sourceIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A]; + } + } +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::ConvertToB8G8R8A8_Scalar(SourceSurface* aSurface) +{ + return ConvertToB8G8R8A8_SIMD<simd::Scalaru8x16_t>(aSurface); +} + +template<MorphologyOperator Operator> +static void +ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius) +{ + static_assert(Operator == MORPHOLOGY_OPERATOR_ERODE || + Operator == MORPHOLOGY_OPERATOR_DILATE, + "unexpected morphology operator"); + + for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++) { + int32_t startX = aDestRect.x - aRadius; + int32_t endX = aDestRect.x + aRadius; + for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x++, startX++, endX++) { + int32_t sourceIndex = y * aSourceStride + 4 * startX; + uint8_t u[4]; + for (size_t i = 0; i < 4; i++) { + u[i] = aSourceData[sourceIndex + i]; + } + sourceIndex += 4; + for (int32_t ix = startX + 1; ix <= endX; ix++, sourceIndex += 4) { + for (size_t i = 0; i < 4; i++) { + if (Operator == MORPHOLOGY_OPERATOR_ERODE) { + u[i] = umin(u[i], aSourceData[sourceIndex + i]); + } else { + u[i] = umax(u[i], aSourceData[sourceIndex + i]); + } + } + } + + int32_t destIndex = y * aDestStride + 4 * x; + for (size_t i = 0; i < 4; i++) { + aDestData[destIndex+i] = u[i]; + } + } + } +} + +void +FilterProcessing::ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOp) +{ + if (aOp == MORPHOLOGY_OPERATOR_ERODE) { + gfx::ApplyMorphologyHorizontal_Scalar<MORPHOLOGY_OPERATOR_ERODE>( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius); + } else { + gfx::ApplyMorphologyHorizontal_Scalar<MORPHOLOGY_OPERATOR_DILATE>( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius); + } +} + +template<MorphologyOperator Operator> +static void ApplyMorphologyVertical_Scalar(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius) +{ + static_assert(Operator == MORPHOLOGY_OPERATOR_ERODE || + Operator == MORPHOLOGY_OPERATOR_DILATE, + "unexpected morphology operator"); + + int32_t startY = aDestRect.y - aRadius; + int32_t endY = aDestRect.y + aRadius; + for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++, startY++, endY++) { + for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x++) { + int32_t sourceIndex = startY * aSourceStride + 4 * x; + uint8_t u[4]; + for (size_t i = 0; i < 4; i++) { + u[i] = aSourceData[sourceIndex + i]; + } + sourceIndex += aSourceStride; + for (int32_t iy = startY + 1; iy <= endY; iy++, sourceIndex += aSourceStride) { + for (size_t i = 0; i < 4; i++) { + if (Operator == MORPHOLOGY_OPERATOR_ERODE) { + u[i] = umin(u[i], aSourceData[sourceIndex + i]); + } else { + u[i] = umax(u[i], aSourceData[sourceIndex + i]); + } + } + } + + int32_t destIndex = y * aDestStride + 4 * x; + for (size_t i = 0; i < 4; i++) { + aDestData[destIndex+i] = u[i]; + } + } + } +} + +void +FilterProcessing::ApplyMorphologyVertical_Scalar(uint8_t* aSourceData, int32_t aSourceStride, + uint8_t* aDestData, int32_t aDestStride, + const IntRect& aDestRect, int32_t aRadius, + MorphologyOperator aOp) +{ + if (aOp == MORPHOLOGY_OPERATOR_ERODE) { + gfx::ApplyMorphologyVertical_Scalar<MORPHOLOGY_OPERATOR_ERODE>( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius); + } else { + gfx::ApplyMorphologyVertical_Scalar<MORPHOLOGY_OPERATOR_DILATE>( + aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius); + } +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::ApplyColorMatrix_Scalar(DataSourceSurface* aInput, const Matrix5x4 &aMatrix) +{ + return ApplyColorMatrix_SIMD<simd::Scalari32x4_t,simd::Scalari16x8_t,simd::Scalaru8x16_t>(aInput, aMatrix); +} + +void +FilterProcessing::ApplyComposition_Scalar(DataSourceSurface* aSource, DataSourceSurface* aDest, + CompositeOperator aOperator) +{ + return ApplyComposition_SIMD<simd::Scalari32x4_t,simd::Scalaru16x8_t,simd::Scalaru8x16_t>(aSource, aDest, aOperator); +} + +void +FilterProcessing::SeparateColorChannels_Scalar(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride) +{ + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x++) { + int32_t sourceIndex = y * sourceStride + 4 * x; + int32_t targetIndex = y * channelStride + x; + channel0Data[targetIndex] = sourceData[sourceIndex]; + channel1Data[targetIndex] = sourceData[sourceIndex+1]; + channel2Data[targetIndex] = sourceData[sourceIndex+2]; + channel3Data[targetIndex] = sourceData[sourceIndex+3]; + } + } +} + +void +FilterProcessing::CombineColorChannels_Scalar(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data) +{ + for (int32_t y = 0; y < size.height; y++) { + for (int32_t x = 0; x < size.width; x++) { + int32_t resultIndex = y * resultStride + 4 * x; + int32_t channelIndex = y * channelStride + x; + resultData[resultIndex] = channel0Data[channelIndex]; + resultData[resultIndex+1] = channel1Data[channelIndex]; + resultData[resultIndex+2] = channel2Data[channelIndex]; + resultData[resultIndex+3] = channel3Data[channelIndex]; + } + } +} + +void +FilterProcessing::DoPremultiplicationCalculation_Scalar(const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride) +{ + for (int32_t y = 0; y < aSize.height; y++) { + for (int32_t x = 0; x < aSize.width; x++) { + int32_t inputIndex = y * aSourceStride + 4 * x; + int32_t targetIndex = y * aTargetStride + 4 * x; + uint8_t alpha = aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A]; + aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] = + FastDivideBy255<uint8_t>(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] * alpha); + aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] = + FastDivideBy255<uint8_t>(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] * alpha); + aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] = + FastDivideBy255<uint8_t>(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] * alpha); + aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A] = alpha; + } + } +} + +void +FilterProcessing::DoUnpremultiplicationCalculation_Scalar( + const IntSize& aSize, + uint8_t* aTargetData, int32_t aTargetStride, + uint8_t* aSourceData, int32_t aSourceStride) +{ + for (int32_t y = 0; y < aSize.height; y++) { + for (int32_t x = 0; x < aSize.width; x++) { + int32_t inputIndex = y * aSourceStride + 4 * x; + int32_t targetIndex = y * aTargetStride + 4 * x; + uint8_t alpha = aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A]; + uint16_t alphaFactor = sAlphaFactors[alpha]; + // inputColor * alphaFactor + 128 is guaranteed to fit into uint16_t + // because the input is premultiplied and thus inputColor <= inputAlpha. + // The maximum value this can attain is 65520 (which is less than 65535) + // for color == alpha == 244: + // 244 * sAlphaFactors[244] + 128 == 244 * 268 + 128 == 65520 + aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] = + (aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] * alphaFactor + 128) >> 8; + aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] = + (aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] * alphaFactor + 128) >> 8; + aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] = + (aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] * alphaFactor + 128) >> 8; + aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A] = alpha; + } + } +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::RenderTurbulence_Scalar(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency, + int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect) +{ + return RenderTurbulence_SIMD<simd::Scalarf32x4_t,simd::Scalari32x4_t,simd::Scalaru8x16_t>( + aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect); +} + +TemporaryRef<DataSourceSurface> +FilterProcessing::ApplyArithmeticCombine_Scalar(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4) +{ + return ApplyArithmeticCombine_SIMD<simd::Scalari32x4_t,simd::Scalari16x8_t,simd::Scalaru8x16_t>(aInput1, aInput2, aK1, aK2, aK3, aK4); +} + +} // namespace mozilla +} // namespace gfx diff --git a/gfx/2d/Filters.h b/gfx/2d/Filters.h new file mode 100644 index 000000000..31c3e8394 --- /dev/null +++ b/gfx/2d/Filters.h @@ -0,0 +1,512 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_FILTERS_H_ +#define MOZILLA_GFX_FILTERS_H_ + +#include "Types.h" +#include "mozilla/RefPtr.h" + +#include "Point.h" +#include "Matrix.h" +#include <vector> + +namespace mozilla { +namespace gfx { + +class SourceSurface; + +enum FilterBackend { + FILTER_BACKEND_SOFTWARE = 0, + FILTER_BACKEND_DIRECT2D1_1, + FILTER_BACKEND_RECORDING +}; + +enum TransformFilterAtts +{ + ATT_TRANSFORM_MATRIX = 0, // Matrix + ATT_TRANSFORM_FILTER // Filter +}; + +enum TransformFilterInputs +{ + IN_TRANSFORM_IN = 0 +}; + +enum BlendFilterAtts +{ + ATT_BLEND_BLENDMODE = 0 // uint32_t +}; + +enum BlendMode +{ + BLEND_MODE_MULTIPLY = 0, + BLEND_MODE_SCREEN, + BLEND_MODE_DARKEN, + BLEND_MODE_LIGHTEN, + BLEND_MODE_OVERLAY, + BLEND_MODE_COLOR_DODGE, + BLEND_MODE_COLOR_BURN, + BLEND_MODE_HARD_LIGHT, + BLEND_MODE_SOFT_LIGHT, + BLEND_MODE_DIFFERENCE, + BLEND_MODE_EXCLUSION, + BLEND_MODE_HUE, + BLEND_MODE_SATURATION, + BLEND_MODE_COLOR, + BLEND_MODE_LUMINOSITY +}; + +enum BlendFilterInputs +{ + IN_BLEND_IN = 0, + IN_BLEND_IN2 +}; + +enum MorphologyFilterAtts +{ + ATT_MORPHOLOGY_RADII = 0, // IntSize + ATT_MORPHOLOGY_OPERATOR // MorphologyOperator +}; + +enum MorphologyOperator +{ + MORPHOLOGY_OPERATOR_ERODE = 0, + MORPHOLOGY_OPERATOR_DILATE +}; + +enum MorphologyFilterInputs +{ + IN_MORPHOLOGY_IN = 0 +}; + +enum AlphaMode +{ + ALPHA_MODE_PREMULTIPLIED = 0, + ALPHA_MODE_STRAIGHT +}; + +enum ColorMatrixFilterAtts +{ + ATT_COLOR_MATRIX_MATRIX = 0, // Matrix5x4 + ATT_COLOR_MATRIX_ALPHA_MODE // AlphaMode +}; + +enum ColorMatrixFilterInputs +{ + IN_COLOR_MATRIX_IN = 0 +}; + +enum FloodFilterAtts +{ + ATT_FLOOD_COLOR = 0 // Color +}; + +enum FloodFilterInputs +{ + IN_FLOOD_IN = 0 +}; + +enum TileFilterAtts +{ + ATT_TILE_SOURCE_RECT = 0 // IntRect +}; + +enum TileFilterInputs +{ + IN_TILE_IN = 0 +}; + +enum TransferAtts +{ + ATT_TRANSFER_DISABLE_R = 0, // bool + ATT_TRANSFER_DISABLE_G, // bool + ATT_TRANSFER_DISABLE_B, // bool + ATT_TRANSFER_DISABLE_A // bool +}; + +enum TransferInputs +{ + IN_TRANSFER_IN = 0 +}; + +enum TableTransferAtts +{ + ATT_TABLE_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R, + ATT_TABLE_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G, + ATT_TABLE_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B, + ATT_TABLE_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A, + ATT_TABLE_TRANSFER_TABLE_R, // Float[] + ATT_TABLE_TRANSFER_TABLE_G, // Float[] + ATT_TABLE_TRANSFER_TABLE_B, // Float[] + ATT_TABLE_TRANSFER_TABLE_A // Float[] +}; + +enum TableTransferInputs +{ + IN_TABLE_TRANSFER_IN = IN_TRANSFER_IN +}; + +enum DiscreteTransferAtts +{ + ATT_DISCRETE_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R, + ATT_DISCRETE_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G, + ATT_DISCRETE_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B, + ATT_DISCRETE_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A, + ATT_DISCRETE_TRANSFER_TABLE_R, // Float[] + ATT_DISCRETE_TRANSFER_TABLE_G, // Float[] + ATT_DISCRETE_TRANSFER_TABLE_B, // Float[] + ATT_DISCRETE_TRANSFER_TABLE_A // Float[] +}; + +enum DiscreteTransferInputs +{ + IN_DISCRETE_TRANSFER_IN = IN_TRANSFER_IN +}; + +enum LinearTransferAtts +{ + ATT_LINEAR_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R, + ATT_LINEAR_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G, + ATT_LINEAR_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B, + ATT_LINEAR_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A, + ATT_LINEAR_TRANSFER_SLOPE_R, // Float + ATT_LINEAR_TRANSFER_SLOPE_G, // Float + ATT_LINEAR_TRANSFER_SLOPE_B, // Float + ATT_LINEAR_TRANSFER_SLOPE_A, // Float + ATT_LINEAR_TRANSFER_INTERCEPT_R, // Float + ATT_LINEAR_TRANSFER_INTERCEPT_G, // Float + ATT_LINEAR_TRANSFER_INTERCEPT_B, // Float + ATT_LINEAR_TRANSFER_INTERCEPT_A // Float +}; + +enum LinearTransferInputs +{ + IN_LINEAR_TRANSFER_IN = IN_TRANSFER_IN +}; + +enum GammaTransferAtts +{ + ATT_GAMMA_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R, + ATT_GAMMA_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G, + ATT_GAMMA_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B, + ATT_GAMMA_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A, + ATT_GAMMA_TRANSFER_AMPLITUDE_R, // Float + ATT_GAMMA_TRANSFER_AMPLITUDE_G, // Float + ATT_GAMMA_TRANSFER_AMPLITUDE_B, // Float + ATT_GAMMA_TRANSFER_AMPLITUDE_A, // Float + ATT_GAMMA_TRANSFER_EXPONENT_R, // Float + ATT_GAMMA_TRANSFER_EXPONENT_G, // Float + ATT_GAMMA_TRANSFER_EXPONENT_B, // Float + ATT_GAMMA_TRANSFER_EXPONENT_A, // Float + ATT_GAMMA_TRANSFER_OFFSET_R, // Float + ATT_GAMMA_TRANSFER_OFFSET_G, // Float + ATT_GAMMA_TRANSFER_OFFSET_B, // Float + ATT_GAMMA_TRANSFER_OFFSET_A // Float +}; + +enum GammaTransferInputs +{ + IN_GAMMA_TRANSFER_IN = IN_TRANSFER_IN +}; + +enum ConvolveMatrixAtts +{ + ATT_CONVOLVE_MATRIX_KERNEL_SIZE = 0, // IntSize + ATT_CONVOLVE_MATRIX_KERNEL_MATRIX, // Float[] + ATT_CONVOLVE_MATRIX_DIVISOR, // Float + ATT_CONVOLVE_MATRIX_BIAS, // Float + ATT_CONVOLVE_MATRIX_TARGET, // IntPoint + ATT_CONVOLVE_MATRIX_SOURCE_RECT, // IntRect + ATT_CONVOLVE_MATRIX_EDGE_MODE, // ConvolveMatrixEdgeMode + ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH, // Size + ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA, // bool +}; + +enum ConvolveMatrixEdgeMode +{ + EDGE_MODE_DUPLICATE = 0, + EDGE_MODE_WRAP, + EDGE_MODE_NONE +}; + +enum ConvolveMatrixInputs +{ + IN_CONVOLVE_MATRIX_IN = 0 +}; + +enum DisplacementMapAtts +{ + ATT_DISPLACEMENT_MAP_SCALE = 0, // Float + ATT_DISPLACEMENT_MAP_X_CHANNEL, // ColorChannel + ATT_DISPLACEMENT_MAP_Y_CHANNEL // ColorChannel +}; + +enum ColorChannel +{ + COLOR_CHANNEL_R = 0, + COLOR_CHANNEL_G, + COLOR_CHANNEL_B, + COLOR_CHANNEL_A +}; + +enum DisplacementMapInputs +{ + IN_DISPLACEMENT_MAP_IN = 0, + IN_DISPLACEMENT_MAP_IN2 +}; + +enum TurbulenceAtts +{ + ATT_TURBULENCE_BASE_FREQUENCY = 0, // Size + ATT_TURBULENCE_NUM_OCTAVES, // uint32_t + ATT_TURBULENCE_SEED, // uint32_t + ATT_TURBULENCE_STITCHABLE, // bool + ATT_TURBULENCE_TYPE, // TurbulenceType + ATT_TURBULENCE_RECT // IntRect +}; + +enum TurbulenceType +{ + TURBULENCE_TYPE_TURBULENCE = 0, + TURBULENCE_TYPE_FRACTAL_NOISE +}; + +enum ArithmeticCombineAtts +{ + ATT_ARITHMETIC_COMBINE_COEFFICIENTS = 0 // Float[4] +}; + +enum ArithmeticCombineInputs +{ + IN_ARITHMETIC_COMBINE_IN = 0, + IN_ARITHMETIC_COMBINE_IN2 +}; + +enum CompositeAtts +{ + ATT_COMPOSITE_OPERATOR = 0 // CompositeOperator +}; + +enum CompositeOperator +{ + COMPOSITE_OPERATOR_OVER = 0, + COMPOSITE_OPERATOR_IN, + COMPOSITE_OPERATOR_OUT, + COMPOSITE_OPERATOR_ATOP, + COMPOSITE_OPERATOR_XOR +}; + +enum CompositeInputs +{ + // arbitrary number of inputs + IN_COMPOSITE_IN_START = 0 +}; + +enum GaussianBlurAtts +{ + ATT_GAUSSIAN_BLUR_STD_DEVIATION = 0 // Float +}; + +enum GaussianBlurInputs +{ + IN_GAUSSIAN_BLUR_IN = 0 +}; + +enum DirectionalBlurAtts +{ + ATT_DIRECTIONAL_BLUR_STD_DEVIATION = 0, // Float + ATT_DIRECTIONAL_BLUR_DIRECTION // BlurDirection +}; + +enum BlurDirection +{ + BLUR_DIRECTION_X = 0, + BLUR_DIRECTION_Y +}; + +enum DirectionalBlurInputs +{ + IN_DIRECTIONAL_BLUR_IN = 0 +}; + +enum LightingAtts +{ + ATT_POINT_LIGHT_POSITION = 0, // Point3D + + ATT_SPOT_LIGHT_POSITION, // Point3D + ATT_SPOT_LIGHT_POINTS_AT, // Point3D + ATT_SPOT_LIGHT_FOCUS, // Float + ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE, // Float + + ATT_DISTANT_LIGHT_AZIMUTH, // Float + ATT_DISTANT_LIGHT_ELEVATION, // Float + + ATT_LIGHTING_COLOR, // Color + ATT_LIGHTING_SURFACE_SCALE, // Float + ATT_LIGHTING_KERNEL_UNIT_LENGTH, // Size + + ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT, // Float + + ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT, // Float + ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT // Float +}; + +enum LightingInputs +{ + IN_LIGHTING_IN = 0 +}; + +enum PointDiffuseAtts +{ + ATT_POINT_DIFFUSE_POSITION = ATT_POINT_LIGHT_POSITION, + ATT_POINT_DIFFUSE_COLOR = ATT_LIGHTING_COLOR, + ATT_POINT_DIFFUSE_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE, + ATT_POINT_DIFFUSE_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH, + ATT_POINT_DIFFUSE_DIFFUSE_CONSTANT = ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT +}; + +enum PointDiffuseInputs +{ + IN_POINT_DIFFUSE_IN = IN_LIGHTING_IN +}; + +enum SpotDiffuseAtts +{ + ATT_SPOT_DIFFUSE_POSITION = ATT_SPOT_LIGHT_POSITION, + ATT_SPOT_DIFFUSE_POINTS_AT = ATT_SPOT_LIGHT_POINTS_AT, + ATT_SPOT_DIFFUSE_FOCUS = ATT_SPOT_LIGHT_FOCUS, + ATT_SPOT_DIFFUSE_LIMITING_CONE_ANGLE = ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE, + ATT_SPOT_DIFFUSE_COLOR = ATT_LIGHTING_COLOR, + ATT_SPOT_DIFFUSE_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE, + ATT_SPOT_DIFFUSE_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH, + ATT_SPOT_DIFFUSE_DIFFUSE_CONSTANT = ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT +}; + +enum SpotDiffuseInputs +{ + IN_SPOT_DIFFUSE_IN = IN_LIGHTING_IN +}; + +enum DistantDiffuseAtts +{ + ATT_DISTANT_DIFFUSE_AZIMUTH = ATT_DISTANT_LIGHT_AZIMUTH, + ATT_DISTANT_DIFFUSE_ELEVATION = ATT_DISTANT_LIGHT_ELEVATION, + ATT_DISTANT_DIFFUSE_COLOR = ATT_LIGHTING_COLOR, + ATT_DISTANT_DIFFUSE_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE, + ATT_DISTANT_DIFFUSE_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH, + ATT_DISTANT_DIFFUSE_DIFFUSE_CONSTANT = ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT +}; + +enum DistantDiffuseInputs +{ + IN_DISTANT_DIFFUSE_IN = IN_LIGHTING_IN +}; + +enum PointSpecularAtts +{ + ATT_POINT_SPECULAR_POSITION = ATT_POINT_LIGHT_POSITION, + ATT_POINT_SPECULAR_COLOR = ATT_LIGHTING_COLOR, + ATT_POINT_SPECULAR_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE, + ATT_POINT_SPECULAR_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH, + ATT_POINT_SPECULAR_SPECULAR_CONSTANT = ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT, + ATT_POINT_SPECULAR_SPECULAR_EXPONENT = ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT +}; + +enum PointSpecularInputs +{ + IN_POINT_SPECULAR_IN = IN_LIGHTING_IN +}; + +enum SpotSpecularAtts +{ + ATT_SPOT_SPECULAR_POSITION = ATT_SPOT_LIGHT_POSITION, + ATT_SPOT_SPECULAR_POINTS_AT = ATT_SPOT_LIGHT_POINTS_AT, + ATT_SPOT_SPECULAR_FOCUS = ATT_SPOT_LIGHT_FOCUS, + ATT_SPOT_SPECULAR_LIMITING_CONE_ANGLE = ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE, + ATT_SPOT_SPECULAR_COLOR = ATT_LIGHTING_COLOR, + ATT_SPOT_SPECULAR_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE, + ATT_SPOT_SPECULAR_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH, + ATT_SPOT_SPECULAR_SPECULAR_CONSTANT = ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT, + ATT_SPOT_SPECULAR_SPECULAR_EXPONENT = ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT +}; + +enum SpotSpecularInputs +{ + IN_SPOT_SPECULAR_IN = IN_LIGHTING_IN +}; + +enum DistantSpecularAtts +{ + ATT_DISTANT_SPECULAR_AZIMUTH = ATT_DISTANT_LIGHT_AZIMUTH, + ATT_DISTANT_SPECULAR_ELEVATION = ATT_DISTANT_LIGHT_ELEVATION, + ATT_DISTANT_SPECULAR_COLOR = ATT_LIGHTING_COLOR, + ATT_DISTANT_SPECULAR_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE, + ATT_DISTANT_SPECULAR_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH, + ATT_DISTANT_SPECULAR_SPECULAR_CONSTANT = ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT, + ATT_DISTANT_SPECULAR_SPECULAR_EXPONENT = ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT +}; + +enum DistantSpecularInputs +{ + IN_DISTANT_SPECULAR_IN = IN_LIGHTING_IN +}; + +enum CropAtts +{ + ATT_CROP_RECT = 0 // Rect +}; + +enum CropInputs +{ + IN_CROP_IN = 0 +}; + +enum PremultiplyInputs +{ + IN_PREMULTIPLY_IN = 0 +}; + +enum UnpremultiplyInputs +{ + IN_UNPREMULTIPLY_IN = 0 +}; + +class FilterNode : public RefCounted<FilterNode> +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNode) + virtual ~FilterNode() {} + + virtual FilterBackend GetBackendType() = 0; + + virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) { MOZ_CRASH(); } + virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) { MOZ_CRASH(); } + + virtual void SetAttribute(uint32_t aIndex, bool) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, uint32_t) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, Float) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, const Size &) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, const IntSize &) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, const IntPoint &) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, const Rect &) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, const IntRect &) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, const Point &) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, const Matrix &) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, const Point3D &) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, const Color &) { MOZ_CRASH(); } + virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) { MOZ_CRASH(); } + +protected: + friend class Factory; + + FilterNode() {} +}; + +} +} + +#endif diff --git a/gfx/2d/GenericRefCounted.h b/gfx/2d/GenericRefCounted.h new file mode 100644 index 000000000..b1de3f754 --- /dev/null +++ b/gfx/2d/GenericRefCounted.h @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This header provides virtual, non-templated alternatives to MFBT's RefCounted<T>. +// It intentionally uses MFBT coding style with the intention of moving there +// should there be other use cases for it. + +#ifndef MOZILLA_GENERICREFCOUNTED_H_ +#define MOZILLA_GENERICREFCOUNTED_H_ + +#include "mozilla/RefPtr.h" + +namespace mozilla { + +/** + * Common base class for GenericRefCounted and GenericAtomicRefCounted. + * + * Having this shared base class, common to both the atomic and non-atomic + * cases, allows to have RefPtr's that don't care about whether the + * objects they're managing have atomic refcounts or not. + */ +class GenericRefCountedBase +{ + protected: + virtual ~GenericRefCountedBase() {}; + + public: + // AddRef() and Release() method names are for compatibility with nsRefPtr. + virtual void AddRef() = 0; + + virtual void Release() = 0; + + // ref() and deref() method names are for compatibility with wtf::RefPtr. + // No virtual keywords here: if a subclass wants to override the refcounting + // mechanism, it is welcome to do so by overriding AddRef() and Release(). + void ref() { AddRef(); } + void deref() { Release(); } + +#ifdef MOZ_REFCOUNTED_LEAK_CHECKING + virtual const char* typeName() const = 0; + virtual size_t typeSize() const = 0; +#endif +}; + +namespace detail { + +template<RefCountAtomicity Atomicity> +class GenericRefCounted : public GenericRefCountedBase +{ + protected: + GenericRefCounted() : refCnt(0) { } + + virtual ~GenericRefCounted() { + MOZ_ASSERT(refCnt == detail::DEAD); + } + + public: + virtual void AddRef() override { + // Note: this method must be thread safe for GenericAtomicRefCounted. + MOZ_ASSERT(int32_t(refCnt) >= 0); +#ifndef MOZ_REFCOUNTED_LEAK_CHECKING + ++refCnt; +#else + const char* type = typeName(); + uint32_t size = typeSize(); + const void* ptr = this; + MozRefCountType cnt = ++refCnt; + detail::RefCountLogger::logAddRef(ptr, cnt, type, size); +#endif + } + + virtual void Release() override { + // Note: this method must be thread safe for GenericAtomicRefCounted. + MOZ_ASSERT(int32_t(refCnt) > 0); +#ifndef MOZ_REFCOUNTED_LEAK_CHECKING + MozRefCountType cnt = --refCnt; +#else + const char* type = typeName(); + const void* ptr = this; + MozRefCountType cnt = --refCnt; + // Note: it's not safe to touch |this| after decrementing the refcount, + // except for below. + detail::RefCountLogger::logRelease(ptr, cnt, type); +#endif + if (0 == cnt) { + // Because we have atomically decremented the refcount above, only + // one thread can get a 0 count here, so as long as we can assume that + // everything else in the system is accessing this object through + // RefPtrs, it's safe to access |this| here. +#ifdef DEBUG + refCnt = detail::DEAD; +#endif + delete this; + } + } + + MozRefCountType refCount() const { return refCnt; } + bool hasOneRef() const { + MOZ_ASSERT(refCnt > 0); + return refCnt == 1; + } + + private: + typename Conditional<Atomicity == AtomicRefCount, Atomic<MozRefCountType>, MozRefCountType>::Type refCnt; +}; + +} // namespace detail + +/** + * This reference-counting base class is virtual instead of + * being templated, which is useful in cases where one needs + * genericity at binary code level, but comes at the cost + * of a moderate performance and size overhead, like anything virtual. + */ +class GenericRefCounted : public detail::GenericRefCounted<detail::NonAtomicRefCount> +{ +}; + +/** + * GenericAtomicRefCounted is like GenericRefCounted, with an atomically updated + * reference counter. + */ +class GenericAtomicRefCounted : public detail::GenericRefCounted<detail::AtomicRefCount> +{ +}; + +} // namespace mozilla + +#endif diff --git a/gfx/2d/GradientStopsD2D.h b/gfx/2d/GradientStopsD2D.h index fe5b4e25c..de6e448de 100644 --- a/gfx/2d/GradientStopsD2D.h +++ b/gfx/2d/GradientStopsD2D.h @@ -16,16 +16,22 @@ namespace gfx { class GradientStopsD2D : public GradientStops { public: - GradientStopsD2D(ID2D1GradientStopCollection *aStopCollection) + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsD2D) + GradientStopsD2D(ID2D1GradientStopCollection *aStopCollection, ID3D11Device *aDevice) : mStopCollection(aStopCollection) + , mDevice(aDevice) {} - virtual BackendType GetBackendType() const { return BACKEND_DIRECT2D; } + virtual BackendType GetBackendType() const { return BackendType::DIRECT2D; } + + virtual bool IsValid() const final{ return mDevice == Factory::GetDirect3D11Device(); } private: friend class DrawTargetD2D; + friend class DrawTargetD2D1; mutable RefPtr<ID2D1GradientStopCollection> mStopCollection; + RefPtr<ID3D11Device> mDevice; }; } diff --git a/gfx/2d/Helpers.h b/gfx/2d/Helpers.h index e69de29bb..11c7eec5d 100644 --- a/gfx/2d/Helpers.h +++ b/gfx/2d/Helpers.h @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_2D_HELPERS_H_ +#define MOZILLA_GFX_2D_HELPERS_H_ + +#include "2D.h" + +namespace mozilla { +namespace gfx { + +class AutoRestoreTransform +{ + public: + AutoRestoreTransform() + { + } + + explicit AutoRestoreTransform(DrawTarget *aTarget) + : mDrawTarget(aTarget), + mOldTransform(aTarget->GetTransform()) + { + } + + void Init(DrawTarget *aTarget) + { + MOZ_ASSERT(!mDrawTarget || aTarget == mDrawTarget); + if (!mDrawTarget) { + mDrawTarget = aTarget; + mOldTransform = aTarget->GetTransform(); + } + } + + ~AutoRestoreTransform() + { + if (mDrawTarget) { + mDrawTarget->SetTransform(mOldTransform); + } + } + + private: + RefPtr<DrawTarget> mDrawTarget; + Matrix mOldTransform; +}; + +class AutoPopClips +{ +public: + explicit AutoPopClips(DrawTarget *aTarget) + : mDrawTarget(aTarget) + , mPushCount(0) + { + MOZ_ASSERT(mDrawTarget); + } + + ~AutoPopClips() + { + PopAll(); + } + + void PushClip(const Path *aPath) + { + mDrawTarget->PushClip(aPath); + ++mPushCount; + } + + void PushClipRect(const Rect &aRect) + { + mDrawTarget->PushClipRect(aRect); + ++mPushCount; + } + + void PopClip() + { + MOZ_ASSERT(mPushCount > 0); + mDrawTarget->PopClip(); + --mPushCount; + } + + void PopAll() + { + while (mPushCount-- > 0) { + mDrawTarget->PopClip(); + } + } + +private: + RefPtr<DrawTarget> mDrawTarget; + int32_t mPushCount; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // MOZILLA_GFX_2D_HELPERS_H_ diff --git a/gfx/2d/HelpersCairo.h b/gfx/2d/HelpersCairo.h index 5dfcee261..3c5b74340 100644 --- a/gfx/2d/HelpersCairo.h +++ b/gfx/2d/HelpersCairo.h @@ -18,73 +18,92 @@ GfxOpToCairoOp(CompositionOp op) { switch (op) { - case OP_OVER: + case CompositionOp::OP_OVER: return CAIRO_OPERATOR_OVER; - case OP_ADD: + case CompositionOp::OP_ADD: return CAIRO_OPERATOR_ADD; - case OP_ATOP: + case CompositionOp::OP_ATOP: return CAIRO_OPERATOR_ATOP; - case OP_OUT: + case CompositionOp::OP_OUT: return CAIRO_OPERATOR_OUT; - case OP_IN: + case CompositionOp::OP_IN: return CAIRO_OPERATOR_IN; - case OP_SOURCE: + case CompositionOp::OP_SOURCE: return CAIRO_OPERATOR_SOURCE; - case OP_DEST_IN: + case CompositionOp::OP_DEST_IN: return CAIRO_OPERATOR_DEST_IN; - case OP_DEST_OUT: + case CompositionOp::OP_DEST_OUT: return CAIRO_OPERATOR_DEST_OUT; - case OP_DEST_OVER: + case CompositionOp::OP_DEST_OVER: return CAIRO_OPERATOR_DEST_OVER; - case OP_DEST_ATOP: + case CompositionOp::OP_DEST_ATOP: return CAIRO_OPERATOR_DEST_ATOP; - case OP_XOR: + case CompositionOp::OP_XOR: return CAIRO_OPERATOR_XOR; - case OP_MULTIPLY: + case CompositionOp::OP_MULTIPLY: return CAIRO_OPERATOR_MULTIPLY; - case OP_SCREEN: + case CompositionOp::OP_SCREEN: return CAIRO_OPERATOR_SCREEN; - case OP_OVERLAY: + case CompositionOp::OP_OVERLAY: return CAIRO_OPERATOR_OVERLAY; - case OP_DARKEN: + case CompositionOp::OP_DARKEN: return CAIRO_OPERATOR_DARKEN; - case OP_LIGHTEN: + case CompositionOp::OP_LIGHTEN: return CAIRO_OPERATOR_LIGHTEN; - case OP_COLOR_DODGE: + case CompositionOp::OP_COLOR_DODGE: return CAIRO_OPERATOR_COLOR_DODGE; - case OP_COLOR_BURN: + case CompositionOp::OP_COLOR_BURN: return CAIRO_OPERATOR_COLOR_BURN; - case OP_HARD_LIGHT: + case CompositionOp::OP_HARD_LIGHT: return CAIRO_OPERATOR_HARD_LIGHT; - case OP_SOFT_LIGHT: + case CompositionOp::OP_SOFT_LIGHT: return CAIRO_OPERATOR_SOFT_LIGHT; - case OP_DIFFERENCE: + case CompositionOp::OP_DIFFERENCE: return CAIRO_OPERATOR_DIFFERENCE; - case OP_EXCLUSION: + case CompositionOp::OP_EXCLUSION: return CAIRO_OPERATOR_EXCLUSION; - case OP_HUE: + case CompositionOp::OP_HUE: return CAIRO_OPERATOR_HSL_HUE; - case OP_SATURATION: + case CompositionOp::OP_SATURATION: return CAIRO_OPERATOR_HSL_SATURATION; - case OP_COLOR: + case CompositionOp::OP_COLOR: return CAIRO_OPERATOR_HSL_COLOR; - case OP_LUMINOSITY: + case CompositionOp::OP_LUMINOSITY: return CAIRO_OPERATOR_HSL_LUMINOSITY; - case OP_COUNT: + case CompositionOp::OP_COUNT: break; } return CAIRO_OPERATOR_OVER; } +static inline cairo_antialias_t +GfxAntialiasToCairoAntialias(AntialiasMode antialias) +{ + switch (antialias) + { + case AntialiasMode::NONE: + return CAIRO_ANTIALIAS_NONE; + case AntialiasMode::GRAY: + return CAIRO_ANTIALIAS_GRAY; + case AntialiasMode::SUBPIXEL: + return CAIRO_ANTIALIAS_SUBPIXEL; + case AntialiasMode::DEFAULT: + return CAIRO_ANTIALIAS_DEFAULT; + } + return CAIRO_ANTIALIAS_DEFAULT; +} + static inline cairo_filter_t GfxFilterToCairoFilter(Filter filter) { switch (filter) { - case FILTER_LINEAR: + case Filter::GOOD: + return CAIRO_FILTER_GOOD; + case Filter::LINEAR: return CAIRO_FILTER_BILINEAR; - case FILTER_POINT: + case Filter::POINT: return CAIRO_FILTER_NEAREST; } @@ -96,11 +115,11 @@ GfxExtendToCairoExtend(ExtendMode extend) { switch (extend) { - case EXTEND_CLAMP: + case ExtendMode::CLAMP: return CAIRO_EXTEND_PAD; - case EXTEND_REPEAT: + case ExtendMode::REPEAT: return CAIRO_EXTEND_REPEAT; - case EXTEND_REFLECT: + case ExtendMode::REFLECT: return CAIRO_EXTEND_REFLECT; } @@ -112,16 +131,17 @@ GfxFormatToCairoFormat(SurfaceFormat format) { switch (format) { - case FORMAT_B8G8R8A8: + case SurfaceFormat::B8G8R8A8: return CAIRO_FORMAT_ARGB32; - case FORMAT_B8G8R8X8: + case SurfaceFormat::B8G8R8X8: return CAIRO_FORMAT_RGB24; - case FORMAT_A8: + case SurfaceFormat::A8: return CAIRO_FORMAT_A8; - case FORMAT_R5G6B5: + case SurfaceFormat::R5G6B5: return CAIRO_FORMAT_RGB16_565; default: gfxWarning() << "Unknown image format"; + MOZ_ASSERT(false, "Unknown image format"); return CAIRO_FORMAT_ARGB32; } } @@ -131,15 +151,16 @@ GfxFormatToCairoContent(SurfaceFormat format) { switch (format) { - case FORMAT_B8G8R8A8: + case SurfaceFormat::B8G8R8A8: return CAIRO_CONTENT_COLOR_ALPHA; - case FORMAT_B8G8R8X8: - case FORMAT_R5G6B5: //fall through + case SurfaceFormat::B8G8R8X8: + case SurfaceFormat::R5G6B5: //fall through return CAIRO_CONTENT_COLOR; - case FORMAT_A8: + case SurfaceFormat::A8: return CAIRO_CONTENT_ALPHA; default: gfxWarning() << "Unknown image format"; + MOZ_ASSERT(false, "Unknown image format"); return CAIRO_CONTENT_COLOR_ALPHA; } } @@ -149,13 +170,13 @@ GfxLineJoinToCairoLineJoin(JoinStyle style) { switch (style) { - case JOIN_BEVEL: + case JoinStyle::BEVEL: return CAIRO_LINE_JOIN_BEVEL; - case JOIN_ROUND: + case JoinStyle::ROUND: return CAIRO_LINE_JOIN_ROUND; - case JOIN_MITER: + case JoinStyle::MITER: return CAIRO_LINE_JOIN_MITER; - case JOIN_MITER_OR_BEVEL: + case JoinStyle::MITER_OR_BEVEL: return CAIRO_LINE_JOIN_MITER; } @@ -167,11 +188,11 @@ GfxLineCapToCairoLineCap(CapStyle style) { switch (style) { - case CAP_BUTT: + case CapStyle::BUTT: return CAIRO_LINE_CAP_BUTT; - case CAP_ROUND: + case CapStyle::ROUND: return CAIRO_LINE_CAP_ROUND; - case CAP_SQUARE: + case CapStyle::SQUARE: return CAIRO_LINE_CAP_SQUARE; } @@ -184,15 +205,44 @@ CairoContentToGfxFormat(cairo_content_t content) switch (content) { case CAIRO_CONTENT_COLOR_ALPHA: - return FORMAT_B8G8R8A8; + return SurfaceFormat::B8G8R8A8; case CAIRO_CONTENT_COLOR: // BEWARE! format may be 565 - return FORMAT_B8G8R8X8; + return SurfaceFormat::B8G8R8X8; case CAIRO_CONTENT_ALPHA: - return FORMAT_A8; + return SurfaceFormat::A8; + } + + return SurfaceFormat::B8G8R8A8; +} + +static inline SurfaceFormat +CairoFormatToGfxFormat(cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: + return SurfaceFormat::B8G8R8A8; + case CAIRO_FORMAT_RGB24: + return SurfaceFormat::B8G8R8X8; + case CAIRO_FORMAT_A8: + return SurfaceFormat::A8; + case CAIRO_FORMAT_RGB16_565: + return SurfaceFormat::R5G6B5; + default: + gfxWarning() << "Unknown cairo format"; + MOZ_ASSERT(false, "Unknown cairo format"); + return SurfaceFormat::UNKNOWN; + } +} + +static inline SurfaceFormat +GfxFormatForCairoSurface(cairo_surface_t* surface) +{ + if (cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE) { + return CairoFormatToGfxFormat(cairo_image_surface_get_format(surface)); } - return FORMAT_B8G8R8A8; + return CairoContentToGfxFormat(cairo_surface_get_content(surface)); } static inline void @@ -228,9 +278,9 @@ GfxFillRuleToCairoFillRule(FillRule rule) { switch (rule) { - case FILL_WINDING: + case FillRule::FILL_WINDING: return CAIRO_FILL_RULE_WINDING; - case FILL_EVEN_ODD: + case FillRule::FILL_EVEN_ODD: return CAIRO_FILL_RULE_EVEN_ODD; } diff --git a/gfx/2d/HelpersD2D.h b/gfx/2d/HelpersD2D.h index cc836009d..a25f2cf2f 100644 --- a/gfx/2d/HelpersD2D.h +++ b/gfx/2d/HelpersD2D.h @@ -6,7 +6,7 @@ #ifndef MOZILLA_GFX_HELPERSD2D_H_ #define MOZILLA_GFX_HELPERSD2D_H_ -#include "moz-d2d1-1.h" +#include <d2d1_1.h> #include <vector> @@ -26,6 +26,8 @@ namespace gfx { ID2D1Factory* D2DFactory(); +ID2D1Factory1* D2DFactory1(); + static inline D2D1_POINT_2F D2DPoint(const Point &aPoint) { return D2D1::Point2F(aPoint.x, aPoint.y); @@ -36,7 +38,8 @@ static inline D2D1_SIZE_U D2DIntSize(const IntSize &aSize) return D2D1::SizeU(aSize.width, aSize.height); } -static inline D2D1_RECT_F D2DRect(const Rect &aRect) +template <typename T> +static inline D2D1_RECT_F D2DRect(const T &aRect) { return D2D1::RectF(aRect.x, aRect.y, aRect.XMost(), aRect.YMost()); } @@ -45,10 +48,10 @@ static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode) { D2D1_EXTEND_MODE extend; switch (aExtendMode) { - case EXTEND_REPEAT: + case ExtendMode::REPEAT: extend = D2D1_EXTEND_MODE_WRAP; break; - case EXTEND_REFLECT: + case ExtendMode::REFLECT: extend = D2D1_EXTEND_MODE_MIRROR; break; default: @@ -61,17 +64,41 @@ static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode) static inline D2D1_BITMAP_INTERPOLATION_MODE D2DFilter(const Filter &aFilter) { switch (aFilter) { - case FILTER_POINT: + case Filter::POINT: return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; default: return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; } } +static inline D2D1_INTERPOLATION_MODE D2DInterpolationMode(const Filter &aFilter) +{ + switch (aFilter) { + case Filter::POINT: + return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR; + default: + return D2D1_INTERPOLATION_MODE_LINEAR; + } +} + +static inline D2D1_MATRIX_5X4_F D2DMatrix5x4(const Matrix5x4 &aMatrix) +{ + return D2D1::Matrix5x4F(aMatrix._11, aMatrix._12, aMatrix._13, aMatrix._14, + aMatrix._21, aMatrix._22, aMatrix._23, aMatrix._24, + aMatrix._31, aMatrix._32, aMatrix._33, aMatrix._34, + aMatrix._41, aMatrix._42, aMatrix._43, aMatrix._44, + aMatrix._51, aMatrix._52, aMatrix._53, aMatrix._54); +} + +static inline D2D1_VECTOR_3F D2DVector3D(const Point3D &aPoint) +{ + return D2D1::Vector3F(aPoint.x, aPoint.y, aPoint.z); +} + static inline D2D1_ANTIALIAS_MODE D2DAAMode(AntialiasMode aMode) { switch (aMode) { - case AA_NONE: + case AntialiasMode::NONE: return D2D1_ANTIALIAS_MODE_ALIASED; default: return D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; @@ -99,15 +126,15 @@ static inline SurfaceFormat ToPixelFormat(const D2D1_PIXEL_FORMAT &aFormat) { switch(aFormat.format) { case DXGI_FORMAT_A8_UNORM: - return FORMAT_A8; + return SurfaceFormat::A8; case DXGI_FORMAT_B8G8R8A8_UNORM: if (aFormat.alphaMode == D2D1_ALPHA_MODE_IGNORE) { - return FORMAT_B8G8R8X8; + return SurfaceFormat::B8G8R8X8; } else { - return FORMAT_B8G8R8A8; + return SurfaceFormat::B8G8R8A8; } default: - return FORMAT_B8G8R8A8; + return SurfaceFormat::B8G8R8A8; } } @@ -123,24 +150,29 @@ static inline Matrix ToMatrix(const D2D1_MATRIX_3X2_F &aTransform) aTransform._31, aTransform._32); } +static inline Point ToPoint(const D2D1_POINT_2F &aPoint) +{ + return Point(aPoint.x, aPoint.y); +} + static inline DXGI_FORMAT DXGIFormat(SurfaceFormat aFormat) { switch (aFormat) { - case FORMAT_B8G8R8A8: + case SurfaceFormat::B8G8R8A8: return DXGI_FORMAT_B8G8R8A8_UNORM; - case FORMAT_B8G8R8X8: + case SurfaceFormat::B8G8R8X8: return DXGI_FORMAT_B8G8R8A8_UNORM; - case FORMAT_A8: + case SurfaceFormat::A8: return DXGI_FORMAT_A8_UNORM; default: return DXGI_FORMAT_UNKNOWN; } } -static inline D2D1_ALPHA_MODE AlphaMode(SurfaceFormat aFormat) +static inline D2D1_ALPHA_MODE D2DAlphaModeForFormat(SurfaceFormat aFormat) { switch (aFormat) { - case FORMAT_B8G8R8X8: + case SurfaceFormat::B8G8R8X8: return D2D1_ALPHA_MODE_IGNORE; default: return D2D1_ALPHA_MODE_PREMULTIPLIED; @@ -149,12 +181,100 @@ static inline D2D1_ALPHA_MODE AlphaMode(SurfaceFormat aFormat) static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat) { - return D2D1::PixelFormat(DXGIFormat(aFormat), AlphaMode(aFormat)); + return D2D1::PixelFormat(DXGIFormat(aFormat), D2DAlphaModeForFormat(aFormat)); +} + +static inline bool D2DSupportsCompositeMode(CompositionOp aOp) +{ + switch(aOp) { + case CompositionOp::OP_OVER: + case CompositionOp::OP_ADD: + case CompositionOp::OP_ATOP: + case CompositionOp::OP_OUT: + case CompositionOp::OP_IN: + case CompositionOp::OP_SOURCE: + case CompositionOp::OP_DEST_IN: + case CompositionOp::OP_DEST_OUT: + case CompositionOp::OP_DEST_OVER: + case CompositionOp::OP_DEST_ATOP: + case CompositionOp::OP_XOR: + return true; + default: + return false; + } +} + +static inline D2D1_COMPOSITE_MODE D2DCompositionMode(CompositionOp aOp) +{ + switch(aOp) { + case CompositionOp::OP_OVER: + return D2D1_COMPOSITE_MODE_SOURCE_OVER; + case CompositionOp::OP_ADD: + return D2D1_COMPOSITE_MODE_PLUS; + case CompositionOp::OP_ATOP: + return D2D1_COMPOSITE_MODE_SOURCE_ATOP; + case CompositionOp::OP_OUT: + return D2D1_COMPOSITE_MODE_SOURCE_OUT; + case CompositionOp::OP_IN: + return D2D1_COMPOSITE_MODE_SOURCE_IN; + case CompositionOp::OP_SOURCE: + return D2D1_COMPOSITE_MODE_SOURCE_COPY; + case CompositionOp::OP_DEST_IN: + return D2D1_COMPOSITE_MODE_DESTINATION_IN; + case CompositionOp::OP_DEST_OUT: + return D2D1_COMPOSITE_MODE_DESTINATION_OUT; + case CompositionOp::OP_DEST_OVER: + return D2D1_COMPOSITE_MODE_DESTINATION_OVER; + case CompositionOp::OP_DEST_ATOP: + return D2D1_COMPOSITE_MODE_DESTINATION_ATOP; + case CompositionOp::OP_XOR: + return D2D1_COMPOSITE_MODE_XOR; + default: + return D2D1_COMPOSITE_MODE_SOURCE_OVER; + } +} + +static inline D2D1_BLEND_MODE D2DBlendMode(CompositionOp aOp) +{ + switch (aOp) { + case CompositionOp::OP_MULTIPLY: + return D2D1_BLEND_MODE_MULTIPLY; + case CompositionOp::OP_SCREEN: + return D2D1_BLEND_MODE_SCREEN; + case CompositionOp::OP_OVERLAY: + return D2D1_BLEND_MODE_OVERLAY; + case CompositionOp::OP_DARKEN: + return D2D1_BLEND_MODE_DARKEN; + case CompositionOp::OP_LIGHTEN: + return D2D1_BLEND_MODE_LIGHTEN; + case CompositionOp::OP_COLOR_DODGE: + return D2D1_BLEND_MODE_COLOR_DODGE; + case CompositionOp::OP_COLOR_BURN: + return D2D1_BLEND_MODE_COLOR_BURN; + case CompositionOp::OP_HARD_LIGHT: + return D2D1_BLEND_MODE_HARD_LIGHT; + case CompositionOp::OP_SOFT_LIGHT: + return D2D1_BLEND_MODE_SOFT_LIGHT; + case CompositionOp::OP_DIFFERENCE: + return D2D1_BLEND_MODE_DIFFERENCE; + case CompositionOp::OP_EXCLUSION: + return D2D1_BLEND_MODE_EXCLUSION; + case CompositionOp::OP_HUE: + return D2D1_BLEND_MODE_HUE; + case CompositionOp::OP_SATURATION: + return D2D1_BLEND_MODE_SATURATION; + case CompositionOp::OP_COLOR: + return D2D1_BLEND_MODE_COLOR; + case CompositionOp::OP_LUMINOSITY: + return D2D1_BLEND_MODE_LUMINOSITY; + default: + return D2D1_BLEND_MODE_MULTIPLY; + } } static inline bool IsPatternSupportedByD2D(const Pattern &aPattern) { - if (aPattern.GetType() != PATTERN_RADIAL_GRADIENT) { + if (aPattern.GetType() != PatternType::RADIAL_GRADIENT) { return true; } @@ -265,7 +385,7 @@ DWriteGlyphRunFromGlyphs(const GlyphBuffer &aGlyphs, ScaledFontDWrite *aFont, Au run->isSideways = FALSE; } -static TemporaryRef<ID2D1Geometry> +static inline TemporaryRef<ID2D1Geometry> ConvertRectToGeometry(const D2D1_RECT_F& aRect) { RefPtr<ID2D1RectangleGeometry> rectGeom; @@ -273,7 +393,7 @@ ConvertRectToGeometry(const D2D1_RECT_F& aRect) return rectGeom.forget(); } -static TemporaryRef<ID2D1Geometry> +static inline TemporaryRef<ID2D1Geometry> GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform) { RefPtr<ID2D1PathGeometry> tmpGeometry; @@ -283,10 +403,10 @@ GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTrans aGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES, aTransform, currentSink); currentSink->Close(); - return tmpGeometry; + return tmpGeometry.forget(); } -static TemporaryRef<ID2D1Geometry> +static inline TemporaryRef<ID2D1Geometry> IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB) { RefPtr<ID2D1PathGeometry> pathGeom; @@ -296,10 +416,10 @@ IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB) aGeometryA->CombineWithGeometry(aGeometryB, D2D1_COMBINE_MODE_INTERSECT, nullptr, sink); sink->Close(); - return pathGeom; + return pathGeom.forget(); } -static TemporaryRef<ID2D1StrokeStyle> +static inline TemporaryRef<ID2D1StrokeStyle> CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions) { RefPtr<ID2D1StrokeStyle> style; @@ -308,35 +428,38 @@ CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions) D2D1_LINE_JOIN joinStyle; switch (aStrokeOptions.mLineCap) { - case CAP_BUTT: + case CapStyle::BUTT: capStyle = D2D1_CAP_STYLE_FLAT; break; - case CAP_ROUND: + case CapStyle::ROUND: capStyle = D2D1_CAP_STYLE_ROUND; break; - case CAP_SQUARE: + case CapStyle::SQUARE: capStyle = D2D1_CAP_STYLE_SQUARE; break; } switch (aStrokeOptions.mLineJoin) { - case JOIN_MITER: + case JoinStyle::MITER: joinStyle = D2D1_LINE_JOIN_MITER; break; - case JOIN_MITER_OR_BEVEL: + case JoinStyle::MITER_OR_BEVEL: joinStyle = D2D1_LINE_JOIN_MITER_OR_BEVEL; break; - case JOIN_ROUND: + case JoinStyle::ROUND: joinStyle = D2D1_LINE_JOIN_ROUND; break; - case JOIN_BEVEL: + case JoinStyle::BEVEL: joinStyle = D2D1_LINE_JOIN_BEVEL; break; } HRESULT hr; - if (aStrokeOptions.mDashPattern) { + // We need to check mDashLength in addition to mDashPattern here since if + // mDashPattern is set but mDashLength is zero then the stroke will fail to + // paint. + if (aStrokeOptions.mDashLength > 0 && aStrokeOptions.mDashPattern) { typedef std::vector<Float> FloatVector; // D2D "helpfully" multiplies the dash pattern by the line width. // That's not what cairo does, or is what <canvas>'s dash wants. @@ -353,7 +476,7 @@ CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions) capStyle, joinStyle, aStrokeOptions.mMiterLimit, D2D1_DASH_STYLE_CUSTOM, - aStrokeOptions.mDashOffset), + aStrokeOptions.mDashOffset / lineWidth), &dash[0], // data() is not C++98, although it's in recent gcc // and VC10's STL dash.size(), @@ -370,16 +493,17 @@ CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions) gfxWarning() << "Failed to create Direct2D stroke style."; } - return style; + return style.forget(); } // This creates a (partially) uploaded bitmap for a DataSourceSurface. It // uploads the minimum requirement and possibly downscales. It adjusts the // input Matrix to compensate. -static TemporaryRef<ID2D1Bitmap> +static inline TemporaryRef<ID2D1Bitmap> CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestinationTransform, const IntSize &aDestinationSize, ExtendMode aExtendMode, - Matrix &aSourceTransform, ID2D1RenderTarget *aRT) + Matrix &aSourceTransform, ID2D1RenderTarget *aRT, + const IntRect* aSourceRect = nullptr) { RefPtr<ID2D1Bitmap> bitmap; @@ -404,6 +528,9 @@ CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestin IntSize size = aSurface->GetSize(); Rect uploadRect(0, 0, Float(size.width), Float(size.height)); + if (aSourceRect) { + uploadRect = Rect(aSourceRect->x, aSourceRect->y, aSourceRect->width, aSourceRect->height); + } // Limit the uploadRect as much as possible without supporting discontiguous uploads // @@ -422,9 +549,9 @@ CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestin // Extend mode is irrelevant, the displayed rect is completely contained // by the source bitmap. uploadRect = rect; - } else if (aExtendMode == EXTEND_CLAMP && uploadRect.Intersects(rect)) { + } else if (aExtendMode == ExtendMode::CLAMP && uploadRect.Intersects(rect)) { // Calculate the rectangle on the source bitmap that touches our - // surface, and upload that, for EXTEND_CLAMP we can actually guarantee + // surface, and upload that, for ExtendMode::CLAMP we can actually guarantee // correct behaviour in this case. uploadRect = uploadRect.Intersect(rect); @@ -451,9 +578,9 @@ CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestin D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())), byRef(bitmap)); - aSourceTransform.Translate(uploadRect.x, uploadRect.y); + aSourceTransform.PreTranslate(uploadRect.x, uploadRect.y); - return bitmap; + return bitmap.forget(); } else { int Bpp = BytesPerPixel(aSurface->GetFormat()); @@ -491,15 +618,19 @@ CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestin scaler.ScaleForSize(scaleSize); IntSize newSize = scaler.GetSize(); + + if (newSize.IsEmpty()) { + return nullptr; + } aRT->CreateBitmap(D2D1::SizeU(newSize.width, newSize.height), scaler.GetScaledData(), scaler.GetStride(), D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())), byRef(bitmap)); - aSourceTransform.Scale(Float(size.width / newSize.width), - Float(size.height / newSize.height)); - return bitmap; + aSourceTransform.PreScale(Float(size.width) / newSize.width, + Float(size.height) / newSize.height); + return bitmap.forget(); } } diff --git a/gfx/2d/HelpersSkia.h b/gfx/2d/HelpersSkia.h index ad7d5ae98..72ad45372 100644 --- a/gfx/2d/HelpersSkia.h +++ b/gfx/2d/HelpersSkia.h @@ -19,24 +19,39 @@ namespace mozilla { namespace gfx { -static inline SkBitmap::Config -GfxFormatToSkiaConfig(SurfaceFormat format) +static inline SkColorType +GfxFormatToSkiaColorType(SurfaceFormat format) { switch (format) { - case FORMAT_B8G8R8A8: - return SkBitmap::kARGB_8888_Config; - case FORMAT_B8G8R8X8: + case SurfaceFormat::B8G8R8A8: + return kBGRA_8888_SkColorType; + case SurfaceFormat::B8G8R8X8: // We probably need to do something here. - return SkBitmap::kARGB_8888_Config; - case FORMAT_R5G6B5: - return SkBitmap::kRGB_565_Config; - case FORMAT_A8: - return SkBitmap::kA8_Config; - + return kBGRA_8888_SkColorType; + case SurfaceFormat::R5G6B5: + return kRGB_565_SkColorType; + case SurfaceFormat::A8: + return kAlpha_8_SkColorType; + default: + return kRGBA_8888_SkColorType; } +} - return SkBitmap::kARGB_8888_Config; +static inline SurfaceFormat +SkiaColorTypeToGfxFormat(SkColorType type) +{ + switch (type) + { + case kBGRA_8888_SkColorType: + return SurfaceFormat::B8G8R8A8; + case kRGB_565_SkColorType: + return SurfaceFormat::R5G6B5; + case kAlpha_8_SkColorType: + return SurfaceFormat::A8; + default: + return SurfaceFormat::B8G8R8A8; + } } #ifdef USE_SKIA_GPU @@ -45,14 +60,14 @@ GfxFormatToGrConfig(SurfaceFormat format) { switch (format) { - case FORMAT_B8G8R8A8: + case SurfaceFormat::B8G8R8A8: return kBGRA_8888_GrPixelConfig; - case FORMAT_B8G8R8X8: + case SurfaceFormat::B8G8R8X8: // We probably need to do something here. return kBGRA_8888_GrPixelConfig; - case FORMAT_R5G6B5: + case SurfaceFormat::R5G6B5: return kRGB_565_GrPixelConfig; - case FORMAT_A8: + case SurfaceFormat::A8: return kAlpha_8_GrPixelConfig; default: return kRGBA_8888_GrPixelConfig; @@ -73,11 +88,11 @@ CapStyleToSkiaCap(CapStyle aCap) { switch (aCap) { - case CAP_BUTT: + case CapStyle::BUTT: return SkPaint::kButt_Cap; - case CAP_ROUND: + case CapStyle::ROUND: return SkPaint::kRound_Cap; - case CAP_SQUARE: + case CapStyle::SQUARE: return SkPaint::kSquare_Cap; } return SkPaint::kDefault_Cap; @@ -88,12 +103,12 @@ JoinStyleToSkiaJoin(JoinStyle aJoin) { switch (aJoin) { - case JOIN_BEVEL: + case JoinStyle::BEVEL: return SkPaint::kBevel_Join; - case JOIN_ROUND: + case JoinStyle::ROUND: return SkPaint::kRound_Join; - case JOIN_MITER: - case JOIN_MITER_OR_BEVEL: + case JoinStyle::MITER: + case JoinStyle::MITER_OR_BEVEL: return SkPaint::kMiter_Join; } return SkPaint::kDefault_Join; @@ -129,9 +144,9 @@ StrokeOptionsToPaint(SkPaint& aPaint, const StrokeOptions &aOptions) pattern[i] = SkFloatToScalar(aOptions.mDashPattern[i % aOptions.mDashLength]); } - SkDashPathEffect* dash = new SkDashPathEffect(&pattern.front(), - dashCount, - SkFloatToScalar(aOptions.mDashOffset)); + SkDashPathEffect* dash = SkDashPathEffect::Create(&pattern.front(), + dashCount, + SkFloatToScalar(aOptions.mDashOffset)); SkSafeUnref(aPaint.setPathEffect(dash)); } @@ -139,86 +154,82 @@ StrokeOptionsToPaint(SkPaint& aPaint, const StrokeOptions &aOptions) return true; } -static inline void -ConvertBGRXToBGRA(unsigned char* aData, const IntSize &aSize, int32_t aStride) -{ - uint32_t* pixel = reinterpret_cast<uint32_t*>(aData); - - for (int row = 0; row < aSize.height; ++row) { - for (int column = 0; column < aSize.width; ++column) { - pixel[column] |= 0xFF000000; - } - pixel += (aStride/4); - } -} - static inline SkXfermode::Mode GfxOpToSkiaOp(CompositionOp op) { switch (op) { - case OP_OVER: + case CompositionOp::OP_OVER: return SkXfermode::kSrcOver_Mode; - case OP_ADD: + case CompositionOp::OP_ADD: return SkXfermode::kPlus_Mode; - case OP_ATOP: + case CompositionOp::OP_ATOP: return SkXfermode::kSrcATop_Mode; - case OP_OUT: + case CompositionOp::OP_OUT: return SkXfermode::kSrcOut_Mode; - case OP_IN: + case CompositionOp::OP_IN: return SkXfermode::kSrcIn_Mode; - case OP_SOURCE: + case CompositionOp::OP_SOURCE: return SkXfermode::kSrc_Mode; - case OP_DEST_IN: + case CompositionOp::OP_DEST_IN: return SkXfermode::kDstIn_Mode; - case OP_DEST_OUT: + case CompositionOp::OP_DEST_OUT: return SkXfermode::kDstOut_Mode; - case OP_DEST_OVER: + case CompositionOp::OP_DEST_OVER: return SkXfermode::kDstOver_Mode; - case OP_DEST_ATOP: + case CompositionOp::OP_DEST_ATOP: return SkXfermode::kDstATop_Mode; - case OP_XOR: + case CompositionOp::OP_XOR: return SkXfermode::kXor_Mode; - case OP_MULTIPLY: + case CompositionOp::OP_MULTIPLY: return SkXfermode::kMultiply_Mode; - case OP_SCREEN: + case CompositionOp::OP_SCREEN: return SkXfermode::kScreen_Mode; - case OP_OVERLAY: + case CompositionOp::OP_OVERLAY: return SkXfermode::kOverlay_Mode; - case OP_DARKEN: + case CompositionOp::OP_DARKEN: return SkXfermode::kDarken_Mode; - case OP_LIGHTEN: + case CompositionOp::OP_LIGHTEN: return SkXfermode::kLighten_Mode; - case OP_COLOR_DODGE: + case CompositionOp::OP_COLOR_DODGE: return SkXfermode::kColorDodge_Mode; - case OP_COLOR_BURN: + case CompositionOp::OP_COLOR_BURN: return SkXfermode::kColorBurn_Mode; - case OP_HARD_LIGHT: + case CompositionOp::OP_HARD_LIGHT: return SkXfermode::kHardLight_Mode; - case OP_SOFT_LIGHT: + case CompositionOp::OP_SOFT_LIGHT: return SkXfermode::kSoftLight_Mode; - case OP_DIFFERENCE: + case CompositionOp::OP_DIFFERENCE: return SkXfermode::kDifference_Mode; - case OP_EXCLUSION: + case CompositionOp::OP_EXCLUSION: return SkXfermode::kExclusion_Mode; - case OP_HUE: + case CompositionOp::OP_HUE: return SkXfermode::kHue_Mode; - case OP_SATURATION: + case CompositionOp::OP_SATURATION: return SkXfermode::kSaturation_Mode; - case OP_COLOR: + case CompositionOp::OP_COLOR: return SkXfermode::kColor_Mode; - case OP_LUMINOSITY: + case CompositionOp::OP_LUMINOSITY: return SkXfermode::kLuminosity_Mode; default: return SkXfermode::kSrcOver_Mode; } } -static inline SkColor ColorToSkColor(const Color &color, Float aAlpha) +/* There's quite a bit of inconsistency about + * whether float colors should be rounded with .5f. + * We choose to do it to match cairo which also + * happens to match the Direct3D specs */ +static inline U8CPU ColorFloatToByte(Float color) { //XXX: do a better job converting to int - return SkColorSetARGB(U8CPU(color.a*aAlpha*255.0), U8CPU(color.r*255.0), - U8CPU(color.g*255.0), U8CPU(color.b*255.0)); + return U8CPU(color*255.f + .5f); +}; + +static inline SkColor ColorToSkColor(const Color &color, Float aAlpha) +{ + return SkColorSetARGB(ColorFloatToByte(color.a*aAlpha), ColorFloatToByte(color.r), + ColorFloatToByte(color.g), ColorFloatToByte(color.b)); } static inline SkRect @@ -248,21 +259,102 @@ IntRectToSkIRect(const IntRect& aRect) return SkIRect::MakeXYWH(aRect.x, aRect.y, aRect.width, aRect.height); } +static inline Point +SkPointToPoint(const SkPoint &aPoint) +{ + return Point(SkScalarToFloat(aPoint.x()), SkScalarToFloat(aPoint.y())); +} + +static inline Rect +SkRectToRect(const SkRect &aRect) +{ + return Rect(SkScalarToFloat(aRect.x()), SkScalarToFloat(aRect.y()), + SkScalarToFloat(aRect.width()), SkScalarToFloat(aRect.height())); +} + static inline SkShader::TileMode ExtendModeToTileMode(ExtendMode aMode) { switch (aMode) { - case EXTEND_CLAMP: + case ExtendMode::CLAMP: return SkShader::kClamp_TileMode; - case EXTEND_REPEAT: + case ExtendMode::REPEAT: return SkShader::kRepeat_TileMode; - case EXTEND_REFLECT: + case ExtendMode::REFLECT: return SkShader::kMirror_TileMode; } return SkShader::kClamp_TileMode; } +// The following class was imported from Skia, which is under the +// following licence: +// +// Copyright (c) 2011 Google Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +template <typename T> class RefPtrSkia { +public: + RefPtrSkia() : fObj(NULL) {} + explicit RefPtrSkia(T* obj) : fObj(obj) { SkSafeRef(fObj); } + RefPtrSkia(const RefPtrSkia& o) : fObj(o.fObj) { SkSafeRef(fObj); } + ~RefPtrSkia() { SkSafeUnref(fObj); } + + RefPtrSkia& operator=(const RefPtrSkia& rp) { + SkRefCnt_SafeAssign(fObj, rp.fObj); + return *this; + } + RefPtrSkia& operator=(T* obj) { + SkRefCnt_SafeAssign(fObj, obj); + return *this; + } + + T* get() const { return fObj; } + T& operator*() const { return *fObj; } + T* operator->() const { return fObj; } + + RefPtrSkia& adopt(T* obj) { + SkSafeUnref(fObj); + fObj = obj; + return *this; + } + + typedef T* RefPtrSkia::*unspecified_bool_type; + operator unspecified_bool_type() const { + return fObj ? &RefPtrSkia::fObj : NULL; + } + +private: + T* fObj; +}; + +// End of code imported from Skia. + } } diff --git a/gfx/2d/ImageScaling.cpp b/gfx/2d/ImageScaling.cpp index 313782e8e..62d08d056 100644 --- a/gfx/2d/ImageScaling.cpp +++ b/gfx/2d/ImageScaling.cpp @@ -5,6 +5,7 @@ #include "ImageScaling.h" #include "2D.h" +#include "DataSurfaceHelpers.h" #include <math.h> #include <algorithm> @@ -77,7 +78,13 @@ ImageHalfScaler::ScaleForSize(const IntSize &aSize) delete [] mDataStorage; // Allocate 15 bytes extra to make sure we can get 16 byte alignment. We // should add tools for this, see bug 751696. - mDataStorage = new uint8_t[internalSurfSize.height * mStride + 15]; + size_t bufLen = BufferSizeFromStrideAndHeight(mStride, internalSurfSize.height, 15); + if (bufLen == 0) { + mSize.SizeTo(0, 0); + mDataStorage = nullptr; + return; + } + mDataStorage = new uint8_t[bufLen]; if (uintptr_t(mDataStorage) % 16) { // Our storage does not start at a 16-byte boundary. Make sure mData does! diff --git a/gfx/2d/ImageScalingSSE2.cpp b/gfx/2d/ImageScalingSSE2.cpp index f87653b4c..92bad43a1 100644 --- a/gfx/2d/ImageScalingSSE2.cpp +++ b/gfx/2d/ImageScalingSSE2.cpp @@ -248,7 +248,7 @@ ImageHalfScaler::HalfImageVertical_SSE2(uint8_t *aSource, int32_t aSourceStride, *storage++ = avg_sse2_4x2_4x1(a, b); } - } else if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) { + } else if (!(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) { for (; x < (aSourceSize.width - 3); x += 4) { uint8_t *upperRow = aSource + (y * aSourceStride + x * 4); uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4); diff --git a/gfx/2d/Logging.h b/gfx/2d/Logging.h index 4911293a2..54803fdfb 100644 --- a/gfx/2d/Logging.h +++ b/gfx/2d/Logging.h @@ -9,57 +9,210 @@ #include <string> #include <sstream> #include <stdio.h> +#include <vector> +#ifdef MOZ_LOGGING +#include <prlog.h> +#endif + +#if defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID) +#include "nsDebug.h" +#endif #include "Point.h" +#include "BaseRect.h" #include "Matrix.h" #ifdef WIN32 -#include <windows.h> +// This file gets included from nsGlobalWindow.cpp, which doesn't like +// having windows.h included in it. Since OutputDebugStringA is the only +// thing we need from windows.h, we just declare it here directly. +// Note: the function's documented signature is +// WINBASEAPI void WINAPI OutputDebugStringA(LPCSTR lpOutputString) +// but if we don't include windows.h, the macros WINBASEAPI, WINAPI, and +// LPCSTR are not defined, so we need to replace them with their expansions. +extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString); #endif -#ifdef PR_LOGGING -#include <prlog.h> - -extern PRLogModuleInfo *GetGFX2DLog(); +#if defined(PR_LOGGING) +extern GFX2D_API PRLogModuleInfo *GetGFX2DLog(); #endif namespace mozilla { namespace gfx { -const int LOG_DEBUG = 1; +// Attempting to be consistent with prlog values, but that isn't critical +// (and note that 5 has a special meaning - see the description +// with sGfxLogLevel) +const int LOG_CRITICAL = 1; const int LOG_WARNING = 2; +const int LOG_DEBUG = 3; +const int LOG_DEBUG_PRLOG = 4; +const int LOG_EVERYTHING = 5; // This needs to be the highest value -#ifdef PR_LOGGING +#if defined(DEBUG) +const int LOG_DEFAULT = LOG_EVERYTHING; +#else +const int LOG_DEFAULT = LOG_CRITICAL; +#endif + +#if defined(PR_LOGGING) inline PRLogModuleLevel PRLogLevelForLevel(int aLevel) { switch (aLevel) { - case LOG_DEBUG: - return PR_LOG_DEBUG; + case LOG_CRITICAL: + return PR_LOG_ERROR; case LOG_WARNING: return PR_LOG_WARNING; + case LOG_DEBUG: + return PR_LOG_DEBUG; + case LOG_DEBUG_PRLOG: + return PR_LOG_DEBUG; + case LOG_EVERYTHING: + return PR_LOG_ALWAYS; } return PR_LOG_DEBUG; } #endif -extern int sGfxLogLevel; +class PreferenceAccess +{ +public: + virtual ~PreferenceAccess(); + + // This should connect the variable aVar to be updated whenever a preference + // aName is modified. aDefault would be used if the preference is undefined, + // so that we always get the valid value for aVar. + virtual void LivePref(const char* aName, int32_t* aVar, int32_t aDefault); -static inline void OutputMessage(const std::string &aString, int aLevel) { -#if defined(WIN32) && !defined(PR_LOGGING) - if (aLevel >= sGfxLogLevel) { - ::OutputDebugStringA(aString.c_str()); +public: + static void SetAccess(PreferenceAccess* aAccess); + +public: + // For each preference that needs to be accessed in Moz2D, add a variable + // to hold it, as well as the call to LivePref in the RegisterAll() method + // below. + + // Used to choose the level of logging we get. The higher the number, + // the more logging we get. Value of zero will give you no logging, + // 1 just errors, 2 adds warnings and 3 adds logging/debug. 4 is used to + // selectively enable logging on the configurations that + // support prlog (on other systems, 3 and 4 are the same.) For prlog, + // in addition to setting the value to 4, you will need to set an + // environment variable NSPR_LOG_MODULES to gfx:4. See prlog.h for details. + static int32_t sGfxLogLevel; + +private: + static void RegisterAll() { + // The default values (last parameter) should match the initialization + // values in Factory.cpp, otherwise the standalone Moz2D will get different + // defaults. + sAccess->LivePref("gfx.logging.level", &sGfxLogLevel, LOG_DEFAULT); } + static PreferenceAccess* sAccess; +}; + +/// Graphics logging is available in both debug and release builds and is +/// controlled with a gfx.logging.level preference. If not set, the default +/// for the preference is 5 in the debug builds, 1 in the release builds. +/// +/// gfxDebug only works in the debug builds, and is used for information +/// level messages, helping with debugging. In addition to only working +/// in the debug builds, the value of the above preference of 3 or higher +/// is required. +/// +/// gfxWarning messages are available in both debug and release builds, +/// on by default in the debug builds, and off by default in the release builds. +/// Setting the preference gfx.logging.level to a value of 2 or higher will +/// show the warnings. +/// +/// gfxCriticalError is available in debug and release builds by default. +/// It is only unavailable if gfx.logging.level is set to 0 (or less.) +/// It outputs the message to stderr or equivalent, like gfxWarning. +/// In the event of a crash, the crash report is annotated with first and +/// the last few of these errors, under the key GraphicsCriticalError. +/// The total number of errors stored in the crash report is controlled +/// by preference gfx.logging.crash.length (default is six, so by default, +/// the first as well as the last five would show up in the crash log.) +/// +/// On platforms that support PR_LOGGING, the story is slightly more involved. +/// In that case, unless gfx.logging.level is set to 4 or higher, the output +/// is further controlled by "gfx2d" PR logging module. However, in the case +/// where such module would disable the output, in all but gfxDebug cases, +/// we will still send a printf. +struct BasicLogger +{ + // For efficiency, this method exists and copies the logic of the + // OutputMessage below. If making any changes here, also make it + // in the appropriate places in that method. + static bool ShouldOutputMessage(int aLevel) { + if (PreferenceAccess::sGfxLogLevel >= aLevel) { +#if defined(WIN32) && !defined(PR_LOGGING) + return true; +#elif defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID) + return true; #elif defined(PR_LOGGING) - if (PR_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) { - PR_LogPrint(aString.c_str()); - } + if (PR_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) { + return true; + } else if ((PreferenceAccess::sGfxLogLevel >= LOG_DEBUG_PRLOG) || + (aLevel < LOG_DEBUG)) { + return true; + } #else - if (aLevel >= sGfxLogLevel) { - printf("%s", aString.c_str()); + return true; +#endif + } + return false; } + + static void OutputMessage(const std::string &aString, + int aLevel, + bool aNoNewline) { + // This behavior (the higher the preference, the more we log) + // is consistent with what prlog does in general. Note that if prlog + // is in the build, but disabled, we will printf if the preferences + // requires us to log something (see sGfxLogLevel for the special + // treatment of LOG_DEBUG and LOG_DEBUG_PRLOG) + // + // If making any logic changes to this method, you should probably + // make the corresponding change in the ShouldOutputMessage method + // above. + if (PreferenceAccess::sGfxLogLevel >= aLevel) { +#if defined(WIN32) && !defined(PR_LOGGING) + ::OutputDebugStringA((aNoNewline ? aString : aString+"\n").c_str()); +#elif defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID) + printf_stderr("%s%s", aString.c_str(), aNoNewline ? "" : "\n"); +#elif defined(PR_LOGGING) + if (PR_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) { + PR_LogPrint("%s%s", aString.c_str(), aNoNewline ? "" : "\n"); + } else if ((PreferenceAccess::sGfxLogLevel >= LOG_DEBUG_PRLOG) || + (aLevel < LOG_DEBUG)) { + printf("%s%s", aString.c_str(), aNoNewline ? "" : "\n"); + } +#else + printf("%s%s", aString.c_str(), aNoNewline ? "" : "\n"); #endif -} + } + } +}; + +struct CriticalLogger { + static void OutputMessage(const std::string &aString, int aLevel, bool aNoNewline); +}; + +// Implement this interface and init the Factory with an instance to +// forward critical logs. +class LogForwarder { +public: + virtual ~LogForwarder() {} + virtual void Log(const std::string &aString) = 0; + + // Provide a copy of the logs to the caller. The int is the index + // of the Log call, if the number of logs exceeds some preset capacity + // we may not get all of them, so the indices help figure out which + // ones we did save. + virtual std::vector<std::pair<int32_t,std::string> > StringsVectorCopy() = 0; +}; class NoLog { @@ -71,45 +224,402 @@ public: NoLog &operator <<(const T &aLogText) { return *this; } }; -template<int L> +enum class LogOptions : int { + NoNewline = 0x01, + AutoPrefix = 0x02, + AssertOnCall = 0x04 +}; + +template<typename T> +struct Hexa { + explicit Hexa(T aVal) : mVal(aVal) {} + T mVal; +}; +template<typename T> +Hexa<T> hexa(T val) { return Hexa<T>(val); } + +template<int L, typename Logger = BasicLogger> class Log { public: - Log() {} - ~Log() { mMessage << '\n'; WriteLog(mMessage.str()); } + // The default is to have the prefix, have the new line, and for critical + // logs assert on each call. + static int DefaultOptions(bool aWithAssert = true) { + return (int(LogOptions::AutoPrefix) | + (aWithAssert ? int(LogOptions::AssertOnCall) : 0)); + } - Log &operator <<(const std::string &aLogText) { mMessage << aLogText; return *this; } - Log &operator <<(unsigned int aInt) { mMessage << aInt; return *this; } - Log &operator <<(const Size &aSize) - { mMessage << "(" << aSize.width << "x" << aSize.height << ")"; return *this; } - Log &operator <<(const IntSize &aSize) - { mMessage << "(" << aSize.width << "x" << aSize.height << ")"; return *this; } - Log &operator<<(const Matrix& aMatrix) - { mMessage << "[ " << aMatrix._11 << " " << aMatrix._12 << " ; " << aMatrix._21 << " " << aMatrix._22 << " ; " << aMatrix._31 << " " << aMatrix._32 << " ]"; return *this; } + // Note that we're calling BasicLogger::ShouldOutputMessage, rather than + // Logger::ShouldOutputMessage. Since we currently don't have a different + // version of that method for different loggers, this is OK. Once we do, + // change BasicLogger::ShouldOutputMessage to Logger::ShouldOutputMessage. + explicit Log(int aOptions = Log::DefaultOptions(L == LOG_CRITICAL)) + : mOptions(aOptions) + , mLogIt(BasicLogger::ShouldOutputMessage(L)) + { + if (mLogIt && AutoPrefix()) { + if (mOptions & int(LogOptions::AssertOnCall)) { + mMessage << "[GFX" << L << "]: "; + } else { + mMessage << "[GFX" << L << "-]: "; + } + } + } + ~Log() { + Flush(); + } + void Flush() { + if (MOZ_LIKELY(!LogIt())) return; -private: + std::string str = mMessage.str(); + if (!str.empty()) { + WriteLog(str); + } + if (AutoPrefix()) { + mMessage.str("[GFX"); + mMessage << L << "]: "; + } else { + mMessage.str(""); + } + mMessage.clear(); + } + Log &operator <<(char aChar) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << aChar; + } + return *this; + } + Log &operator <<(const std::string &aLogText) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << aLogText; + } + return *this; + } + Log &operator <<(const char aStr[]) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << static_cast<const char*>(aStr); + } + return *this; + } + Log &operator <<(bool aBool) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << (aBool ? "true" : "false"); + } + return *this; + } + Log &operator <<(int aInt) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << aInt; + } + return *this; + } + Log &operator <<(unsigned int aInt) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << aInt; + } + return *this; + } + Log &operator <<(long aLong) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << aLong; + } + return *this; + } + Log &operator <<(unsigned long aLong) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << aLong; + } + return *this; + } + Log &operator <<(long long aLong) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << aLong; + } + return *this; + } + Log &operator <<(unsigned long long aLong) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << aLong; + } + return *this; + } + Log &operator <<(Float aFloat) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << aFloat; + } + return *this; + } + Log &operator <<(double aDouble) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << aDouble; + } + return *this; + } + template <typename T, typename Sub, typename Coord> + Log &operator <<(const BasePoint<T, Sub, Coord>& aPoint) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << "Point" << aPoint; + } + return *this; + } + template <typename T, typename Sub> + Log &operator <<(const BaseSize<T, Sub>& aSize) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << "Size(" << aSize.width << "," << aSize.height << ")"; + } + return *this; + } + template <typename T, typename Sub, typename Point, typename SizeT, typename Margin> + Log &operator <<(const BaseRect<T, Sub, Point, SizeT, Margin>& aRect) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << "Rect" << aRect; + } + return *this; + } + Log &operator<<(const Matrix& aMatrix) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << "Matrix(" << aMatrix._11 << " " << aMatrix._12 << " ; " << aMatrix._21 << " " << aMatrix._22 << " ; " << aMatrix._31 << " " << aMatrix._32 << ")"; + } + return *this; + } + template<typename T> + Log &operator<<(Hexa<T> aHex) { + if (MOZ_UNLIKELY(LogIt())) { + mMessage << "0x" << std::hex << aHex.mVal << std::dec; + } + return *this; + } + + Log& operator<<(SurfaceFormat aFormat) { + if (MOZ_UNLIKELY(LogIt())) { + switch(aFormat) { + case SurfaceFormat::B8G8R8A8: + mMessage << "SurfaceFormat::B8G8R8A8"; + break; + case SurfaceFormat::B8G8R8X8: + mMessage << "SurfaceFormat::B8G8R8X8"; + break; + case SurfaceFormat::R8G8B8A8: + mMessage << "SurfaceFormat::R8G8B8A8"; + break; + case SurfaceFormat::R8G8B8X8: + mMessage << "SurfaceFormat::R8G8B8X8"; + break; + case SurfaceFormat::R5G6B5: + mMessage << "SurfaceFormat::R5G6B5"; + break; + case SurfaceFormat::A8: + mMessage << "SurfaceFormat::A8"; + break; + case SurfaceFormat::YUV: + mMessage << "SurfaceFormat::YUV"; + break; + case SurfaceFormat::UNKNOWN: + mMessage << "SurfaceFormat::UNKNOWN"; + break; + default: + mMessage << "Invalid SurfaceFormat (" << (int)aFormat << ")"; + break; + } + } + return *this; + } + + Log& operator<<(SurfaceType aType) { + if (MOZ_UNLIKELY(LogIt())) { + switch(aType) { + case SurfaceType::DATA: + mMessage << "SurfaceType::DATA"; + break; + case SurfaceType::D2D1_BITMAP: + mMessage << "SurfaceType::D2D1_BITMAP"; + break; + case SurfaceType::D2D1_DRAWTARGET: + mMessage << "SurfaceType::D2D1_DRAWTARGET"; + break; + case SurfaceType::CAIRO: + mMessage << "SurfaceType::CAIRO"; + break; + case SurfaceType::CAIRO_IMAGE: + mMessage << "SurfaceType::CAIRO_IMAGE"; + break; + case SurfaceType::COREGRAPHICS_IMAGE: + mMessage << "SurfaceType::COREGRAPHICS_IMAGE"; + break; + case SurfaceType::COREGRAPHICS_CGCONTEXT: + mMessage << "SurfaceType::COREGRAPHICS_CGCONTEXT"; + break; + case SurfaceType::SKIA: + mMessage << "SurfaceType::SKIA"; + break; + case SurfaceType::DUAL_DT: + mMessage << "SurfaceType::DUAL_DT"; + break; + case SurfaceType::D2D1_1_IMAGE: + mMessage << "SurfaceType::D2D1_1_IMAGE"; + break; + case SurfaceType::RECORDING: + mMessage << "SurfaceType::RECORDING"; + break; + case SurfaceType::TILED: + mMessage << "SurfaceType::TILED"; + break; + default: + mMessage << "Invalid SurfaceType (" << (int)aType << ")"; + break; + } + } + return *this; + } + + inline bool LogIt() const { return mLogIt; } + inline bool NoNewline() const { return mOptions & int(LogOptions::NoNewline); } + inline bool AutoPrefix() const { return mOptions & int(LogOptions::AutoPrefix); } + + +private: void WriteLog(const std::string &aString) { - OutputMessage(aString, L); + if (MOZ_UNLIKELY(LogIt())) { + Logger::OutputMessage(aString, L, NoNewline()); + if (mOptions & int(LogOptions::AssertOnCall)) { + MOZ_ASSERT(false, "An assert from the graphics logger"); + } + } } std::stringstream mMessage; + int mOptions; + bool mLogIt; }; typedef Log<LOG_DEBUG> DebugLog; typedef Log<LOG_WARNING> WarningLog; +typedef Log<LOG_CRITICAL, CriticalLogger> CriticalLog; #ifdef GFX_LOG_DEBUG -#define gfxDebug DebugLog +#define gfxDebug mozilla::gfx::DebugLog #else -#define gfxDebug if (1) ; else NoLog +#define gfxDebug if (1) ; else mozilla::gfx::NoLog #endif #ifdef GFX_LOG_WARNING -#define gfxWarning WarningLog +#define gfxWarning mozilla::gfx::WarningLog +#else +#define gfxWarning if (1) ; else mozilla::gfx::NoLog +#endif + +// This log goes into crash reports, use with care. +#define gfxCriticalError mozilla::gfx::CriticalLog + +// See nsDebug.h and the NS_WARN_IF macro + +#ifdef __cplusplus + // For now, have MOZ2D_ERROR_IF available in debug and non-debug builds +inline bool MOZ2D_error_if_impl(bool aCondition, const char* aExpr, + const char* aFile, int32_t aLine) +{ + if (MOZ_UNLIKELY(aCondition)) { + gfxCriticalError() << aExpr << " at " << aFile << ":" << aLine; + } + return aCondition; +} +#define MOZ2D_ERROR_IF(condition) \ + MOZ2D_error_if_impl(condition, #condition, __FILE__, __LINE__) + +#ifdef DEBUG +inline bool MOZ2D_warn_if_impl(bool aCondition, const char* aExpr, + const char* aFile, int32_t aLine) +{ + if (MOZ_UNLIKELY(aCondition)) { + gfxWarning() << aExpr << " at " << aFile << ":" << aLine; + } + return aCondition; +} +#define MOZ2D_WARN_IF(condition) \ + MOZ2D_warn_if_impl(condition, #condition, __FILE__, __LINE__) #else -#define gfxWarning if (1) ; else NoLog +#define MOZ2D_WARN_IF(condition) (bool)(condition) #endif +#endif + +const int INDENT_PER_LEVEL = 2; + +class TreeLog +{ +public: + explicit TreeLog(const std::string& aPrefix = "") + : mLog(int(LogOptions::NoNewline)), + mPrefix(aPrefix), + mDepth(0), + mStartOfLine(true), + mConditionedOnPref(false), + mPrefFunction(nullptr) {} + + template <typename T> + TreeLog& operator<<(const T& aObject) { + if (mConditionedOnPref && !mPrefFunction()) { + return *this; + } + if (mStartOfLine) { + mLog << '[' << mPrefix << "] " << std::string(mDepth * INDENT_PER_LEVEL, ' '); + mStartOfLine = false; + } + mLog << aObject; + if (EndsInNewline(aObject)) { + // Don't indent right here as the user may change the indent + // between now and the first output to the next line. + mLog.Flush(); + mStartOfLine = true; + } + return *this; + } + + void IncreaseIndent() { ++mDepth; } + void DecreaseIndent() { --mDepth; } + + void ConditionOnPrefFunction(bool(*aPrefFunction)()) { + mConditionedOnPref = true; + mPrefFunction = aPrefFunction; + } +private: + Log<LOG_DEBUG> mLog; + std::string mPrefix; + uint32_t mDepth; + bool mStartOfLine; + bool mConditionedOnPref; + bool (*mPrefFunction)(); + + template <typename T> + static bool EndsInNewline(const T& aObject) { + return false; + } + + static bool EndsInNewline(const std::string& aString) { + return !aString.empty() && aString[aString.length() - 1] == '\n'; + } + + static bool EndsInNewline(char aChar) { + return aChar == '\n'; + } + + static bool EndsInNewline(const char* aString) { + return EndsInNewline(std::string(aString)); + } +}; + +class TreeAutoIndent +{ +public: + explicit TreeAutoIndent(TreeLog& aTreeLog) : mTreeLog(aTreeLog) { + mTreeLog.IncreaseIndent(); + } + ~TreeAutoIndent() { + mTreeLog.DecreaseIndent(); + } +private: + TreeLog& mTreeLog; +}; } } diff --git a/gfx/2d/MacIOSurface.cpp b/gfx/2d/MacIOSurface.cpp new file mode 100644 index 000000000..8d0696685 --- /dev/null +++ b/gfx/2d/MacIOSurface.cpp @@ -0,0 +1,536 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:set ts=2 sts=2 sw=2 et cin: +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MacIOSurface.h" +#include <OpenGL/gl.h> +#include <QuartzCore/QuartzCore.h> +#include <dlfcn.h> +#include "mozilla/RefPtr.h" +#include "mozilla/Assertions.h" + +using namespace mozilla; +// IOSurface signatures +#define IOSURFACE_FRAMEWORK_PATH \ + "/System/Library/Frameworks/IOSurface.framework/IOSurface" +#define OPENGL_FRAMEWORK_PATH \ + "/System/Library/Frameworks/OpenGL.framework/OpenGL" +#define COREGRAPHICS_FRAMEWORK_PATH \ + "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/" \ + "CoreGraphics.framework/CoreGraphics" +#define COREVIDEO_FRAMEWORK_PATH \ + "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/" \ + "CoreVideo.framework/CoreVideo" + +#define GET_CONST(const_name) \ + ((CFStringRef*) dlsym(sIOSurfaceFramework, const_name)) +#define GET_IOSYM(dest,sym_name) \ + (typeof(dest)) dlsym(sIOSurfaceFramework, sym_name) +#define GET_CGLSYM(dest,sym_name) \ + (typeof(dest)) dlsym(sOpenGLFramework, sym_name) +#define GET_CGSYM(dest,sym_name) \ + (typeof(dest)) dlsym(sCoreGraphicsFramework, sym_name) +#define GET_CVSYM(dest, sym_name) \ + (typeof(dest)) dlsym(sCoreVideoFramework, sym_name) + +MacIOSurfaceLib::LibraryUnloader MacIOSurfaceLib::sLibraryUnloader; +bool MacIOSurfaceLib::isLoaded = false; +void* MacIOSurfaceLib::sIOSurfaceFramework; +void* MacIOSurfaceLib::sOpenGLFramework; +void* MacIOSurfaceLib::sCoreGraphicsFramework; +void* MacIOSurfaceLib::sCoreVideoFramework; +IOSurfaceCreateFunc MacIOSurfaceLib::sCreate; +IOSurfaceGetIDFunc MacIOSurfaceLib::sGetID; +IOSurfaceLookupFunc MacIOSurfaceLib::sLookup; +IOSurfaceGetBaseAddressFunc MacIOSurfaceLib::sGetBaseAddress; +IOSurfaceGetBaseAddressOfPlaneFunc MacIOSurfaceLib::sGetBaseAddressOfPlane; +IOSurfaceSizeTFunc MacIOSurfaceLib::sWidth; +IOSurfaceSizeTFunc MacIOSurfaceLib::sHeight; +IOSurfaceSizeTFunc MacIOSurfaceLib::sPlaneCount; +IOSurfaceSizeTFunc MacIOSurfaceLib::sBytesPerRow; +IOSurfaceGetPropertyMaximumFunc MacIOSurfaceLib::sGetPropertyMaximum; +IOSurfaceVoidFunc MacIOSurfaceLib::sIncrementUseCount; +IOSurfaceVoidFunc MacIOSurfaceLib::sDecrementUseCount; +IOSurfaceLockFunc MacIOSurfaceLib::sLock; +IOSurfaceUnlockFunc MacIOSurfaceLib::sUnlock; +CGLTexImageIOSurface2DFunc MacIOSurfaceLib::sTexImage; +IOSurfaceContextCreateFunc MacIOSurfaceLib::sIOSurfaceContextCreate; +IOSurfaceContextCreateImageFunc MacIOSurfaceLib::sIOSurfaceContextCreateImage; +IOSurfaceContextGetSurfaceFunc MacIOSurfaceLib::sIOSurfaceContextGetSurface; +CVPixelBufferGetIOSurfaceFunc MacIOSurfaceLib::sCVPixelBufferGetIOSurface; +unsigned int (*MacIOSurfaceLib::sCGContextGetTypePtr) (CGContextRef) = nullptr; + +CFStringRef MacIOSurfaceLib::kPropWidth; +CFStringRef MacIOSurfaceLib::kPropHeight; +CFStringRef MacIOSurfaceLib::kPropBytesPerElem; +CFStringRef MacIOSurfaceLib::kPropBytesPerRow; +CFStringRef MacIOSurfaceLib::kPropIsGlobal; + +bool MacIOSurfaceLib::isInit() { + // Guard against trying to reload the library + // if it is not available. + if (!isLoaded) + LoadLibrary(); + MOZ_ASSERT(sIOSurfaceFramework); + return sIOSurfaceFramework; +} + +IOSurfacePtr MacIOSurfaceLib::IOSurfaceCreate(CFDictionaryRef properties) { + return sCreate(properties); +} + +IOSurfacePtr MacIOSurfaceLib::IOSurfaceLookup(IOSurfaceID aIOSurfaceID) { + return sLookup(aIOSurfaceID); +} + +IOSurfaceID MacIOSurfaceLib::IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr) { + return sGetID(aIOSurfacePtr); +} + +void* MacIOSurfaceLib::IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr) { + return sGetBaseAddress(aIOSurfacePtr); +} + +void* MacIOSurfaceLib::IOSurfaceGetBaseAddressOfPlane(IOSurfacePtr aIOSurfacePtr, + size_t planeIndex) { + return sGetBaseAddressOfPlane(aIOSurfacePtr, planeIndex); +} + +size_t MacIOSurfaceLib::IOSurfaceGetPlaneCount(IOSurfacePtr aIOSurfacePtr) { + return sPlaneCount(aIOSurfacePtr); +} + +size_t MacIOSurfaceLib::IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr) { + return sWidth(aIOSurfacePtr); +} + +size_t MacIOSurfaceLib::IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr) { + return sHeight(aIOSurfacePtr); +} + +size_t MacIOSurfaceLib::IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr) { + return sBytesPerRow(aIOSurfacePtr); +} + +size_t MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(CFStringRef property) { + return sGetPropertyMaximum(property); +} + +IOReturn MacIOSurfaceLib::IOSurfaceLock(IOSurfacePtr aIOSurfacePtr, + uint32_t options, uint32_t* seed) { + return sLock(aIOSurfacePtr, options, seed); +} + +IOReturn MacIOSurfaceLib::IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr, + uint32_t options, uint32_t *seed) { + return sUnlock(aIOSurfacePtr, options, seed); +} + +void MacIOSurfaceLib::IOSurfaceIncrementUseCount(IOSurfacePtr aIOSurfacePtr) { + sIncrementUseCount(aIOSurfacePtr); +} + +void MacIOSurfaceLib::IOSurfaceDecrementUseCount(IOSurfacePtr aIOSurfacePtr) { + sDecrementUseCount(aIOSurfacePtr); +} + +CGLError MacIOSurfaceLib::CGLTexImageIOSurface2D(CGLContextObj ctxt, + GLenum target, GLenum internalFormat, + GLsizei width, GLsizei height, + GLenum format, GLenum type, + IOSurfacePtr ioSurface, GLuint plane) { + return sTexImage(ctxt, target, internalFormat, width, height, + format, type, ioSurface, plane); +} + +IOSurfacePtr MacIOSurfaceLib::CVPixelBufferGetIOSurface(CVPixelBufferRef aPixelBuffer) { + return sCVPixelBufferGetIOSurface(aPixelBuffer); +} + +CGContextRef MacIOSurfaceLib::IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr, + unsigned aWidth, unsigned aHeight, + unsigned aBitsPerComponent, unsigned aBytes, + CGColorSpaceRef aColorSpace, CGBitmapInfo bitmapInfo) { + if (!sIOSurfaceContextCreate) + return nullptr; + return sIOSurfaceContextCreate(aIOSurfacePtr, aWidth, aHeight, aBitsPerComponent, aBytes, aColorSpace, bitmapInfo); +} + +CGImageRef MacIOSurfaceLib::IOSurfaceContextCreateImage(CGContextRef aContext) { + if (!sIOSurfaceContextCreateImage) + return nullptr; + return sIOSurfaceContextCreateImage(aContext); +} + +IOSurfacePtr MacIOSurfaceLib::IOSurfaceContextGetSurface(CGContextRef aContext) { + if (!sIOSurfaceContextGetSurface) + return nullptr; + return sIOSurfaceContextGetSurface(aContext); +} + +CFStringRef MacIOSurfaceLib::GetIOConst(const char* symbole) { + CFStringRef *address = (CFStringRef*)dlsym(sIOSurfaceFramework, symbole); + if (!address) + return nullptr; + + return *address; +} + +void MacIOSurfaceLib::LoadLibrary() { + if (isLoaded) { + return; + } + isLoaded = true; + sIOSurfaceFramework = dlopen(IOSURFACE_FRAMEWORK_PATH, + RTLD_LAZY | RTLD_LOCAL); + sOpenGLFramework = dlopen(OPENGL_FRAMEWORK_PATH, + RTLD_LAZY | RTLD_LOCAL); + + sCoreGraphicsFramework = dlopen(COREGRAPHICS_FRAMEWORK_PATH, + RTLD_LAZY | RTLD_LOCAL); + + sCoreVideoFramework = dlopen(COREVIDEO_FRAMEWORK_PATH, + RTLD_LAZY | RTLD_LOCAL); + + if (!sIOSurfaceFramework || !sOpenGLFramework || !sCoreGraphicsFramework || + !sCoreVideoFramework) { + if (sIOSurfaceFramework) + dlclose(sIOSurfaceFramework); + if (sOpenGLFramework) + dlclose(sOpenGLFramework); + if (sCoreGraphicsFramework) + dlclose(sCoreGraphicsFramework); + if (sCoreVideoFramework) + dlclose(sCoreVideoFramework); + sIOSurfaceFramework = nullptr; + sOpenGLFramework = nullptr; + sCoreGraphicsFramework = nullptr; + sCoreVideoFramework = nullptr; + return; + } + + kPropWidth = GetIOConst("kIOSurfaceWidth"); + kPropHeight = GetIOConst("kIOSurfaceHeight"); + kPropBytesPerElem = GetIOConst("kIOSurfaceBytesPerElement"); + kPropBytesPerRow = GetIOConst("kIOSurfaceBytesPerRow"); + kPropIsGlobal = GetIOConst("kIOSurfaceIsGlobal"); + sCreate = GET_IOSYM(sCreate, "IOSurfaceCreate"); + sGetID = GET_IOSYM(sGetID, "IOSurfaceGetID"); + sWidth = GET_IOSYM(sWidth, "IOSurfaceGetWidth"); + sHeight = GET_IOSYM(sHeight, "IOSurfaceGetHeight"); + sBytesPerRow = GET_IOSYM(sBytesPerRow, "IOSurfaceGetBytesPerRow"); + sGetPropertyMaximum = GET_IOSYM(sGetPropertyMaximum, "IOSurfaceGetPropertyMaximum"); + sLookup = GET_IOSYM(sLookup, "IOSurfaceLookup"); + sLock = GET_IOSYM(sLock, "IOSurfaceLock"); + sUnlock = GET_IOSYM(sUnlock, "IOSurfaceUnlock"); + sIncrementUseCount = + GET_IOSYM(sIncrementUseCount, "IOSurfaceIncrementUseCount"); + sDecrementUseCount = + GET_IOSYM(sDecrementUseCount, "IOSurfaceDecrementUseCount"); + sGetBaseAddress = GET_IOSYM(sGetBaseAddress, "IOSurfaceGetBaseAddress"); + sGetBaseAddressOfPlane = + GET_IOSYM(sGetBaseAddressOfPlane, "IOSurfaceGetBaseAddressOfPlane"); + sPlaneCount = GET_IOSYM(sPlaneCount, "IOSurfaceGetPlaneCount"); + + sTexImage = GET_CGLSYM(sTexImage, "CGLTexImageIOSurface2D"); + sCGContextGetTypePtr = (unsigned int (*)(CGContext*))dlsym(RTLD_DEFAULT, "CGContextGetType"); + + sCVPixelBufferGetIOSurface = + GET_CVSYM(sCVPixelBufferGetIOSurface, "CVPixelBufferGetIOSurface"); + + // Optional symbols + sIOSurfaceContextCreate = GET_CGSYM(sIOSurfaceContextCreate, "CGIOSurfaceContextCreate"); + sIOSurfaceContextCreateImage = GET_CGSYM(sIOSurfaceContextCreateImage, "CGIOSurfaceContextCreateImage"); + sIOSurfaceContextGetSurface = GET_CGSYM(sIOSurfaceContextGetSurface, "CGIOSurfaceContextGetSurface"); + + if (!sCreate || !sGetID || !sLookup || !sTexImage || !sGetBaseAddress || + !sGetBaseAddressOfPlane || !sPlaneCount || + !kPropWidth || !kPropHeight || !kPropBytesPerElem || !kPropIsGlobal || + !sLock || !sUnlock || !sIncrementUseCount || !sDecrementUseCount || + !sWidth || !sHeight || !kPropBytesPerRow || + !sBytesPerRow || !sGetPropertyMaximum || !sCVPixelBufferGetIOSurface) { + CloseLibrary(); + } +} + +void MacIOSurfaceLib::CloseLibrary() { + if (sIOSurfaceFramework) { + dlclose(sIOSurfaceFramework); + } + if (sOpenGLFramework) { + dlclose(sOpenGLFramework); + } + if (sCoreVideoFramework) { + dlclose(sCoreVideoFramework); + } + sIOSurfaceFramework = nullptr; + sOpenGLFramework = nullptr; + sCoreVideoFramework = nullptr; +} + +MacIOSurface::MacIOSurface(const void* aIOSurfacePtr, + double aContentsScaleFactor, bool aHasAlpha) + : mIOSurfacePtr(aIOSurfacePtr) + , mContentsScaleFactor(aContentsScaleFactor) + , mHasAlpha(aHasAlpha) +{ + CFRetain(mIOSurfacePtr); + IncrementUseCount(); +} + +MacIOSurface::~MacIOSurface() { + DecrementUseCount(); + CFRelease(mIOSurfacePtr); +} + +TemporaryRef<MacIOSurface> MacIOSurface::CreateIOSurface(int aWidth, int aHeight, + double aContentsScaleFactor, + bool aHasAlpha) { + if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0) + return nullptr; + + CFMutableDictionaryRef props = ::CFDictionaryCreateMutable( + kCFAllocatorDefault, 4, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (!props) + return nullptr; + + MOZ_ASSERT((size_t)aWidth <= GetMaxWidth()); + MOZ_ASSERT((size_t)aHeight <= GetMaxHeight()); + + int32_t bytesPerElem = 4; + size_t intScaleFactor = ceil(aContentsScaleFactor); + aWidth *= intScaleFactor; + aHeight *= intScaleFactor; + CFNumberRef cfWidth = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aWidth); + CFNumberRef cfHeight = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aHeight); + CFNumberRef cfBytesPerElem = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &bytesPerElem); + ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropWidth, + cfWidth); + ::CFRelease(cfWidth); + ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropHeight, + cfHeight); + ::CFRelease(cfHeight); + ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropBytesPerElem, + cfBytesPerElem); + ::CFRelease(cfBytesPerElem); + ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropIsGlobal, + kCFBooleanTrue); + + IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceCreate(props); + ::CFRelease(props); + + if (!surfaceRef) + return nullptr; + + RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor, aHasAlpha); + if (!ioSurface) { + ::CFRelease(surfaceRef); + return nullptr; + } + + // Release the IOSurface because MacIOSurface retained it + CFRelease(surfaceRef); + + return ioSurface.forget(); +} + +TemporaryRef<MacIOSurface> MacIOSurface::LookupSurface(IOSurfaceID aIOSurfaceID, + double aContentsScaleFactor, + bool aHasAlpha) { + if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0) + return nullptr; + + IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceLookup(aIOSurfaceID); + if (!surfaceRef) + return nullptr; + + RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor, aHasAlpha); + if (!ioSurface) { + ::CFRelease(surfaceRef); + return nullptr; + } + + // Release the IOSurface because MacIOSurface retained it + CFRelease(surfaceRef); + + return ioSurface.forget(); +} + +IOSurfaceID MacIOSurface::GetIOSurfaceID() { + return MacIOSurfaceLib::IOSurfaceGetID(mIOSurfacePtr); +} + +void* MacIOSurface::GetBaseAddress() { + return MacIOSurfaceLib::IOSurfaceGetBaseAddress(mIOSurfacePtr); +} + +void* MacIOSurface::GetBaseAddressOfPlane(size_t aPlaneIndex) +{ + return MacIOSurfaceLib::IOSurfaceGetBaseAddressOfPlane(mIOSurfacePtr, + aPlaneIndex); +} + +size_t MacIOSurface::GetWidth() { + size_t intScaleFactor = ceil(mContentsScaleFactor); + return GetDevicePixelWidth() / intScaleFactor; +} + +size_t MacIOSurface::GetHeight() { + size_t intScaleFactor = ceil(mContentsScaleFactor); + return GetDevicePixelHeight() / intScaleFactor; +} + +size_t MacIOSurface::GetPlaneCount() { + return MacIOSurfaceLib::IOSurfaceGetPlaneCount(mIOSurfacePtr); +} + +/*static*/ size_t MacIOSurface::GetMaxWidth() { + if (!MacIOSurfaceLib::isInit()) + return -1; + return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropWidth); +} + +/*static*/ size_t MacIOSurface::GetMaxHeight() { + if (!MacIOSurfaceLib::isInit()) + return -1; + return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropHeight); +} + +size_t MacIOSurface::GetDevicePixelWidth() { + return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr); +} + +size_t MacIOSurface::GetDevicePixelHeight() { + return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr); +} + +size_t MacIOSurface::GetBytesPerRow() { + return MacIOSurfaceLib::IOSurfaceGetBytesPerRow(mIOSurfacePtr); +} + +void MacIOSurface::IncrementUseCount() { + MacIOSurfaceLib::IOSurfaceIncrementUseCount(mIOSurfacePtr); +} + +void MacIOSurface::DecrementUseCount() { + MacIOSurfaceLib::IOSurfaceDecrementUseCount(mIOSurfacePtr); +} + +#define READ_ONLY 0x1 +void MacIOSurface::Lock() { + MacIOSurfaceLib::IOSurfaceLock(mIOSurfacePtr, READ_ONLY, nullptr); +} + +void MacIOSurface::Unlock() { + MacIOSurfaceLib::IOSurfaceUnlock(mIOSurfacePtr, READ_ONLY, nullptr); +} + +#include "SourceSurfaceRawData.h" +using mozilla::gfx::SourceSurface; +using mozilla::gfx::SourceSurfaceRawData; +using mozilla::gfx::IntSize; +using mozilla::gfx::SurfaceFormat; + +TemporaryRef<SourceSurface> +MacIOSurface::GetAsSurface() { + Lock(); + size_t bytesPerRow = GetBytesPerRow(); + size_t ioWidth = GetDevicePixelWidth(); + size_t ioHeight = GetDevicePixelHeight(); + + unsigned char* ioData = (unsigned char*)GetBaseAddress(); + unsigned char* dataCpy = (unsigned char*)malloc(bytesPerRow*ioHeight); + for (size_t i = 0; i < ioHeight; i++) { + memcpy(dataCpy + i * bytesPerRow, + ioData + i * bytesPerRow, ioWidth * 4); + } + + Unlock(); + + SurfaceFormat format = HasAlpha() ? mozilla::gfx::SurfaceFormat::B8G8R8A8 : + mozilla::gfx::SurfaceFormat::B8G8R8X8; + + RefPtr<SourceSurfaceRawData> surf = new SourceSurfaceRawData(); + surf->InitWrappingData(dataCpy, IntSize(ioWidth, ioHeight), bytesPerRow, format, true); + + return surf.forget(); +} + +CGLError +MacIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctx) +{ + return MacIOSurfaceLib::CGLTexImageIOSurface2D(ctx, + GL_TEXTURE_RECTANGLE_ARB, + HasAlpha() ? GL_RGBA : GL_RGB, + GetDevicePixelWidth(), + GetDevicePixelHeight(), + GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, + mIOSurfacePtr, 0); +} + +static +CGColorSpaceRef CreateSystemColorSpace() { + CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID()); + if (!cspace) { + cspace = ::CGColorSpaceCreateDeviceRGB(); + } + return cspace; +} + +CGContextRef MacIOSurface::CreateIOSurfaceContext() { + CGColorSpaceRef cspace = CreateSystemColorSpace(); + CGContextRef ref = MacIOSurfaceLib::IOSurfaceContextCreate(mIOSurfacePtr, + GetDevicePixelWidth(), + GetDevicePixelHeight(), + 8, 32, cspace, 0x2002); + ::CGColorSpaceRelease(cspace); + return ref; +} + +CGImageRef MacIOSurface::CreateImageFromIOSurfaceContext(CGContextRef aContext) { + if (!MacIOSurfaceLib::isInit()) + return nullptr; + + return MacIOSurfaceLib::IOSurfaceContextCreateImage(aContext); +} + +TemporaryRef<MacIOSurface> MacIOSurface::IOSurfaceContextGetSurface(CGContextRef aContext, + double aContentsScaleFactor, + bool aHasAlpha) { + if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0) + return nullptr; + + IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceContextGetSurface(aContext); + if (!surfaceRef) + return nullptr; + + RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor, aHasAlpha); + if (!ioSurface) { + ::CFRelease(surfaceRef); + return nullptr; + } + return ioSurface.forget(); +} + + +CGContextType GetContextType(CGContextRef ref) +{ + if (!MacIOSurfaceLib::isInit() || !MacIOSurfaceLib::sCGContextGetTypePtr) + return CG_CONTEXT_TYPE_UNKNOWN; + + unsigned int type = MacIOSurfaceLib::sCGContextGetTypePtr(ref); + if (type == CG_CONTEXT_TYPE_BITMAP) { + return CG_CONTEXT_TYPE_BITMAP; + } else if (type == CG_CONTEXT_TYPE_IOSURFACE) { + return CG_CONTEXT_TYPE_IOSURFACE; + } else { + return CG_CONTEXT_TYPE_UNKNOWN; + } +} + + diff --git a/gfx/2d/MacIOSurface.h b/gfx/2d/MacIOSurface.h index 853ad3569..62ddcd734 100644 --- a/gfx/2d/MacIOSurface.h +++ b/gfx/2d/MacIOSurface.h @@ -7,12 +7,43 @@ #ifndef MacIOSurface_h__ #define MacIOSurface_h__ #ifdef XP_MACOSX +#include <QuartzCore/QuartzCore.h> +#include <dlfcn.h> +#include "mozilla/RefPtr.h" + +typedef CFTypeRef IOSurfacePtr; +typedef IOSurfacePtr (*IOSurfaceCreateFunc) (CFDictionaryRef properties); +typedef IOSurfacePtr (*IOSurfaceLookupFunc) (uint32_t io_surface_id); +typedef IOSurfaceID (*IOSurfaceGetIDFunc)(IOSurfacePtr io_surface); +typedef void (*IOSurfaceVoidFunc)(IOSurfacePtr io_surface); +typedef IOReturn (*IOSurfaceLockFunc)(IOSurfacePtr io_surface, uint32_t options, + uint32_t *seed); +typedef IOReturn (*IOSurfaceUnlockFunc)(IOSurfacePtr io_surface, + uint32_t options, uint32_t *seed); +typedef void* (*IOSurfaceGetBaseAddressFunc)(IOSurfacePtr io_surface); +typedef void* (*IOSurfaceGetBaseAddressOfPlaneFunc)(IOSurfacePtr io_surface, + size_t planeIndex); +typedef size_t (*IOSurfaceSizeTFunc)(IOSurfacePtr io_surface); +typedef size_t (*IOSurfaceGetPropertyMaximumFunc) (CFStringRef property); +typedef CGLError (*CGLTexImageIOSurface2DFunc) (CGLContextObj ctxt, + GLenum target, GLenum internalFormat, + GLsizei width, GLsizei height, + GLenum format, GLenum type, + IOSurfacePtr ioSurface, GLuint plane); +typedef CGContextRef (*IOSurfaceContextCreateFunc)(CFTypeRef io_surface, + unsigned width, unsigned height, + unsigned bitsPerComponent, unsigned bytes, + CGColorSpaceRef colorSpace, CGBitmapInfo bitmapInfo); +typedef CGImageRef (*IOSurfaceContextCreateImageFunc)(CGContextRef ref); +typedef IOSurfacePtr (*IOSurfaceContextGetSurfaceFunc)(CGContextRef ref); + +typedef IOSurfacePtr (*CVPixelBufferGetIOSurfaceFunc)( + CVPixelBufferRef pixelBuffer); #import <OpenGL/OpenGL.h> #include "2D.h" #include "mozilla/RefPtr.h" -class gfxASurface; struct _CGLContextObject; typedef _CGLContextObject* CGLContextObj; @@ -20,21 +51,40 @@ typedef struct CGContext* CGContextRef; typedef struct CGImage* CGImageRef; typedef uint32_t IOSurfaceID; +enum CGContextType { + CG_CONTEXT_TYPE_UNKNOWN = 0, + // These are found by inspection, it's possible they could be changed + CG_CONTEXT_TYPE_BITMAP = 4, + CG_CONTEXT_TYPE_IOSURFACE = 8 +}; + +CGContextType GetContextType(CGContextRef ref); + class MacIOSurface : public mozilla::RefCounted<MacIOSurface> { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(MacIOSurface) typedef mozilla::gfx::SourceSurface SourceSurface; + // The usage count of the IOSurface is increased by 1 during the lifetime + // of the MacIOSurface instance. + // MacIOSurface holds a reference to the corresponding IOSurface. + static mozilla::TemporaryRef<MacIOSurface> CreateIOSurface(int aWidth, int aHeight, - double aContentsScaleFactor = 1.0); + double aContentsScaleFactor = 1.0, + bool aHasAlpha = true); static void ReleaseIOSurface(MacIOSurface *aIOSurface); static mozilla::TemporaryRef<MacIOSurface> LookupSurface(IOSurfaceID aSurfaceID, - double aContentsScaleFactor = 1.0); + double aContentsScaleFactor = 1.0, + bool aHasAlpha = true); - MacIOSurface(const void *aIOSurfacePtr, double aContentsScaleFactor = 1.0) - : mIOSurfacePtr(aIOSurfacePtr), mContentsScaleFactor(aContentsScaleFactor) {} - ~MacIOSurface(); + explicit MacIOSurface(const void *aIOSurfacePtr, + double aContentsScaleFactor = 1.0, + bool aHasAlpha = true); + virtual ~MacIOSurface(); IOSurfaceID GetIOSurfaceID(); void *GetBaseAddress(); + void *GetBaseAddressOfPlane(size_t planeIndex); + size_t GetPlaneCount(); // GetWidth() and GetHeight() return values in "display pixels". A // "display pixel" is the smallest fully addressable part of a display. // But in HiDPI modes each "display pixel" corresponds to more than one @@ -47,23 +97,105 @@ public: size_t GetBytesPerRow(); void Lock(); void Unlock(); + void IncrementUseCount(); + void DecrementUseCount(); + bool HasAlpha() { return mHasAlpha; } // We would like to forward declare NSOpenGLContext, but it is an @interface // and this file is also used from c++, so we use a void *. - CGLError CGLTexImageIOSurface2D(void *ctxt, - GLenum internalFormat, GLenum format, - GLenum type, GLuint plane); + CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt); mozilla::TemporaryRef<SourceSurface> GetAsSurface(); CGContextRef CreateIOSurfaceContext(); // FIXME This doesn't really belong here static CGImageRef CreateImageFromIOSurfaceContext(CGContextRef aContext); static mozilla::TemporaryRef<MacIOSurface> IOSurfaceContextGetSurface(CGContextRef aContext, - double aContentsScaleFactor = 1.0); + double aContentsScaleFactor = 1.0, + bool aHasAlpha = true); + static size_t GetMaxWidth(); + static size_t GetMaxHeight(); private: friend class nsCARenderer; const void* mIOSurfacePtr; double mContentsScaleFactor; + bool mHasAlpha; +}; + +class MacIOSurfaceLib: public MacIOSurface { +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(MacIOSurfaceLib) + static void *sIOSurfaceFramework; + static void *sOpenGLFramework; + static void *sCoreGraphicsFramework; + static void *sCoreVideoFramework; + static bool isLoaded; + static IOSurfaceCreateFunc sCreate; + static IOSurfaceGetIDFunc sGetID; + static IOSurfaceLookupFunc sLookup; + static IOSurfaceGetBaseAddressFunc sGetBaseAddress; + static IOSurfaceGetBaseAddressOfPlaneFunc sGetBaseAddressOfPlane; + static IOSurfaceSizeTFunc sPlaneCount; + static IOSurfaceLockFunc sLock; + static IOSurfaceUnlockFunc sUnlock; + static IOSurfaceVoidFunc sIncrementUseCount; + static IOSurfaceVoidFunc sDecrementUseCount; + static IOSurfaceSizeTFunc sWidth; + static IOSurfaceSizeTFunc sHeight; + static IOSurfaceSizeTFunc sBytesPerRow; + static IOSurfaceGetPropertyMaximumFunc sGetPropertyMaximum; + static CGLTexImageIOSurface2DFunc sTexImage; + static IOSurfaceContextCreateFunc sIOSurfaceContextCreate; + static IOSurfaceContextCreateImageFunc sIOSurfaceContextCreateImage; + static IOSurfaceContextGetSurfaceFunc sIOSurfaceContextGetSurface; + static CVPixelBufferGetIOSurfaceFunc sCVPixelBufferGetIOSurface; + static CFStringRef kPropWidth; + static CFStringRef kPropHeight; + static CFStringRef kPropBytesPerElem; + static CFStringRef kPropBytesPerRow; + static CFStringRef kPropIsGlobal; + + static bool isInit(); + static CFStringRef GetIOConst(const char* symbole); + static IOSurfacePtr IOSurfaceCreate(CFDictionaryRef properties); + static IOSurfacePtr IOSurfaceLookup(IOSurfaceID aIOSurfaceID); + static IOSurfaceID IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr); + static void* IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr); + static void* IOSurfaceGetBaseAddressOfPlane(IOSurfacePtr aIOSurfacePtr, + size_t aPlaneIndex); + static size_t IOSurfaceGetPlaneCount(IOSurfacePtr aIOSurfacePtr); + static size_t IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr); + static size_t IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr); + static size_t IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr); + static size_t IOSurfaceGetPropertyMaximum(CFStringRef property); + static IOReturn IOSurfaceLock(IOSurfacePtr aIOSurfacePtr, + uint32_t options, uint32_t *seed); + static IOReturn IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr, + uint32_t options, uint32_t *seed); + static void IOSurfaceIncrementUseCount(IOSurfacePtr aIOSurfacePtr); + static void IOSurfaceDecrementUseCount(IOSurfacePtr aIOSurfacePtr); + static CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt, + GLenum target, GLenum internalFormat, + GLsizei width, GLsizei height, + GLenum format, GLenum type, + IOSurfacePtr ioSurface, GLuint plane); + static CGContextRef IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr, + unsigned aWidth, unsigned aHeight, + unsigned aBitsPerCompoent, unsigned aBytes, + CGColorSpaceRef aColorSpace, CGBitmapInfo bitmapInfo); + static CGImageRef IOSurfaceContextCreateImage(CGContextRef ref); + static IOSurfacePtr IOSurfaceContextGetSurface(CGContextRef ref); + static IOSurfacePtr CVPixelBufferGetIOSurface(CVPixelBufferRef apixelBuffer); + static unsigned int (*sCGContextGetTypePtr) (CGContextRef); + static void LoadLibrary(); + static void CloseLibrary(); + + // Static deconstructor + static class LibraryUnloader { + public: + ~LibraryUnloader() { + CloseLibrary(); + } + } sLibraryUnloader; }; #endif diff --git a/gfx/2d/Makefile.in b/gfx/2d/Makefile.in index c86a089de..711879fa1 100644 --- a/gfx/2d/Makefile.in +++ b/gfx/2d/Makefile.in @@ -1,74 +1,10 @@ -# # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -DEPTH = @DEPTH@ -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = $(srcdir) $(srcdir)/unittest - -include $(DEPTH)/config/autoconf.mk - -LIBRARY_NAME = gfx2d -MSVC_ENABLE_PGO := 1 -LIBXUL_LIBRARY = 1 -EXPORT_LIBRARY = 1 - -ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) -CMMSRCS = \ - QuartzSupport.mm \ - $(NULL) -endif - -DEFINES += -DMOZ_GFX -DUSE_CAIRO -DGFX2D_INTERNAL - -ifeq ($(MOZ_WIDGET_TOOLKIT),$(findstring $(MOZ_WIDGET_TOOLKIT),android gtk2 gtk3 gonk qt)) -DEFINES += -DMOZ_ENABLE_FREETYPE -endif - -DEFINES += -DSK_A32_SHIFT=24 -DSK_R32_SHIFT=16 -DSK_G32_SHIFT=8 -DSK_B32_SHIFT=0 - -ifdef MOZ_DEBUG -DEFINES += -DGFX_LOG_DEBUG -DGFX_LOG_WARNING -endif - -# Are we targeting x86 or x64? If so, build SSE2 files. -ifneq (,$(INTEL_ARCHITECTURE)) -# VC2005 doesn't support _mm_castsi128_ps, so SSE2 is turned off -ifneq (1400,$(_MSC_VER)) -DEFINES += -DUSE_SSE2 -endif -endif - -ifeq ($(MOZ_WIDGET_TOOLKIT),windows) -DEFINES += -DWIN32 -DINITGUID - -ifdef MOZ_ENABLE_SKIA -DEFINES += -DSKIA_IMPLEMENTATION=1 -DGR_IMPLEMENTATION=1 -endif -endif - include $(topsrcdir)/config/rules.mk -include $(topsrcdir)/ipc/chromium/chromium-config.mk # Due to bug 796023, we can't have -DUNICODE and -D_UNICODE; defining those # macros changes the type of LOGFONT to LOGFONTW instead of LOGFONTA. This # changes the symbol names of exported C++ functions that use LOGFONT. DEFINES := $(filter-out -DUNICODE -D_UNICODE,$(DEFINES)) - -# The file uses SSE2 intrinsics, so it needs special compile flags on some -# compilers. -ifneq (,$(INTEL_ARCHITECTURE)) -ifdef GNU_CC -ImageScalingSSE2.$(OBJ_SUFFIX): CXXFLAGS+=-msse2 -BlurSSE2.$(OBJ_SUFFIX): CXXFLAGS+=-msse2 -endif - -ifdef SOLARIS_SUNPRO_CXX -ImageScalingSSE2.$(OBJ_SUFFIX): OS_CXXFLAGS += -xarch=sse2 -xO4 -BlurSSE2.$(OBJ_SUFFIX): OS_CXXFLAGS += -xarch=sse2 -xO4 -endif -endif - -CXXFLAGS += $(MOZ_CAIRO_CFLAGS) $(MOZ_PIXMAN_CFLAGS) diff --git a/gfx/2d/Matrix.cpp b/gfx/2d/Matrix.cpp index 80e2d3d32..70058fede 100644 --- a/gfx/2d/Matrix.cpp +++ b/gfx/2d/Matrix.cpp @@ -5,11 +5,29 @@ #include "Matrix.h" #include "Tools.h" +#include <algorithm> +#include <ostream> #include <math.h> +#include "mozilla/FloatingPoint.h" // for UnspecifiedNaN + +using namespace std; + namespace mozilla { namespace gfx { +std::ostream& +operator<<(std::ostream& aStream, const Matrix& aMatrix) +{ + return aStream << "[ " << aMatrix._11 + << " " << aMatrix._12 + << "; " << aMatrix._21 + << " " << aMatrix._22 + << "; " << aMatrix._31 + << " " << aMatrix._32 + << "; ]"; +} + Matrix Matrix::Rotation(Float aAngle) { @@ -22,7 +40,7 @@ Matrix::Rotation(Float aAngle) newMatrix._12 = s; newMatrix._21 = -s; newMatrix._22 = c; - + return newMatrix; } @@ -57,7 +75,7 @@ Matrix::TransformBounds(const Rect &aRect) const return Rect(min_x, min_y, max_x - min_x, max_y - min_y); } -void +Matrix& Matrix::NudgeToIntegers() { NudgeToInteger(&_11); @@ -66,6 +84,176 @@ Matrix::NudgeToIntegers() NudgeToInteger(&_22); NudgeToInteger(&_31); NudgeToInteger(&_32); + return *this; +} + +Rect +Matrix4x4::TransformBounds(const Rect& aRect) const +{ + Point quad[4]; + Float min_x, max_x; + Float min_y, max_y; + + quad[0] = *this * aRect.TopLeft(); + quad[1] = *this * aRect.TopRight(); + quad[2] = *this * aRect.BottomLeft(); + quad[3] = *this * aRect.BottomRight(); + + min_x = max_x = quad[0].x; + min_y = max_y = quad[0].y; + + for (int i = 1; i < 4; i++) { + if (quad[i].x < min_x) { + min_x = quad[i].x; + } + if (quad[i].x > max_x) { + max_x = quad[i].x; + } + + if (quad[i].y < min_y) { + min_y = quad[i].y; + } + if (quad[i].y > max_y) { + max_y = quad[i].y; + } + } + + return Rect(min_x, min_y, max_x - min_x, max_y - min_y); +} + +Point4D ComputePerspectivePlaneIntercept(const Point4D& aFirst, + const Point4D& aSecond) +{ + // FIXME: See bug 1035611 + // Since we can't easily deal with points as w=0 (since we divide by w), we + // approximate this by finding a point with w just greater than 0. Unfortunately + // this is a tradeoff between accuracy and floating point precision. + + // We want to interpolate aFirst and aSecond to find a point as close to + // the positive side of the w=0 plane as possible. + + // Since we know what we want the w component to be, we can rearrange the + // interpolation equation and solve for t. + float w = 0.00001f; + float t = (w - aFirst.w) / (aSecond.w - aFirst.w); + + // Use t to find the remainder of the components + return aFirst + (aSecond - aFirst) * t; +} + +Rect Matrix4x4::ProjectRectBounds(const Rect& aRect) const +{ + Point4D points[4]; + + points[0] = ProjectPoint(aRect.TopLeft()); + points[1] = ProjectPoint(aRect.TopRight()); + points[2] = ProjectPoint(aRect.BottomRight()); + points[3] = ProjectPoint(aRect.BottomLeft()); + + Float min_x = std::numeric_limits<Float>::max(); + Float min_y = std::numeric_limits<Float>::max(); + Float max_x = -std::numeric_limits<Float>::max(); + Float max_y = -std::numeric_limits<Float>::max(); + + bool foundPoint = false; + for (int i=0; i<4; i++) { + // Only use points that exist above the w=0 plane + if (points[i].HasPositiveWCoord()) { + foundPoint = true; + Point point2d = points[i].As2DPoint(); + min_x = min<Float>(point2d.x, min_x); + max_x = max<Float>(point2d.x, max_x); + min_y = min<Float>(point2d.y, min_y); + max_y = max<Float>(point2d.y, max_y); + } + + int next = (i == 3) ? 0 : i + 1; + if (points[i].HasPositiveWCoord() != points[next].HasPositiveWCoord()) { + // If the line between two points crosses the w=0 plane, then interpolate a point + // as close to the w=0 plane as possible and use that instead. + Point4D intercept = ComputePerspectivePlaneIntercept(points[i], points[next]); + + Point point2d = intercept.As2DPoint(); + min_x = min<Float>(point2d.x, min_x); + max_x = max<Float>(point2d.x, max_x); + min_y = min<Float>(point2d.y, min_y); + max_y = max<Float>(point2d.y, max_y); + } + } + + if (!foundPoint) { + return Rect(0, 0, 0, 0); + } + + return Rect(min_x, min_y, max_x - min_x, max_y - min_y); +} + +bool +Matrix4x4::Invert() +{ + Float det = Determinant(); + if (!det) { + return false; + } + + Matrix4x4 result; + result._11 = _23 * _34 * _42 - _24 * _33 * _42 + _24 * _32 * _43 - _22 * _34 * _43 - _23 * _32 * _44 + _22 * _33 * _44; + result._12 = _14 * _33 * _42 - _13 * _34 * _42 - _14 * _32 * _43 + _12 * _34 * _43 + _13 * _32 * _44 - _12 * _33 * _44; + result._13 = _13 * _24 * _42 - _14 * _23 * _42 + _14 * _22 * _43 - _12 * _24 * _43 - _13 * _22 * _44 + _12 * _23 * _44; + result._14 = _14 * _23 * _32 - _13 * _24 * _32 - _14 * _22 * _33 + _12 * _24 * _33 + _13 * _22 * _34 - _12 * _23 * _34; + result._21 = _24 * _33 * _41 - _23 * _34 * _41 - _24 * _31 * _43 + _21 * _34 * _43 + _23 * _31 * _44 - _21 * _33 * _44; + result._22 = _13 * _34 * _41 - _14 * _33 * _41 + _14 * _31 * _43 - _11 * _34 * _43 - _13 * _31 * _44 + _11 * _33 * _44; + result._23 = _14 * _23 * _41 - _13 * _24 * _41 - _14 * _21 * _43 + _11 * _24 * _43 + _13 * _21 * _44 - _11 * _23 * _44; + result._24 = _13 * _24 * _31 - _14 * _23 * _31 + _14 * _21 * _33 - _11 * _24 * _33 - _13 * _21 * _34 + _11 * _23 * _34; + result._31 = _22 * _34 * _41 - _24 * _32 * _41 + _24 * _31 * _42 - _21 * _34 * _42 - _22 * _31 * _44 + _21 * _32 * _44; + result._32 = _14 * _32 * _41 - _12 * _34 * _41 - _14 * _31 * _42 + _11 * _34 * _42 + _12 * _31 * _44 - _11 * _32 * _44; + result._33 = _12 * _24 * _41 - _14 * _22 * _41 + _14 * _21 * _42 - _11 * _24 * _42 - _12 * _21 * _44 + _11 * _22 * _44; + result._34 = _14 * _22 * _31 - _12 * _24 * _31 - _14 * _21 * _32 + _11 * _24 * _32 + _12 * _21 * _34 - _11 * _22 * _34; + result._41 = _23 * _32 * _41 - _22 * _33 * _41 - _23 * _31 * _42 + _21 * _33 * _42 + _22 * _31 * _43 - _21 * _32 * _43; + result._42 = _12 * _33 * _41 - _13 * _32 * _41 + _13 * _31 * _42 - _11 * _33 * _42 - _12 * _31 * _43 + _11 * _32 * _43; + result._43 = _13 * _22 * _41 - _12 * _23 * _41 - _13 * _21 * _42 + _11 * _23 * _42 + _12 * _21 * _43 - _11 * _22 * _43; + result._44 = _12 * _23 * _31 - _13 * _22 * _31 + _13 * _21 * _32 - _11 * _23 * _32 - _12 * _21 * _33 + _11 * _22 * _33; + + result._11 /= det; + result._12 /= det; + result._13 /= det; + result._14 /= det; + result._21 /= det; + result._22 /= det; + result._23 /= det; + result._24 /= det; + result._31 /= det; + result._32 /= det; + result._33 /= det; + result._34 /= det; + result._41 /= det; + result._42 /= det; + result._43 /= det; + result._44 /= det; + *this = result; + + return true; +} + +void +Matrix4x4::SetNAN() +{ + _11 = UnspecifiedNaN<Float>(); + _21 = UnspecifiedNaN<Float>(); + _31 = UnspecifiedNaN<Float>(); + _41 = UnspecifiedNaN<Float>(); + _12 = UnspecifiedNaN<Float>(); + _22 = UnspecifiedNaN<Float>(); + _32 = UnspecifiedNaN<Float>(); + _42 = UnspecifiedNaN<Float>(); + _13 = UnspecifiedNaN<Float>(); + _23 = UnspecifiedNaN<Float>(); + _33 = UnspecifiedNaN<Float>(); + _43 = UnspecifiedNaN<Float>(); + _14 = UnspecifiedNaN<Float>(); + _24 = UnspecifiedNaN<Float>(); + _34 = UnspecifiedNaN<Float>(); + _44 = UnspecifiedNaN<Float>(); } } diff --git a/gfx/2d/Matrix.h b/gfx/2d/Matrix.h index 0fd9fde9c..11dd2412b 100644 --- a/gfx/2d/Matrix.h +++ b/gfx/2d/Matrix.h @@ -9,11 +9,19 @@ #include "Types.h" #include "Rect.h" #include "Point.h" +#include <iosfwd> #include <math.h> +#include "mozilla/Attributes.h" +#include "mozilla/DebugOnly.h" namespace mozilla { namespace gfx { +static bool FuzzyEqual(Float aV1, Float aV2) { + // XXX - Check if fabs does the smart thing and just negates the sign bit. + return fabs(aV2 - aV1) < 1e-6; +} + class Matrix { public: @@ -31,6 +39,13 @@ public: Float _21, _22; Float _31, _32; + MOZ_ALWAYS_INLINE Matrix Copy() const + { + return Matrix(*this); + } + + friend std::ostream& operator<<(std::ostream& aStream, const Matrix& aMatrix); + Point operator *(const Point &aPoint) const { Point retPoint; @@ -53,9 +68,81 @@ public: GFX2D_API Rect TransformBounds(const Rect& rect) const; - // Apply a scale to this matrix. This scale will be applied -before- the - // existing transformation of the matrix. - Matrix &Scale(Float aX, Float aY) + static Matrix Translation(Float aX, Float aY) + { + return Matrix(1.0f, 0.0f, 0.0f, 1.0f, aX, aY); + } + + static Matrix Translation(Point aPoint) + { + return Translation(aPoint.x, aPoint.y); + } + + /** + * Apply a translation to this matrix. + * + * The "Pre" in this method's name means that the translation is applied + * -before- this matrix's existing transformation. That is, any vector that + * is multiplied by the resulting matrix will first be translated, then be + * transformed by the original transform. + * + * Calling this method will result in this matrix having the same value as + * the result of: + * + * Matrix::Translation(x, y) * this + * + * (Note that in performance critical code multiplying by the result of a + * Translation()/Scaling() call is not recommended since that results in a + * full matrix multiply involving 12 floating-point multiplications. Calling + * this method would be preferred since it only involves four floating-point + * multiplications.) + */ + Matrix &PreTranslate(Float aX, Float aY) + { + _31 += _11 * aX + _21 * aY; + _32 += _12 * aX + _22 * aY; + + return *this; + } + + Matrix &PreTranslate(const Point &aPoint) + { + return PreTranslate(aPoint.x, aPoint.y); + } + + /** + * Similar to PreTranslate, but the translation is applied -after- this + * matrix's existing transformation instead of before it. + * + * This method is generally less used than PreTranslate since typically code + * want to adjust an existing user space to device space matrix to create a + * transform to device space from a -new- user space (translated from the + * previous user space). In that case consumers will need to use the Pre* + * variants of the matrix methods rather than using the Post* methods, since + * the Post* methods add a transform to the device space end of the + * transformation. + */ + Matrix &PostTranslate(Float aX, Float aY) + { + _31 += aX; + _32 += aY; + return *this; + } + + Matrix &PostTranslate(const Point &aPoint) + { + return PostTranslate(aPoint.x, aPoint.y); + } + + static Matrix Scaling(Float aScaleX, Float aScaleY) + { + return Matrix(aScaleX, 0.0f, 0.0f, aScaleY, 0.0f, 0.0f); + } + + /** + * Similar to PreTranslate, but applies a scale instead of a translation. + */ + Matrix &PreScale(Float aX, Float aY) { _11 *= aX; _12 *= aX; @@ -64,13 +151,15 @@ public: return *this; } + + GFX2D_API static Matrix Rotation(Float aAngle); - Matrix &Translate(Float aX, Float aY) + /** + * Similar to PreTranslate, but applies a rotation instead of a translation. + */ + Matrix &PreRotate(Float aAngle) { - _31 += _11 * aX + _21 * aY; - _32 += _12 * aX + _22 * aY; - - return *this; + return *this = Matrix::Rotation(aAngle) * *this; } bool Invert() @@ -101,12 +190,18 @@ public: return true; } + Matrix Inverse() const + { + Matrix clone = *this; + DebugOnly<bool> inverted = clone.Invert(); + MOZ_ASSERT(inverted, "Attempted to get the inverse of a non-invertible matrix"); + return clone; + } + Float Determinant() const { return _11 * _22 - _12 * _21; } - - GFX2D_API static Matrix Rotation(Float aAngle); Matrix operator*(const Matrix &aMatrix) const { @@ -122,6 +217,21 @@ public: return resultMatrix; } + Matrix& operator*=(const Matrix &aMatrix) + { + *this = *this * aMatrix; + return *this; + } + + /** + * Multiplies in the opposite order to operator=*. + */ + Matrix &PreMultiply(const Matrix &aMatrix) + { + *this = aMatrix * *this; + return *this; + } + /* Returns true if the other matrix is fuzzy-equal to this matrix. * Note that this isn't a cheap comparison! */ @@ -150,6 +260,42 @@ public: return false; } + /** + * Returns true if the matrix is anything other than a straight + * translation by integers. + */ + bool HasNonIntegerTranslation() const { + return HasNonTranslation() || + !FuzzyEqual(_31, floor(_31 + 0.5)) || + !FuzzyEqual(_32, floor(_32 + 0.5)); + } + + /** + * Returns true if the matrix only has an integer translation. + */ + bool HasOnlyIntegerTranslation() const { + return !HasNonIntegerTranslation(); + } + + /** + * Returns true if the matrix has any transform other + * than a straight translation. + */ + bool HasNonTranslation() const { + return !FuzzyEqual(_11, 1.0) || !FuzzyEqual(_22, 1.0) || + !FuzzyEqual(_12, 0.0) || !FuzzyEqual(_21, 0.0); + } + + /** + * Returns true if the matrix has any transform other + * than a translation or a -1 y scale (y axis flip) + */ + bool HasNonTranslationOrFlip() const { + return !FuzzyEqual(_11, 1.0) || + (!FuzzyEqual(_22, 1.0) && !FuzzyEqual(_22, -1.0)) || + !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0); + } + /* Returns true if the matrix is an identity matrix. */ bool IsIdentity() const @@ -159,12 +305,58 @@ public: _31 == 0.0f && _32 == 0.0f; } - GFX2D_API void NudgeToIntegers(); + /* Returns true if the matrix is singular. + */ + bool IsSingular() const + { + return Determinant() == 0; + } + + GFX2D_API Matrix &NudgeToIntegers(); -private: - static bool FuzzyEqual(Float aV1, Float aV2) { - // XXX - Check if fabs does the smart thing and just negates the sign bit. - return fabs(aV2 - aV1) < 1e-6; + bool IsTranslation() const + { + return FuzzyEqual(_11, 1.0f) && FuzzyEqual(_12, 0.0f) && + FuzzyEqual(_21, 0.0f) && FuzzyEqual(_22, 1.0f); + } + + static bool FuzzyIsInteger(Float aValue) + { + return FuzzyEqual(aValue, floorf(aValue + 0.5f)); + } + + bool IsIntegerTranslation() const + { + return IsTranslation() && FuzzyIsInteger(_31) && FuzzyIsInteger(_32); + } + + bool IsAllIntegers() const + { + return FuzzyIsInteger(_11) && FuzzyIsInteger(_12) && + FuzzyIsInteger(_21) && FuzzyIsInteger(_22) && + FuzzyIsInteger(_31) && FuzzyIsInteger(_32); + } + + Point GetTranslation() const { + return Point(_31, _32); + } + + /** + * Returns true if matrix is multiple of 90 degrees rotation with flipping, + * scaling and translation. + */ + bool PreservesAxisAlignedRectangles() const { + return ((FuzzyEqual(_11, 0.0) && FuzzyEqual(_22, 0.0)) + || (FuzzyEqual(_12, 0.0) && FuzzyEqual(_21, 0.0))); + } + + /** + * Returns true if the matrix has any transform other + * than a translation or scale; this is, if there is + * no rotation. + */ + bool HasNonAxisAlignedTransform() const { + return !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0); } }; @@ -178,11 +370,37 @@ public: , _41(0.0f), _42(0.0f), _43(0.0f), _44(1.0f) {} + Matrix4x4(Float a11, Float a12, Float a13, Float a14, + Float a21, Float a22, Float a23, Float a24, + Float a31, Float a32, Float a33, Float a34, + Float a41, Float a42, Float a43, Float a44) + : _11(a11), _12(a12), _13(a13), _14(a14) + , _21(a21), _22(a22), _23(a23), _24(a24) + , _31(a31), _32(a32), _33(a33), _34(a34) + , _41(a41), _42(a42), _43(a43), _44(a44) + {} + + Matrix4x4(const Matrix4x4& aOther) + { + memcpy(this, &aOther, sizeof(*this)); + } + Float _11, _12, _13, _14; Float _21, _22, _23, _24; Float _31, _32, _33, _34; Float _41, _42, _43, _44; + Point4D& operator[](int aIndex) + { + MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index"); + return *reinterpret_cast<Point4D*>((&_11)+4*aIndex); + } + const Point4D& operator[](int aIndex) const + { + MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index"); + return *reinterpret_cast<const Point4D*>((&_11)+4*aIndex); + } + /** * Returns true if the matrix is isomorphic to a 2D affine transformation. */ @@ -197,6 +415,21 @@ public: return true; } + bool Is2D(Matrix* aMatrix) const { + if (!Is2D()) { + return false; + } + if (aMatrix) { + aMatrix->_11 = _11; + aMatrix->_12 = _12; + aMatrix->_21 = _21; + aMatrix->_22 = _22; + aMatrix->_31 = _41; + aMatrix->_32 = _42; + } + return true; + } + Matrix As2D() const { MOZ_ASSERT(Is2D(), "Matrix is not a 2D affine transform"); @@ -204,9 +437,202 @@ public: return Matrix(_11, _12, _21, _22, _41, _42); } - // Apply a scale to this matrix. This scale will be applied -before- the - // existing transformation of the matrix. - Matrix4x4 &Scale(Float aX, Float aY, Float aZ) + bool CanDraw2D(Matrix* aMatrix = nullptr) const { + if (_14 != 0.0f || + _24 != 0.0f || + _44 != 1.0f) { + return false; + } + if (aMatrix) { + aMatrix->_11 = _11; + aMatrix->_12 = _12; + aMatrix->_21 = _21; + aMatrix->_22 = _22; + aMatrix->_31 = _41; + aMatrix->_32 = _42; + } + return true; + } + + Matrix4x4& ProjectTo2D() { + _31 = 0.0f; + _32 = 0.0f; + _13 = 0.0f; + _23 = 0.0f; + _33 = 1.0f; + _43 = 0.0f; + _34 = 0.0f; + return *this; + } + + Point4D ProjectPoint(const Point& aPoint) const { + // Find a value for z that will transform to 0. + + // The transformed value of z is computed as: + // z' = aPoint.x * _13 + aPoint.y * _23 + z * _33 + _43; + + // Solving for z when z' = 0 gives us: + float z = -(aPoint.x * _13 + aPoint.y * _23 + _43) / _33; + + // Compute the transformed point + return *this * Point4D(aPoint.x, aPoint.y, z, 1); + } + + Rect ProjectRectBounds(const Rect& aRect) const; + + static Matrix4x4 From2D(const Matrix &aMatrix) { + Matrix4x4 matrix; + matrix._11 = aMatrix._11; + matrix._12 = aMatrix._12; + matrix._21 = aMatrix._21; + matrix._22 = aMatrix._22; + matrix._41 = aMatrix._31; + matrix._42 = aMatrix._32; + return matrix; + } + + bool Is2DIntegerTranslation() const + { + return Is2D() && As2D().IsIntegerTranslation(); + } + + Point4D TransposeTransform4D(const Point4D& aPoint) const + { + Float x = aPoint.x * _11 + aPoint.y * _12 + aPoint.z * _13 + aPoint.w * _14; + Float y = aPoint.x * _21 + aPoint.y * _22 + aPoint.z * _23 + aPoint.w * _24; + Float z = aPoint.x * _31 + aPoint.y * _32 + aPoint.z * _33 + aPoint.w * _34; + Float w = aPoint.x * _41 + aPoint.y * _42 + aPoint.z * _43 + aPoint.w * _44; + + return Point4D(x, y, z, w); + } + + Point4D operator *(const Point4D& aPoint) const + { + Point4D retPoint; + + retPoint.x = aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + _41; + retPoint.y = aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + _42; + retPoint.z = aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + _43; + retPoint.w = aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + _44; + + return retPoint; + } + + Point3D operator *(const Point3D& aPoint) const + { + Point4D temp(aPoint.x, aPoint.y, aPoint.z, 1); + + temp = *this * temp; + temp /= temp.w; + + return Point3D(temp.x, temp.y, temp.z); + } + + Point operator *(const Point &aPoint) const + { + Point4D temp(aPoint.x, aPoint.y, 0, 1); + + temp = *this * temp; + temp /= temp.w; + + return Point(temp.x, temp.y); + } + + GFX2D_API Rect TransformBounds(const Rect& rect) const; + + + static Matrix4x4 Translation(Float aX, Float aY, Float aZ) + { + return Matrix4x4(1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + aX, aY, aZ, 1.0f); + } + + static Matrix4x4 Translation(const Point3D& aP) + { + return Translation(aP.x, aP.y, aP.z); + } + + /** + * Apply a translation to this matrix. + * + * The "Pre" in this method's name means that the translation is applied + * -before- this matrix's existing transformation. That is, any vector that + * is multiplied by the resulting matrix will first be translated, then be + * transformed by the original transform. + * + * Calling this method will result in this matrix having the same value as + * the result of: + * + * Matrix4x4::Translation(x, y) * this + * + * (Note that in performance critical code multiplying by the result of a + * Translation()/Scaling() call is not recommended since that results in a + * full matrix multiply involving 64 floating-point multiplications. Calling + * this method would be preferred since it only involves 12 floating-point + * multiplications.) + */ + Matrix4x4 &PreTranslate(Float aX, Float aY, Float aZ) + { + _41 += aX * _11 + aY * _21 + aZ * _31; + _42 += aX * _12 + aY * _22 + aZ * _32; + _43 += aX * _13 + aY * _23 + aZ * _33; + _44 += aX * _14 + aY * _24 + aZ * _34; + + return *this; + } + + Matrix4x4 &PreTranslate(const Point3D& aPoint) { + return PreTranslate(aPoint.x, aPoint.y, aPoint.z); + } + + /** + * Similar to PreTranslate, but the translation is applied -after- this + * matrix's existing transformation instead of before it. + * + * This method is generally less used than PreTranslate since typically code + * wants to adjust an existing user space to device space matrix to create a + * transform to device space from a -new- user space (translated from the + * previous user space). In that case consumers will need to use the Pre* + * variants of the matrix methods rather than using the Post* methods, since + * the Post* methods add a transform to the device space end of the + * transformation. + */ + Matrix4x4 &PostTranslate(Float aX, Float aY, Float aZ) + { + _11 += _14 * aX; + _21 += _24 * aX; + _31 += _34 * aX; + _41 += _44 * aX; + _12 += _14 * aY; + _22 += _24 * aY; + _32 += _34 * aY; + _42 += _44 * aY; + _13 += _14 * aZ; + _23 += _24 * aZ; + _33 += _34 * aZ; + _43 += _44 * aZ; + + return *this; + } + + Matrix4x4 &PostTranslate(const Point3D& aPoint) { + return PostTranslate(aPoint.x, aPoint.y, aPoint.z); + } + + static Matrix4x4 Scaling(Float aScaleX, Float aScaleY, float aScaleZ) + { + return Matrix4x4(aScaleX, 0.0f, 0.0f, 0.0f, + 0.0f, aScaleY, 0.0f, 0.0f, + 0.0f, 0.0f, aScaleZ, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + } + + /** + * Similar to PreTranslate, but applies a scale instead of a translation. + */ + Matrix4x4 &PreScale(Float aX, Float aY, Float aZ) { _11 *= aX; _12 *= aX; @@ -220,6 +646,311 @@ public: return *this; } + + /** + * Similar to PostTranslate, but applies a scale instead of a translation. + */ + Matrix4x4 &PostScale(Float aScaleX, Float aScaleY, Float aScaleZ) + { + _11 *= aScaleX; + _21 *= aScaleX; + _31 *= aScaleX; + _41 *= aScaleX; + _12 *= aScaleY; + _22 *= aScaleY; + _32 *= aScaleY; + _42 *= aScaleY; + _13 *= aScaleZ; + _23 *= aScaleZ; + _33 *= aScaleZ; + _43 *= aScaleZ; + + return *this; + } + + void SkewXY(Float aSkew) + { + (*this)[1] += (*this)[0] * aSkew; + } + + void SkewXZ(Float aSkew) + { + (*this)[2] += (*this)[0] * aSkew; + } + + void SkewYZ(Float aSkew) + { + (*this)[2] += (*this)[1] * aSkew; + } + + Matrix4x4 &ChangeBasis(Float aX, Float aY, Float aZ) + { + // Translate to the origin before applying this matrix + PreTranslate(-aX, -aY, -aZ); + + // Translate back into position after applying this matrix + PostTranslate(aX, aY, aZ); + + return *this; + } + + Matrix4x4& Transpose() { + std::swap(_12, _21); + std::swap(_13, _31); + std::swap(_14, _41); + + std::swap(_23, _32); + std::swap(_24, _42); + + std::swap(_34, _43); + + return *this; + } + + bool operator==(const Matrix4x4& o) const + { + // XXX would be nice to memcmp here, but that breaks IEEE 754 semantics + return _11 == o._11 && _12 == o._12 && _13 == o._13 && _14 == o._14 && + _21 == o._21 && _22 == o._22 && _23 == o._23 && _24 == o._24 && + _31 == o._31 && _32 == o._32 && _33 == o._33 && _34 == o._34 && + _41 == o._41 && _42 == o._42 && _43 == o._43 && _44 == o._44; + } + + bool operator!=(const Matrix4x4& o) const + { + return !((*this) == o); + } + + Matrix4x4 operator*(const Matrix4x4 &aMatrix) const + { + Matrix4x4 matrix; + + matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21 + _13 * aMatrix._31 + _14 * aMatrix._41; + matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21 + _23 * aMatrix._31 + _24 * aMatrix._41; + matrix._31 = _31 * aMatrix._11 + _32 * aMatrix._21 + _33 * aMatrix._31 + _34 * aMatrix._41; + matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + _43 * aMatrix._31 + _44 * aMatrix._41; + matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22 + _13 * aMatrix._32 + _14 * aMatrix._42; + matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22 + _23 * aMatrix._32 + _24 * aMatrix._42; + matrix._32 = _31 * aMatrix._12 + _32 * aMatrix._22 + _33 * aMatrix._32 + _34 * aMatrix._42; + matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + _43 * aMatrix._32 + _44 * aMatrix._42; + matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23 + _13 * aMatrix._33 + _14 * aMatrix._43; + matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23 + _23 * aMatrix._33 + _24 * aMatrix._43; + matrix._33 = _31 * aMatrix._13 + _32 * aMatrix._23 + _33 * aMatrix._33 + _34 * aMatrix._43; + matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + _43 * aMatrix._33 + _44 * aMatrix._43; + matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24 + _13 * aMatrix._34 + _14 * aMatrix._44; + matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24 + _23 * aMatrix._34 + _24 * aMatrix._44; + matrix._34 = _31 * aMatrix._14 + _32 * aMatrix._24 + _33 * aMatrix._34 + _34 * aMatrix._44; + matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + _43 * aMatrix._34 + _44 * aMatrix._44; + + return matrix; + } + + Matrix4x4& operator*=(const Matrix4x4 &aMatrix) + { + *this = *this * aMatrix; + return *this; + } + + /* Returns true if the matrix is an identity matrix. + */ + bool IsIdentity() const + { + return _11 == 1.0f && _12 == 0.0f && _13 == 0.0f && _14 == 0.0f && + _21 == 0.0f && _22 == 1.0f && _23 == 0.0f && _24 == 0.0f && + _31 == 0.0f && _32 == 0.0f && _33 == 1.0f && _34 == 0.0f && + _41 == 0.0f && _42 == 0.0f && _43 == 0.0f && _44 == 1.0f; + } + + bool IsSingular() const + { + return Determinant() == 0.0; + } + + Float Determinant() const + { + return _14 * _23 * _32 * _41 + - _13 * _24 * _32 * _41 + - _14 * _22 * _33 * _41 + + _12 * _24 * _33 * _41 + + _13 * _22 * _34 * _41 + - _12 * _23 * _34 * _41 + - _14 * _23 * _31 * _42 + + _13 * _24 * _31 * _42 + + _14 * _21 * _33 * _42 + - _11 * _24 * _33 * _42 + - _13 * _21 * _34 * _42 + + _11 * _23 * _34 * _42 + + _14 * _22 * _31 * _43 + - _12 * _24 * _31 * _43 + - _14 * _21 * _32 * _43 + + _11 * _24 * _32 * _43 + + _12 * _21 * _34 * _43 + - _11 * _22 * _34 * _43 + - _13 * _22 * _31 * _44 + + _12 * _23 * _31 * _44 + + _13 * _21 * _32 * _44 + - _11 * _23 * _32 * _44 + - _12 * _21 * _33 * _44 + + _11 * _22 * _33 * _44; + } + + bool Invert(); + + Matrix4x4 Inverse() const + { + Matrix4x4 clone = *this; + DebugOnly<bool> inverted = clone.Invert(); + MOZ_ASSERT(inverted, "Attempted to get the inverse of a non-invertible matrix"); + return clone; + } + + void Normalize() + { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + (*this)[i][j] /= (*this)[3][3]; + } + } + } + + bool FuzzyEqual(const Matrix4x4& o) const + { + return gfx::FuzzyEqual(_11, o._11) && gfx::FuzzyEqual(_12, o._12) && + gfx::FuzzyEqual(_13, o._13) && gfx::FuzzyEqual(_14, o._14) && + gfx::FuzzyEqual(_21, o._21) && gfx::FuzzyEqual(_22, o._22) && + gfx::FuzzyEqual(_23, o._23) && gfx::FuzzyEqual(_24, o._24) && + gfx::FuzzyEqual(_31, o._31) && gfx::FuzzyEqual(_32, o._32) && + gfx::FuzzyEqual(_33, o._33) && gfx::FuzzyEqual(_34, o._34) && + gfx::FuzzyEqual(_41, o._41) && gfx::FuzzyEqual(_42, o._42) && + gfx::FuzzyEqual(_43, o._43) && gfx::FuzzyEqual(_44, o._44); + } + + bool IsBackfaceVisible() const + { + // Inverse()._33 < 0; + Float det = Determinant(); + Float __33 = _12*_24*_41 - _14*_22*_41 + + _14*_21*_42 - _11*_24*_42 - + _12*_21*_44 + _11*_22*_44; + return (__33 * det) < 0; + } + + Matrix4x4 &NudgeToIntegersFixedEpsilon() + { + NudgeToInteger(&_11); + NudgeToInteger(&_12); + NudgeToInteger(&_13); + NudgeToInteger(&_14); + NudgeToInteger(&_21); + NudgeToInteger(&_22); + NudgeToInteger(&_23); + NudgeToInteger(&_24); + NudgeToInteger(&_31); + NudgeToInteger(&_32); + NudgeToInteger(&_33); + NudgeToInteger(&_34); + static const float error = 1e-5f; + NudgeToInteger(&_41, error); + NudgeToInteger(&_42, error); + NudgeToInteger(&_43, error); + NudgeToInteger(&_44, error); + return *this; + } + + Point4D TransposedVector(int aIndex) const + { + MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index"); + return Point4D(*((&_11)+aIndex), *((&_21)+aIndex), *((&_31)+aIndex), *((&_41)+aIndex)); + } + + void SetTransposedVector(int aIndex, Point4D &aVector) + { + MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index"); + *((&_11)+aIndex) = aVector.x; + *((&_21)+aIndex) = aVector.y; + *((&_31)+aIndex) = aVector.z; + *((&_41)+aIndex) = aVector.w; + } + + // Set all the members of the matrix to NaN + void SetNAN(); +}; + +class Matrix5x4 +{ +public: + Matrix5x4() + : _11(1.0f), _12(0), _13(0), _14(0) + , _21(0), _22(1.0f), _23(0), _24(0) + , _31(0), _32(0), _33(1.0f), _34(0) + , _41(0), _42(0), _43(0), _44(1.0f) + , _51(0), _52(0), _53(0), _54(0) + {} + Matrix5x4(Float a11, Float a12, Float a13, Float a14, + Float a21, Float a22, Float a23, Float a24, + Float a31, Float a32, Float a33, Float a34, + Float a41, Float a42, Float a43, Float a44, + Float a51, Float a52, Float a53, Float a54) + : _11(a11), _12(a12), _13(a13), _14(a14) + , _21(a21), _22(a22), _23(a23), _24(a24) + , _31(a31), _32(a32), _33(a33), _34(a34) + , _41(a41), _42(a42), _43(a43), _44(a44) + , _51(a51), _52(a52), _53(a53), _54(a54) + {} + + bool operator==(const Matrix5x4 &o) const + { + return _11 == o._11 && _12 == o._12 && _13 == o._13 && _14 == o._14 && + _21 == o._21 && _22 == o._22 && _23 == o._23 && _24 == o._24 && + _31 == o._31 && _32 == o._32 && _33 == o._33 && _34 == o._34 && + _41 == o._41 && _42 == o._42 && _43 == o._43 && _44 == o._44 && + _51 == o._51 && _52 == o._52 && _53 == o._53 && _54 == o._54; + } + + bool operator!=(const Matrix5x4 &aMatrix) const + { + return !(*this == aMatrix); + } + + Matrix5x4 operator*(const Matrix5x4 &aMatrix) const + { + Matrix5x4 resultMatrix; + + resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21 + this->_13 * aMatrix._31 + this->_14 * aMatrix._41; + resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22 + this->_13 * aMatrix._32 + this->_14 * aMatrix._42; + resultMatrix._13 = this->_11 * aMatrix._13 + this->_12 * aMatrix._23 + this->_13 * aMatrix._33 + this->_14 * aMatrix._43; + resultMatrix._14 = this->_11 * aMatrix._14 + this->_12 * aMatrix._24 + this->_13 * aMatrix._34 + this->_14 * aMatrix._44; + resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21 + this->_23 * aMatrix._31 + this->_24 * aMatrix._41; + resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22 + this->_23 * aMatrix._32 + this->_24 * aMatrix._42; + resultMatrix._23 = this->_21 * aMatrix._13 + this->_22 * aMatrix._23 + this->_23 * aMatrix._33 + this->_24 * aMatrix._43; + resultMatrix._24 = this->_21 * aMatrix._14 + this->_22 * aMatrix._24 + this->_23 * aMatrix._34 + this->_24 * aMatrix._44; + resultMatrix._31 = this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + this->_33 * aMatrix._31 + this->_34 * aMatrix._41; + resultMatrix._32 = this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + this->_33 * aMatrix._32 + this->_34 * aMatrix._42; + resultMatrix._33 = this->_31 * aMatrix._13 + this->_32 * aMatrix._23 + this->_33 * aMatrix._33 + this->_34 * aMatrix._43; + resultMatrix._34 = this->_31 * aMatrix._14 + this->_32 * aMatrix._24 + this->_33 * aMatrix._34 + this->_34 * aMatrix._44; + resultMatrix._41 = this->_41 * aMatrix._11 + this->_42 * aMatrix._21 + this->_43 * aMatrix._31 + this->_44 * aMatrix._41; + resultMatrix._42 = this->_41 * aMatrix._12 + this->_42 * aMatrix._22 + this->_43 * aMatrix._32 + this->_44 * aMatrix._42; + resultMatrix._43 = this->_41 * aMatrix._13 + this->_42 * aMatrix._23 + this->_43 * aMatrix._33 + this->_44 * aMatrix._43; + resultMatrix._44 = this->_41 * aMatrix._14 + this->_42 * aMatrix._24 + this->_43 * aMatrix._34 + this->_44 * aMatrix._44; + resultMatrix._51 = this->_51 * aMatrix._11 + this->_52 * aMatrix._21 + this->_53 * aMatrix._31 + this->_54 * aMatrix._41 + aMatrix._51; + resultMatrix._52 = this->_51 * aMatrix._12 + this->_52 * aMatrix._22 + this->_53 * aMatrix._32 + this->_54 * aMatrix._42 + aMatrix._52; + resultMatrix._53 = this->_51 * aMatrix._13 + this->_52 * aMatrix._23 + this->_53 * aMatrix._33 + this->_54 * aMatrix._43 + aMatrix._53; + resultMatrix._54 = this->_51 * aMatrix._14 + this->_52 * aMatrix._24 + this->_53 * aMatrix._34 + this->_54 * aMatrix._44 + aMatrix._54; + + return resultMatrix; + } + + Matrix5x4& operator*=(const Matrix5x4 &aMatrix) + { + *this = *this * aMatrix; + return *this; + } + + Float _11, _12, _13, _14; + Float _21, _22, _23, _24; + Float _31, _32, _33, _34; + Float _41, _42, _43, _44; + Float _51, _52, _53, _54; }; } diff --git a/gfx/2d/Path.cpp b/gfx/2d/Path.cpp new file mode 100644 index 000000000..d94639523 --- /dev/null +++ b/gfx/2d/Path.cpp @@ -0,0 +1,530 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "2D.h" +#include "PathAnalysis.h" +#include "PathHelpers.h" + +namespace mozilla { +namespace gfx { + +static float CubicRoot(float aValue) { + if (aValue < 0.0) { + return -CubicRoot(-aValue); + } + else { + return powf(aValue, 1.0f / 3.0f); + } +} + +struct BezierControlPoints +{ + BezierControlPoints() {} + BezierControlPoints(const Point &aCP1, const Point &aCP2, + const Point &aCP3, const Point &aCP4) + : mCP1(aCP1), mCP2(aCP2), mCP3(aCP3), mCP4(aCP4) + { + } + + Point mCP1, mCP2, mCP3, mCP4; +}; + +void +FlattenBezier(const BezierControlPoints &aPoints, + PathSink *aSink, Float aTolerance); + + +Path::Path() +{ +} + +Path::~Path() +{ +} + +Float +Path::ComputeLength() +{ + EnsureFlattenedPath(); + return mFlattenedPath->ComputeLength(); +} + +Point +Path::ComputePointAtLength(Float aLength, Point* aTangent) +{ + EnsureFlattenedPath(); + return mFlattenedPath->ComputePointAtLength(aLength, aTangent); +} + +void +Path::EnsureFlattenedPath() +{ + if (!mFlattenedPath) { + mFlattenedPath = new FlattenedPath(); + StreamToSink(mFlattenedPath); + } +} + +// This is the maximum deviation we allow (with an additional ~20% margin of +// error) of the approximation from the actual Bezier curve. +const Float kFlatteningTolerance = 0.0001f; + +void +FlattenedPath::MoveTo(const Point &aPoint) +{ + MOZ_ASSERT(!mCalculatedLength); + FlatPathOp op; + op.mType = FlatPathOp::OP_MOVETO; + op.mPoint = aPoint; + mPathOps.push_back(op); + + mLastMove = aPoint; +} + +void +FlattenedPath::LineTo(const Point &aPoint) +{ + MOZ_ASSERT(!mCalculatedLength); + FlatPathOp op; + op.mType = FlatPathOp::OP_LINETO; + op.mPoint = aPoint; + mPathOps.push_back(op); +} + +void +FlattenedPath::BezierTo(const Point &aCP1, + const Point &aCP2, + const Point &aCP3) +{ + MOZ_ASSERT(!mCalculatedLength); + FlattenBezier(BezierControlPoints(CurrentPoint(), aCP1, aCP2, aCP3), this, kFlatteningTolerance); +} + +void +FlattenedPath::QuadraticBezierTo(const Point &aCP1, + const Point &aCP2) +{ + MOZ_ASSERT(!mCalculatedLength); + // We need to elevate the degree of this quadratic B�zier to cubic, so we're + // going to add an intermediate control point, and recompute control point 1. + // The first and last control points remain the same. + // This formula can be found on http://fontforge.sourceforge.net/bezier.html + Point CP0 = CurrentPoint(); + Point CP1 = (CP0 + aCP1 * 2.0) / 3.0; + Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0; + Point CP3 = aCP2; + + BezierTo(CP1, CP2, CP3); +} + +void +FlattenedPath::Close() +{ + MOZ_ASSERT(!mCalculatedLength); + LineTo(mLastMove); +} + +void +FlattenedPath::Arc(const Point &aOrigin, float aRadius, float aStartAngle, + float aEndAngle, bool aAntiClockwise) +{ + ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise); +} + +Float +FlattenedPath::ComputeLength() +{ + if (!mCalculatedLength) { + Point currentPoint; + + for (uint32_t i = 0; i < mPathOps.size(); i++) { + if (mPathOps[i].mType == FlatPathOp::OP_MOVETO) { + currentPoint = mPathOps[i].mPoint; + } else { + mCachedLength += Distance(currentPoint, mPathOps[i].mPoint); + currentPoint = mPathOps[i].mPoint; + } + } + + mCalculatedLength = true; + } + + return mCachedLength; +} + +Point +FlattenedPath::ComputePointAtLength(Float aLength, Point *aTangent) +{ + // We track the last point that -wasn't- in the same place as the current + // point so if we pass the edge of the path with a bunch of zero length + // paths we still get the correct tangent vector. + Point lastPointSinceMove; + Point currentPoint; + for (uint32_t i = 0; i < mPathOps.size(); i++) { + if (mPathOps[i].mType == FlatPathOp::OP_MOVETO) { + if (Distance(currentPoint, mPathOps[i].mPoint)) { + lastPointSinceMove = currentPoint; + } + currentPoint = mPathOps[i].mPoint; + } else { + Float segmentLength = Distance(currentPoint, mPathOps[i].mPoint); + + if (segmentLength) { + lastPointSinceMove = currentPoint; + if (segmentLength > aLength) { + Point currentVector = mPathOps[i].mPoint - currentPoint; + Point tangent = currentVector / segmentLength; + if (aTangent) { + *aTangent = tangent; + } + return currentPoint + tangent * aLength; + } + } + + aLength -= segmentLength; + currentPoint = mPathOps[i].mPoint; + } + } + + Point currentVector = currentPoint - lastPointSinceMove; + if (aTangent) { + if (hypotf(currentVector.x, currentVector.y)) { + *aTangent = currentVector / hypotf(currentVector.x, currentVector.y); + } else { + *aTangent = Point(); + } + } + return currentPoint; +} + +// This function explicitly permits aControlPoints to refer to the same object +// as either of the other arguments. +static void +SplitBezier(const BezierControlPoints &aControlPoints, + BezierControlPoints *aFirstSegmentControlPoints, + BezierControlPoints *aSecondSegmentControlPoints, + Float t) +{ + MOZ_ASSERT(aSecondSegmentControlPoints); + + *aSecondSegmentControlPoints = aControlPoints; + + Point cp1a = aControlPoints.mCP1 + (aControlPoints.mCP2 - aControlPoints.mCP1) * t; + Point cp2a = aControlPoints.mCP2 + (aControlPoints.mCP3 - aControlPoints.mCP2) * t; + Point cp1aa = cp1a + (cp2a - cp1a) * t; + Point cp3a = aControlPoints.mCP3 + (aControlPoints.mCP4 - aControlPoints.mCP3) * t; + Point cp2aa = cp2a + (cp3a - cp2a) * t; + Point cp1aaa = cp1aa + (cp2aa - cp1aa) * t; + aSecondSegmentControlPoints->mCP4 = aControlPoints.mCP4; + + if(aFirstSegmentControlPoints) { + aFirstSegmentControlPoints->mCP1 = aControlPoints.mCP1; + aFirstSegmentControlPoints->mCP2 = cp1a; + aFirstSegmentControlPoints->mCP3 = cp1aa; + aFirstSegmentControlPoints->mCP4 = cp1aaa; + } + aSecondSegmentControlPoints->mCP1 = cp1aaa; + aSecondSegmentControlPoints->mCP2 = cp2aa; + aSecondSegmentControlPoints->mCP3 = cp3a; +} + +static void +FlattenBezierCurveSegment(const BezierControlPoints &aControlPoints, + PathSink *aSink, + Float aTolerance) +{ + /* The algorithm implemented here is based on: + * http://cis.usouthal.edu/~hain/general/Publications/Bezier/Bezier%20Offset%20Curves.pdf + * + * The basic premise is that for a small t the third order term in the + * equation of a cubic bezier curve is insignificantly small. This can + * then be approximated by a quadratic equation for which the maximum + * difference from a linear approximation can be much more easily determined. + */ + BezierControlPoints currentCP = aControlPoints; + + Float t = 0; + while (t < 1.0f) { + Point cp21 = currentCP.mCP2 - currentCP.mCP3; + Point cp31 = currentCP.mCP3 - currentCP.mCP1; + + Float s3 = (cp31.x * cp21.y - cp31.y * cp21.x) / hypotf(cp21.x, cp21.y); + + t = 2 * Float(sqrt(aTolerance / (3. * std::abs(s3)))); + + if (t >= 1.0f) { + aSink->LineTo(aControlPoints.mCP4); + break; + } + + Point prevCP2, prevCP3, nextCP1, nextCP2, nextCP3; + SplitBezier(currentCP, nullptr, ¤tCP, t); + + aSink->LineTo(currentCP.mCP1); + } +} + +static inline void +FindInflectionApproximationRange(BezierControlPoints aControlPoints, + Float *aMin, Float *aMax, Float aT, + Float aTolerance) +{ + SplitBezier(aControlPoints, nullptr, &aControlPoints, aT); + + Point cp21 = aControlPoints.mCP2 - aControlPoints.mCP1; + Point cp41 = aControlPoints.mCP4 - aControlPoints.mCP1; + + if (cp21.x == 0.f && cp21.y == 0.f) { + // In this case s3 becomes lim[n->0] (cp41.x * n) / n - (cp41.y * n) / n = cp41.x - cp41.y. + + // Use the absolute value so that Min and Max will correspond with the + // minimum and maximum of the range. + *aMin = aT - CubicRoot(std::abs(aTolerance / (cp41.x - cp41.y))); + *aMax = aT + CubicRoot(std::abs(aTolerance / (cp41.x - cp41.y))); + return; + } + + Float s3 = (cp41.x * cp21.y - cp41.y * cp21.x) / hypotf(cp21.x, cp21.y); + + if (s3 == 0) { + // This means within the precision we have it can be approximated + // infinitely by a linear segment. Deal with this by specifying the + // approximation range as extending beyond the entire curve. + *aMin = -1.0f; + *aMax = 2.0f; + return; + } + + Float tf = CubicRoot(std::abs(aTolerance / s3)); + + *aMin = aT - tf * (1 - aT); + *aMax = aT + tf * (1 - aT); +} + +/* Find the inflection points of a bezier curve. Will return false if the + * curve is degenerate in such a way that it is best approximated by a straight + * line. + * + * The below algorithm was written by Jeff Muizelaar <jmuizelaar@mozilla.com>, explanation follows: + * + * The lower inflection point is returned in aT1, the higher one in aT2. In the + * case of a single inflection point this will be in aT1. + * + * The method is inspired by the algorithm in "analysis of in?ection points for planar cubic bezier curve" + * + * Here are some differences between this algorithm and versions discussed elsewhere in the literature: + * + * zhang et. al compute a0, d0 and e0 incrementally using the follow formula: + * + * Point a0 = CP2 - CP1 + * Point a1 = CP3 - CP2 + * Point a2 = CP4 - CP1 + * + * Point d0 = a1 - a0 + * Point d1 = a2 - a1 + + * Point e0 = d1 - d0 + * + * this avoids any multiplications and may or may not be faster than the approach take below. + * + * "fast, precise flattening of cubic bezier path and ofset curves" by hain et. al + * Point a = CP1 + 3 * CP2 - 3 * CP3 + CP4 + * Point b = 3 * CP1 - 6 * CP2 + 3 * CP3 + * Point c = -3 * CP1 + 3 * CP2 + * Point d = CP1 + * the a, b, c, d can be expressed in terms of a0, d0 and e0 defined above as: + * c = 3 * a0 + * b = 3 * d0 + * a = e0 + * + * + * a = 3a = a.y * b.x - a.x * b.y + * b = 3b = a.y * c.x - a.x * c.y + * c = 9c = b.y * c.x - b.x * c.y + * + * The additional multiples of 3 cancel each other out as show below: + * + * x = (-b + sqrt(b * b - 4 * a * c)) / (2 * a) + * x = (-3 * b + sqrt(3 * b * 3 * b - 4 * a * 3 * 9 * c / 3)) / (2 * 3 * a) + * x = 3 * (-b + sqrt(b * b - 4 * a * c)) / (2 * 3 * a) + * x = (-b + sqrt(b * b - 4 * a * c)) / (2 * a) + * + * I haven't looked into whether the formulation of the quadratic formula in + * hain has any numerical advantages over the one used below. + */ +static inline void +FindInflectionPoints(const BezierControlPoints &aControlPoints, + Float *aT1, Float *aT2, uint32_t *aCount) +{ + // Find inflection points. + // See www.faculty.idc.ac.il/arik/quality/appendixa.html for an explanation + // of this approach. + Point A = aControlPoints.mCP2 - aControlPoints.mCP1; + Point B = aControlPoints.mCP3 - (aControlPoints.mCP2 * 2) + aControlPoints.mCP1; + Point C = aControlPoints.mCP4 - (aControlPoints.mCP3 * 3) + (aControlPoints.mCP2 * 3) - aControlPoints.mCP1; + + Float a = Float(B.x) * C.y - Float(B.y) * C.x; + Float b = Float(A.x) * C.y - Float(A.y) * C.x; + Float c = Float(A.x) * B.y - Float(A.y) * B.x; + + if (a == 0) { + // Not a quadratic equation. + if (b == 0) { + // Instead of a linear acceleration change we have a constant + // acceleration change. This means the equation has no solution + // and there are no inflection points, unless the constant is 0. + // In that case the curve is a straight line, essentially that means + // the easiest way to deal with is is by saying there's an inflection + // point at t == 0. The inflection point approximation range found will + // automatically extend into infinity. + if (c == 0) { + *aCount = 1; + *aT1 = 0; + return; + } + *aCount = 0; + return; + } + *aT1 = -c / b; + *aCount = 1; + return; + } else { + Float discriminant = b * b - 4 * a * c; + + if (discriminant < 0) { + // No inflection points. + *aCount = 0; + } else if (discriminant == 0) { + *aCount = 1; + *aT1 = -b / (2 * a); + } else { + /* Use the following formula for computing the roots: + * + * q = -1/2 * (b + sign(b) * sqrt(b^2 - 4ac)) + * t1 = q / a + * t2 = c / q + */ + Float q = sqrtf(discriminant); + if (b < 0) { + q = b - q; + } else { + q = b + q; + } + q *= Float(-1./2); + + *aT1 = q / a; + *aT2 = c / q; + if (*aT1 > *aT2) { + std::swap(*aT1, *aT2); + } + *aCount = 2; + } + } + + return; +} + +void +FlattenBezier(const BezierControlPoints &aControlPoints, + PathSink *aSink, Float aTolerance) +{ + Float t1; + Float t2; + uint32_t count; + + FindInflectionPoints(aControlPoints, &t1, &t2, &count); + + // Check that at least one of the inflection points is inside [0..1] + if (count == 0 || ((t1 < 0 || t1 > 1.0) && ((t2 < 0 || t2 > 1.0) || count == 1)) ) { + FlattenBezierCurveSegment(aControlPoints, aSink, aTolerance); + return; + } + + Float t1min = t1, t1max = t1, t2min = t2, t2max = t2; + + BezierControlPoints remainingCP = aControlPoints; + + // For both inflection points, calulate the range where they can be linearly + // approximated if they are positioned within [0,1] + if (count > 0 && t1 >= 0 && t1 < 1.0) { + FindInflectionApproximationRange(aControlPoints, &t1min, &t1max, t1, aTolerance); + } + if (count > 1 && t2 >= 0 && t2 < 1.0) { + FindInflectionApproximationRange(aControlPoints, &t2min, &t2max, t2, aTolerance); + } + BezierControlPoints nextCPs = aControlPoints; + BezierControlPoints prevCPs; + + // Process ranges. [t1min, t1max] and [t2min, t2max] are approximated by line + // segments. + if (count == 1 && t1min <= 0 && t1max >= 1.0) { + // The whole range can be approximated by a line segment. + aSink->LineTo(aControlPoints.mCP4); + return; + } + + if (t1min > 0) { + // Flatten the Bezier up until the first inflection point's approximation + // point. + SplitBezier(aControlPoints, &prevCPs, + &remainingCP, t1min); + FlattenBezierCurveSegment(prevCPs, aSink, aTolerance); + } + if (t1max >= 0 && t1max < 1.0 && (count == 1 || t2min > t1max)) { + // The second inflection point's approximation range begins after the end + // of the first, approximate the first inflection point by a line and + // subsequently flatten up until the end or the next inflection point. + SplitBezier(aControlPoints, nullptr, &nextCPs, t1max); + + aSink->LineTo(nextCPs.mCP1); + + if (count == 1 || (count > 1 && t2min >= 1.0)) { + // No more inflection points to deal with, flatten the rest of the curve. + FlattenBezierCurveSegment(nextCPs, aSink, aTolerance); + } + } else if (count > 1 && t2min > 1.0) { + // We've already concluded t2min <= t1max, so if this is true the + // approximation range for the first inflection point runs past the + // end of the curve, draw a line to the end and we're done. + aSink->LineTo(aControlPoints.mCP4); + return; + } + + if (count > 1 && t2min < 1.0 && t2max > 0) { + if (t2min > 0 && t2min < t1max) { + // In this case the t2 approximation range starts inside the t1 + // approximation range. + SplitBezier(aControlPoints, nullptr, &nextCPs, t1max); + aSink->LineTo(nextCPs.mCP1); + } else if (t2min > 0 && t1max > 0) { + SplitBezier(aControlPoints, nullptr, &nextCPs, t1max); + + // Find a control points describing the portion of the curve between t1max and t2min. + Float t2mina = (t2min - t1max) / (1 - t1max); + SplitBezier(nextCPs, &prevCPs, &nextCPs, t2mina); + FlattenBezierCurveSegment(prevCPs, aSink, aTolerance); + } else if (t2min > 0) { + // We have nothing interesting before t2min, find that bit and flatten it. + SplitBezier(aControlPoints, &prevCPs, &nextCPs, t2min); + FlattenBezierCurveSegment(prevCPs, aSink, aTolerance); + } + if (t2max < 1.0) { + // Flatten the portion of the curve after t2max + SplitBezier(aControlPoints, nullptr, &nextCPs, t2max); + + // Draw a line to the start, this is the approximation between t2min and + // t2max. + aSink->LineTo(nextCPs.mCP1); + FlattenBezierCurveSegment(nextCPs, aSink, aTolerance); + } else { + // Our approximation range extends beyond the end of the curve. + aSink->LineTo(aControlPoints.mCP4); + return; + } + } +} + +} +} diff --git a/gfx/2d/PathAnalysis.h b/gfx/2d/PathAnalysis.h new file mode 100644 index 000000000..6b9b33f89 --- /dev/null +++ b/gfx/2d/PathAnalysis.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "2D.h" +#include <vector> + +namespace mozilla { +namespace gfx { + +struct FlatPathOp +{ + enum OpType { + OP_MOVETO, + OP_LINETO, + }; + + OpType mType; + Point mPoint; +}; + +class FlattenedPath : public PathSink +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FlattenedPath) + FlattenedPath() : mCachedLength(0) + , mCalculatedLength(false) + { + } + + virtual void MoveTo(const Point &aPoint); + virtual void LineTo(const Point &aPoint); + virtual void BezierTo(const Point &aCP1, + const Point &aCP2, + const Point &aCP3); + virtual void QuadraticBezierTo(const Point &aCP1, + const Point &aCP2); + virtual void Close(); + virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle, + float aEndAngle, bool aAntiClockwise = false); + + virtual Point CurrentPoint() const { return mPathOps.empty() ? Point() : mPathOps[mPathOps.size() - 1].mPoint; } + + Float ComputeLength(); + Point ComputePointAtLength(Float aLength, Point *aTangent); + +private: + Float mCachedLength; + bool mCalculatedLength; + Point mLastMove; + + std::vector<FlatPathOp> mPathOps; +}; + +} +} diff --git a/gfx/2d/PathCG.cpp b/gfx/2d/PathCG.cpp index 191a4039b..0798a6ee3 100644 --- a/gfx/2d/PathCG.cpp +++ b/gfx/2d/PathCG.cpp @@ -7,6 +7,7 @@ #include <math.h> #include "DrawTargetCG.h" #include "Logging.h" +#include "PathHelpers.h" namespace mozilla { namespace gfx { @@ -19,12 +20,19 @@ PathBuilderCG::~PathBuilderCG() void PathBuilderCG::MoveTo(const Point &aPoint) { + if (!aPoint.IsFinite()) { + return; + } CGPathMoveToPoint(mCGPath, nullptr, aPoint.x, aPoint.y); } void PathBuilderCG::LineTo(const Point &aPoint) { + if (!aPoint.IsFinite()) { + return; + } + if (CGPathIsEmpty(mCGPath)) MoveTo(aPoint); else @@ -36,6 +44,9 @@ PathBuilderCG::BezierTo(const Point &aCP1, const Point &aCP2, const Point &aCP3) { + if (!aCP1.IsFinite() || !aCP2.IsFinite() || !aCP3.IsFinite()) { + return; + } if (CGPathIsEmpty(mCGPath)) MoveTo(aCP1); @@ -48,13 +59,17 @@ PathBuilderCG::BezierTo(const Point &aCP1, void PathBuilderCG::QuadraticBezierTo(const Point &aCP1, - const Point &aCP2) + const Point &aCP2) { + if (!aCP1.IsFinite() || !aCP2.IsFinite()) { + return; + } + if (CGPathIsEmpty(mCGPath)) MoveTo(aCP1); CGPathAddQuadCurveToPoint(mCGPath, nullptr, - aCP1.x, aCP1.y, - aCP2.x, aCP2.y); + aCP1.x, aCP1.y, + aCP2.x, aCP2.y); } void @@ -68,13 +83,42 @@ void PathBuilderCG::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle, Float aEndAngle, bool aAntiClockwise) { + if (!aOrigin.IsFinite() || !IsFinite(aRadius) || + !IsFinite(aStartAngle) || !IsFinite(aEndAngle)) { + return; + } + + // Disabled for now due to a CG bug when using CGPathAddArc with stroke + // dashing and rotation transforms that are multiples of 90 degrees. See: + // https://bugzilla.mozilla.org/show_bug.cgi?id=949661#c8 +#if 0 + // Core Graphic's initial coordinate system is y-axis up, whereas Moz2D's is + // y-axis down. Core Graphics therefore considers "clockwise" to mean "sweep + // in the direction of decreasing angle" whereas Moz2D considers it to mean + // "sweep in the direction of increasing angle". In other words if this + // Moz2D method is instructed to sweep anti-clockwise we need to tell + // CGPathAddArc to sweep clockwise, and vice versa. Hence why we pass the + // value of aAntiClockwise directly to CGPathAddArc's "clockwise" bool + // parameter. + CGPathAddArc(mCGPath, nullptr, + aOrigin.x, aOrigin.y, + aRadius, + aStartAngle, + aEndAngle, + aAntiClockwise); +#endif + ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, + aAntiClockwise); } Point PathBuilderCG::CurrentPoint() const { - CGPoint pt = CGPathGetCurrentPoint(mCGPath); - Point ret(pt.x, pt.y); + Point ret; + if (!CGPathIsEmpty(mCGPath)) { + CGPoint pt = CGPathGetCurrentPoint(mCGPath); + ret.MoveTo(pt.x, pt.y); + } return ret; } @@ -86,16 +130,14 @@ PathBuilderCG::EnsureActive(const Point &aPoint) TemporaryRef<Path> PathBuilderCG::Finish() { - RefPtr<PathCG> path = new PathCG(mCGPath, mFillRule); - return path; + return new PathCG(mCGPath, mFillRule); } TemporaryRef<PathBuilder> PathCG::CopyToBuilder(FillRule aFillRule) const { CGMutablePathRef path = CGPathCreateMutableCopy(mPath); - RefPtr<PathBuilderCG> builder = new PathBuilderCG(path, aFillRule); - return builder; + return new PathBuilderCG(path, aFillRule); } @@ -155,10 +197,57 @@ PathCG::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) c ta.transform = GfxMatrixToCGAffineTransform(aTransform); CGPathApply(mPath, &ta, TransformApplier::TranformCGPathApplierFunc); - RefPtr<PathBuilderCG> builder = new PathBuilderCG(ta.path, aFillRule); - return builder; + return new PathBuilderCG(ta.path, aFillRule); } +static void +StreamPathToSinkApplierFunc(void *vinfo, const CGPathElement *element) +{ + PathSink *sink = reinterpret_cast<PathSink*>(vinfo); + switch (element->type) { + case kCGPathElementMoveToPoint: + { + CGPoint pt = element->points[0]; + sink->MoveTo(CGPointToPoint(pt)); + break; + } + case kCGPathElementAddLineToPoint: + { + CGPoint pt = element->points[0]; + sink->LineTo(CGPointToPoint(pt)); + break; + } + case kCGPathElementAddQuadCurveToPoint: + { + CGPoint cpt = element->points[0]; + CGPoint pt = element->points[1]; + sink->QuadraticBezierTo(CGPointToPoint(cpt), + CGPointToPoint(pt)); + break; + } + case kCGPathElementAddCurveToPoint: + { + CGPoint cpt1 = element->points[0]; + CGPoint cpt2 = element->points[1]; + CGPoint pt = element->points[2]; + sink->BezierTo(CGPointToPoint(cpt1), + CGPointToPoint(cpt2), + CGPointToPoint(pt)); + break; + } + case kCGPathElementCloseSubpath: + { + sink->Close(); + break; + } + } +} + +void +PathCG::StreamToSink(PathSink *aSink) const +{ + CGPathApply(mPath, aSink, StreamPathToSinkApplierFunc); +} bool PathCG::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const @@ -171,7 +260,7 @@ PathCG::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const // The transform parameter of CGPathContainsPoint doesn't seem to work properly on OS X 10.5 // so we transform aPoint ourselves. - return CGPathContainsPoint(mPath, nullptr, point, mFillRule == FILL_EVEN_ODD); + return CGPathContainsPoint(mPath, nullptr, point, mFillRule == FillRule::FILL_EVEN_ODD); } static size_t @@ -235,7 +324,8 @@ PathCG::GetBounds(const Matrix &aTransform) const { //XXX: are these bounds tight enough Rect bounds = CGRectToRect(CGPathGetBoundingBox(mPath)); - //XXX: curretnly this returns the bounds of the transformed bounds + + //XXX: currently this returns the bounds of the transformed bounds // this is strictly looser than the bounds of the transformed path return aTransform.TransformBounds(bounds); } @@ -260,6 +350,10 @@ PathCG::GetStrokedBounds(const StrokeOptions &aStrokeOptions, CGContextRestoreGState(cg); + if (!bounds.IsFinite()) { + return Rect(); + } + return aTransform.TransformBounds(bounds); } diff --git a/gfx/2d/PathCG.h b/gfx/2d/PathCG.h index 660e91dc0..9598edb87 100644 --- a/gfx/2d/PathCG.h +++ b/gfx/2d/PathCG.h @@ -17,6 +17,7 @@ class PathCG; class PathBuilderCG : public PathBuilder { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderCG) // absorbs a reference of aPath PathBuilderCG(CGMutablePathRef aPath, FillRule aFillRule) : mFillRule(aFillRule) @@ -24,7 +25,7 @@ public: mCGPath = aPath; } - PathBuilderCG(FillRule aFillRule) + explicit PathBuilderCG(FillRule aFillRule) : mFillRule(aFillRule) { mCGPath = CGPathCreateMutable(); @@ -46,8 +47,11 @@ public: virtual TemporaryRef<Path> Finish(); + virtual BackendType GetBackendType() const { return BackendType::COREGRAPHICS; } + private: friend class PathCG; + friend class ScaledFontMac; void EnsureActive(const Point &aPoint); @@ -60,6 +64,7 @@ private: class PathCG : public Path { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathCG) PathCG(CGMutablePathRef aPath, FillRule aFillRule) : mPath(aPath) , mFillRule(aFillRule) @@ -68,13 +73,13 @@ public: } virtual ~PathCG() { CGPathRelease(mPath); } - // Paths will always return BACKEND_COREGRAPHICS, but note that they - // are compatible with BACKEND_COREGRAPHICS_ACCELERATED backend. - virtual BackendType GetBackendType() const { return BACKEND_COREGRAPHICS; } + // Paths will always return BackendType::COREGRAPHICS, but note that they + // are compatible with BackendType::COREGRAPHICS_ACCELERATED backend. + virtual BackendType GetBackendType() const { return BackendType::COREGRAPHICS; } - virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const; + virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const; virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform, - FillRule aFillRule = FILL_WINDING) const; + FillRule aFillRule = FillRule::FILL_WINDING) const; virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const; virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions, @@ -84,6 +89,8 @@ public: virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions, const Matrix &aTransform = Matrix()) const; + virtual void StreamToSink(PathSink *aSink) const; + virtual FillRule GetFillRule() const { return mFillRule; } CGMutablePathRef GetPath() const { return mPath; } @@ -92,7 +99,6 @@ private: friend class DrawTargetCG; CGMutablePathRef mPath; - bool mEndedActive; Point mEndPoint; FillRule mFillRule; }; diff --git a/gfx/2d/PathCairo.cpp b/gfx/2d/PathCairo.cpp index c2c370b56..d0c9d056d 100644 --- a/gfx/2d/PathCairo.cpp +++ b/gfx/2d/PathCairo.cpp @@ -13,138 +13,37 @@ namespace mozilla { namespace gfx { -CairoPathContext::CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget) - : mContext(aCtx) - , mDrawTarget(aDrawTarget) +PathBuilderCairo::PathBuilderCairo(FillRule aFillRule) + : mFillRule(aFillRule) { - cairo_reference(mContext); - - // A new path in the DrawTarget's context. - aDrawTarget->SetPathObserver(this); - cairo_new_path(mContext); -} - -CairoPathContext::CairoPathContext(CairoPathContext& aPathContext) - : mContext(aPathContext.mContext) - , mDrawTarget(nullptr) -{ - cairo_reference(mContext); - DuplicateContextAndPath(); -} - -CairoPathContext::~CairoPathContext() -{ - if (mDrawTarget) { - DrawTargetCairo* drawTarget = mDrawTarget; - ForgetDrawTarget(); - - // We need to set mDrawTarget to nullptr before we tell DrawTarget otherwise - // we will think we need to make a defensive copy of the path. - drawTarget->SetPathObserver(nullptr); - } - cairo_destroy(mContext); -} - -void -CairoPathContext::DuplicateContextAndPath() -{ - // Duplicate the path. - cairo_path_t* path = cairo_copy_path(mContext); - - // Duplicate the context. - cairo_surface_t* surf = cairo_get_target(mContext); - cairo_matrix_t matrix; - cairo_get_matrix(mContext, &matrix); - cairo_destroy(mContext); - - mContext = cairo_create(surf); - - // Set the matrix to match the source context so that the path is copied in - // device space. After this point it doesn't matter what the transform is - // set to because it's always swapped out before use. - cairo_set_matrix(mContext, &matrix); - - // Add the path, and throw away our duplicate. - cairo_append_path(mContext, path); - cairo_path_destroy(path); } void -CairoPathContext::ForgetDrawTarget() -{ - // We don't need to set the path observer back to nullptr in this case - // because ForgetDrawTarget() is trigged when the target has been - // grabbed by another path observer. - mDrawTarget = nullptr; -} - -void -CairoPathContext::PathWillChange() -{ - // Once we've copied out the context's path, there's no use to holding on to - // the draw target. Thus, there's nothing for us to do if we're independent - // of the draw target, since we'll have already copied out the context's - // path. - if (mDrawTarget) { - // The context we point to is going to change from under us. To continue - // using this path, we need to copy it to a new context. - DuplicateContextAndPath(); - ForgetDrawTarget(); - } -} - -void -CairoPathContext::CopyPathTo(cairo_t* aToContext, Matrix& aTransform) -{ - if (aToContext != mContext) { - CairoTempMatrix tempMatrix(mContext, aTransform); - cairo_path_t* path = cairo_copy_path(mContext); - cairo_new_path(aToContext); - cairo_append_path(aToContext, path); - cairo_path_destroy(path); - } -} - -bool -CairoPathContext::ContainsPath(const Path* aPath) -{ - if (aPath->GetBackendType() != BACKEND_CAIRO) { - return false; - } - - const PathCairo* path = static_cast<const PathCairo*>(aPath); - RefPtr<CairoPathContext> ctx = const_cast<PathCairo*>(path)->GetPathContext(); - return ctx == this; -} - -PathBuilderCairo::PathBuilderCairo(CairoPathContext* aPathContext, - FillRule aFillRule, - const Matrix& aTransform /* = Matrix() */) - : mPathContext(aPathContext) - , mTransform(aTransform) - , mFillRule(aFillRule) -{} - -PathBuilderCairo::PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule) - : mPathContext(new CairoPathContext(aCtx, aDrawTarget)) - , mTransform(aDrawTarget->GetTransform()) - , mFillRule(aFillRule) -{} - -void PathBuilderCairo::MoveTo(const Point &aPoint) { - PrepareForWrite(); - CairoTempMatrix tempMatrix(*mPathContext, mTransform); - cairo_move_to(*mPathContext, aPoint.x, aPoint.y); + cairo_path_data_t data; + data.header.type = CAIRO_PATH_MOVE_TO; + data.header.length = 2; + mPathData.push_back(data); + data.point.x = aPoint.x; + data.point.y = aPoint.y; + mPathData.push_back(data); + + mBeginPoint = mCurrentPoint = aPoint; } void PathBuilderCairo::LineTo(const Point &aPoint) { - PrepareForWrite(); - CairoTempMatrix tempMatrix(*mPathContext, mTransform); - cairo_line_to(*mPathContext, aPoint.x, aPoint.y); + cairo_path_data_t data; + data.header.type = CAIRO_PATH_LINE_TO; + data.header.length = 2; + mPathData.push_back(data); + data.point.x = aPoint.x; + data.point.y = aPoint.y; + mPathData.push_back(data); + + mCurrentPoint = aPoint; } void @@ -152,18 +51,27 @@ PathBuilderCairo::BezierTo(const Point &aCP1, const Point &aCP2, const Point &aCP3) { - PrepareForWrite(); - CairoTempMatrix tempMatrix(*mPathContext, mTransform); - cairo_curve_to(*mPathContext, aCP1.x, aCP1.y, aCP2.x, aCP2.y, aCP3.x, aCP3.y); + cairo_path_data_t data; + data.header.type = CAIRO_PATH_CURVE_TO; + data.header.length = 4; + mPathData.push_back(data); + data.point.x = aCP1.x; + data.point.y = aCP1.y; + mPathData.push_back(data); + data.point.x = aCP2.x; + data.point.y = aCP2.y; + mPathData.push_back(data); + data.point.x = aCP3.x; + data.point.y = aCP3.y; + mPathData.push_back(data); + + mCurrentPoint = aCP3; } void PathBuilderCairo::QuadraticBezierTo(const Point &aCP1, const Point &aCP2) { - PrepareForWrite(); - CairoTempMatrix tempMatrix(*mPathContext, mTransform); - // We need to elevate the degree of this quadratic Bézier to cubic, so we're // going to add an intermediate control point, and recompute control point 1. // The first and last control points remain the same. @@ -173,94 +81,116 @@ PathBuilderCairo::QuadraticBezierTo(const Point &aCP1, Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0; Point CP3 = aCP2; - cairo_curve_to(*mPathContext, CP1.x, CP1.y, CP2.x, CP2.y, CP3.x, CP3.y); + cairo_path_data_t data; + data.header.type = CAIRO_PATH_CURVE_TO; + data.header.length = 4; + mPathData.push_back(data); + data.point.x = CP1.x; + data.point.y = CP1.y; + mPathData.push_back(data); + data.point.x = CP2.x; + data.point.y = CP2.y; + mPathData.push_back(data); + data.point.x = CP3.x; + data.point.y = CP3.y; + mPathData.push_back(data); + + mCurrentPoint = aCP2; } void PathBuilderCairo::Close() { - PrepareForWrite(); - cairo_close_path(*mPathContext); + cairo_path_data_t data; + data.header.type = CAIRO_PATH_CLOSE_PATH; + data.header.length = 1; + mPathData.push_back(data); + + mCurrentPoint = mBeginPoint; } void PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle, float aEndAngle, bool aAntiClockwise) { - ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise); + ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise); } Point PathBuilderCairo::CurrentPoint() const { - CairoTempMatrix tempMatrix(*mPathContext, mTransform); - double x, y; - cairo_get_current_point(*mPathContext, &x, &y); - return Point((Float)x, (Float)y); + return mCurrentPoint; } TemporaryRef<Path> PathBuilderCairo::Finish() { - return new PathCairo(mPathContext, mTransform, mFillRule); + return new PathCairo(mFillRule, mPathData, mCurrentPoint); } -TemporaryRef<CairoPathContext> -PathBuilderCairo::GetPathContext() +PathCairo::PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint) + : mFillRule(aFillRule) + , mContainingContext(nullptr) + , mCurrentPoint(aCurrentPoint) { - return mPathContext; + mPathData.swap(aPathData); } -void -PathBuilderCairo::PrepareForWrite() +PathCairo::PathCairo(cairo_t *aContext) + : mFillRule(FillRule::FILL_WINDING) + , mContainingContext(nullptr) { - // Only PathBuilder and PathCairo maintain references to CairoPathContext. - // DrawTarget does not. If we're sharing a reference to the context then we - // need to create a copy that we can modify. This provides copy on write - // behaviour. - if (mPathContext->refCount() != 1) { - mPathContext = new CairoPathContext(*mPathContext); + cairo_path_t *path = cairo_copy_path(aContext); + + // XXX - mCurrentPoint is not properly set here, the same is true for the + // D2D Path code, we never require current point when hitting this codepath + // but this should be fixed. + for (int i = 0; i < path->num_data; i++) { + mPathData.push_back(path->data[i]); } + + cairo_path_destroy(path); } -PathCairo::PathCairo(CairoPathContext* aPathContext, Matrix& aTransform, - FillRule aFillRule) - : mPathContext(aPathContext) - , mTransform(aTransform) - , mFillRule(aFillRule) -{} +PathCairo::~PathCairo() +{ + if (mContainingContext) { + cairo_destroy(mContainingContext); + } +} TemporaryRef<PathBuilder> PathCairo::CopyToBuilder(FillRule aFillRule) const { - return new PathBuilderCairo(mPathContext, aFillRule, mTransform); + RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule); + + builder->mPathData = mPathData; + builder->mCurrentPoint = mCurrentPoint; + + return builder.forget(); } TemporaryRef<PathBuilder> PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const { - // We are given the transform we would apply from device space to user space. - // However in cairo our path is in device space so we view the transform as - // being the other way round. We therefore need to apply the inverse transform - // to our current cairo transform. - Matrix inverse = aTransform; - inverse.Invert(); + RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule); + + AppendPathToBuilder(builder, &aTransform); + builder->mCurrentPoint = aTransform * mCurrentPoint; - return new PathBuilderCairo(mPathContext, aFillRule, mTransform * inverse); + return builder.forget(); } bool PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const { - CairoTempMatrix(*mPathContext, mTransform); - Matrix inverse = aTransform; inverse.Invert(); Point transformed = inverse * aPoint; - // Needs the correct fill rule set. - cairo_set_fill_rule(*mPathContext, GfxFillRuleToCairoFillRule(mFillRule)); - return cairo_in_fill(*mPathContext, transformed.x, transformed.y); + EnsureContainingContext(); + + return cairo_in_fill(mContainingContext, transformed.x, transformed.y); } bool @@ -268,24 +198,25 @@ PathCairo::StrokeContainsPoint(const StrokeOptions &aStrokeOptions, const Point &aPoint, const Matrix &aTransform) const { - CairoTempMatrix(*mPathContext, mTransform); - Matrix inverse = aTransform; inverse.Invert(); Point transformed = inverse * aPoint; - SetCairoStrokeOptions(*mPathContext, aStrokeOptions); - return cairo_in_stroke(*mPathContext, transformed.x, transformed.y); + EnsureContainingContext(); + + SetCairoStrokeOptions(mContainingContext, aStrokeOptions); + + return cairo_in_stroke(mContainingContext, transformed.x, transformed.y); } Rect PathCairo::GetBounds(const Matrix &aTransform) const { - CairoTempMatrix(*mPathContext, mTransform); + EnsureContainingContext(); double x1, y1, x2, y2; - cairo_path_extents(*mPathContext, &x1, &y1, &x2, &y2); + cairo_path_extents(mContainingContext, &x1, &y1, &x2, &y2); Rect bounds(Float(x1), Float(y1), Float(x2 - x1), Float(y2 - y1)); return aTransform.TransformBounds(bounds); } @@ -294,28 +225,98 @@ Rect PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions, const Matrix &aTransform) const { - CairoTempMatrix(*mPathContext, mTransform); + EnsureContainingContext(); double x1, y1, x2, y2; - SetCairoStrokeOptions(*mPathContext, aStrokeOptions); + SetCairoStrokeOptions(mContainingContext, aStrokeOptions); - cairo_stroke_extents(*mPathContext, &x1, &y1, &x2, &y2); + cairo_stroke_extents(mContainingContext, &x1, &y1, &x2, &y2); Rect bounds((Float)x1, (Float)y1, (Float)(x2 - x1), (Float)(y2 - y1)); return aTransform.TransformBounds(bounds); } -TemporaryRef<CairoPathContext> -PathCairo::GetPathContext() +void +PathCairo::StreamToSink(PathSink *aSink) const +{ + for (size_t i = 0; i < mPathData.size(); i++) { + switch (mPathData[i].header.type) { + case CAIRO_PATH_MOVE_TO: + i++; + aSink->MoveTo(Point(mPathData[i].point.x, mPathData[i].point.y)); + break; + case CAIRO_PATH_LINE_TO: + i++; + aSink->LineTo(Point(mPathData[i].point.x, mPathData[i].point.y)); + break; + case CAIRO_PATH_CURVE_TO: + aSink->BezierTo(Point(mPathData[i + 1].point.x, mPathData[i + 1].point.y), + Point(mPathData[i + 2].point.x, mPathData[i + 2].point.y), + Point(mPathData[i + 3].point.x, mPathData[i + 3].point.y)); + i += 3; + break; + case CAIRO_PATH_CLOSE_PATH: + aSink->Close(); + break; + default: + // Corrupt path data! + MOZ_ASSERT(false); + } + } +} + +void +PathCairo::EnsureContainingContext() const { - return mPathContext; + if (mContainingContext) { + return; + } + + mContainingContext = cairo_create(DrawTargetCairo::GetDummySurface()); + + SetPathOnContext(mContainingContext); } void -PathCairo::CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget) +PathCairo::SetPathOnContext(cairo_t *aContext) const { - mPathContext->CopyPathTo(aContext, mTransform); + // Needs the correct fill rule set. cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule)); + + cairo_new_path(aContext); + + if (mPathData.size()) { + cairo_path_t path; + path.data = const_cast<cairo_path_data_t*>(&mPathData.front()); + path.num_data = mPathData.size(); + path.status = CAIRO_STATUS_SUCCESS; + cairo_append_path(aContext, &path); + } +} + +void +PathCairo::AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform) const +{ + if (aTransform) { + size_t i = 0; + while (i < mPathData.size()) { + uint32_t pointCount = mPathData[i].header.length - 1; + aBuilder->mPathData.push_back(mPathData[i]); + i++; + for (uint32_t c = 0; c < pointCount; c++) { + cairo_path_data_t data; + Point newPoint = *aTransform * Point(mPathData[i].point.x, mPathData[i].point.y); + data.point.x = newPoint.x; + data.point.y = newPoint.y; + aBuilder->mPathData.push_back(data); + i++; + } + } + } else { + for (size_t i = 0; i < mPathData.size(); i++) { + aBuilder->mPathData.push_back(mPathData[i]); + } + } } } diff --git a/gfx/2d/PathCairo.h b/gfx/2d/PathCairo.h index 50cb366cf..898181242 100644 --- a/gfx/2d/PathCairo.h +++ b/gfx/2d/PathCairo.h @@ -8,86 +8,19 @@ #include "2D.h" #include "cairo.h" +#include <vector> namespace mozilla { namespace gfx { class DrawTargetCairo; - -// A reference to a cairo context that can maintain and set a path. -// -// This class exists to make it possible for us to not construct paths manually -// using cairo_path_t, which in the common case is a speed and memory -// optimization (as the cairo_t maintains the path for us, and we don't have to -// use cairo_append_path). Instead, we can share a cairo_t with a DrawTarget, -// and have it inform us when we need to make a copy of the path. -// -// Exactly one Path* object represents the current path on a given DrawTarget's -// context. That Path* object registers its CairoPathContext with the -// DrawTarget it's associated with. If that DrawTarget is going to change its -// path, it has to tell the CairoPathContext beforehand so the path can be -// saved off. -// The path ownership is transferred to every new instance of CairoPathContext -// in the constructor. We inform the draw target of the new context object, -// which causes us to save off a copy of the path, as we're not going to be -// informed upon changes any more. -// Any transformation on aCtx is not applied to this path, though a path can be -// transformed separately from its context by passing a matrix to the -// constructor. -class CairoPathContext : public RefCounted<CairoPathContext> -{ -public: - // Construct a new empty CairoPathContext that uses the given draw target and - // its cairo context. Using the existing context may save having to copy the - // path later. - CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget); - - // Copy the path. - CairoPathContext(CairoPathContext& aPathContext); - - ~CairoPathContext(); - - // Copy the path on mContext to be the path on aToContext, if they aren't the - // same. At this point we set the fill rule for the destination context as - // there is little point in doing this earlier. - void CopyPathTo(cairo_t* aToContext, Matrix& aTransform); - - // This method must be called by the draw target before it changes the path - // currently on the cairo context. - void PathWillChange(); - - // This method must be called as the draw target is dying. In this case, we - // forget our reference to the draw target, and become the only reference to - // our context. - void ForgetDrawTarget(); - - // Create a duplicate context, and copy this path to that context. - void DuplicateContextAndPath(); - - // Returns true if this CairoPathContext represents path. - bool ContainsPath(const Path* path); - - cairo_t* GetContext() const { return mContext; } - DrawTargetCairo* GetDrawTarget() const { return mDrawTarget; } - operator cairo_t* () const { return mContext; } - -private: // data - cairo_t* mContext; - // Not a RefPtr to avoid cycles. - DrawTargetCairo* mDrawTarget; -}; +class PathCairo; class PathBuilderCairo : public PathBuilder { public: - // Creates a new empty path. It also implicitly takes ownership of aCtx by - // calling aDrawTarget->SetPathObserver(). Therefore, if the draw target has a - // path observer, this constructor will cause it to copy out its path. - PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule); - - // Creates a path builder out of an existing CairoPathContext with a new fill - // rule and transform. - PathBuilderCairo(CairoPathContext* aContext, FillRule aFillRule, const Matrix& aTransform = Matrix()); + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderCairo) + explicit PathBuilderCairo(FillRule aFillRule); virtual void MoveTo(const Point &aPoint); virtual void LineTo(const Point &aPoint); @@ -102,26 +35,32 @@ public: virtual Point CurrentPoint() const; virtual TemporaryRef<Path> Finish(); - TemporaryRef<CairoPathContext> GetPathContext(); + virtual BackendType GetBackendType() const { return BackendType::CAIRO; } private: // data - void PrepareForWrite(); + friend class PathCairo; - RefPtr<CairoPathContext> mPathContext; - Matrix mTransform; FillRule mFillRule; + std::vector<cairo_path_data_t> mPathData; + // It's easiest to track this here, parsing the path data to find the current + // point is a little tricky. + Point mCurrentPoint; + Point mBeginPoint; }; class PathCairo : public Path { public: - PathCairo(CairoPathContext* aPathContex, Matrix& aTransform, FillRule aFillRule); + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathCairo) + PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint); + explicit PathCairo(cairo_t *aContext); + ~PathCairo(); - virtual BackendType GetBackendType() const { return BACKEND_CAIRO; } + virtual BackendType GetBackendType() const { return BackendType::CAIRO; } - virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const; + virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const; virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform, - FillRule aFillRule = FILL_WINDING) const; + FillRule aFillRule = FillRule::FILL_WINDING) const; virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const; @@ -134,19 +73,20 @@ public: virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions, const Matrix &aTransform = Matrix()) const; - virtual FillRule GetFillRule() const { return mFillRule; } + virtual void StreamToSink(PathSink *aSink) const; - TemporaryRef<CairoPathContext> GetPathContext(); + virtual FillRule GetFillRule() const { return mFillRule; } - // Set this path to be the current path for aContext (if it's not already - // aContext's path). You must pass the draw target associated with the - // context as aDrawTarget. - void CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget); + void SetPathOnContext(cairo_t *aContext) const; + void AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform = nullptr) const; private: - RefPtr<CairoPathContext> mPathContext; - Matrix mTransform; + void EnsureContainingContext() const; + FillRule mFillRule; + std::vector<cairo_path_data_t> mPathData; + mutable cairo_t *mContainingContext; + Point mCurrentPoint; }; } diff --git a/gfx/2d/PathD2D.cpp b/gfx/2d/PathD2D.cpp index cd4d25471..e18dae8a5 100644 --- a/gfx/2d/PathD2D.cpp +++ b/gfx/2d/PathD2D.cpp @@ -91,6 +91,70 @@ private: bool mNeedsFigureEnded; }; +class StreamingGeometrySink : public ID2D1SimplifiedGeometrySink +{ +public: + StreamingGeometrySink(PathSink *aSink) + : mSink(aSink) + { + } + + HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr) + { + if (!aPtr) { + return E_POINTER; + } + + if (aIID == IID_IUnknown) { + *aPtr = static_cast<IUnknown*>(this); + return S_OK; + } else if (aIID == IID_ID2D1SimplifiedGeometrySink) { + *aPtr = static_cast<ID2D1SimplifiedGeometrySink*>(this); + return S_OK; + } + + return E_NOINTERFACE; + } + + ULONG STDMETHODCALLTYPE AddRef() + { + return 1; + } + + ULONG STDMETHODCALLTYPE Release() + { + return 1; + } + + // We ignore SetFillMode, this depends on the destination sink. + STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE aMode) + { return; } + STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F aPoint, D2D1_FIGURE_BEGIN aBegin) + { mSink->MoveTo(ToPoint(aPoint)); } + STDMETHOD_(void, AddLines)(const D2D1_POINT_2F *aLines, UINT aCount) + { for (UINT i = 0; i < aCount; i++) { mSink->LineTo(ToPoint(aLines[i])); } } + STDMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *aSegments, UINT aCount) + { + for (UINT i = 0; i < aCount; i++) { + mSink->BezierTo(ToPoint(aSegments[i].point1), ToPoint(aSegments[i].point2), ToPoint(aSegments[i].point3)); + } + } + STDMETHOD(Close)() + { /* Should never be called! */ return S_OK; } + STDMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT aFlags) + { /* Should never be called! */ } + + STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END aEnd) + { + if (aEnd == D2D1_FIGURE_END_CLOSED) { + return mSink->Close(); + } + } +private: + + PathSink *mSink; +}; + PathBuilderD2D::~PathBuilderD2D() { } @@ -235,11 +299,11 @@ PathBuilderD2D::Finish() HRESULT hr = mSink->Close(); if (FAILED(hr)) { - gfxDebug() << "Failed to close PathSink. Code: " << hr; + gfxDebug() << "Failed to close PathSink. Code: " << hexa(hr); return nullptr; } - return new PathD2D(mGeometry, mFigureActive, mCurrentPoint, mFillRule); + return new PathD2D(mGeometry, mFigureActive, mCurrentPoint, mFillRule, mBackendType); } TemporaryRef<PathBuilder> @@ -255,18 +319,18 @@ PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) HRESULT hr = DrawTargetD2D::factory()->CreatePathGeometry(byRef(path)); if (FAILED(hr)) { - gfxWarning() << "Failed to create PathGeometry. Code: " << hr; + gfxWarning() << "Failed to create PathGeometry. Code: " << hexa(hr); return nullptr; } RefPtr<ID2D1GeometrySink> sink; hr = path->Open(byRef(sink)); if (FAILED(hr)) { - gfxWarning() << "Failed to open Geometry for writing. Code: " << hr; + gfxWarning() << "Failed to open Geometry for writing. Code: " << hexa(hr); return nullptr; } - if (aFillRule == FILL_WINDING) { + if (aFillRule == FillRule::FILL_WINDING) { sink->SetFillMode(D2D1_FILL_MODE_WINDING); } @@ -281,7 +345,7 @@ PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) sink); } - RefPtr<PathBuilderD2D> pathBuilder = new PathBuilderD2D(sink, path, mFillRule); + RefPtr<PathBuilderD2D> pathBuilder = new PathBuilderD2D(sink, path, aFillRule, mBackendType); pathBuilder->mCurrentPoint = aTransform * mEndPoint; @@ -289,9 +353,25 @@ PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) pathBuilder->mFigureActive = true; } - return pathBuilder; + return pathBuilder.forget(); } +void +PathD2D::StreamToSink(PathSink *aSink) const +{ + HRESULT hr; + + StreamingGeometrySink sink(aSink); + + hr = mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES, + D2D1::IdentityMatrix(), &sink); + + if (FAILED(hr)) { + gfxWarning() << "Failed to stream D2D path to sink. Code: " << hexa(hr); + return; + } +} + bool PathD2D::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const { @@ -332,35 +412,37 @@ PathD2D::StrokeContainsPoint(const StrokeOptions &aStrokeOptions, Rect PathD2D::GetBounds(const Matrix &aTransform) const { - D2D1_RECT_F bounds; + D2D1_RECT_F d2dBounds; - HRESULT hr = mGeometry->GetBounds(D2DMatrix(aTransform), &bounds); + HRESULT hr = mGeometry->GetBounds(D2DMatrix(aTransform), &d2dBounds); - if (FAILED(hr)) { - gfxWarning() << "Failed to get stroked bounds for path. Code: " << hr; - bounds.bottom = bounds.left = bounds.right = bounds.top = 0; + Rect bounds = ToRect(d2dBounds); + if (FAILED(hr) || !bounds.IsFinite()) { + gfxWarning() << "Failed to get stroked bounds for path. Code: " << hexa(hr); + return Rect(); } - return ToRect(bounds); + return bounds; } Rect PathD2D::GetStrokedBounds(const StrokeOptions &aStrokeOptions, const Matrix &aTransform) const { - D2D1_RECT_F bounds; + D2D1_RECT_F d2dBounds; RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); HRESULT hr = mGeometry->GetWidenedBounds(aStrokeOptions.mLineWidth, strokeStyle, - D2DMatrix(aTransform), &bounds); + D2DMatrix(aTransform), &d2dBounds); - if (FAILED(hr)) { - gfxWarning() << "Failed to get stroked bounds for path. Code: " << hr; - bounds.bottom = bounds.left = bounds.right = bounds.top = 0; + Rect bounds = ToRect(d2dBounds); + if (FAILED(hr) || !bounds.IsFinite()) { + gfxWarning() << "Failed to get stroked bounds for path. Code: " << hexa(hr); + return Rect(); } - return ToRect(bounds); + return bounds; } } diff --git a/gfx/2d/PathD2D.h b/gfx/2d/PathD2D.h index 9b72b6cdb..3afd82783 100644 --- a/gfx/2d/PathD2D.h +++ b/gfx/2d/PathD2D.h @@ -6,8 +6,9 @@ #ifndef MOZILLA_GFX_PATHD2D_H_ #define MOZILLA_GFX_PATHD2D_H_ +#include <d2d1.h> + #include "2D.h" -#include "moz-d2d1-1.h" namespace mozilla { namespace gfx { @@ -17,11 +18,13 @@ class PathD2D; class PathBuilderD2D : public PathBuilder { public: - PathBuilderD2D(ID2D1GeometrySink *aSink, ID2D1PathGeometry *aGeom, FillRule aFillRule) + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderD2D) + PathBuilderD2D(ID2D1GeometrySink *aSink, ID2D1PathGeometry *aGeom, FillRule aFillRule, BackendType aBackendType) : mSink(aSink) , mGeometry(aGeom) , mFigureActive(false) , mFillRule(aFillRule) + , mBackendType(aBackendType) { } virtual ~PathBuilderD2D(); @@ -40,6 +43,8 @@ public: virtual TemporaryRef<Path> Finish(); + virtual BackendType GetBackendType() const { return mBackendType; } + ID2D1GeometrySink *GetSink() { return mSink; } private: @@ -54,24 +59,27 @@ private: Point mCurrentPoint; Point mBeginPoint; FillRule mFillRule; + BackendType mBackendType; }; class PathD2D : public Path { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathD2D) PathD2D(ID2D1PathGeometry *aGeometry, bool aEndedActive, - const Point &aEndPoint, FillRule aFillRule) + const Point &aEndPoint, FillRule aFillRule, BackendType aBackendType) : mGeometry(aGeometry) , mEndedActive(aEndedActive) , mEndPoint(aEndPoint) , mFillRule(aFillRule) + , mBackendType(aBackendType) {} - virtual BackendType GetBackendType() const { return BACKEND_DIRECT2D; } + virtual BackendType GetBackendType() const { return mBackendType; } - virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const; + virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const; virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform, - FillRule aFillRule = FILL_WINDING) const; + FillRule aFillRule = FillRule::FILL_WINDING) const; virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const; @@ -84,17 +92,21 @@ public: virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions, const Matrix &aTransform = Matrix()) const; + virtual void StreamToSink(PathSink *aSink) const; + virtual FillRule GetFillRule() const { return mFillRule; } ID2D1Geometry *GetGeometry() { return mGeometry; } private: friend class DrawTargetD2D; + friend class DrawTargetD2D1; mutable RefPtr<ID2D1PathGeometry> mGeometry; bool mEndedActive; Point mEndPoint; FillRule mFillRule; + BackendType mBackendType; }; } diff --git a/gfx/2d/PathHelpers.cpp b/gfx/2d/PathHelpers.cpp new file mode 100644 index 000000000..b9c0a3c1f --- /dev/null +++ b/gfx/2d/PathHelpers.cpp @@ -0,0 +1,243 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "PathHelpers.h" + +namespace mozilla { +namespace gfx { + +UserDataKey sDisablePixelSnapping; + +void +AppendRectToPath(PathBuilder* aPathBuilder, + const Rect& aRect, + bool aDrawClockwise) +{ + if (aDrawClockwise) { + aPathBuilder->MoveTo(aRect.TopLeft()); + aPathBuilder->LineTo(aRect.TopRight()); + aPathBuilder->LineTo(aRect.BottomRight()); + aPathBuilder->LineTo(aRect.BottomLeft()); + } else { + aPathBuilder->MoveTo(aRect.TopRight()); + aPathBuilder->LineTo(aRect.TopLeft()); + aPathBuilder->LineTo(aRect.BottomLeft()); + aPathBuilder->LineTo(aRect.BottomRight()); + } + aPathBuilder->Close(); +} + +void +AppendRoundedRectToPath(PathBuilder* aPathBuilder, + const Rect& aRect, + const RectCornerRadii& aRadii, + bool aDrawClockwise) +{ + // For CW drawing, this looks like: + // + // ...******0** 1 C + // **** + // *** 2 + // ** + // * + // * + // 3 + // * + // * + // + // Where 0, 1, 2, 3 are the control points of the Bezier curve for + // the corner, and C is the actual corner point. + // + // At the start of the loop, the current point is assumed to be + // the point adjacent to the top left corner on the top + // horizontal. Note that corner indices start at the top left and + // continue clockwise, whereas in our loop i = 0 refers to the top + // right corner. + // + // When going CCW, the control points are swapped, and the first + // corner that's drawn is the top left (along with the top segment). + // + // There is considerable latitude in how one chooses the four + // control points for a Bezier curve approximation to an ellipse. + // For the overall path to be continuous and show no corner at the + // endpoints of the arc, points 0 and 3 must be at the ends of the + // straight segments of the rectangle; points 0, 1, and C must be + // collinear; and points 3, 2, and C must also be collinear. This + // leaves only two free parameters: the ratio of the line segments + // 01 and 0C, and the ratio of the line segments 32 and 3C. See + // the following papers for extensive discussion of how to choose + // these ratios: + // + // Dokken, Tor, et al. "Good approximation of circles by + // curvature-continuous Bezier curves." Computer-Aided + // Geometric Design 7(1990) 33--41. + // Goldapp, Michael. "Approximation of circular arcs by cubic + // polynomials." Computer-Aided Geometric Design 8(1991) 227--238. + // Maisonobe, Luc. "Drawing an elliptical arc using polylines, + // quadratic, or cubic Bezier curves." + // http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf + // + // We follow the approach in section 2 of Goldapp (least-error, + // Hermite-type approximation) and make both ratios equal to + // + // 2 2 + n - sqrt(2n + 28) + // alpha = - * --------------------- + // 3 n - 4 + // + // where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ). + // + // This is the result of Goldapp's equation (10b) when the angle + // swept out by the arc is pi/2, and the parameter "a-bar" is the + // expression given immediately below equation (21). + // + // Using this value, the maximum radial error for a circle, as a + // fraction of the radius, is on the order of 0.2 x 10^-3. + // Neither Dokken nor Goldapp discusses error for a general + // ellipse; Maisonobe does, but his choice of control points + // follows different constraints, and Goldapp's expression for + // 'alpha' gives much smaller radial error, even for very flat + // ellipses, than Maisonobe's equivalent. + // + // For the various corners and for each axis, the sign of this + // constant changes, or it might be 0 -- it's multiplied by the + // appropriate multiplier from the list before using. + + const Float alpha = Float(0.55191497064665766025); + + typedef struct { Float a, b; } twoFloats; + + twoFloats cwCornerMults[4] = { { -1, 0 }, // cc == clockwise + { 0, -1 }, + { +1, 0 }, + { 0, +1 } }; + twoFloats ccwCornerMults[4] = { { +1, 0 }, // ccw == counter-clockwise + { 0, -1 }, + { -1, 0 }, + { 0, +1 } }; + + twoFloats *cornerMults = aDrawClockwise ? cwCornerMults : ccwCornerMults; + + Point cornerCoords[] = { aRect.TopLeft(), aRect.TopRight(), + aRect.BottomRight(), aRect.BottomLeft() }; + + Point pc, p0, p1, p2, p3; + + if (aDrawClockwise) { + aPathBuilder->MoveTo(Point(aRect.X() + aRadii[RectCorner::TopLeft].width, + aRect.Y())); + } else { + aPathBuilder->MoveTo(Point(aRect.X() + aRect.Width() - aRadii[RectCorner::TopRight].width, + aRect.Y())); + } + + for (int i = 0; i < 4; ++i) { + // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw) + int c = aDrawClockwise ? ((i+1) % 4) : ((4-i) % 4); + + // i+2 and i+3 respectively. These are used to index into the corner + // multiplier table, and were deduced by calculating out the long form + // of each corner and finding a pattern in the signs and values. + int i2 = (i+2) % 4; + int i3 = (i+3) % 4; + + pc = cornerCoords[c]; + + if (aRadii[c].width > 0.0 && aRadii[c].height > 0.0) { + p0.x = pc.x + cornerMults[i].a * aRadii[c].width; + p0.y = pc.y + cornerMults[i].b * aRadii[c].height; + + p3.x = pc.x + cornerMults[i3].a * aRadii[c].width; + p3.y = pc.y + cornerMults[i3].b * aRadii[c].height; + + p1.x = p0.x + alpha * cornerMults[i2].a * aRadii[c].width; + p1.y = p0.y + alpha * cornerMults[i2].b * aRadii[c].height; + + p2.x = p3.x - alpha * cornerMults[i3].a * aRadii[c].width; + p2.y = p3.y - alpha * cornerMults[i3].b * aRadii[c].height; + + aPathBuilder->LineTo(p0); + aPathBuilder->BezierTo(p1, p2, p3); + } else { + aPathBuilder->LineTo(pc); + } + } + + aPathBuilder->Close(); +} + +void +AppendEllipseToPath(PathBuilder* aPathBuilder, + const Point& aCenter, + const Size& aDimensions) +{ + Size halfDim = aDimensions / 2.f; + Rect rect(aCenter - Point(halfDim.width, halfDim.height), aDimensions); + RectCornerRadii radii(halfDim.width, halfDim.height); + + AppendRoundedRectToPath(aPathBuilder, rect, radii); +} + +bool +SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2, + const DrawTarget& aDrawTarget) +{ + Matrix mat = aDrawTarget.GetTransform(); + if (mat.HasNonTranslation()) { + return false; + } + if (aP1.x != aP2.x && aP1.y != aP2.y) { + return false; // not a horizontal or vertical line + } + Point p1 = aP1 + mat.GetTranslation(); // into device space + Point p2 = aP2 + mat.GetTranslation(); + p1.Round(); + p2.Round(); + p1 -= mat.GetTranslation(); // back into user space + p2 -= mat.GetTranslation(); + if (aP1.x == aP2.x) { + // snap vertical line, adding 0.5 to align it to be mid-pixel: + aP1 = p1 + Point(0.5, 0); + aP2 = p2 + Point(0.5, 0); + } else { + // snap horizontal line, adding 0.5 to align it to be mid-pixel: + aP1 = p1 + Point(0, 0.5); + aP2 = p2 + Point(0, 0.5); + } + return true; +} + +void +StrokeSnappedEdgesOfRect(const Rect& aRect, DrawTarget& aDrawTarget, + const ColorPattern& aColor, + const StrokeOptions& aStrokeOptions) +{ + if (aRect.IsEmpty()) { + return; + } + + Point p1 = aRect.TopLeft(); + Point p2 = aRect.BottomLeft(); + SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); + aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); + + p1 = aRect.BottomLeft(); + p2 = aRect.BottomRight(); + SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); + aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); + + p1 = aRect.TopLeft(); + p2 = aRect.TopRight(); + SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); + aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); + + p1 = aRect.TopRight(); + p2 = aRect.BottomRight(); + SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); + aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); +} + +} // namespace gfx +} // namespace mozilla + diff --git a/gfx/2d/PathHelpers.h b/gfx/2d/PathHelpers.h index 9cecae830..d7a732648 100644 --- a/gfx/2d/PathHelpers.h +++ b/gfx/2d/PathHelpers.h @@ -8,16 +8,17 @@ #include "2D.h" #include "mozilla/Constants.h" +#include "UserData.h" namespace mozilla { namespace gfx { template <typename T> -void ArcToBezier(T* aSink, const Point &aOrigin, float aRadius, float aStartAngle, - float aEndAngle, bool aAntiClockwise) +void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius, + float aStartAngle, float aEndAngle, bool aAntiClockwise) { - Point startPoint(aOrigin.x + cos(aStartAngle) * aRadius, - aOrigin.y + sin(aStartAngle) * aRadius); + Point startPoint(aOrigin.x + cosf(aStartAngle) * aRadius.width, + aOrigin.y + sinf(aStartAngle) * aRadius.height); aSink->LineTo(startPoint); @@ -56,23 +57,25 @@ void ArcToBezier(T* aSink, const Point &aOrigin, float aRadius, float aStartAngl currentEndAngle = currentStartAngle + arcSweepLeft * sweepDirection; } - Point currentStartPoint(aOrigin.x + cos(currentStartAngle) * aRadius, - aOrigin.y + sin(currentStartAngle) * aRadius); - Point currentEndPoint(aOrigin.x + cos(currentEndAngle) * aRadius, - aOrigin.y + sin(currentEndAngle) * aRadius); + Point currentStartPoint(aOrigin.x + cosf(currentStartAngle) * aRadius.width, + aOrigin.y + sinf(currentStartAngle) * aRadius.height); + Point currentEndPoint(aOrigin.x + cosf(currentEndAngle) * aRadius.width, + aOrigin.y + sinf(currentEndAngle) * aRadius.height); // Calculate kappa constant for partial curve. The sign of angle in the // tangent will actually ensure this is negative for a counter clockwise // sweep, so changing signs later isn't needed. - Float kappa = (4.0f / 3.0f) * tan((currentEndAngle - currentStartAngle) / 4.0f) * aRadius; + Float kappaFactor = (4.0f / 3.0f) * tan((currentEndAngle - currentStartAngle) / 4.0f); + Float kappaX = kappaFactor * aRadius.width; + Float kappaY = kappaFactor * aRadius.height; Point tangentStart(-sin(currentStartAngle), cos(currentStartAngle)); Point cp1 = currentStartPoint; - cp1 += tangentStart * kappa; + cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY); Point revTangentEnd(sin(currentEndAngle), -cos(currentEndAngle)); Point cp2 = currentEndPoint; - cp2 += revTangentEnd * kappa; + cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY); aSink->BezierTo(cp1, cp2, currentEndPoint); @@ -81,7 +84,279 @@ void ArcToBezier(T* aSink, const Point &aOrigin, float aRadius, float aStartAngl } } +/* This is basically the ArcToBezier with the parameters for drawing a circle + * inlined which vastly simplifies it and avoids a bunch of transcedental function + * calls which should make it faster. */ +template <typename T> +void EllipseToBezier(T* aSink, const Point &aOrigin, const Size &aRadius) +{ + Point startPoint(aOrigin.x + aRadius.width, + aOrigin.y); + + aSink->LineTo(startPoint); + + // Calculate kappa constant for partial curve. The sign of angle in the + // tangent will actually ensure this is negative for a counter clockwise + // sweep, so changing signs later isn't needed. + Float kappaFactor = (4.0f / 3.0f) * tan((M_PI/2.0f) / 4.0f); + Float kappaX = kappaFactor * aRadius.width; + Float kappaY = kappaFactor * aRadius.height; + Float cosStartAngle = 1; + Float sinStartAngle = 0; + for (int i = 0; i < 4; i++) { + // We guarantee here the current point is the start point of the next + // curve segment. + Point currentStartPoint(aOrigin.x + cosStartAngle * aRadius.width, + aOrigin.y + sinStartAngle * aRadius.height); + Point currentEndPoint(aOrigin.x + -sinStartAngle * aRadius.width, + aOrigin.y + cosStartAngle * aRadius.height); + + Point tangentStart(-sinStartAngle, cosStartAngle); + Point cp1 = currentStartPoint; + cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY); + + Point revTangentEnd(cosStartAngle, sinStartAngle); + Point cp2 = currentEndPoint; + cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY); + + aSink->BezierTo(cp1, cp2, currentEndPoint); + + // cos(x+pi/2) == -sin(x) + // sin(x+pi/2) == cos(x) + Float tmp = cosStartAngle; + cosStartAngle = -sinStartAngle; + sinStartAngle = tmp; + } } + +/** + * Appends a path represending a rectangle to the path being built by + * aPathBuilder. + * + * aRect The rectangle to append. + * aDrawClockwise If set to true, the path will start at the left of the top + * left edge and draw clockwise. If set to false the path will + * start at the right of the top left edge and draw counter- + * clockwise. + */ +GFX2D_API void AppendRectToPath(PathBuilder* aPathBuilder, + const Rect& aRect, + bool aDrawClockwise = true); + +inline TemporaryRef<Path> MakePathForRect(const DrawTarget& aDrawTarget, + const Rect& aRect, + bool aDrawClockwise = true) +{ + RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder(); + AppendRectToPath(builder, aRect, aDrawClockwise); + return builder->Finish(); } +struct RectCornerRadii { + Size radii[RectCorner::Count]; + + RectCornerRadii() {} + + explicit RectCornerRadii(Float radius) { + for (int i = 0; i < RectCorner::Count; i++) { + radii[i].SizeTo(radius, radius); + } + } + + explicit RectCornerRadii(Float radiusX, Float radiusY) { + for (int i = 0; i < RectCorner::Count; i++) { + radii[i].SizeTo(radiusX, radiusY); + } + } + + RectCornerRadii(Float tl, Float tr, Float br, Float bl) { + radii[RectCorner::TopLeft].SizeTo(tl, tl); + radii[RectCorner::TopRight].SizeTo(tr, tr); + radii[RectCorner::BottomRight].SizeTo(br, br); + radii[RectCorner::BottomLeft].SizeTo(bl, bl); + } + + RectCornerRadii(const Size& tl, const Size& tr, + const Size& br, const Size& bl) { + radii[RectCorner::TopLeft] = tl; + radii[RectCorner::TopRight] = tr; + radii[RectCorner::BottomRight] = br; + radii[RectCorner::BottomLeft] = bl; + } + + const Size& operator[](size_t aCorner) const { + return radii[aCorner]; + } + + Size& operator[](size_t aCorner) { + return radii[aCorner]; + } + + void Scale(Float aXScale, Float aYScale) { + for (int i = 0; i < RectCorner::Count; i++) { + radii[i].Scale(aXScale, aYScale); + } + } + + const Size TopLeft() const { return radii[RectCorner::TopLeft]; } + Size& TopLeft() { return radii[RectCorner::TopLeft]; } + + const Size TopRight() const { return radii[RectCorner::TopRight]; } + Size& TopRight() { return radii[RectCorner::TopRight]; } + + const Size BottomRight() const { return radii[RectCorner::BottomRight]; } + Size& BottomRight() { return radii[RectCorner::BottomRight]; } + + const Size BottomLeft() const { return radii[RectCorner::BottomLeft]; } + Size& BottomLeft() { return radii[RectCorner::BottomLeft]; } +}; + +/** + * Appends a path represending a rounded rectangle to the path being built by + * aPathBuilder. + * + * aRect The rectangle to append. + * aCornerRadii Contains the radii of the top-left, top-right, bottom-right + * and bottom-left corners, in that order. + * aDrawClockwise If set to true, the path will start at the left of the top + * left edge and draw clockwise. If set to false the path will + * start at the right of the top left edge and draw counter- + * clockwise. + */ +GFX2D_API void AppendRoundedRectToPath(PathBuilder* aPathBuilder, + const Rect& aRect, + const RectCornerRadii& aRadii, + bool aDrawClockwise = true); + +inline TemporaryRef<Path> MakePathForRoundedRect(const DrawTarget& aDrawTarget, + const Rect& aRect, + const RectCornerRadii& aRadii, + bool aDrawClockwise = true) +{ + RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder(); + AppendRoundedRectToPath(builder, aRect, aRadii, aDrawClockwise); + return builder->Finish(); +} + +/** + * Appends a path represending an ellipse to the path being built by + * aPathBuilder. + * + * The ellipse extends aDimensions.width / 2.0 in the horizontal direction + * from aCenter, and aDimensions.height / 2.0 in the vertical direction. + */ +GFX2D_API void AppendEllipseToPath(PathBuilder* aPathBuilder, + const Point& aCenter, + const Size& aDimensions); + +inline TemporaryRef<Path> MakePathForEllipse(const DrawTarget& aDrawTarget, + const Point& aCenter, + const Size& aDimensions) +{ + RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder(); + AppendEllipseToPath(builder, aCenter, aDimensions); + return builder->Finish(); +} + +/** + * If aDrawTarget's transform only contains a translation, and if this line is + * a horizontal or vertical line, this function will snap the line's vertices + * to align with the device pixel grid so that stroking the line with a one + * pixel wide stroke will result in a crisp line that is not antialiased over + * two pixels across its width. + * + * @return Returns true if this function snaps aRect's vertices, else returns + * false. + */ +GFX2D_API bool SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2, + const DrawTarget& aDrawTarget); + +/** + * This function paints each edge of aRect separately, snapping the edges using + * SnapLineToDevicePixelsForStroking. Stroking the edges as separate paths + * helps ensure not only that the stroke spans a single row of device pixels if + * possible, but also that the ends of stroke dashes start and end on device + * pixels too. + */ +GFX2D_API void StrokeSnappedEdgesOfRect(const Rect& aRect, + DrawTarget& aDrawTarget, + const ColorPattern& aColor, + const StrokeOptions& aStrokeOptions); + +extern UserDataKey sDisablePixelSnapping; + +/** + * If aDrawTarget's transform only contains a translation or, if + * aAllowScaleOr90DegreeRotate is true, and/or a scale/90 degree rotation, this + * function will convert aRect to device space and snap it to device pixels. + * This function returns true if aRect is modified, otherwise it returns false. + * + * Note that the snapping is such that filling the rect using a DrawTarget + * which has the identity matrix as its transform will result in crisp edges. + * (That is, aRect will have integer values, aligning its edges between pixel + * boundaries.) If on the other hand you stroking the rect with an odd valued + * stroke width then the edges of the stroke will be antialiased (assuming an + * AntialiasMode that does antialiasing). + */ +inline bool UserToDevicePixelSnapped(Rect& aRect, const DrawTarget& aDrawTarget, + bool aAllowScaleOr90DegreeRotate = false) +{ + if (aDrawTarget.GetUserData(&sDisablePixelSnapping)) { + return false; + } + + Matrix mat = aDrawTarget.GetTransform(); + + const Float epsilon = 0.0000001f; +#define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon) + if (!aAllowScaleOr90DegreeRotate && + (!WITHIN_E(mat._11, 1.f) || !WITHIN_E(mat._22, 1.f) || + !WITHIN_E(mat._12, 0.f) || !WITHIN_E(mat._21, 0.f))) { + // We have non-translation, but only translation is allowed. + return false; + } +#undef WITHIN_E + + Point p1 = mat * aRect.TopLeft(); + Point p2 = mat * aRect.TopRight(); + Point p3 = mat * aRect.BottomRight(); + + // Check that the rectangle is axis-aligned. For an axis-aligned rectangle, + // two opposite corners define the entire rectangle. So check if + // the axis-aligned rectangle with opposite corners p1 and p3 + // define an axis-aligned rectangle whose other corners are p2 and p4. + // We actually only need to check one of p2 and p4, since an affine + // transform maps parallelograms to parallelograms. + if (p2 == Point(p1.x, p3.y) || p2 == Point(p3.x, p1.y)) { + p1.Round(); + p3.Round(); + + aRect.MoveTo(Point(std::min(p1.x, p3.x), std::min(p1.y, p3.y))); + aRect.SizeTo(Size(std::max(p1.x, p3.x) - aRect.X(), + std::max(p1.y, p3.y) - aRect.Y())); + return true; + } + + return false; +} + +/** + * This function has the same behavior as UserToDevicePixelSnapped except that + * aRect is not transformed to device space. + */ +inline void MaybeSnapToDevicePixels(Rect& aRect, const DrawTarget& aDrawTarget, + bool aIgnoreScale = false) +{ + if (UserToDevicePixelSnapped(aRect, aDrawTarget, aIgnoreScale)) { + // Since UserToDevicePixelSnapped returned true we know there is no + // rotation/skew in 'mat', so we can just use TransformBounds() here. + Matrix mat = aDrawTarget.GetTransform(); + mat.Invert(); + aRect = mat.TransformBounds(aRect); + } +} + +} // namespace gfx +} // namespace mozilla + #endif /* MOZILLA_GFX_PATHHELPERS_H_ */ diff --git a/gfx/2d/PathRecording.cpp b/gfx/2d/PathRecording.cpp index 647649b1a..e43871290 100644 --- a/gfx/2d/PathRecording.cpp +++ b/gfx/2d/PathRecording.cpp @@ -90,7 +90,7 @@ PathRecording::CopyToBuilder(FillRule aFillRule) const RefPtr<PathBuilder> pathBuilder = mPath->CopyToBuilder(aFillRule); RefPtr<PathBuilderRecording> recording = new PathBuilderRecording(pathBuilder, aFillRule); recording->mPathOps = mPathOps; - return recording; + return recording.forget(); } TemporaryRef<PathBuilder> @@ -113,7 +113,7 @@ PathRecording::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFill } recording->mPathOps.push_back(newPathOp); } - return recording; + return recording.forget(); } } diff --git a/gfx/2d/PathRecording.h b/gfx/2d/PathRecording.h index 8c89cb7e3..7fde43ea9 100644 --- a/gfx/2d/PathRecording.h +++ b/gfx/2d/PathRecording.h @@ -37,6 +37,7 @@ class DrawEventRecorderPrivate; class PathBuilderRecording : public PathBuilder { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderRecording) PathBuilderRecording(PathBuilder *aBuilder, FillRule aFillRule) : mPathBuilder(aBuilder), mFillRule(aFillRule) { @@ -71,6 +72,8 @@ public: virtual TemporaryRef<Path> Finish(); + virtual BackendType GetBackendType() const { return BackendType::RECORDING; } + private: friend class PathRecording; @@ -82,6 +85,7 @@ private: class PathRecording : public Path { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathRecording) PathRecording(Path *aPath, const std::vector<PathOp> aOps, FillRule aFillRule) : mPath(aPath), mPathOps(aOps), mFillRule(aFillRule) { @@ -89,10 +93,10 @@ public: ~PathRecording(); - virtual BackendType GetBackendType() const { return BACKEND_RECORDING; } - virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const; + virtual BackendType GetBackendType() const { return BackendType::RECORDING; } + virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const; virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform, - FillRule aFillRule = FILL_WINDING) const; + FillRule aFillRule = FillRule::FILL_WINDING) const; virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const { return mPath->ContainsPoint(aPoint, aTransform); } virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions, @@ -106,7 +110,9 @@ public: virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions, const Matrix &aTransform = Matrix()) const { return mPath->GetStrokedBounds(aStrokeOptions, aTransform); } - + + virtual void StreamToSink(PathSink *aSink) const { mPath->StreamToSink(aSink); } + virtual FillRule GetFillRule() const { return mFillRule; } void StorePath(std::ostream &aStream) const; diff --git a/gfx/2d/PathSkia.cpp b/gfx/2d/PathSkia.cpp index 132e5dc5a..efa0c6f56 100644 --- a/gfx/2d/PathSkia.cpp +++ b/gfx/2d/PathSkia.cpp @@ -31,7 +31,7 @@ void PathBuilderSkia::SetFillRule(FillRule aFillRule) { mFillRule = aFillRule; - if (mFillRule == FILL_WINDING) { + if (mFillRule == FillRule::FILL_WINDING) { mPath.setFillType(SkPath::kWinding_FillType); } else { mPath.setFillType(SkPath::kEvenOdd_FillType); @@ -88,7 +88,7 @@ void PathBuilderSkia::Arc(const Point &aOrigin, float aRadius, float aStartAngle, float aEndAngle, bool aAntiClockwise) { - ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise); + ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise); } Point @@ -105,8 +105,13 @@ PathBuilderSkia::CurrentPoint() const TemporaryRef<Path> PathBuilderSkia::Finish() { - RefPtr<PathSkia> path = new PathSkia(mPath, mFillRule); - return path; + return new PathSkia(mPath, mFillRule); +} + +void +PathBuilderSkia::AppendPath(const SkPath &aPath) +{ + mPath.addPath(aPath); } TemporaryRef<PathBuilder> @@ -118,8 +123,7 @@ PathSkia::CopyToBuilder(FillRule aFillRule) const TemporaryRef<PathBuilder> PathSkia::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const { - RefPtr<PathBuilderSkia> builder = new PathBuilderSkia(aTransform, mPath, aFillRule); - return builder; + return new PathBuilderSkia(aTransform, mPath, aFillRule); } bool @@ -137,24 +141,16 @@ PathSkia::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const } SkRegion pointRect; - pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1)), - int32_t(SkFloatToScalar(transformed.y - 1)), - int32_t(SkFloatToScalar(transformed.x + 1)), - int32_t(SkFloatToScalar(transformed.y + 1))); + pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1.f)), + int32_t(SkFloatToScalar(transformed.y - 1.f)), + int32_t(SkFloatToScalar(transformed.x + 1.f)), + int32_t(SkFloatToScalar(transformed.y + 1.f))); SkRegion pathRegion; return pathRegion.setPath(mPath, pointRect); } -static Rect SkRectToRect(const SkRect& aBounds) -{ - return Rect(SkScalarToFloat(aBounds.fLeft), - SkScalarToFloat(aBounds.fTop), - SkScalarToFloat(aBounds.fRight - aBounds.fLeft), - SkScalarToFloat(aBounds.fBottom - aBounds.fTop)); -} - bool PathSkia::StrokeContainsPoint(const StrokeOptions &aStrokeOptions, const Point &aPoint, @@ -178,10 +174,10 @@ PathSkia::StrokeContainsPoint(const StrokeOptions &aStrokeOptions, } SkRegion pointRect; - pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1)), - int32_t(SkFloatToScalar(transformed.y - 1)), - int32_t(SkFloatToScalar(transformed.x + 1)), - int32_t(SkFloatToScalar(transformed.y + 1))); + pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1.f)), + int32_t(SkFloatToScalar(transformed.y - 1.f)), + int32_t(SkFloatToScalar(transformed.x + 1.f)), + int32_t(SkFloatToScalar(transformed.y + 1.f))); SkRegion pathRegion; @@ -209,5 +205,39 @@ PathSkia::GetStrokedBounds(const StrokeOptions &aStrokeOptions, return aTransform.TransformBounds(bounds); } +void +PathSkia::StreamToSink(PathSink *aSink) const +{ + SkPath::RawIter iter(mPath); + + SkPoint points[4]; + SkPath::Verb currentVerb; + while ((currentVerb = iter.next(points)) != SkPath::kDone_Verb) { + switch (currentVerb) { + case SkPath::kMove_Verb: + aSink->MoveTo(SkPointToPoint(points[0])); + break; + case SkPath::kLine_Verb: + aSink->LineTo(SkPointToPoint(points[1])); + break; + case SkPath::kCubic_Verb: + aSink->BezierTo(SkPointToPoint(points[1]), + SkPointToPoint(points[2]), + SkPointToPoint(points[3])); + break; + case SkPath::kQuad_Verb: + aSink->QuadraticBezierTo(SkPointToPoint(points[1]), + SkPointToPoint(points[2])); + break; + case SkPath::kClose_Verb: + aSink->Close(); + break; + default: + MOZ_ASSERT(false); + // Unexpected verb found in path! + } + } +} + } } diff --git a/gfx/2d/PathSkia.h b/gfx/2d/PathSkia.h index df6f5718a..57fd709b0 100644 --- a/gfx/2d/PathSkia.h +++ b/gfx/2d/PathSkia.h @@ -17,8 +17,9 @@ class PathSkia; class PathBuilderSkia : public PathBuilder { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderSkia) PathBuilderSkia(const Matrix& aTransform, const SkPath& aPath, FillRule aFillRule); - PathBuilderSkia(FillRule aFillRule); + explicit PathBuilderSkia(FillRule aFillRule); virtual void MoveTo(const Point &aPoint); virtual void LineTo(const Point &aPoint); @@ -33,7 +34,12 @@ public: virtual Point CurrentPoint() const; virtual TemporaryRef<Path> Finish(); + void AppendPath(const SkPath &aPath); + + virtual BackendType GetBackendType() const { return BackendType::SKIA; } + private: + void SetFillRule(FillRule aFillRule); SkPath mPath; @@ -43,17 +49,18 @@ private: class PathSkia : public Path { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathSkia) PathSkia(SkPath& aPath, FillRule aFillRule) : mFillRule(aFillRule) { mPath.swap(aPath); } - virtual BackendType GetBackendType() const { return BACKEND_SKIA; } + virtual BackendType GetBackendType() const { return BackendType::SKIA; } - virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const; + virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const; virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform, - FillRule aFillRule = FILL_WINDING) const; + FillRule aFillRule = FillRule::FILL_WINDING) const; virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const; @@ -66,6 +73,8 @@ public: virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions, const Matrix &aTransform = Matrix()) const; + virtual void StreamToSink(PathSink *aSink) const; + virtual FillRule GetFillRule() const { return mFillRule; } const SkPath& GetPath() const { return mPath; } diff --git a/gfx/2d/PatternHelpers.h b/gfx/2d/PatternHelpers.h new file mode 100644 index 000000000..79c2502b9 --- /dev/null +++ b/gfx/2d/PatternHelpers.h @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MOZILLA_GFX_PATTERNHELPERS_H +#define _MOZILLA_GFX_PATTERNHELPERS_H + +#include "mozilla/Alignment.h" +#include "mozilla/gfx/2D.h" + +namespace mozilla { +namespace gfx { + +/** + * This class is used to allow general pattern creation functions to return + * any type of pattern via an out-paramater without allocating a pattern + * instance on the free-store (an instance of this class being created on the + * stack before passing it in to the creation function). Without this class + * writing pattern creation functions would be a pain since Pattern objects are + * not reference counted, making lifetime management of instances created on + * the free-store and returned from a creation function hazardous. Besides + * that, in the case that ColorPattern's are expected to be common, it is + * particularly desirable to avoid the overhead of allocating on the + * free-store. + */ +class GeneralPattern +{ +public: + explicit GeneralPattern() + : mPattern(nullptr) + {} + + GeneralPattern(const GeneralPattern& aOther) + : mPattern(nullptr) + {} + + ~GeneralPattern() { + if (mPattern) { + mPattern->~Pattern(); + } + } + + Pattern* Init(const Pattern& aPattern) { + MOZ_ASSERT(!mPattern); + switch (aPattern.GetType()) { + case PatternType::COLOR: + mPattern = new (mColorPattern.addr()) + ColorPattern(static_cast<const ColorPattern&>(aPattern)); + break; + case PatternType::LINEAR_GRADIENT: + mPattern = new (mLinearGradientPattern.addr()) + LinearGradientPattern(static_cast<const LinearGradientPattern&>(aPattern)); + break; + case PatternType::RADIAL_GRADIENT: + mPattern = new (mRadialGradientPattern.addr()) + RadialGradientPattern(static_cast<const RadialGradientPattern&>(aPattern)); + break; + case PatternType::SURFACE: + mPattern = new (mSurfacePattern.addr()) + SurfacePattern(static_cast<const SurfacePattern&>(aPattern)); + break; + default: + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown pattern type"); + } + return mPattern; + } + + ColorPattern* InitColorPattern(const Color &aColor) { + MOZ_ASSERT(!mPattern); + mPattern = new (mColorPattern.addr()) ColorPattern(aColor); + return mColorPattern.addr(); + } + + LinearGradientPattern* InitLinearGradientPattern(const Point &aBegin, + const Point &aEnd, + GradientStops *aStops, + const Matrix &aMatrix = Matrix()) { + MOZ_ASSERT(!mPattern); + mPattern = new (mLinearGradientPattern.addr()) + LinearGradientPattern(aBegin, aEnd, aStops, aMatrix); + return mLinearGradientPattern.addr(); + } + + RadialGradientPattern* InitRadialGradientPattern(const Point &aCenter1, + const Point &aCenter2, + Float aRadius1, + Float aRadius2, + GradientStops *aStops, + const Matrix &aMatrix = Matrix()) { + MOZ_ASSERT(!mPattern); + mPattern = new (mRadialGradientPattern.addr()) + RadialGradientPattern(aCenter1, aCenter2, aRadius1, aRadius2, aStops, aMatrix); + return mRadialGradientPattern.addr(); + } + + SurfacePattern* InitSurfacePattern(SourceSurface *aSourceSurface, + ExtendMode aExtendMode, + const Matrix &aMatrix = Matrix(), + Filter aFilter = Filter::GOOD, + const IntRect &aSamplingRect = IntRect()) { + MOZ_ASSERT(!mPattern); + mPattern = new (mSurfacePattern.addr()) + SurfacePattern(aSourceSurface, aExtendMode, aMatrix, aFilter, aSamplingRect); + return mSurfacePattern.addr(); + } + + Pattern* GetPattern() { + return mPattern; + } + + const Pattern* GetPattern() const { + return mPattern; + } + + operator Pattern&() { + if (!mPattern) { + MOZ_CRASH("GeneralPattern not initialized"); + } + return *mPattern; + } + +private: + union { + AlignedStorage2<ColorPattern> mColorPattern; + AlignedStorage2<LinearGradientPattern> mLinearGradientPattern; + AlignedStorage2<RadialGradientPattern> mRadialGradientPattern; + AlignedStorage2<SurfacePattern> mSurfacePattern; + }; + Pattern *mPattern; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _MOZILLA_GFX_PATTERNHELPERS_H + diff --git a/gfx/2d/Point.h b/gfx/2d/Point.h index 026b44604..0e271ccc2 100644 --- a/gfx/2d/Point.h +++ b/gfx/2d/Point.h @@ -6,24 +6,50 @@ #ifndef MOZILLA_GFX_POINT_H_ #define MOZILLA_GFX_POINT_H_ +#include "mozilla/Attributes.h" #include "Types.h" +#include "Coord.h" +#include "BaseCoord.h" #include "BasePoint.h" +#include "BasePoint3D.h" +#include "BasePoint4D.h" #include "BaseSize.h" +#include "mozilla/TypeTraits.h" + +#include <cmath> namespace mozilla { + +template <typename> struct IsPixel; + namespace gfx { // This should only be used by the typedefs below. struct UnknownUnits {}; +} // close namespace 'gfx' because IsPixel specialization must be in 'mozilla' + +template<> struct IsPixel<gfx::UnknownUnits> : TrueType {}; + +namespace gfx { + template<class units> struct IntPointTyped : - public BasePoint< int32_t, IntPointTyped<units> >, + public BasePoint< int32_t, IntPointTyped<units>, IntCoordTyped<units> >, public units { - typedef BasePoint< int32_t, IntPointTyped<units> > Super; + static_assert(IsPixel<units>::value, + "'units' must be a coordinate system tag"); + + typedef IntCoordTyped<units> Coord; + typedef BasePoint< int32_t, IntPointTyped<units>, IntCoordTyped<units> > Super; - IntPointTyped() : Super() {} - IntPointTyped(int32_t aX, int32_t aY) : Super(aX, aY) {} + MOZ_CONSTEXPR IntPointTyped() : Super() {} + MOZ_CONSTEXPR IntPointTyped(int32_t aX, int32_t aY) : Super(Coord(aX), Coord(aY)) {} + // The mixed-type constructors (int, Coord) and (Coord, int) are needed to + // avoid ambiguities because Coord is implicitly convertible to int. + MOZ_CONSTEXPR IntPointTyped(int32_t aX, Coord aY) : Super(Coord(aX), aY) {} + MOZ_CONSTEXPR IntPointTyped(Coord aX, int32_t aY) : Super(aX, Coord(aY)) {} + MOZ_CONSTEXPR IntPointTyped(Coord aX, Coord aY) : Super(aX, aY) {} // XXX When all of the code is ported, the following functions to convert to and from // unknown types should be removed. @@ -40,13 +66,22 @@ typedef IntPointTyped<UnknownUnits> IntPoint; template<class units> struct PointTyped : - public BasePoint< Float, PointTyped<units> >, + public BasePoint< Float, PointTyped<units>, CoordTyped<units> >, public units { - typedef BasePoint< Float, PointTyped<units> > Super; + static_assert(IsPixel<units>::value, + "'units' must be a coordinate system tag"); + + typedef CoordTyped<units> Coord; + typedef BasePoint< Float, PointTyped<units>, CoordTyped<units> > Super; - PointTyped() : Super() {} - PointTyped(Float aX, Float aY) : Super(aX, aY) {} - PointTyped(const IntPointTyped<units>& point) : Super(float(point.x), float(point.y)) {} + MOZ_CONSTEXPR PointTyped() : Super() {} + MOZ_CONSTEXPR PointTyped(Float aX, Float aY) : Super(Coord(aX), Coord(aY)) {} + // The mixed-type constructors (Float, Coord) and (Coord, Float) are needed to + // avoid ambiguities because Coord is implicitly convertible to Float. + MOZ_CONSTEXPR PointTyped(Float aX, Coord aY) : Super(Coord(aX), aY) {} + MOZ_CONSTEXPR PointTyped(Coord aX, Float aY) : Super(aX, Coord(aY)) {} + MOZ_CONSTEXPR PointTyped(Coord aX, Coord aY) : Super(aX.value, aY.value) {} + MOZ_CONSTEXPR MOZ_IMPLICIT PointTyped(const IntPointTyped<units>& point) : Super(float(point.x), float(point.y)) {} // XXX When all of the code is ported, the following functions to convert to and from // unknown types should be removed. @@ -63,18 +98,79 @@ typedef PointTyped<UnknownUnits> Point; template<class units> IntPointTyped<units> RoundedToInt(const PointTyped<units>& aPoint) { - return IntPointTyped<units>(NS_lround(aPoint.x), - NS_lround(aPoint.y)); + return IntPointTyped<units>(int32_t(floorf(aPoint.x + 0.5f)), + int32_t(floorf(aPoint.y + 0.5f))); +} + +template<class units> +IntPointTyped<units> TruncatedToInt(const PointTyped<units>& aPoint) { + return IntPointTyped<units>(int32_t(aPoint.x), + int32_t(aPoint.y)); } template<class units> +struct Point3DTyped : + public BasePoint3D< Float, Point3DTyped<units> > { + static_assert(IsPixel<units>::value, + "'units' must be a coordinate system tag"); + + typedef BasePoint3D< Float, Point3DTyped<units> > Super; + + Point3DTyped() : Super() {} + Point3DTyped(Float aX, Float aY, Float aZ) : Super(aX, aY, aZ) {} + + // XXX When all of the code is ported, the following functions to convert to and from + // unknown types should be removed. + + static Point3DTyped<units> FromUnknownPoint(const Point3DTyped<UnknownUnits>& aPoint) { + return Point3DTyped<units>(aPoint.x, aPoint.y, aPoint.z); + } + + Point3DTyped<UnknownUnits> ToUnknownPoint() const { + return Point3DTyped<UnknownUnits>(this->x, this->y, this->z); + } +}; +typedef Point3DTyped<UnknownUnits> Point3D; + +template<class units> +struct Point4DTyped : + public BasePoint4D< Float, Point4DTyped<units> > { + static_assert(IsPixel<units>::value, + "'units' must be a coordinate system tag"); + + typedef BasePoint4D< Float, Point4DTyped<units> > Super; + + Point4DTyped() : Super() {} + Point4DTyped(Float aX, Float aY, Float aZ, Float aW) : Super(aX, aY, aZ, aW) {} + + // XXX When all of the code is ported, the following functions to convert to and from + // unknown types should be removed. + + static Point4DTyped<units> FromUnknownPoint(const Point4DTyped<UnknownUnits>& aPoint) { + return Point4DTyped<units>(aPoint.x, aPoint.y, aPoint.z, aPoint.w); + } + + Point4DTyped<UnknownUnits> ToUnknownPoint() const { + return Point4DTyped<UnknownUnits>(this->x, this->y, this->z, this->w); + } + + PointTyped<units> As2DPoint() { + return PointTyped<units>(this->x / this->w, this->y / this->w); + } +}; +typedef Point4DTyped<UnknownUnits> Point4D; + +template<class units> struct IntSizeTyped : public BaseSize< int32_t, IntSizeTyped<units> >, public units { + static_assert(IsPixel<units>::value, + "'units' must be a coordinate system tag"); + typedef BaseSize< int32_t, IntSizeTyped<units> > Super; - IntSizeTyped() : Super() {} - IntSizeTyped(int32_t aWidth, int32_t aHeight) : Super(aWidth, aHeight) {} + MOZ_CONSTEXPR IntSizeTyped() : Super() {} + MOZ_CONSTEXPR IntSizeTyped(int32_t aWidth, int32_t aHeight) : Super(aWidth, aHeight) {} // XXX When all of the code is ported, the following functions to convert to and from // unknown types should be removed. @@ -93,10 +189,13 @@ template<class units> struct SizeTyped : public BaseSize< Float, SizeTyped<units> >, public units { + static_assert(IsPixel<units>::value, + "'units' must be a coordinate system tag"); + typedef BaseSize< Float, SizeTyped<units> > Super; - SizeTyped() : Super() {} - SizeTyped(Float aWidth, Float aHeight) : Super(aWidth, aHeight) {} + MOZ_CONSTEXPR SizeTyped() : Super() {} + MOZ_CONSTEXPR SizeTyped(Float aWidth, Float aHeight) : Super(aWidth, aHeight) {} explicit SizeTyped(const IntSizeTyped<units>& size) : Super(float(size.width), float(size.height)) {} @@ -113,6 +212,12 @@ struct SizeTyped : }; typedef SizeTyped<UnknownUnits> Size; +template<class units> +IntSizeTyped<units> RoundedToInt(const SizeTyped<units>& aSize) { + return IntSizeTyped<units>(int32_t(floorf(aSize.width + 0.5f)), + int32_t(floorf(aSize.height + 0.5f))); +} + } } diff --git a/gfx/2d/QuartzSupport.h b/gfx/2d/QuartzSupport.h index d02db8738..a85c8e2d9 100644 --- a/gfx/2d/QuartzSupport.h +++ b/gfx/2d/QuartzSupport.h @@ -14,6 +14,7 @@ #include "gfxTypes.h" #include "mozilla/RefPtr.h" #include "mozilla/gfx/MacIOSurface.h" +#include "nsError.h" // Get the system color space. CGColorSpaceRef CreateSystemColorSpace(); @@ -26,6 +27,7 @@ enum AllowOfflineRendererEnum { ALLOW_OFFLINE_RENDERER, DISALLOW_OFFLINE_RENDERE class nsCARenderer : public mozilla::RefCounted<nsCARenderer> { public: + MOZ_DECLARE_REFCOUNTED_TYPENAME(nsCARenderer) nsCARenderer() : mCARenderer(nullptr), mWrapperCALayer(nullptr), mFBOTexture(0), mOpenGLContext(nullptr), mCGImage(nullptr), mCGData(nullptr), mIOSurface(nullptr), mFBO(0), mIOTexture(0), @@ -51,7 +53,7 @@ public: * is attached then an internal pixel buffer will be * used. */ - void AttachIOSurface(mozilla::RefPtr<MacIOSurface> aSurface); + void AttachIOSurface(MacIOSurface *aSurface); IOSurfaceID GetIOSurfaceID(); // aX, aY, aWidth and aHeight are in "display pixels". Multiply by // surf->GetContentsScaleFactor() to get device pixels. @@ -86,21 +88,12 @@ private: mozilla::RefPtr<MacIOSurface> mIOSurface; uint32_t mFBO; uint32_t mIOTexture; - uint32_t mUnsupportedWidth; - uint32_t mUnsupportedHeight; + int mUnsupportedWidth; + int mUnsupportedHeight; AllowOfflineRendererEnum mAllowOfflineRenderer; double mContentsScaleFactor; }; -enum CGContextType { - CG_CONTEXT_TYPE_UNKNOWN = 0, - // These are found by inspection, it's possible they could be changed - CG_CONTEXT_TYPE_BITMAP = 4, - CG_CONTEXT_TYPE_IOSURFACE = 8 -}; - -CGContextType GetContextType(CGContextRef ref); - #endif // XP_MACOSX #endif // nsCoreAnimationSupport_h__ diff --git a/gfx/2d/QuartzSupport.mm b/gfx/2d/QuartzSupport.mm index 90e380037..cdefa0557 100644 --- a/gfx/2d/QuartzSupport.mm +++ b/gfx/2d/QuartzSupport.mm @@ -6,10 +6,12 @@ #include "QuartzSupport.h" #include "nsDebug.h" +#include "MacIOSurface.h" #import <QuartzCore/QuartzCore.h> #import <AppKit/NSOpenGL.h> #include <dlfcn.h> +#include "GLDefs.h" #define IOSURFACE_FRAMEWORK_PATH \ "/System/Library/Frameworks/IOSurface.framework/IOSurface" @@ -26,453 +28,12 @@ using mozilla::RefPtr; using mozilla::TemporaryRef; -// IOSurface signatures -typedef CFTypeRef IOSurfacePtr; -typedef IOSurfacePtr (*IOSurfaceCreateFunc) (CFDictionaryRef properties); -typedef IOSurfacePtr (*IOSurfaceLookupFunc) (uint32_t io_surface_id); -typedef IOSurfaceID (*IOSurfaceGetIDFunc) (CFTypeRef io_surface); -typedef IOReturn (*IOSurfaceLockFunc) (CFTypeRef io_surface, - uint32_t options, - uint32_t *seed); -typedef IOReturn (*IOSurfaceUnlockFunc) (CFTypeRef io_surface, - uint32_t options, - uint32_t *seed); -typedef void* (*IOSurfaceGetBaseAddressFunc) (CFTypeRef io_surface); -typedef size_t (*IOSurfaceGetWidthFunc) (IOSurfacePtr io_surface); -typedef size_t (*IOSurfaceGetHeightFunc) (IOSurfacePtr io_surface); -typedef size_t (*IOSurfaceGetBytesPerRowFunc) (IOSurfacePtr io_surface); -typedef CGLError (*CGLTexImageIOSurface2DFunc) (CGLContextObj ctxt, - GLenum target, GLenum internalFormat, - GLsizei width, GLsizei height, - GLenum format, GLenum type, - IOSurfacePtr ioSurface, GLuint plane); -typedef CGContextRef (*IOSurfaceContextCreateFunc)(CFTypeRef io_surface, - unsigned width, unsigned height, - unsigned bitsPerComponent, unsigned bytes, - CGColorSpaceRef colorSpace, CGBitmapInfo bitmapInfo); -typedef CGImageRef (*IOSurfaceContextCreateImageFunc)(CGContextRef ref); -typedef IOSurfacePtr (*IOSurfaceContextGetSurfaceFunc)(CGContextRef ref); - -#define GET_CONST(const_name) \ - ((CFStringRef*) dlsym(sIOSurfaceFramework, const_name)) -#define GET_IOSYM(dest,sym_name) \ - (typeof(dest)) dlsym(sIOSurfaceFramework, sym_name) -#define GET_CGLSYM(dest,sym_name) \ - (typeof(dest)) dlsym(sOpenGLFramework, sym_name) -#define GET_CGSYM(dest,sym_name) \ - (typeof(dest)) dlsym(sCoreGraphicsFramework, sym_name) - -class MacIOSurfaceLib: public MacIOSurface { -public: - static void *sIOSurfaceFramework; - static void *sOpenGLFramework; - static void *sCoreGraphicsFramework; - static bool isLoaded; - static IOSurfaceCreateFunc sCreate; - static IOSurfaceGetIDFunc sGetID; - static IOSurfaceLookupFunc sLookup; - static IOSurfaceGetBaseAddressFunc sGetBaseAddress; - static IOSurfaceLockFunc sLock; - static IOSurfaceUnlockFunc sUnlock; - static IOSurfaceGetWidthFunc sWidth; - static IOSurfaceGetHeightFunc sHeight; - static IOSurfaceGetBytesPerRowFunc sBytesPerRow; - static CGLTexImageIOSurface2DFunc sTexImage; - static IOSurfaceContextCreateFunc sIOSurfaceContextCreate; - static IOSurfaceContextCreateImageFunc sIOSurfaceContextCreateImage; - static IOSurfaceContextGetSurfaceFunc sIOSurfaceContextGetSurface; - static CFStringRef kPropWidth; - static CFStringRef kPropHeight; - static CFStringRef kPropBytesPerElem; - static CFStringRef kPropBytesPerRow; - static CFStringRef kPropIsGlobal; - - static bool isInit(); - static CFStringRef GetIOConst(const char* symbole); - static IOSurfacePtr IOSurfaceCreate(CFDictionaryRef properties); - static IOSurfacePtr IOSurfaceLookup(IOSurfaceID aIOSurfaceID); - static IOSurfaceID IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr); - static void *IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr); - static size_t IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr); - static size_t IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr); - static size_t IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr); - static IOReturn IOSurfaceLock(IOSurfacePtr aIOSurfacePtr, - uint32_t options, uint32_t *seed); - static IOReturn IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr, - uint32_t options, uint32_t *seed); - static CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt, - GLenum target, GLenum internalFormat, - GLsizei width, GLsizei height, - GLenum format, GLenum type, - IOSurfacePtr ioSurface, GLuint plane); - static CGContextRef IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr, - unsigned aWidth, unsigned aHeight, - unsigned aBitsPerCompoent, unsigned aBytes, - CGColorSpaceRef aColorSpace, CGBitmapInfo bitmapInfo); - static CGImageRef IOSurfaceContextCreateImage(CGContextRef ref); - static IOSurfacePtr IOSurfaceContextGetSurface(CGContextRef ref); - static unsigned int (*sCGContextGetTypePtr) (CGContextRef); - static void LoadLibrary(); - static void CloseLibrary(); - - // Static deconstructor - static class LibraryUnloader { - public: - ~LibraryUnloader() { - CloseLibrary(); - } - } sLibraryUnloader; -}; - -MacIOSurfaceLib::LibraryUnloader MacIOSurfaceLib::sLibraryUnloader; -bool MacIOSurfaceLib::isLoaded = false; -void* MacIOSurfaceLib::sIOSurfaceFramework; -void* MacIOSurfaceLib::sOpenGLFramework; -void* MacIOSurfaceLib::sCoreGraphicsFramework; -IOSurfaceCreateFunc MacIOSurfaceLib::sCreate; -IOSurfaceGetIDFunc MacIOSurfaceLib::sGetID; -IOSurfaceLookupFunc MacIOSurfaceLib::sLookup; -IOSurfaceGetBaseAddressFunc MacIOSurfaceLib::sGetBaseAddress; -IOSurfaceGetWidthFunc MacIOSurfaceLib::sWidth; -IOSurfaceGetHeightFunc MacIOSurfaceLib::sHeight; -IOSurfaceGetBytesPerRowFunc MacIOSurfaceLib::sBytesPerRow; -IOSurfaceLockFunc MacIOSurfaceLib::sLock; -IOSurfaceUnlockFunc MacIOSurfaceLib::sUnlock; -CGLTexImageIOSurface2DFunc MacIOSurfaceLib::sTexImage; -IOSurfaceContextCreateFunc MacIOSurfaceLib::sIOSurfaceContextCreate; -IOSurfaceContextCreateImageFunc MacIOSurfaceLib::sIOSurfaceContextCreateImage; -IOSurfaceContextGetSurfaceFunc MacIOSurfaceLib::sIOSurfaceContextGetSurface; -unsigned int (*MacIOSurfaceLib::sCGContextGetTypePtr) (CGContextRef) = nullptr; - -CFStringRef MacIOSurfaceLib::kPropWidth; -CFStringRef MacIOSurfaceLib::kPropHeight; -CFStringRef MacIOSurfaceLib::kPropBytesPerElem; -CFStringRef MacIOSurfaceLib::kPropBytesPerRow; -CFStringRef MacIOSurfaceLib::kPropIsGlobal; - -bool MacIOSurfaceLib::isInit() { - // Guard against trying to reload the library - // if it is not available. - if (!isLoaded) - LoadLibrary(); - if (!sIOSurfaceFramework) { - NS_ERROR("MacIOSurfaceLib failed to initialize"); - } - return sIOSurfaceFramework; -} - -IOSurfacePtr MacIOSurfaceLib::IOSurfaceCreate(CFDictionaryRef properties) { - return sCreate(properties); -} - -IOSurfacePtr MacIOSurfaceLib::IOSurfaceLookup(IOSurfaceID aIOSurfaceID) { - return sLookup(aIOSurfaceID); -} - -IOSurfaceID MacIOSurfaceLib::IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr) { - return sGetID(aIOSurfacePtr); -} - -void* MacIOSurfaceLib::IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr) { - return sGetBaseAddress(aIOSurfacePtr); -} - -size_t MacIOSurfaceLib::IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr) { - return sWidth(aIOSurfacePtr); -} - -size_t MacIOSurfaceLib::IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr) { - return sHeight(aIOSurfacePtr); -} - -size_t MacIOSurfaceLib::IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr) { - return sBytesPerRow(aIOSurfacePtr); -} - -IOReturn MacIOSurfaceLib::IOSurfaceLock(IOSurfacePtr aIOSurfacePtr, - uint32_t options, uint32_t *seed) { - return sLock(aIOSurfacePtr, options, seed); -} - -IOReturn MacIOSurfaceLib::IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr, - uint32_t options, uint32_t *seed) { - return sUnlock(aIOSurfacePtr, options, seed); -} - -CGLError MacIOSurfaceLib::CGLTexImageIOSurface2D(CGLContextObj ctxt, - GLenum target, GLenum internalFormat, - GLsizei width, GLsizei height, - GLenum format, GLenum type, - IOSurfacePtr ioSurface, GLuint plane) { - return sTexImage(ctxt, target, internalFormat, width, height, - format, type, ioSurface, plane); -} - -CGContextRef MacIOSurfaceLib::IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr, - unsigned aWidth, unsigned aHeight, - unsigned aBitsPerComponent, unsigned aBytes, - CGColorSpaceRef aColorSpace, CGBitmapInfo bitmapInfo) { - if (!sIOSurfaceContextCreate) - return nullptr; - return sIOSurfaceContextCreate(aIOSurfacePtr, aWidth, aHeight, aBitsPerComponent, aBytes, aColorSpace, bitmapInfo); -} - -CGImageRef MacIOSurfaceLib::IOSurfaceContextCreateImage(CGContextRef aContext) { - if (!sIOSurfaceContextCreateImage) - return nullptr; - return sIOSurfaceContextCreateImage(aContext); -} - -IOSurfacePtr MacIOSurfaceLib::IOSurfaceContextGetSurface(CGContextRef aContext) { - if (!sIOSurfaceContextGetSurface) - return nullptr; - return sIOSurfaceContextGetSurface(aContext); -} - -CFStringRef MacIOSurfaceLib::GetIOConst(const char* symbole) { - CFStringRef *address = (CFStringRef*)dlsym(sIOSurfaceFramework, symbole); - if (!address) - return nullptr; - - return *address; -} - -void MacIOSurfaceLib::LoadLibrary() { - if (isLoaded) { - return; - } - isLoaded = true; - sIOSurfaceFramework = dlopen(IOSURFACE_FRAMEWORK_PATH, - RTLD_LAZY | RTLD_LOCAL); - sOpenGLFramework = dlopen(OPENGL_FRAMEWORK_PATH, - RTLD_LAZY | RTLD_LOCAL); - - sCoreGraphicsFramework = dlopen(COREGRAPHICS_FRAMEWORK_PATH, - RTLD_LAZY | RTLD_LOCAL); - if (!sIOSurfaceFramework || !sOpenGLFramework || !sCoreGraphicsFramework) { - if (sIOSurfaceFramework) - dlclose(sIOSurfaceFramework); - if (sOpenGLFramework) - dlclose(sOpenGLFramework); - if (sCoreGraphicsFramework) - dlclose(sCoreGraphicsFramework); - sIOSurfaceFramework = nullptr; - sOpenGLFramework = nullptr; - sCoreGraphicsFramework = nullptr; - return; - } - - kPropWidth = GetIOConst("kIOSurfaceWidth"); - kPropHeight = GetIOConst("kIOSurfaceHeight"); - kPropBytesPerElem = GetIOConst("kIOSurfaceBytesPerElement"); - kPropBytesPerRow = GetIOConst("kIOSurfaceBytesPerRow"); - kPropIsGlobal = GetIOConst("kIOSurfaceIsGlobal"); - sCreate = GET_IOSYM(sCreate, "IOSurfaceCreate"); - sGetID = GET_IOSYM(sGetID, "IOSurfaceGetID"); - sWidth = GET_IOSYM(sWidth, "IOSurfaceGetWidth"); - sHeight = GET_IOSYM(sHeight, "IOSurfaceGetHeight"); - sBytesPerRow = GET_IOSYM(sBytesPerRow, "IOSurfaceGetBytesPerRow"); - sLookup = GET_IOSYM(sLookup, "IOSurfaceLookup"); - sLock = GET_IOSYM(sLock, "IOSurfaceLock"); - sUnlock = GET_IOSYM(sUnlock, "IOSurfaceUnlock"); - sGetBaseAddress = GET_IOSYM(sGetBaseAddress, "IOSurfaceGetBaseAddress"); - sTexImage = GET_CGLSYM(sTexImage, "CGLTexImageIOSurface2D"); - sCGContextGetTypePtr = (unsigned int (*)(CGContext*))dlsym(RTLD_DEFAULT, "CGContextGetType"); - - // Optional symbols - sIOSurfaceContextCreate = GET_CGSYM(sIOSurfaceContextCreate, "CGIOSurfaceContextCreate"); - sIOSurfaceContextCreateImage = GET_CGSYM(sIOSurfaceContextCreateImage, "CGIOSurfaceContextCreateImage"); - sIOSurfaceContextGetSurface = GET_CGSYM(sIOSurfaceContextGetSurface, "CGIOSurfaceContextGetSurface"); - - if (!sCreate || !sGetID || !sLookup || !sTexImage || !sGetBaseAddress || - !kPropWidth || !kPropHeight || !kPropBytesPerElem || !kPropIsGlobal || - !sLock || !sUnlock || !sWidth || !sHeight || !kPropBytesPerRow || - !sBytesPerRow) { - CloseLibrary(); - } -} - -void MacIOSurfaceLib::CloseLibrary() { - if (sIOSurfaceFramework) { - dlclose(sIOSurfaceFramework); - } - if (sOpenGLFramework) { - dlclose(sOpenGLFramework); - } - sIOSurfaceFramework = nullptr; - sOpenGLFramework = nullptr; -} - -MacIOSurface::~MacIOSurface() { - CFRelease(mIOSurfacePtr); -} - -TemporaryRef<MacIOSurface> MacIOSurface::CreateIOSurface(int aWidth, int aHeight, - double aContentsScaleFactor) { - if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0) - return nullptr; - - CFMutableDictionaryRef props = ::CFDictionaryCreateMutable( - kCFAllocatorDefault, 4, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (!props) - return nullptr; - - int32_t bytesPerElem = 4; - size_t intScaleFactor = ceil(aContentsScaleFactor); - aWidth *= intScaleFactor; - aHeight *= intScaleFactor; - CFNumberRef cfWidth = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aWidth); - CFNumberRef cfHeight = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aHeight); - CFNumberRef cfBytesPerElem = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &bytesPerElem); - ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropWidth, - cfWidth); - ::CFRelease(cfWidth); - ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropHeight, - cfHeight); - ::CFRelease(cfHeight); - ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropBytesPerElem, - cfBytesPerElem); - ::CFRelease(cfBytesPerElem); - ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropIsGlobal, - kCFBooleanTrue); - - IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceCreate(props); - ::CFRelease(props); - - if (!surfaceRef) - return nullptr; - - RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor); - if (!ioSurface) { - ::CFRelease(surfaceRef); - return nullptr; - } - - return ioSurface.forget(); -} - -TemporaryRef<MacIOSurface> MacIOSurface::LookupSurface(IOSurfaceID aIOSurfaceID, - double aContentsScaleFactor) { - if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0) - return nullptr; - - IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceLookup(aIOSurfaceID); - if (!surfaceRef) - return nullptr; - - RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor); - if (!ioSurface) { - ::CFRelease(surfaceRef); - return nullptr; - } - return ioSurface.forget(); -} - -IOSurfaceID MacIOSurface::GetIOSurfaceID() { - return MacIOSurfaceLib::IOSurfaceGetID(mIOSurfacePtr); -} - -void* MacIOSurface::GetBaseAddress() { - return MacIOSurfaceLib::IOSurfaceGetBaseAddress(mIOSurfacePtr); -} - -size_t MacIOSurface::GetWidth() { - size_t intScaleFactor = ceil(mContentsScaleFactor); - return GetDevicePixelWidth() / intScaleFactor; -} - -size_t MacIOSurface::GetHeight() { - size_t intScaleFactor = ceil(mContentsScaleFactor); - return GetDevicePixelHeight() / intScaleFactor; -} - -size_t MacIOSurface::GetDevicePixelWidth() { - return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr); -} - -size_t MacIOSurface::GetDevicePixelHeight() { - return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr); -} - -size_t MacIOSurface::GetBytesPerRow() { - return MacIOSurfaceLib::IOSurfaceGetBytesPerRow(mIOSurfacePtr); -} - -#define READ_ONLY 0x1 -void MacIOSurface::Lock() { - MacIOSurfaceLib::IOSurfaceLock(mIOSurfacePtr, READ_ONLY, nullptr); -} - -void MacIOSurface::Unlock() { - MacIOSurfaceLib::IOSurfaceUnlock(mIOSurfacePtr, READ_ONLY, nullptr); -} - -#include "SourceSurfaceRawData.h" -using mozilla::gfx::SourceSurface; -using mozilla::gfx::SourceSurfaceRawData; -using mozilla::gfx::IntSize; - -TemporaryRef<SourceSurface> -MacIOSurface::GetAsSurface() { - Lock(); - size_t bytesPerRow = GetBytesPerRow(); - size_t ioWidth = GetDevicePixelWidth(); - size_t ioHeight = GetDevicePixelHeight(); - - unsigned char* ioData = (unsigned char*)GetBaseAddress(); - unsigned char* dataCpy = (unsigned char*)malloc(bytesPerRow*ioHeight); - for (size_t i = 0; i < ioHeight; i++) { - memcpy(dataCpy + i * bytesPerRow, - ioData + i * bytesPerRow, ioWidth * 4); - } - - Unlock(); - - RefPtr<SourceSurfaceRawData> surf = new SourceSurfaceRawData(); - surf->InitWrappingData(dataCpy, IntSize(ioWidth, ioHeight), bytesPerRow, mozilla::gfx::FORMAT_B8G8R8A8, true); - - return surf.forget(); -} - -CGLError -MacIOSurface::CGLTexImageIOSurface2D(void *c, - GLenum internalFormat, GLenum format, - GLenum type, GLuint plane) -{ - NSOpenGLContext *ctxt = static_cast<NSOpenGLContext*>(c); - return MacIOSurfaceLib::CGLTexImageIOSurface2D((CGLContextObj)[ctxt CGLContextObj], - GL_TEXTURE_RECTANGLE_ARB, - internalFormat, - GetDevicePixelWidth(), - GetDevicePixelHeight(), - format, type, - mIOSurfacePtr, plane); -} - CGColorSpaceRef CreateSystemColorSpace() { - CMProfileRef system_profile = nullptr; - CGColorSpaceRef cspace = nullptr; - - if (::CMGetSystemProfile(&system_profile) == noErr) { - // Create a colorspace with the systems profile - cspace = ::CGColorSpaceCreateWithPlatformColorSpace(system_profile); - ::CMCloseProfile(system_profile); - } else { - // Default to generic - cspace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - } - - return cspace; -} - -CGContextRef MacIOSurface::CreateIOSurfaceContext() { - CGContextRef ref = MacIOSurfaceLib::IOSurfaceContextCreate(mIOSurfacePtr, - GetDevicePixelWidth(), - GetDevicePixelHeight(), - 8, 32, CreateSystemColorSpace(), 0x2002); - return ref; + CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID()); + if (!cspace) { + cspace = ::CGColorSpaceCreateDeviceRGB(); + } + return cspace; } nsCARenderer::~nsCARenderer() { @@ -633,9 +194,7 @@ nsresult nsCARenderer::SetupRenderer(void *aCALayer, int aWidth, int aHeight, dataProvider, nullptr, true, kCGRenderingIntentDefault); ::CGDataProviderRelease(dataProvider); - if (colorSpace) { - ::CGColorSpaceRelease(colorSpace); - } + ::CGColorSpaceRelease(colorSpace); if (!mCGImage) { mUnsupportedWidth = aWidth; mUnsupportedHeight = aHeight; @@ -778,12 +337,9 @@ void nsCARenderer::SetViewport(int aWidth, int aHeight) { ::glScalef(1.0, -1.0, 1.0); } -void nsCARenderer::AttachIOSurface(RefPtr<MacIOSurface> aSurface) { +void nsCARenderer::AttachIOSurface(MacIOSurface *aSurface) { if (mIOSurface && aSurface->GetIOSurfaceID() == mIOSurface->GetIOSurfaceID()) { - // This object isn't needed since we already have a - // handle to the same io surface. - aSurface = nullptr; return; } @@ -809,7 +365,8 @@ void nsCARenderer::AttachIOSurface(RefPtr<MacIOSurface> aSurface) { // Rebind the FBO to make it live ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); - if (mIOSurface->GetWidth() != width || mIOSurface->GetHeight() != height) { + if (static_cast<int>(mIOSurface->GetWidth()) != width || + static_cast<int>(mIOSurface->GetHeight()) != height) { width = mIOSurface->GetWidth(); height = mIOSurface->GetHeight(); SetBounds(width, height); @@ -1068,48 +625,3 @@ void nsCARenderer::SaveToDisk(MacIOSurface *surf) { } #endif - -CGImageRef MacIOSurface::CreateImageFromIOSurfaceContext(CGContextRef aContext) { - if (!MacIOSurfaceLib::isInit()) - return nullptr; - - return MacIOSurfaceLib::IOSurfaceContextCreateImage(aContext); -} - -TemporaryRef<MacIOSurface> MacIOSurface::IOSurfaceContextGetSurface(CGContextRef aContext, - double aContentsScaleFactor) { - if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0) - return nullptr; - - IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceContextGetSurface(aContext); - if (!surfaceRef) - return nullptr; - - // Retain the IOSurface because MacIOSurface will release it - CFRetain(surfaceRef); - - RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor); - if (!ioSurface) { - ::CFRelease(surfaceRef); - return nullptr; - } - return ioSurface.forget(); -} - - -CGContextType GetContextType(CGContextRef ref) -{ - if (!MacIOSurfaceLib::isInit() || !MacIOSurfaceLib::sCGContextGetTypePtr) - return CG_CONTEXT_TYPE_UNKNOWN; - - unsigned int type = MacIOSurfaceLib::sCGContextGetTypePtr(ref); - if (type == CG_CONTEXT_TYPE_BITMAP) { - return CG_CONTEXT_TYPE_BITMAP; - } else if (type == CG_CONTEXT_TYPE_IOSURFACE) { - return CG_CONTEXT_TYPE_IOSURFACE; - } else { - return CG_CONTEXT_TYPE_UNKNOWN; - } -} - - diff --git a/gfx/2d/RadialGradientEffectD2D1.cpp b/gfx/2d/RadialGradientEffectD2D1.cpp index 42bdf7027..e53a3f855 100644 --- a/gfx/2d/RadialGradientEffectD2D1.cpp +++ b/gfx/2d/RadialGradientEffectD2D1.cpp @@ -50,11 +50,11 @@ static const PCWSTR kXmlDescription = ); // {FB947CDA-718E-40CC-AE7B-D255830D7D14} -DEFINE_GUID(GUID_SampleRadialGradientPS, -0xfb947cda, 0x718e, 0x40cc, 0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14); +static const GUID GUID_SampleRadialGradientPS = + {0xfb947cda, 0x718e, 0x40cc, {0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14}}; // {2C468128-6546-453C-8E25-F2DF0DE10A0F} -DEFINE_GUID(GUID_SampleRadialGradientA0PS, -0x2c468128, 0x6546, 0x453c, 0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf); +static const GUID GUID_SampleRadialGradientA0PS = + {0x2c468128, 0x6546, 0x453c, {0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf}}; namespace mozilla { namespace gfx { @@ -116,7 +116,7 @@ RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType) return S_OK; } - D2D1_POINT_2F dc = D2D1::Point2F(mCenter2.x - mCenter1.x, mCenter2.y - mCenter2.y); + D2D1_POINT_2F dc = D2D1::Point2F(mCenter2.x - mCenter1.x, mCenter2.y - mCenter1.y); float dr = mRadius2 - mRadius1; float A = dc.x * dc.x + dc.y * dc.y - dr * dr; @@ -147,14 +147,18 @@ RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType) float A; float radius1; float sq_radius1; - float padding2[3]; + float repeat_correct; + float allow_odd; + float padding2[1]; float transform[8]; }; PSConstantBuffer buffer = { { dc.x, dc.y, dr }, 0, { mCenter1.x, mCenter1.y }, A, mRadius1, mRadius1 * mRadius1, - { 0, 0, 0 }, { mat._11, mat._21, mat._31, 0, + mStopCollection->GetExtendMode() != D2D1_EXTEND_MODE_CLAMP ? 1 : 0, + mStopCollection->GetExtendMode() == D2D1_EXTEND_MODE_MIRROR ? 1 : 0, + { 0 }, { mat._11, mat._21, mat._31, 0, mat._12, mat._22, mat._32, 0 } }; hr = mDrawInfo->SetPixelShaderConstantBuffer((BYTE*)&buffer, sizeof(buffer)); @@ -264,12 +268,13 @@ HRESULT RadialGradientEffectD2D1::Register(ID2D1Factory1 *aFactory) { D2D1_PROPERTY_BINDING bindings[] = { - D2D1_VALUE_TYPE_BINDING(L"StopCollection", &SetStopCollection, &GetStopCollection), - D2D1_VALUE_TYPE_BINDING(L"Center1", &SetCenter1, &GetCenter1), - D2D1_VALUE_TYPE_BINDING(L"Center2", &SetCenter2, &GetCenter2), - D2D1_VALUE_TYPE_BINDING(L"Radius1", &SetRadius1, &GetRadius1), - D2D1_VALUE_TYPE_BINDING(L"Radius2", &SetRadius2, &GetRadius2), - D2D1_VALUE_TYPE_BINDING(L"Transform", &SetTransform, &GetTransform) + D2D1_VALUE_TYPE_BINDING(L"StopCollection", &RadialGradientEffectD2D1::SetStopCollection, + &RadialGradientEffectD2D1::GetStopCollection), + D2D1_VALUE_TYPE_BINDING(L"Center1", &RadialGradientEffectD2D1::SetCenter1, &RadialGradientEffectD2D1::GetCenter1), + D2D1_VALUE_TYPE_BINDING(L"Center2", &RadialGradientEffectD2D1::SetCenter2, &RadialGradientEffectD2D1::GetCenter2), + D2D1_VALUE_TYPE_BINDING(L"Radius1", &RadialGradientEffectD2D1::SetRadius1, &RadialGradientEffectD2D1::GetRadius1), + D2D1_VALUE_TYPE_BINDING(L"Radius2", &RadialGradientEffectD2D1::SetRadius2, &RadialGradientEffectD2D1::GetRadius2), + D2D1_VALUE_TYPE_BINDING(L"Transform", &RadialGradientEffectD2D1::SetTransform, &RadialGradientEffectD2D1::GetTransform) }; HRESULT hr = aFactory->RegisterEffectFromString(CLSID_RadialGradientEffect, kXmlDescription, bindings, ARRAYSIZE(bindings), CreateEffect); @@ -279,6 +284,12 @@ RadialGradientEffectD2D1::Register(ID2D1Factory1 *aFactory) return hr; } +void +RadialGradientEffectD2D1::Unregister(ID2D1Factory1 *aFactory) +{ + aFactory->UnregisterEffect(CLSID_RadialGradientEffect); +} + HRESULT __stdcall RadialGradientEffectD2D1::CreateEffect(IUnknown **aEffectImpl) { @@ -364,21 +375,23 @@ RadialGradientEffectD2D1::CreateGradientTexture() UINT32 width = 4096; UINT32 stride = 4096 * 4; D2D1_RESOURCE_TEXTURE_PROPERTIES props; - props.dimensions = 1; - props.extents = &width; + // Older shader models do not support 1D textures. So just use a width x 1 texture. + props.dimensions = 2; + UINT32 dims[] = { width, 1 }; + props.extents = dims; props.channelDepth = D2D1_CHANNEL_DEPTH_4; props.bufferPrecision = D2D1_BUFFER_PRECISION_8BPC_UNORM; props.filter = D2D1_FILTER_MIN_MAG_MIP_LINEAR; - D2D1_EXTEND_MODE extendMode = mStopCollection->GetExtendMode(); - props.extendModes = &extendMode; + D2D1_EXTEND_MODE extendMode[] = { mStopCollection->GetExtendMode(), mStopCollection->GetExtendMode() }; + props.extendModes = extendMode; HRESULT hr = mEffectContext->CreateResourceTexture(nullptr, &props, &textureData.front(), &stride, 4096 * 4, byRef(tex)); if (FAILED(hr)) { - gfxWarning() << "Failed to create resource texture: " << hr; + gfxWarning() << "Failed to create resource texture: " << hexa(hr); } - return tex; + return tex.forget(); } } diff --git a/gfx/2d/RadialGradientEffectD2D1.h b/gfx/2d/RadialGradientEffectD2D1.h index 6218f7384..ac8d433dc 100644 --- a/gfx/2d/RadialGradientEffectD2D1.h +++ b/gfx/2d/RadialGradientEffectD2D1.h @@ -11,6 +11,7 @@ #include <d2d1effecthelpers.h> #include "2D.h" +#include "mozilla/Attributes.h" // {97143DC6-CBC4-4DD4-A8BA-13342B0BA46D} DEFINE_GUID(CLSID_RadialGradientEffect, @@ -37,8 +38,8 @@ enum { RADIAL_PROP_TRANSFORM }; -class RadialGradientEffectD2D1 : public ID2D1EffectImpl - , public ID2D1DrawTransform +class RadialGradientEffectD2D1 final : public ID2D1EffectImpl + , public ID2D1DrawTransform { public: // ID2D1EffectImpl @@ -71,6 +72,7 @@ public: IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo *pDrawInfo); static HRESULT Register(ID2D1Factory1* aFactory); + static void Unregister(ID2D1Factory1* aFactory); static HRESULT __stdcall CreateEffect(IUnknown** aEffectImpl); HRESULT SetStopCollection(IUnknown *aStopCollection); diff --git a/gfx/2d/RecordedEvent.cpp b/gfx/2d/RecordedEvent.cpp index c70fd5899..8506029a2 100644 --- a/gfx/2d/RecordedEvent.cpp +++ b/gfx/2d/RecordedEvent.cpp @@ -7,6 +7,8 @@ #include "PathRecording.h" #include "Tools.h" +#include "Filters.h" +#include "Logging.h" namespace mozilla { namespace gfx { @@ -16,9 +18,9 @@ using namespace std; static std::string NameFromBackend(BackendType aType) { switch (aType) { - case BACKEND_NONE: + case BackendType::NONE: return "None"; - case BACKEND_DIRECT2D: + case BackendType::DIRECT2D: return "Direct2D"; default: return "Unknown"; @@ -49,18 +51,96 @@ RecordedEvent::LoadEventFromStream(std::istream &aStream, EventType aType) LOAD_EVENT_TYPE(STROKE, RecordedStroke); LOAD_EVENT_TYPE(DRAWSURFACE, RecordedDrawSurface); LOAD_EVENT_TYPE(DRAWSURFACEWITHSHADOW, RecordedDrawSurfaceWithShadow); + LOAD_EVENT_TYPE(DRAWFILTER, RecordedDrawFilter); LOAD_EVENT_TYPE(PATHCREATION, RecordedPathCreation); LOAD_EVENT_TYPE(PATHDESTRUCTION, RecordedPathDestruction); LOAD_EVENT_TYPE(SOURCESURFACECREATION, RecordedSourceSurfaceCreation); LOAD_EVENT_TYPE(SOURCESURFACEDESTRUCTION, RecordedSourceSurfaceDestruction); + LOAD_EVENT_TYPE(FILTERNODECREATION, RecordedFilterNodeCreation); + LOAD_EVENT_TYPE(FILTERNODEDESTRUCTION, RecordedFilterNodeDestruction); LOAD_EVENT_TYPE(GRADIENTSTOPSCREATION, RecordedGradientStopsCreation); LOAD_EVENT_TYPE(GRADIENTSTOPSDESTRUCTION, RecordedGradientStopsDestruction); LOAD_EVENT_TYPE(SNAPSHOT, RecordedSnapshot); LOAD_EVENT_TYPE(SCALEDFONTCREATION, RecordedScaledFontCreation); LOAD_EVENT_TYPE(SCALEDFONTDESTRUCTION, RecordedScaledFontDestruction); LOAD_EVENT_TYPE(MASKSURFACE, RecordedMaskSurface); + LOAD_EVENT_TYPE(FILTERNODESETATTRIBUTE, RecordedFilterNodeSetAttribute); + LOAD_EVENT_TYPE(FILTERNODESETINPUT, RecordedFilterNodeSetInput); default: - return NULL; + return nullptr; + } +} + +string +RecordedEvent::GetEventName(EventType aType) +{ + switch (aType) { + case DRAWTARGETCREATION: + return "DrawTarget Creation"; + case DRAWTARGETDESTRUCTION: + return "DrawTarget Destruction"; + case FILLRECT: + return "FillRect"; + case STROKERECT: + return "StrokeRect"; + case STROKELINE: + return "StrokeLine"; + case CLEARRECT: + return "ClearRect"; + case COPYSURFACE: + return "CopySurface"; + case SETTRANSFORM: + return "SetTransform"; + case PUSHCLIP: + return "PushClip"; + case PUSHCLIPRECT: + return "PushClipRect"; + case POPCLIP: + return "PopClip"; + case FILL: + return "Fill"; + case FILLGLYPHS: + return "FillGlyphs"; + case MASK: + return "Mask"; + case STROKE: + return "Stroke"; + case DRAWSURFACE: + return "DrawSurface"; + case DRAWSURFACEWITHSHADOW: + return "DrawSurfaceWithShadow"; + case DRAWFILTER: + return "DrawFilter"; + case PATHCREATION: + return "PathCreation"; + case PATHDESTRUCTION: + return "PathDestruction"; + case SOURCESURFACECREATION: + return "SourceSurfaceCreation"; + case SOURCESURFACEDESTRUCTION: + return "SourceSurfaceDestruction"; + case FILTERNODECREATION: + return "FilterNodeCreation"; + case FILTERNODEDESTRUCTION: + return "FilterNodeDestruction"; + case GRADIENTSTOPSCREATION: + return "GradientStopsCreation"; + case GRADIENTSTOPSDESTRUCTION: + return "GradientStopsDestruction"; + case SNAPSHOT: + return "Snapshot"; + case SCALEDFONTCREATION: + return "ScaledFontCreation"; + case SCALEDFONTDESTRUCTION: + return "ScaledFontDestruction"; + case MASKSURFACE: + return "MaskSurface"; + case FILTERNODESETATTRIBUTE: + return "SetAttribute"; + case FILTERNODESETINPUT: + return "SetInput"; + default: + return "Unknown"; } } @@ -70,22 +150,22 @@ RecordedEvent::RecordPatternData(std::ostream &aStream, const PatternStorage &aP WriteElement(aStream, aPattern.mType); switch (aPattern.mType) { - case PATTERN_COLOR: + case PatternType::COLOR: { WriteElement(aStream, *reinterpret_cast<const ColorPatternStorage*>(&aPattern.mStorage)); return; } - case PATTERN_LINEAR_GRADIENT: + case PatternType::LINEAR_GRADIENT: { WriteElement(aStream, *reinterpret_cast<const LinearGradientPatternStorage*>(&aPattern.mStorage)); return; } - case PATTERN_RADIAL_GRADIENT: + case PatternType::RADIAL_GRADIENT: { WriteElement(aStream, *reinterpret_cast<const RadialGradientPatternStorage*>(&aPattern.mStorage)); return; } - case PATTERN_SURFACE: + case PatternType::SURFACE: { WriteElement(aStream, *reinterpret_cast<const SurfacePatternStorage*>(&aPattern.mStorage)); return; @@ -101,22 +181,22 @@ RecordedEvent::ReadPatternData(std::istream &aStream, PatternStorage &aPattern) ReadElement(aStream, aPattern.mType); switch (aPattern.mType) { - case PATTERN_COLOR: + case PatternType::COLOR: { ReadElement(aStream, *reinterpret_cast<ColorPatternStorage*>(&aPattern.mStorage)); return; } - case PATTERN_LINEAR_GRADIENT: + case PatternType::LINEAR_GRADIENT: { ReadElement(aStream, *reinterpret_cast<LinearGradientPatternStorage*>(&aPattern.mStorage)); return; } - case PATTERN_RADIAL_GRADIENT: + case PatternType::RADIAL_GRADIENT: { ReadElement(aStream, *reinterpret_cast<RadialGradientPatternStorage*>(&aPattern.mStorage)); return; } - case PATTERN_SURFACE: + case PatternType::SURFACE: { ReadElement(aStream, *reinterpret_cast<SurfacePatternStorage*>(&aPattern.mStorage)); return; @@ -132,13 +212,13 @@ RecordedEvent::StorePattern(PatternStorage &aDestination, const Pattern &aSource aDestination.mType = aSource.GetType(); switch (aSource.GetType()) { - case PATTERN_COLOR: + case PatternType::COLOR: { reinterpret_cast<ColorPatternStorage*>(&aDestination.mStorage)->mColor = static_cast<const ColorPattern*>(&aSource)->mColor; return; } - case PATTERN_LINEAR_GRADIENT: + case PatternType::LINEAR_GRADIENT: { LinearGradientPatternStorage *store = reinterpret_cast<LinearGradientPatternStorage*>(&aDestination.mStorage); @@ -150,7 +230,7 @@ RecordedEvent::StorePattern(PatternStorage &aDestination, const Pattern &aSource store->mStops = pat->mStops.get(); return; } - case PATTERN_RADIAL_GRADIENT: + case PatternType::RADIAL_GRADIENT: { RadialGradientPatternStorage *store = reinterpret_cast<RadialGradientPatternStorage*>(&aDestination.mStorage); @@ -164,7 +244,7 @@ RecordedEvent::StorePattern(PatternStorage &aDestination, const Pattern &aSource store->mStops = pat->mStops.get(); return; } - case PATTERN_SURFACE: + case PatternType::SURFACE: { SurfacePatternStorage *store = reinterpret_cast<SurfacePatternStorage*>(&aDestination.mStorage); @@ -231,13 +311,13 @@ void RecordedEvent::OutputSimplePatternInfo(const PatternStorage &aStorage, std::stringstream &aOutput) const { switch (aStorage.mType) { - case PATTERN_COLOR: + case PatternType::COLOR: { const Color color = reinterpret_cast<const ColorPatternStorage*>(&aStorage.mStorage)->mColor; aOutput << "Color: (" << color.r << ", " << color.g << ", " << color.b << ", " << color.a << ")"; return; } - case PATTERN_LINEAR_GRADIENT: + case PatternType::LINEAR_GRADIENT: { const LinearGradientPatternStorage *store = reinterpret_cast<const LinearGradientPatternStorage*>(&aStorage.mStorage); @@ -246,7 +326,7 @@ RecordedEvent::OutputSimplePatternInfo(const PatternStorage &aStorage, std::stri ") - (" << store->mEnd.x << ", " << store->mEnd.y << ") Stops: " << store->mStops; return; } - case PATTERN_RADIAL_GRADIENT: + case PatternType::RADIAL_GRADIENT: { const RadialGradientPatternStorage *store = reinterpret_cast<const RadialGradientPatternStorage*>(&aStorage.mStorage); @@ -254,7 +334,7 @@ RecordedEvent::OutputSimplePatternInfo(const PatternStorage &aStorage, std::stri store->mCenter2.y << ") Radius 2: " << store->mRadius2; return; } - case PATTERN_SURFACE: + case PatternType::SURFACE: { const SurfacePatternStorage *store = reinterpret_cast<const SurfacePatternStorage*>(&aStorage.mStorage); @@ -277,7 +357,7 @@ RecordedDrawingEvent::RecordToStream(ostream &aStream) const } ReferencePtr -RecordedDrawingEvent::GetObject() const +RecordedDrawingEvent::GetObjectRef() const { return mDT; } @@ -288,6 +368,11 @@ RecordedDrawTargetCreation::PlayEvent(Translator *aTranslator) const RefPtr<DrawTarget> newDT = aTranslator->GetReferenceDrawTarget()->CreateSimilarDrawTarget(mSize, mFormat); aTranslator->AddDrawTarget(mRefPtr, newDT); + + if (mHasExistingData) { + Rect dataRect(0, 0, mExistingData->GetSize().width, mExistingData->GetSize().height); + newDT->DrawSurface(mExistingData, dataRect, dataRect); + } } void @@ -297,15 +382,43 @@ RecordedDrawTargetCreation::RecordToStream(ostream &aStream) const WriteElement(aStream, mBackendType); WriteElement(aStream, mSize); WriteElement(aStream, mFormat); + WriteElement(aStream, mHasExistingData); + + if (mHasExistingData) { + MOZ_ASSERT(mExistingData); + MOZ_ASSERT(mExistingData->GetSize() == mSize); + RefPtr<DataSourceSurface> dataSurf = mExistingData->GetDataSurface(); + for (int y = 0; y < mSize.height; y++) { + aStream.write((const char*)dataSurf->GetData() + y * dataSurf->Stride(), + BytesPerPixel(mFormat) * mSize.width); + } + } } RecordedDrawTargetCreation::RecordedDrawTargetCreation(istream &aStream) : RecordedEvent(DRAWTARGETCREATION) + , mExistingData(nullptr) { ReadElement(aStream, mRefPtr); ReadElement(aStream, mBackendType); ReadElement(aStream, mSize); ReadElement(aStream, mFormat); + ReadElement(aStream, mHasExistingData); + + if (mHasExistingData) { + RefPtr<DataSourceSurface> dataSurf = Factory::CreateDataSourceSurface(mSize, mFormat); + if (!dataSurf) { + gfxWarning() << "RecordedDrawTargetCreation had to reset mHasExistingData"; + mHasExistingData = false; + return; + } + + for (int y = 0; y < mSize.height; y++) { + aStream.read((char*)dataSurf->GetData() + y * dataSurf->Stride(), + BytesPerPixel(mFormat) * mSize.width); + } + mExistingData = dataSurf; + } } void @@ -342,7 +455,7 @@ RecordedDrawTargetDestruction::OutputSimpleEventInfo(stringstream &aStringStream struct GenericPattern { GenericPattern(const PatternStorage &aStorage, Translator *aTranslator) - : mPattern(NULL), mTranslator(aTranslator) + : mPattern(nullptr), mTranslator(aTranslator) { mStorage = const_cast<PatternStorage*>(&aStorage); } @@ -356,9 +469,9 @@ struct GenericPattern operator Pattern*() { switch(mStorage->mType) { - case PATTERN_COLOR: + case PatternType::COLOR: return new (mColPat) ColorPattern(reinterpret_cast<ColorPatternStorage*>(&mStorage->mStorage)->mColor); - case PATTERN_SURFACE: + case PatternType::SURFACE: { SurfacePatternStorage *storage = reinterpret_cast<SurfacePatternStorage*>(&mStorage->mStorage); mPattern = @@ -366,7 +479,7 @@ struct GenericPattern storage->mExtend, storage->mMatrix, storage->mFilter); return mPattern; } - case PATTERN_LINEAR_GRADIENT: + case PatternType::LINEAR_GRADIENT: { LinearGradientPatternStorage *storage = reinterpret_cast<LinearGradientPatternStorage*>(&mStorage->mStorage); mPattern = @@ -375,7 +488,7 @@ struct GenericPattern storage->mMatrix); return mPattern; } - case PATTERN_RADIAL_GRADIENT: + case PatternType::RADIAL_GRADIENT: { RadialGradientPatternStorage *storage = reinterpret_cast<RadialGradientPatternStorage*>(&mStorage->mStorage); mPattern = @@ -829,6 +942,39 @@ RecordedDrawSurface::OutputSimpleEventInfo(stringstream &aStringStream) const } void +RecordedDrawFilter::PlayEvent(Translator *aTranslator) const +{ + aTranslator->LookupDrawTarget(mDT)-> + DrawFilter(aTranslator->LookupFilterNode(mNode), mSourceRect, + mDestPoint, mOptions); +} + +void +RecordedDrawFilter::RecordToStream(ostream &aStream) const +{ + RecordedDrawingEvent::RecordToStream(aStream); + WriteElement(aStream, mNode); + WriteElement(aStream, mSourceRect); + WriteElement(aStream, mDestPoint); + WriteElement(aStream, mOptions); +} + +RecordedDrawFilter::RecordedDrawFilter(istream &aStream) + : RecordedDrawingEvent(DRAWFILTER, aStream) +{ + ReadElement(aStream, mNode); + ReadElement(aStream, mSourceRect); + ReadElement(aStream, mDestPoint); + ReadElement(aStream, mOptions); +} + +void +RecordedDrawFilter::OutputSimpleEventInfo(stringstream &aStringStream) const +{ + aStringStream << "[" << mDT << "] DrawFilter (" << mNode << ")"; +} + +void RecordedDrawSurfaceWithShadow::PlayEvent(Translator *aTranslator) const { aTranslator->LookupDrawTarget(mDT)-> @@ -1050,6 +1196,62 @@ RecordedSourceSurfaceDestruction::OutputSimpleEventInfo(stringstream &aStringStr aStringStream << "[" << mRefPtr << "] SourceSurface Destroyed"; } +RecordedFilterNodeCreation::~RecordedFilterNodeCreation() +{ +} + +void +RecordedFilterNodeCreation::PlayEvent(Translator *aTranslator) const +{ + RefPtr<FilterNode> node = aTranslator->GetReferenceDrawTarget()-> + CreateFilter(mType); + aTranslator->AddFilterNode(mRefPtr, node); +} + +void +RecordedFilterNodeCreation::RecordToStream(ostream &aStream) const +{ + WriteElement(aStream, mRefPtr); + WriteElement(aStream, mType); +} + +RecordedFilterNodeCreation::RecordedFilterNodeCreation(istream &aStream) + : RecordedEvent(FILTERNODECREATION) +{ + ReadElement(aStream, mRefPtr); + ReadElement(aStream, mType); +} + +void +RecordedFilterNodeCreation::OutputSimpleEventInfo(stringstream &aStringStream) const +{ + aStringStream << "[" << mRefPtr << "] FilterNode created (Type: " << int(mType) << ")"; +} + +void +RecordedFilterNodeDestruction::PlayEvent(Translator *aTranslator) const +{ + aTranslator->RemoveFilterNode(mRefPtr); +} + +void +RecordedFilterNodeDestruction::RecordToStream(ostream &aStream) const +{ + WriteElement(aStream, mRefPtr); +} + +RecordedFilterNodeDestruction::RecordedFilterNodeDestruction(istream &aStream) + : RecordedEvent(FILTERNODEDESTRUCTION) +{ + ReadElement(aStream, mRefPtr); +} + +void +RecordedFilterNodeDestruction::OutputSimpleEventInfo(stringstream &aStringStream) const +{ + aStringStream << "[" << mRefPtr << "] FilterNode Destroyed"; +} + RecordedGradientStopsCreation::~RecordedGradientStopsCreation() { if (mDataOwned) { @@ -1252,5 +1454,116 @@ RecordedMaskSurface::OutputSimpleEventInfo(stringstream &aStringStream) const OutputSimplePatternInfo(mPattern, aStringStream); } +template<typename T> +void +ReplaySetAttribute(FilterNode *aNode, uint32_t aIndex, T aValue) +{ + aNode->SetAttribute(aIndex, aValue); +} + +void +RecordedFilterNodeSetAttribute::PlayEvent(Translator *aTranslator) const +{ +#define REPLAY_SET_ATTRIBUTE(type, argtype) \ + case ARGTYPE_##argtype: \ + ReplaySetAttribute(aTranslator->LookupFilterNode(mNode), mIndex, *(type*)&mPayload.front()); \ + break + + switch (mArgType) { + REPLAY_SET_ATTRIBUTE(bool, BOOL); + REPLAY_SET_ATTRIBUTE(uint32_t, UINT32); + REPLAY_SET_ATTRIBUTE(Float, FLOAT); + REPLAY_SET_ATTRIBUTE(Size, SIZE); + REPLAY_SET_ATTRIBUTE(IntSize, INTSIZE); + REPLAY_SET_ATTRIBUTE(IntPoint, INTPOINT); + REPLAY_SET_ATTRIBUTE(Rect, RECT); + REPLAY_SET_ATTRIBUTE(IntRect, INTRECT); + REPLAY_SET_ATTRIBUTE(Point, POINT); + REPLAY_SET_ATTRIBUTE(Matrix5x4, MATRIX5X4); + REPLAY_SET_ATTRIBUTE(Point3D, POINT3D); + REPLAY_SET_ATTRIBUTE(Color, COLOR); + case ARGTYPE_FLOAT_ARRAY: + aTranslator->LookupFilterNode(mNode)->SetAttribute( + mIndex, + reinterpret_cast<const Float*>(&mPayload.front()), + mPayload.size() / sizeof(Float)); + break; + } +} + +void +RecordedFilterNodeSetAttribute::RecordToStream(ostream &aStream) const +{ + RecordedEvent::RecordToStream(aStream); + WriteElement(aStream, mNode); + WriteElement(aStream, mIndex); + WriteElement(aStream, mArgType); + WriteElement(aStream, uint64_t(mPayload.size())); + aStream.write((const char*)&mPayload.front(), mPayload.size()); +} + +RecordedFilterNodeSetAttribute::RecordedFilterNodeSetAttribute(istream &aStream) + : RecordedEvent(FILTERNODESETATTRIBUTE) +{ + ReadElement(aStream, mNode); + ReadElement(aStream, mIndex); + ReadElement(aStream, mArgType); + uint64_t size; + ReadElement(aStream, size); + mPayload.resize(size_t(size)); + aStream.read((char*)&mPayload.front(), size); +} + +void +RecordedFilterNodeSetAttribute::OutputSimpleEventInfo(stringstream &aStringStream) const +{ + aStringStream << "[" << mNode << "] SetAttribute (" << mIndex << ")"; +} + +void +RecordedFilterNodeSetInput::PlayEvent(Translator *aTranslator) const +{ + if (mInputFilter) { + aTranslator->LookupFilterNode(mNode)->SetInput( + mIndex, aTranslator->LookupFilterNode(mInputFilter)); + } else { + aTranslator->LookupFilterNode(mNode)->SetInput( + mIndex, aTranslator->LookupSourceSurface(mInputSurface)); + } +} + +void +RecordedFilterNodeSetInput::RecordToStream(ostream &aStream) const +{ + RecordedEvent::RecordToStream(aStream); + WriteElement(aStream, mNode); + WriteElement(aStream, mIndex); + WriteElement(aStream, mInputFilter); + WriteElement(aStream, mInputSurface); +} + +RecordedFilterNodeSetInput::RecordedFilterNodeSetInput(istream &aStream) + : RecordedEvent(FILTERNODESETINPUT) +{ + ReadElement(aStream, mNode); + ReadElement(aStream, mIndex); + ReadElement(aStream, mInputFilter); + ReadElement(aStream, mInputSurface); +} + +void +RecordedFilterNodeSetInput::OutputSimpleEventInfo(stringstream &aStringStream) const +{ + aStringStream << "[" << mNode << "] SetAttribute (" << mIndex << ", "; + + if (mInputFilter) { + aStringStream << "Filter: " << mInputFilter; + } else { + aStringStream << "Surface: " << mInputSurface; + } + + aStringStream << ")"; +} + } } diff --git a/gfx/2d/RecordedEvent.h b/gfx/2d/RecordedEvent.h index 27e2b1838..fca007650 100644 --- a/gfx/2d/RecordedEvent.h +++ b/gfx/2d/RecordedEvent.h @@ -20,10 +20,10 @@ namespace gfx { // loss of backwards compatibility. Old streams will not work in a player // using a newer major revision. And new streams will not work in a player // using an older major revision. -const uint16_t kMajorRevision = 2; +const uint16_t kMajorRevision = 3; // A change in minor revision means additions of new events. New streams will // not play in older players. -const uint16_t kMinorRevision = 1; +const uint16_t kMinorRevision = 2; struct ReferencePtr { @@ -31,7 +31,7 @@ struct ReferencePtr : mLongPtr(0) {} - ReferencePtr(const void* aLongPtr) + MOZ_IMPLICIT ReferencePtr(const void* aLongPtr) : mLongPtr(uint64_t(aLongPtr)) {} @@ -69,9 +69,12 @@ inline std::string StringFromPtr(ReferencePtr aPtr) class Translator { public: + virtual ~Translator() {} + virtual DrawTarget *LookupDrawTarget(ReferencePtr aRefPtr) = 0; virtual Path *LookupPath(ReferencePtr aRefPtr) = 0; virtual SourceSurface *LookupSourceSurface(ReferencePtr aRefPtr) = 0; + virtual FilterNode *LookupFilterNode(ReferencePtr aRefPtr) = 0; virtual GradientStops *LookupGradientStops(ReferencePtr aRefPtr) = 0; virtual ScaledFont *LookupScaledFont(ReferencePtr aRefPtr) = 0; virtual void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget *aDT) = 0; @@ -80,6 +83,8 @@ public: virtual void RemovePath(ReferencePtr aRefPtr) = 0; virtual void AddSourceSurface(ReferencePtr aRefPtr, SourceSurface *aPath) = 0; virtual void RemoveSourceSurface(ReferencePtr aRefPtr) = 0; + virtual void AddFilterNode(mozilla::gfx::ReferencePtr aRefPtr, FilterNode *aSurface) = 0; + virtual void RemoveFilterNode(mozilla::gfx::ReferencePtr aRefPtr) = 0; virtual void AddGradientStops(ReferencePtr aRefPtr, GradientStops *aPath) = 0; virtual void RemoveGradientStops(ReferencePtr aRefPtr) = 0; virtual void AddScaledFont(ReferencePtr aRefPtr, ScaledFont *aScaledFont) = 0; @@ -161,8 +166,16 @@ public: SNAPSHOT, SCALEDFONTCREATION, SCALEDFONTDESTRUCTION, - MASKSURFACE + MASKSURFACE, + FILTERNODECREATION, + FILTERNODEDESTRUCTION, + DRAWFILTER, + FILTERNODESETATTRIBUTE, + FILTERNODESETINPUT }; + static const uint32_t kTotalEventTypes = RecordedEvent::FILTERNODESETINPUT + 1; + + static std::string GetEventName(EventType aType); virtual void PlayEvent(Translator *aTranslator) const {} @@ -178,7 +191,7 @@ public: virtual std::string GetName() const = 0; - virtual ReferencePtr GetObject() const = 0; + virtual ReferencePtr GetObjectRef() const = 0; virtual ReferencePtr GetDestinedDT() { return nullptr; } @@ -190,7 +203,7 @@ public: protected: friend class DrawEventRecorderPrivate; - RecordedEvent(int32_t aType) : mType(aType) + MOZ_IMPLICIT RecordedEvent(int32_t aType) : mType(aType) {} int32_t mType; @@ -211,15 +224,17 @@ protected: RecordedDrawingEvent(EventType aType, std::istream &aStream); virtual void RecordToStream(std::ostream &aStream) const; - virtual ReferencePtr GetObject() const; + virtual ReferencePtr GetObjectRef() const; ReferencePtr mDT; }; class RecordedDrawTargetCreation : public RecordedEvent { public: - RecordedDrawTargetCreation(ReferencePtr aRefPtr, BackendType aType, const IntSize &aSize, SurfaceFormat aFormat) + RecordedDrawTargetCreation(ReferencePtr aRefPtr, BackendType aType, const IntSize &aSize, SurfaceFormat aFormat, + bool aHasExistingData = false, SourceSurface *aExistingData = nullptr) : RecordedEvent(DRAWTARGETCREATION), mRefPtr(aRefPtr), mBackendType(aType), mSize(aSize), mFormat(aFormat) + , mHasExistingData(aHasExistingData), mExistingData(aExistingData) {} virtual void PlayEvent(Translator *aTranslator) const; @@ -228,22 +243,24 @@ public: virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; virtual std::string GetName() const { return "DrawTarget Creation"; } - virtual ReferencePtr GetObject() const { return mRefPtr; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } ReferencePtr mRefPtr; BackendType mBackendType; IntSize mSize; SurfaceFormat mFormat; + bool mHasExistingData; + RefPtr<SourceSurface> mExistingData; private: friend class RecordedEvent; - RecordedDrawTargetCreation(std::istream &aStream); + MOZ_IMPLICIT RecordedDrawTargetCreation(std::istream &aStream); }; class RecordedDrawTargetDestruction : public RecordedEvent { public: - RecordedDrawTargetDestruction(ReferencePtr aRefPtr) + MOZ_IMPLICIT RecordedDrawTargetDestruction(ReferencePtr aRefPtr) : RecordedEvent(DRAWTARGETDESTRUCTION), mRefPtr(aRefPtr) {} @@ -253,7 +270,7 @@ public: virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; virtual std::string GetName() const { return "DrawTarget Destruction"; } - virtual ReferencePtr GetObject() const { return mRefPtr; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } ReferencePtr mRefPtr; @@ -261,7 +278,7 @@ public: private: friend class RecordedEvent; - RecordedDrawTargetDestruction(std::istream &aStream); + MOZ_IMPLICIT RecordedDrawTargetDestruction(std::istream &aStream); }; class RecordedFillRect : public RecordedDrawingEvent { @@ -281,7 +298,7 @@ public: private: friend class RecordedEvent; - RecordedFillRect(std::istream &aStream); + MOZ_IMPLICIT RecordedFillRect(std::istream &aStream); Rect mRect; PatternStorage mPattern; @@ -307,7 +324,7 @@ public: private: friend class RecordedEvent; - RecordedStrokeRect(std::istream &aStream); + MOZ_IMPLICIT RecordedStrokeRect(std::istream &aStream); Rect mRect; PatternStorage mPattern; @@ -335,7 +352,7 @@ public: private: friend class RecordedEvent; - RecordedStrokeLine(std::istream &aStream); + MOZ_IMPLICIT RecordedStrokeLine(std::istream &aStream); Point mBegin; Point mEnd; @@ -361,7 +378,7 @@ public: private: friend class RecordedEvent; - RecordedFill(std::istream &aStream); + MOZ_IMPLICIT RecordedFill(std::istream &aStream); ReferencePtr mPath; PatternStorage mPattern; @@ -390,7 +407,7 @@ public: private: friend class RecordedEvent; - RecordedFillGlyphs(std::istream &aStream); + MOZ_IMPLICIT RecordedFillGlyphs(std::istream &aStream); ReferencePtr mScaledFont; PatternStorage mPattern; @@ -417,7 +434,7 @@ public: private: friend class RecordedEvent; - RecordedMask(std::istream &aStream); + MOZ_IMPLICIT RecordedMask(std::istream &aStream); PatternStorage mSource; PatternStorage mMask; @@ -443,7 +460,7 @@ public: private: friend class RecordedEvent; - RecordedStroke(std::istream &aStream); + MOZ_IMPLICIT RecordedStroke(std::istream &aStream); ReferencePtr mPath; PatternStorage mPattern; @@ -467,7 +484,7 @@ public: private: friend class RecordedEvent; - RecordedClearRect(std::istream &aStream); + MOZ_IMPLICIT RecordedClearRect(std::istream &aStream); Rect mRect; }; @@ -490,7 +507,7 @@ public: private: friend class RecordedEvent; - RecordedCopySurface(std::istream &aStream); + MOZ_IMPLICIT RecordedCopySurface(std::istream &aStream); ReferencePtr mSourceSurface; IntRect mSourceRect; @@ -513,7 +530,7 @@ public: private: friend class RecordedEvent; - RecordedPushClip(std::istream &aStream); + MOZ_IMPLICIT RecordedPushClip(std::istream &aStream); ReferencePtr mPath; }; @@ -534,14 +551,14 @@ public: private: friend class RecordedEvent; - RecordedPushClipRect(std::istream &aStream); + MOZ_IMPLICIT RecordedPushClipRect(std::istream &aStream); Rect mRect; }; class RecordedPopClip : public RecordedDrawingEvent { public: - RecordedPopClip(DrawTarget *aDT) + MOZ_IMPLICIT RecordedPopClip(DrawTarget *aDT) : RecordedDrawingEvent(POPCLIP, aDT) {} @@ -554,7 +571,7 @@ public: private: friend class RecordedEvent; - RecordedPopClip(std::istream &aStream); + MOZ_IMPLICIT RecordedPopClip(std::istream &aStream); }; class RecordedSetTransform : public RecordedDrawingEvent { @@ -573,7 +590,7 @@ public: private: friend class RecordedEvent; - RecordedSetTransform(std::istream &aStream); + MOZ_IMPLICIT RecordedSetTransform(std::istream &aStream); Matrix mTransform; }; @@ -597,7 +614,7 @@ public: private: friend class RecordedEvent; - RecordedDrawSurface(std::istream &aStream); + MOZ_IMPLICIT RecordedDrawSurface(std::istream &aStream); ReferencePtr mRefSource; Rect mDest; @@ -625,7 +642,7 @@ public: private: friend class RecordedEvent; - RecordedDrawSurfaceWithShadow(std::istream &aStream); + MOZ_IMPLICIT RecordedDrawSurfaceWithShadow(std::istream &aStream); ReferencePtr mRefSource; Point mDest; @@ -635,9 +652,37 @@ private: CompositionOp mOp; }; +class RecordedDrawFilter : public RecordedDrawingEvent { +public: + RecordedDrawFilter(DrawTarget *aDT, ReferencePtr aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions) + : RecordedDrawingEvent(DRAWFILTER, aDT), mNode(aNode), mSourceRect(aSourceRect) + , mDestPoint(aDestPoint), mOptions(aOptions) + { + } + + virtual void PlayEvent(Translator *aTranslator) const; + + virtual void RecordToStream(std::ostream &aStream) const; + virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; + + virtual std::string GetName() const { return "DrawFilter"; } +private: + friend class RecordedEvent; + + MOZ_IMPLICIT RecordedDrawFilter(std::istream &aStream); + + ReferencePtr mNode; + Rect mSourceRect; + Point mDestPoint; + DrawOptions mOptions; +}; + class RecordedPathCreation : public RecordedEvent { public: - RecordedPathCreation(PathRecording *aPath); + MOZ_IMPLICIT RecordedPathCreation(PathRecording *aPath); ~RecordedPathCreation(); virtual void PlayEvent(Translator *aTranslator) const; @@ -646,7 +691,7 @@ public: virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; virtual std::string GetName() const { return "Path Creation"; } - virtual ReferencePtr GetObject() const { return mRefPtr; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } private: friend class RecordedEvent; @@ -654,12 +699,12 @@ private: FillRule mFillRule; std::vector<PathOp> mPathOps; - RecordedPathCreation(std::istream &aStream); + MOZ_IMPLICIT RecordedPathCreation(std::istream &aStream); }; class RecordedPathDestruction : public RecordedEvent { public: - RecordedPathDestruction(PathRecording *aPath) + MOZ_IMPLICIT RecordedPathDestruction(PathRecording *aPath) : RecordedEvent(PATHDESTRUCTION), mRefPtr(aPath) { } @@ -670,13 +715,13 @@ public: virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; virtual std::string GetName() const { return "Path Destruction"; } - virtual ReferencePtr GetObject() const { return mRefPtr; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } private: friend class RecordedEvent; ReferencePtr mRefPtr; - RecordedPathDestruction(std::istream &aStream); + MOZ_IMPLICIT RecordedPathDestruction(std::istream &aStream); }; class RecordedSourceSurfaceCreation : public RecordedEvent { @@ -696,7 +741,7 @@ public: virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; virtual std::string GetName() const { return "SourceSurface Creation"; } - virtual ReferencePtr GetObject() const { return mRefPtr; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } private: friend class RecordedEvent; @@ -707,12 +752,12 @@ private: SurfaceFormat mFormat; bool mDataOwned; - RecordedSourceSurfaceCreation(std::istream &aStream); + MOZ_IMPLICIT RecordedSourceSurfaceCreation(std::istream &aStream); }; class RecordedSourceSurfaceDestruction : public RecordedEvent { public: - RecordedSourceSurfaceDestruction(ReferencePtr aRefPtr) + MOZ_IMPLICIT RecordedSourceSurfaceDestruction(ReferencePtr aRefPtr) : RecordedEvent(SOURCESURFACEDESTRUCTION), mRefPtr(aRefPtr) { } @@ -723,13 +768,60 @@ public: virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; virtual std::string GetName() const { return "SourceSurface Destruction"; } - virtual ReferencePtr GetObject() const { return mRefPtr; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } private: friend class RecordedEvent; ReferencePtr mRefPtr; - RecordedSourceSurfaceDestruction(std::istream &aStream); + MOZ_IMPLICIT RecordedSourceSurfaceDestruction(std::istream &aStream); +}; + +class RecordedFilterNodeCreation : public RecordedEvent { +public: + RecordedFilterNodeCreation(ReferencePtr aRefPtr, FilterType aType) + : RecordedEvent(FILTERNODECREATION), mRefPtr(aRefPtr), mType(aType) + { + } + + ~RecordedFilterNodeCreation(); + + virtual void PlayEvent(Translator *aTranslator) const; + + virtual void RecordToStream(std::ostream &aStream) const; + virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; + + virtual std::string GetName() const { return "FilterNode Creation"; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } +private: + friend class RecordedEvent; + + ReferencePtr mRefPtr; + FilterType mType; + + MOZ_IMPLICIT RecordedFilterNodeCreation(std::istream &aStream); +}; + +class RecordedFilterNodeDestruction : public RecordedEvent { +public: + MOZ_IMPLICIT RecordedFilterNodeDestruction(ReferencePtr aRefPtr) + : RecordedEvent(FILTERNODEDESTRUCTION), mRefPtr(aRefPtr) + { + } + + virtual void PlayEvent(Translator *aTranslator) const; + + virtual void RecordToStream(std::ostream &aStream) const; + virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; + + virtual std::string GetName() const { return "FilterNode Destruction"; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } +private: + friend class RecordedEvent; + + ReferencePtr mRefPtr; + + MOZ_IMPLICIT RecordedFilterNodeDestruction(std::istream &aStream); }; class RecordedGradientStopsCreation : public RecordedEvent { @@ -749,7 +841,7 @@ public: virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; virtual std::string GetName() const { return "GradientStops Creation"; } - virtual ReferencePtr GetObject() const { return mRefPtr; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } private: friend class RecordedEvent; @@ -759,12 +851,12 @@ private: ExtendMode mExtendMode; bool mDataOwned; - RecordedGradientStopsCreation(std::istream &aStream); + MOZ_IMPLICIT RecordedGradientStopsCreation(std::istream &aStream); }; class RecordedGradientStopsDestruction : public RecordedEvent { public: - RecordedGradientStopsDestruction(ReferencePtr aRefPtr) + MOZ_IMPLICIT RecordedGradientStopsDestruction(ReferencePtr aRefPtr) : RecordedEvent(GRADIENTSTOPSDESTRUCTION), mRefPtr(aRefPtr) { } @@ -775,13 +867,13 @@ public: virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; virtual std::string GetName() const { return "GradientStops Destruction"; } - virtual ReferencePtr GetObject() const { return mRefPtr; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } private: friend class RecordedEvent; ReferencePtr mRefPtr; - RecordedGradientStopsDestruction(std::istream &aStream); + MOZ_IMPLICIT RecordedGradientStopsDestruction(std::istream &aStream); }; class RecordedSnapshot : public RecordedEvent { @@ -797,14 +889,14 @@ public: virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; virtual std::string GetName() const { return "Snapshot"; } - virtual ReferencePtr GetObject() const { return mRefPtr; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } private: friend class RecordedEvent; ReferencePtr mRefPtr; ReferencePtr mDT; - RecordedSnapshot(std::istream &aStream); + MOZ_IMPLICIT RecordedSnapshot(std::istream &aStream); }; class RecordedScaledFontCreation : public RecordedEvent { @@ -815,7 +907,7 @@ public: } RecordedScaledFontCreation(ReferencePtr aRefPtr, ScaledFont *aScaledFont) - : RecordedEvent(SCALEDFONTCREATION), mRefPtr(aRefPtr), mData(NULL) + : RecordedEvent(SCALEDFONTCREATION), mRefPtr(aRefPtr), mData(nullptr) { aScaledFont->GetFontFileData(&FontDataProc, this); } @@ -828,7 +920,7 @@ public: virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; virtual std::string GetName() const { return "ScaledFont Creation"; } - virtual ReferencePtr GetObject() const { return mRefPtr; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } void SetFontData(const uint8_t *aData, uint32_t aSize, uint32_t aIndex, Float aGlyphSize); @@ -841,12 +933,12 @@ private: Float mGlyphSize; uint32_t mIndex; - RecordedScaledFontCreation(std::istream &aStream); + MOZ_IMPLICIT RecordedScaledFontCreation(std::istream &aStream); }; class RecordedScaledFontDestruction : public RecordedEvent { public: - RecordedScaledFontDestruction(ReferencePtr aRefPtr) + MOZ_IMPLICIT RecordedScaledFontDestruction(ReferencePtr aRefPtr) : RecordedEvent(SCALEDFONTDESTRUCTION), mRefPtr(aRefPtr) { } @@ -857,13 +949,13 @@ public: virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; virtual std::string GetName() const { return "ScaledFont Destruction"; } - virtual ReferencePtr GetObject() const { return mRefPtr; } + virtual ReferencePtr GetObjectRef() const { return mRefPtr; } private: friend class RecordedEvent; ReferencePtr mRefPtr; - RecordedScaledFontDestruction(std::istream &aStream); + MOZ_IMPLICIT RecordedScaledFontDestruction(std::istream &aStream); }; class RecordedMaskSurface : public RecordedDrawingEvent { @@ -885,7 +977,7 @@ public: private: friend class RecordedEvent; - RecordedMaskSurface(std::istream &aStream); + MOZ_IMPLICIT RecordedMaskSurface(std::istream &aStream); PatternStorage mPattern; ReferencePtr mRefMask; @@ -893,6 +985,94 @@ private: DrawOptions mOptions; }; +class RecordedFilterNodeSetAttribute : public RecordedEvent +{ +public: + enum ArgType { + ARGTYPE_UINT32, + ARGTYPE_BOOL, + ARGTYPE_FLOAT, + ARGTYPE_SIZE, + ARGTYPE_INTSIZE, + ARGTYPE_INTPOINT, + ARGTYPE_RECT, + ARGTYPE_INTRECT, + ARGTYPE_POINT, + ARGTYPE_MATRIX5X4, + ARGTYPE_POINT3D, + ARGTYPE_COLOR, + ARGTYPE_FLOAT_ARRAY + }; + + template<typename T> + RecordedFilterNodeSetAttribute(FilterNode *aNode, uint32_t aIndex, T aArgument, ArgType aArgType) + : RecordedEvent(FILTERNODESETATTRIBUTE), mNode(aNode), mIndex(aIndex), mArgType(aArgType) + { + mPayload.resize(sizeof(T)); + memcpy(&mPayload.front(), &aArgument, sizeof(T)); + } + + RecordedFilterNodeSetAttribute(FilterNode *aNode, uint32_t aIndex, const Float *aFloat, uint32_t aSize) + : RecordedEvent(FILTERNODESETATTRIBUTE), mNode(aNode), mIndex(aIndex), mArgType(ARGTYPE_FLOAT_ARRAY) + { + mPayload.resize(sizeof(Float) * aSize); + memcpy(&mPayload.front(), aFloat, sizeof(Float) * aSize); + } + + virtual void PlayEvent(Translator *aTranslator) const; + virtual void RecordToStream(std::ostream &aStream) const; + virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; + + virtual std::string GetName() const { return "SetAttribute"; } + + virtual ReferencePtr GetObjectRef() const { return mNode; } + +private: + friend class RecordedEvent; + + ReferencePtr mNode; + + uint32_t mIndex; + ArgType mArgType; + std::vector<uint8_t> mPayload; + + MOZ_IMPLICIT RecordedFilterNodeSetAttribute(std::istream &aStream); +}; + +class RecordedFilterNodeSetInput : public RecordedEvent +{ +public: + RecordedFilterNodeSetInput(FilterNode* aNode, uint32_t aIndex, FilterNode* aInputNode) + : RecordedEvent(FILTERNODESETINPUT), mNode(aNode), mIndex(aIndex) + , mInputFilter(aInputNode), mInputSurface(nullptr) + { + } + + RecordedFilterNodeSetInput(FilterNode *aNode, uint32_t aIndex, SourceSurface *aInputSurface) + : RecordedEvent(FILTERNODESETINPUT), mNode(aNode), mIndex(aIndex) + , mInputFilter(nullptr), mInputSurface(aInputSurface) + { + } + + virtual void PlayEvent(Translator *aTranslator) const; + virtual void RecordToStream(std::ostream &aStream) const; + virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const; + + virtual std::string GetName() const { return "SetInput"; } + + virtual ReferencePtr GetObjectRef() const { return mNode; } + +private: + friend class RecordedEvent; + + ReferencePtr mNode; + uint32_t mIndex; + ReferencePtr mInputFilter; + ReferencePtr mInputSurface; + + MOZ_IMPLICIT RecordedFilterNodeSetInput(std::istream &aStream); +}; + } } diff --git a/gfx/2d/Rect.h b/gfx/2d/Rect.h index 2084b394e..db0f45d37 100644 --- a/gfx/2d/Rect.h +++ b/gfx/2d/Rect.h @@ -11,28 +11,67 @@ #include "Point.h" #include "Tools.h" +#include <cmath> + namespace mozilla { + +template <typename> struct IsPixel; + namespace gfx { -struct Margin : - public BaseMargin<Float, Margin> { - typedef BaseMargin<Float, Margin> Super; +template<class units> +struct IntMarginTyped: + public BaseMargin<int32_t, IntMarginTyped<units> >, + public units { + static_assert(IsPixel<units>::value, + "'units' must be a coordinate system tag"); + + typedef BaseMargin<int32_t, IntMarginTyped<units> > Super; + + IntMarginTyped() : Super() {} + IntMarginTyped(int32_t aTop, int32_t aRight, int32_t aBottom, int32_t aLeft) : + Super(aTop, aRight, aBottom, aLeft) {} +}; +typedef IntMarginTyped<UnknownUnits> IntMargin; + +template<class units> +struct MarginTyped: + public BaseMargin<Float, MarginTyped<units> >, + public units { + static_assert(IsPixel<units>::value, + "'units' must be a coordinate system tag"); + + typedef BaseMargin<Float, MarginTyped<units> > Super; - // Constructors - Margin() : Super(0, 0, 0, 0) {} - Margin(const Margin& aMargin) : Super(aMargin) {} - Margin(Float aTop, Float aRight, Float aBottom, Float aLeft) - : Super(aTop, aRight, aBottom, aLeft) {} + MarginTyped() : Super() {} + MarginTyped(Float aTop, Float aRight, Float aBottom, Float aLeft) : + Super(aTop, aRight, aBottom, aLeft) {} + explicit MarginTyped(const IntMarginTyped<units>& aMargin) : + Super(float(aMargin.top), float(aMargin.right), + float(aMargin.bottom), float(aMargin.left)) {} }; +typedef MarginTyped<UnknownUnits> Margin; + +template<class units> +IntMarginTyped<units> RoundedToInt(const MarginTyped<units>& aMargin) +{ + return IntMarginTyped<units>(int32_t(floorf(aMargin.top + 0.5f)), + int32_t(floorf(aMargin.right + 0.5f)), + int32_t(floorf(aMargin.bottom + 0.5f)), + int32_t(floorf(aMargin.left + 0.5f))); +} template<class units> struct IntRectTyped : - public BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>, IntSizeTyped<units>, Margin>, + public BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>, IntSizeTyped<units>, IntMarginTyped<units> >, public units { - typedef BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>, IntSizeTyped<units>, Margin> Super; + static_assert(IsPixel<units>::value, + "'units' must be a coordinate system tag"); + + typedef BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>, IntSizeTyped<units>, IntMarginTyped<units> > Super; IntRectTyped() : Super() {} - IntRectTyped(IntPointTyped<units> aPos, IntSizeTyped<units> aSize) : + IntRectTyped(const IntPointTyped<units>& aPos, const IntSizeTyped<units>& aSize) : Super(aPos, aSize) {} IntRectTyped(int32_t _x, int32_t _y, int32_t _width, int32_t _height) : Super(_x, _y, _width, _height) {} @@ -52,17 +91,28 @@ struct IntRectTyped : IntRectTyped<UnknownUnits> ToUnknownRect() const { return IntRectTyped<UnknownUnits>(this->x, this->y, this->width, this->height); } + + bool Overflows() const { + CheckedInt<int32_t> xMost = this->x; + xMost += this->width; + CheckedInt<int32_t> yMost = this->y; + yMost += this->height; + return !xMost.isValid() || !yMost.isValid(); + } }; typedef IntRectTyped<UnknownUnits> IntRect; template<class units> struct RectTyped : - public BaseRect<Float, RectTyped<units>, PointTyped<units>, SizeTyped<units>, Margin>, + public BaseRect<Float, RectTyped<units>, PointTyped<units>, SizeTyped<units>, MarginTyped<units> >, public units { - typedef BaseRect<Float, RectTyped<units>, PointTyped<units>, SizeTyped<units>, Margin> Super; + static_assert(IsPixel<units>::value, + "'units' must be a coordinate system tag"); + + typedef BaseRect<Float, RectTyped<units>, PointTyped<units>, SizeTyped<units>, MarginTyped<units> > Super; RectTyped() : Super() {} - RectTyped(PointTyped<units> aPos, SizeTyped<units> aSize) : + RectTyped(const PointTyped<units>& aPos, const SizeTyped<units>& aSize) : Super(aPos, aSize) {} RectTyped(Float _x, Float _y, Float _width, Float _height) : Super(_x, _y, _width, _height) {} @@ -70,7 +120,7 @@ struct RectTyped : Super(float(rect.x), float(rect.y), float(rect.width), float(rect.height)) {} - GFX2D_API void NudgeToIntegers() + void NudgeToIntegers() { NudgeToInteger(&(this->x)); NudgeToInteger(&(this->y)); @@ -97,16 +147,22 @@ struct RectTyped : RectTyped<UnknownUnits> ToUnknownRect() const { return RectTyped<UnknownUnits>(this->x, this->y, this->width, this->height); } + + // This is here only to keep IPDL-generated code happy. DO NOT USE. + bool operator==(const RectTyped<units>& aRect) const + { + return RectTyped<units>::IsEqualEdges(aRect); + } }; typedef RectTyped<UnknownUnits> Rect; template<class units> IntRectTyped<units> RoundedToInt(const RectTyped<units>& aRect) { - return IntRectTyped<units>(NS_lround(aRect.x), - NS_lround(aRect.y), - NS_lround(aRect.width), - NS_lround(aRect.height)); + return IntRectTyped<units>(int32_t(floorf(aRect.x + 0.5f)), + int32_t(floorf(aRect.y + 0.5f)), + int32_t(floorf(aRect.width + 0.5f)), + int32_t(floorf(aRect.height + 0.5f))); } template<class units> @@ -120,6 +176,17 @@ IntRectTyped<units> RoundedIn(const RectTyped<units>& aRect) int32_t(copy.height)); } +template<class units> +IntRectTyped<units> RoundedOut(const RectTyped<units>& aRect) +{ + RectTyped<units> copy(aRect); + copy.RoundOut(); + return IntRectTyped<units>(int32_t(copy.x), + int32_t(copy.y), + int32_t(copy.width), + int32_t(copy.height)); +} + } } diff --git a/gfx/2d/SIMD.h b/gfx/2d/SIMD.h new file mode 100644 index 000000000..6bf53a38e --- /dev/null +++ b/gfx/2d/SIMD.h @@ -0,0 +1,1180 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MOZILLA_GFX_SIMD_H_ +#define _MOZILLA_GFX_SIMD_H_ + +/** + * Consumers of this file need to #define SIMD_COMPILE_SSE2 before including it + * if they want access to the SSE2 functions. + */ + +#ifdef SIMD_COMPILE_SSE2 +#include <xmmintrin.h> +#endif + +namespace mozilla { +namespace gfx { + +namespace simd { + +template<typename u8x16_t> +u8x16_t Load8(const uint8_t* aSource); + +template<typename u8x16_t> +u8x16_t From8(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h, + uint8_t i, uint8_t j, uint8_t k, uint8_t l, uint8_t m, uint8_t n, uint8_t o, uint8_t p); + +template<typename u8x16_t> +u8x16_t FromZero8(); + +template<typename i16x8_t> +i16x8_t FromI16(int16_t a, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g, int16_t h); + +template<typename u16x8_t> +u16x8_t FromU16(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h); + +template<typename i16x8_t> +i16x8_t FromI16(int16_t a); + +template<typename u16x8_t> +u16x8_t FromU16(uint16_t a); + +template<typename i32x4_t> +i32x4_t From32(int32_t a, int32_t b, int32_t c, int32_t d); + +template<typename i32x4_t> +i32x4_t From32(int32_t a); + +template<typename f32x4_t> +f32x4_t FromF32(float a, float b, float c, float d); + +template<typename f32x4_t> +f32x4_t FromF32(float a); + +// All SIMD backends overload these functions for their SIMD types: + +#if 0 + +// Store 16 bytes to a 16-byte aligned address +void Store8(uint8_t* aTarget, u8x16_t aM); + +// Fixed shifts +template<int32_t aNumberOfBits> i16x8_t ShiftRight16(i16x8_t aM); +template<int32_t aNumberOfBits> i32x4_t ShiftRight32(i32x4_t aM); + +i16x8_t Add16(i16x8_t aM1, i16x8_t aM2); +i32x4_t Add32(i32x4_t aM1, i32x4_t aM2); +i16x8_t Sub16(i16x8_t aM1, i16x8_t aM2); +i32x4_t Sub32(i32x4_t aM1, i32x4_t aM2); +u8x16_t Min8(u8x16_t aM1, iu8x16_t aM2); +u8x16_t Max8(u8x16_t aM1, iu8x16_t aM2); +i32x4_t Min32(i32x4_t aM1, i32x4_t aM2); +i32x4_t Max32(i32x4_t aM1, i32x4_t aM2); + +// Truncating i16 -> i16 multiplication +i16x8_t Mul16(i16x8_t aM1, i16x8_t aM2); + +// Long multiplication i16 -> i32 +// aFactorsA1B1 = (a1[4] b1[4]) +// aFactorsA2B2 = (a2[4] b2[4]) +// aProductA = a1 * a2, aProductB = b1 * b2 +void Mul16x4x2x2To32x4x2(i16x8_t aFactorsA1B1, i16x8_t aFactorsA2B2, + i32x4_t& aProductA, i32x4_t& aProductB); + +// Long multiplication + pairwise addition i16 -> i32 +// See the scalar implementation for specifics. +i32x4_t MulAdd16x8x2To32x4(i16x8_t aFactorsA, i16x8_t aFactorsB); +i32x4_t MulAdd16x8x2To32x4(u16x8_t aFactorsA, u16x8_t aFactorsB); + +// Set all four 32-bit components to the value of the component at aIndex. +template<int8_t aIndex> +i32x4_t Splat32(i32x4_t aM); + +// Interpret the input as four 32-bit values, apply Splat32<aIndex> on them, +// re-interpret the result as sixteen 8-bit values. +template<int8_t aIndex> +u8x16_t Splat32On8(u8x16_t aM); + +template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> i32x4 Shuffle32(i32x4 aM); +template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> i16x8 ShuffleLo16(i16x8 aM); +template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> i16x8 ShuffleHi16(i16x8 aM); + +u8x16_t InterleaveLo8(u8x16_t m1, u8x16_t m2); +u8x16_t InterleaveHi8(u8x16_t m1, u8x16_t m2); +i16x8_t InterleaveLo16(i16x8_t m1, i16x8_t m2); +i16x8_t InterleaveHi16(i16x8_t m1, i16x8_t m2); +i32x4_t InterleaveLo32(i32x4_t m1, i32x4_t m2); + +i16x8_t UnpackLo8x8ToI16x8(u8x16_t m); +i16x8_t UnpackHi8x8ToI16x8(u8x16_t m); +u16x8_t UnpackLo8x8ToU16x8(u8x16_t m); +u16x8_t UnpackHi8x8ToU16x8(u8x16_t m); + +i16x8_t PackAndSaturate32To16(i32x4_t m1, i32x4_t m2); +u8x16_t PackAndSaturate16To8(i16x8_t m1, i16x8_t m2); +u8x16_t PackAndSaturate32To8(i32x4_t m1, i32x4_t m2, i32x4_t m3, const i32x4_t& m4); + +i32x4 FastDivideBy255(i32x4 m); +i16x8 FastDivideBy255_16(i16x8 m); + +#endif + +// Scalar + +struct Scalaru8x16_t { + uint8_t u8[16]; +}; + +union Scalari16x8_t { + int16_t i16[8]; + uint16_t u16[8]; +}; + +typedef Scalari16x8_t Scalaru16x8_t; + +struct Scalari32x4_t { + int32_t i32[4]; +}; + +struct Scalarf32x4_t { + float f32[4]; +}; + +template<> +inline Scalaru8x16_t +Load8<Scalaru8x16_t>(const uint8_t* aSource) +{ + return *(Scalaru8x16_t*)aSource; +} + +inline void Store8(uint8_t* aTarget, Scalaru8x16_t aM) +{ + *(Scalaru8x16_t*)aTarget = aM; +} + +template<> +inline Scalaru8x16_t From8<Scalaru8x16_t>(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h, + uint8_t i, uint8_t j, uint8_t k, uint8_t l, uint8_t m, uint8_t n, uint8_t o, uint8_t p) +{ + Scalaru8x16_t _m; + _m.u8[0] = a; + _m.u8[1] = b; + _m.u8[2] = c; + _m.u8[3] = d; + _m.u8[4] = e; + _m.u8[5] = f; + _m.u8[6] = g; + _m.u8[7] = h; + _m.u8[8+0] = i; + _m.u8[8+1] = j; + _m.u8[8+2] = k; + _m.u8[8+3] = l; + _m.u8[8+4] = m; + _m.u8[8+5] = n; + _m.u8[8+6] = o; + _m.u8[8+7] = p; + return _m; +} + +template<> +inline Scalaru8x16_t FromZero8<Scalaru8x16_t>() +{ + return From8<Scalaru8x16_t>(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); +} + +template<> +inline Scalari16x8_t FromI16<Scalari16x8_t>(int16_t a, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g, int16_t h) +{ + Scalari16x8_t m; + m.i16[0] = a; + m.i16[1] = b; + m.i16[2] = c; + m.i16[3] = d; + m.i16[4] = e; + m.i16[5] = f; + m.i16[6] = g; + m.i16[7] = h; + return m; +} + +template<> +inline Scalaru16x8_t FromU16<Scalaru16x8_t>(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h) +{ + Scalaru16x8_t m; + m.u16[0] = a; + m.u16[1] = b; + m.u16[2] = c; + m.u16[3] = d; + m.u16[4] = e; + m.u16[5] = f; + m.u16[6] = g; + m.u16[7] = h; + return m; +} + +template<> +inline Scalari16x8_t FromI16<Scalari16x8_t>(int16_t a) +{ + return FromI16<Scalari16x8_t>(a, a, a, a, a, a, a, a); +} + +template<> +inline Scalaru16x8_t FromU16<Scalaru16x8_t>(uint16_t a) +{ + return FromU16<Scalaru16x8_t>(a, a, a, a, a, a, a, a); +} + +template<> +inline Scalari32x4_t From32<Scalari32x4_t>(int32_t a, int32_t b, int32_t c, int32_t d) +{ + Scalari32x4_t m; + m.i32[0] = a; + m.i32[1] = b; + m.i32[2] = c; + m.i32[3] = d; + return m; +} + +template<> +inline Scalarf32x4_t FromF32<Scalarf32x4_t>(float a, float b, float c, float d) +{ + Scalarf32x4_t m; + m.f32[0] = a; + m.f32[1] = b; + m.f32[2] = c; + m.f32[3] = d; + return m; +} + +template<> +inline Scalarf32x4_t FromF32<Scalarf32x4_t>(float a) +{ + return FromF32<Scalarf32x4_t>(a, a, a, a); +} + +template<> +inline Scalari32x4_t From32<Scalari32x4_t>(int32_t a) +{ + return From32<Scalari32x4_t>(a, a, a, a); +} + +template<int32_t aNumberOfBits> +inline Scalari16x8_t ShiftRight16(Scalari16x8_t aM) +{ + return FromI16<Scalari16x8_t>(uint16_t(aM.i16[0]) >> aNumberOfBits, uint16_t(aM.i16[1]) >> aNumberOfBits, + uint16_t(aM.i16[2]) >> aNumberOfBits, uint16_t(aM.i16[3]) >> aNumberOfBits, + uint16_t(aM.i16[4]) >> aNumberOfBits, uint16_t(aM.i16[5]) >> aNumberOfBits, + uint16_t(aM.i16[6]) >> aNumberOfBits, uint16_t(aM.i16[7]) >> aNumberOfBits); +} + +template<int32_t aNumberOfBits> +inline Scalari32x4_t ShiftRight32(Scalari32x4_t aM) +{ + return From32<Scalari32x4_t>(aM.i32[0] >> aNumberOfBits, aM.i32[1] >> aNumberOfBits, + aM.i32[2] >> aNumberOfBits, aM.i32[3] >> aNumberOfBits); +} + +inline Scalaru16x8_t Add16(Scalaru16x8_t aM1, Scalaru16x8_t aM2) +{ + return FromU16<Scalaru16x8_t>(aM1.u16[0] + aM2.u16[0], aM1.u16[1] + aM2.u16[1], + aM1.u16[2] + aM2.u16[2], aM1.u16[3] + aM2.u16[3], + aM1.u16[4] + aM2.u16[4], aM1.u16[5] + aM2.u16[5], + aM1.u16[6] + aM2.u16[6], aM1.u16[7] + aM2.u16[7]); +} + +inline Scalari32x4_t Add32(Scalari32x4_t aM1, Scalari32x4_t aM2) +{ + return From32<Scalari32x4_t>(aM1.i32[0] + aM2.i32[0], aM1.i32[1] + aM2.i32[1], + aM1.i32[2] + aM2.i32[2], aM1.i32[3] + aM2.i32[3]); +} + +inline Scalaru16x8_t Sub16(Scalaru16x8_t aM1, Scalaru16x8_t aM2) +{ + return FromU16<Scalaru16x8_t>(aM1.u16[0] - aM2.u16[0], aM1.u16[1] - aM2.u16[1], + aM1.u16[2] - aM2.u16[2], aM1.u16[3] - aM2.u16[3], + aM1.u16[4] - aM2.u16[4], aM1.u16[5] - aM2.u16[5], + aM1.u16[6] - aM2.u16[6], aM1.u16[7] - aM2.u16[7]); +} + +inline Scalari32x4_t Sub32(Scalari32x4_t aM1, Scalari32x4_t aM2) +{ + return From32<Scalari32x4_t>(aM1.i32[0] - aM2.i32[0], aM1.i32[1] - aM2.i32[1], + aM1.i32[2] - aM2.i32[2], aM1.i32[3] - aM2.i32[3]); +} + +inline int32_t +umin(int32_t a, int32_t b) +{ + return a - ((a - b) & -(a > b)); +} + +inline int32_t +umax(int32_t a, int32_t b) +{ + return a - ((a - b) & -(a < b)); +} + +inline Scalaru8x16_t Min8(Scalaru8x16_t aM1, Scalaru8x16_t aM2) +{ + return From8<Scalaru8x16_t>(umin(aM1.u8[0], aM2.u8[0]), umin(aM1.u8[1], aM2.u8[1]), + umin(aM1.u8[2], aM2.u8[2]), umin(aM1.u8[3], aM2.u8[3]), + umin(aM1.u8[4], aM2.u8[4]), umin(aM1.u8[5], aM2.u8[5]), + umin(aM1.u8[6], aM2.u8[6]), umin(aM1.u8[7], aM2.u8[7]), + umin(aM1.u8[8+0], aM2.u8[8+0]), umin(aM1.u8[8+1], aM2.u8[8+1]), + umin(aM1.u8[8+2], aM2.u8[8+2]), umin(aM1.u8[8+3], aM2.u8[8+3]), + umin(aM1.u8[8+4], aM2.u8[8+4]), umin(aM1.u8[8+5], aM2.u8[8+5]), + umin(aM1.u8[8+6], aM2.u8[8+6]), umin(aM1.u8[8+7], aM2.u8[8+7])); +} + +inline Scalaru8x16_t Max8(Scalaru8x16_t aM1, Scalaru8x16_t aM2) +{ + return From8<Scalaru8x16_t>(umax(aM1.u8[0], aM2.u8[0]), umax(aM1.u8[1], aM2.u8[1]), + umax(aM1.u8[2], aM2.u8[2]), umax(aM1.u8[3], aM2.u8[3]), + umax(aM1.u8[4], aM2.u8[4]), umax(aM1.u8[5], aM2.u8[5]), + umax(aM1.u8[6], aM2.u8[6]), umax(aM1.u8[7], aM2.u8[7]), + umax(aM1.u8[8+0], aM2.u8[8+0]), umax(aM1.u8[8+1], aM2.u8[8+1]), + umax(aM1.u8[8+2], aM2.u8[8+2]), umax(aM1.u8[8+3], aM2.u8[8+3]), + umax(aM1.u8[8+4], aM2.u8[8+4]), umax(aM1.u8[8+5], aM2.u8[8+5]), + umax(aM1.u8[8+6], aM2.u8[8+6]), umax(aM1.u8[8+7], aM2.u8[8+7])); +} + +inline Scalari32x4_t Min32(Scalari32x4_t aM1, Scalari32x4_t aM2) +{ + return From32<Scalari32x4_t>(umin(aM1.i32[0], aM2.i32[0]), umin(aM1.i32[1], aM2.i32[1]), + umin(aM1.i32[2], aM2.i32[2]), umin(aM1.i32[3], aM2.i32[3])); +} + +inline Scalari32x4_t Max32(Scalari32x4_t aM1, Scalari32x4_t aM2) +{ + return From32<Scalari32x4_t>(umax(aM1.i32[0], aM2.i32[0]), umax(aM1.i32[1], aM2.i32[1]), + umax(aM1.i32[2], aM2.i32[2]), umax(aM1.i32[3], aM2.i32[3])); +} + +inline Scalaru16x8_t Mul16(Scalaru16x8_t aM1, Scalaru16x8_t aM2) +{ + return FromU16<Scalaru16x8_t>(uint16_t(int32_t(aM1.u16[0]) * int32_t(aM2.u16[0])), uint16_t(int32_t(aM1.u16[1]) * int32_t(aM2.u16[1])), + uint16_t(int32_t(aM1.u16[2]) * int32_t(aM2.u16[2])), uint16_t(int32_t(aM1.u16[3]) * int32_t(aM2.u16[3])), + uint16_t(int32_t(aM1.u16[4]) * int32_t(aM2.u16[4])), uint16_t(int32_t(aM1.u16[5]) * int32_t(aM2.u16[5])), + uint16_t(int32_t(aM1.u16[6]) * int32_t(aM2.u16[6])), uint16_t(int32_t(aM1.u16[7]) * int32_t(aM2.u16[7]))); +} + +inline void Mul16x4x2x2To32x4x2(Scalari16x8_t aFactorsA1B1, + Scalari16x8_t aFactorsA2B2, + Scalari32x4_t& aProductA, + Scalari32x4_t& aProductB) +{ + aProductA = From32<Scalari32x4_t>(aFactorsA1B1.i16[0] * aFactorsA2B2.i16[0], + aFactorsA1B1.i16[1] * aFactorsA2B2.i16[1], + aFactorsA1B1.i16[2] * aFactorsA2B2.i16[2], + aFactorsA1B1.i16[3] * aFactorsA2B2.i16[3]); + aProductB = From32<Scalari32x4_t>(aFactorsA1B1.i16[4] * aFactorsA2B2.i16[4], + aFactorsA1B1.i16[5] * aFactorsA2B2.i16[5], + aFactorsA1B1.i16[6] * aFactorsA2B2.i16[6], + aFactorsA1B1.i16[7] * aFactorsA2B2.i16[7]); +} + +inline Scalari32x4_t MulAdd16x8x2To32x4(Scalari16x8_t aFactorsA, + Scalari16x8_t aFactorsB) +{ + return From32<Scalari32x4_t>(aFactorsA.i16[0] * aFactorsB.i16[0] + aFactorsA.i16[1] * aFactorsB.i16[1], + aFactorsA.i16[2] * aFactorsB.i16[2] + aFactorsA.i16[3] * aFactorsB.i16[3], + aFactorsA.i16[4] * aFactorsB.i16[4] + aFactorsA.i16[5] * aFactorsB.i16[5], + aFactorsA.i16[6] * aFactorsB.i16[6] + aFactorsA.i16[7] * aFactorsB.i16[7]); +} + +template<int8_t aIndex> +inline void AssertIndex() +{ + static_assert(aIndex == 0 || aIndex == 1 || aIndex == 2 || aIndex == 3, + "Invalid splat index"); +} + +template<int8_t aIndex> +inline Scalari32x4_t Splat32(Scalari32x4_t aM) +{ + AssertIndex<aIndex>(); + return From32<Scalari32x4_t>(aM.i32[aIndex], aM.i32[aIndex], + aM.i32[aIndex], aM.i32[aIndex]); +} + +template<int8_t i> +inline Scalaru8x16_t Splat32On8(Scalaru8x16_t aM) +{ + AssertIndex<i>(); + return From8<Scalaru8x16_t>(aM.u8[i*4], aM.u8[i*4+1], aM.u8[i*4+2], aM.u8[i*4+3], + aM.u8[i*4], aM.u8[i*4+1], aM.u8[i*4+2], aM.u8[i*4+3], + aM.u8[i*4], aM.u8[i*4+1], aM.u8[i*4+2], aM.u8[i*4+3], + aM.u8[i*4], aM.u8[i*4+1], aM.u8[i*4+2], aM.u8[i*4+3]); +} + +template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> +inline Scalari32x4_t Shuffle32(Scalari32x4_t aM) +{ + AssertIndex<i0>(); + AssertIndex<i1>(); + AssertIndex<i2>(); + AssertIndex<i3>(); + Scalari32x4_t m = aM; + m.i32[0] = aM.i32[i3]; + m.i32[1] = aM.i32[i2]; + m.i32[2] = aM.i32[i1]; + m.i32[3] = aM.i32[i0]; + return m; +} + +template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> +inline Scalari16x8_t ShuffleLo16(Scalari16x8_t aM) +{ + AssertIndex<i0>(); + AssertIndex<i1>(); + AssertIndex<i2>(); + AssertIndex<i3>(); + Scalari16x8_t m = aM; + m.i16[0] = aM.i16[i3]; + m.i16[1] = aM.i16[i2]; + m.i16[2] = aM.i16[i1]; + m.i16[3] = aM.i16[i0]; + return m; +} + +template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> +inline Scalari16x8_t ShuffleHi16(Scalari16x8_t aM) +{ + AssertIndex<i0>(); + AssertIndex<i1>(); + AssertIndex<i2>(); + AssertIndex<i3>(); + Scalari16x8_t m = aM; + m.i16[4 + 0] = aM.i16[4 + i3]; + m.i16[4 + 1] = aM.i16[4 + i2]; + m.i16[4 + 2] = aM.i16[4 + i1]; + m.i16[4 + 3] = aM.i16[4 + i0]; + return m; +} + +template<int8_t aIndexLo, int8_t aIndexHi> +inline Scalaru16x8_t Splat16(Scalaru16x8_t aM) +{ + AssertIndex<aIndexLo>(); + AssertIndex<aIndexHi>(); + Scalaru16x8_t m; + int16_t chosenValueLo = aM.u16[aIndexLo]; + m.u16[0] = chosenValueLo; + m.u16[1] = chosenValueLo; + m.u16[2] = chosenValueLo; + m.u16[3] = chosenValueLo; + int16_t chosenValueHi = aM.u16[4 + aIndexHi]; + m.u16[4] = chosenValueHi; + m.u16[5] = chosenValueHi; + m.u16[6] = chosenValueHi; + m.u16[7] = chosenValueHi; + return m; +} + +inline Scalaru8x16_t +InterleaveLo8(Scalaru8x16_t m1, Scalaru8x16_t m2) +{ + return From8<Scalaru8x16_t>(m1.u8[0], m2.u8[0], m1.u8[1], m2.u8[1], + m1.u8[2], m2.u8[2], m1.u8[3], m2.u8[3], + m1.u8[4], m2.u8[4], m1.u8[5], m2.u8[5], + m1.u8[6], m2.u8[6], m1.u8[7], m2.u8[7]); +} + +inline Scalaru8x16_t +InterleaveHi8(Scalaru8x16_t m1, Scalaru8x16_t m2) +{ + return From8<Scalaru8x16_t>(m1.u8[8+0], m2.u8[8+0], m1.u8[8+1], m2.u8[8+1], + m1.u8[8+2], m2.u8[8+2], m1.u8[8+3], m2.u8[8+3], + m1.u8[8+4], m2.u8[8+4], m1.u8[8+5], m2.u8[8+5], + m1.u8[8+6], m2.u8[8+6], m1.u8[8+7], m2.u8[8+7]); +} + +inline Scalaru16x8_t +InterleaveLo16(Scalaru16x8_t m1, Scalaru16x8_t m2) +{ + return FromU16<Scalaru16x8_t>(m1.u16[0], m2.u16[0], m1.u16[1], m2.u16[1], + m1.u16[2], m2.u16[2], m1.u16[3], m2.u16[3]); +} + +inline Scalaru16x8_t +InterleaveHi16(Scalaru16x8_t m1, Scalaru16x8_t m2) +{ + return FromU16<Scalaru16x8_t>(m1.u16[4], m2.u16[4], m1.u16[5], m2.u16[5], + m1.u16[6], m2.u16[6], m1.u16[7], m2.u16[7]); +} + +inline Scalari32x4_t +InterleaveLo32(Scalari32x4_t m1, Scalari32x4_t m2) +{ + return From32<Scalari32x4_t>(m1.i32[0], m2.i32[0], m1.i32[1], m2.i32[1]); +} + +inline Scalari16x8_t +UnpackLo8x8ToI16x8(Scalaru8x16_t aM) +{ + Scalari16x8_t m; + m.i16[0] = aM.u8[0]; + m.i16[1] = aM.u8[1]; + m.i16[2] = aM.u8[2]; + m.i16[3] = aM.u8[3]; + m.i16[4] = aM.u8[4]; + m.i16[5] = aM.u8[5]; + m.i16[6] = aM.u8[6]; + m.i16[7] = aM.u8[7]; + return m; +} + +inline Scalari16x8_t +UnpackHi8x8ToI16x8(Scalaru8x16_t aM) +{ + Scalari16x8_t m; + m.i16[0] = aM.u8[8+0]; + m.i16[1] = aM.u8[8+1]; + m.i16[2] = aM.u8[8+2]; + m.i16[3] = aM.u8[8+3]; + m.i16[4] = aM.u8[8+4]; + m.i16[5] = aM.u8[8+5]; + m.i16[6] = aM.u8[8+6]; + m.i16[7] = aM.u8[8+7]; + return m; +} + +inline Scalaru16x8_t +UnpackLo8x8ToU16x8(Scalaru8x16_t aM) +{ + return FromU16<Scalaru16x8_t>(uint16_t(aM.u8[0]), uint16_t(aM.u8[1]), uint16_t(aM.u8[2]), uint16_t(aM.u8[3]), + uint16_t(aM.u8[4]), uint16_t(aM.u8[5]), uint16_t(aM.u8[6]), uint16_t(aM.u8[7])); +} + +inline Scalaru16x8_t +UnpackHi8x8ToU16x8(Scalaru8x16_t aM) +{ + return FromU16<Scalaru16x8_t>(aM.u8[8+0], aM.u8[8+1], aM.u8[8+2], aM.u8[8+3], + aM.u8[8+4], aM.u8[8+5], aM.u8[8+6], aM.u8[8+7]); +} + +template<uint8_t aNumBytes> +inline Scalaru8x16_t +Rotate8(Scalaru8x16_t a1234, Scalaru8x16_t a5678) +{ + Scalaru8x16_t m; + for (uint8_t i = 0; i < 16; i++) { + uint8_t sourceByte = i + aNumBytes; + m.u8[i] = sourceByte < 16 ? a1234.u8[sourceByte] : a5678.u8[sourceByte - 16]; + } + return m; +} + +template<typename T> +inline int16_t +SaturateTo16(T a) +{ + return int16_t(a >= INT16_MIN ? (a <= INT16_MAX ? a : INT16_MAX) : INT16_MIN); +} + +inline Scalari16x8_t +PackAndSaturate32To16(Scalari32x4_t m1, Scalari32x4_t m2) +{ + Scalari16x8_t m; + m.i16[0] = SaturateTo16(m1.i32[0]); + m.i16[1] = SaturateTo16(m1.i32[1]); + m.i16[2] = SaturateTo16(m1.i32[2]); + m.i16[3] = SaturateTo16(m1.i32[3]); + m.i16[4] = SaturateTo16(m2.i32[0]); + m.i16[5] = SaturateTo16(m2.i32[1]); + m.i16[6] = SaturateTo16(m2.i32[2]); + m.i16[7] = SaturateTo16(m2.i32[3]); + return m; +} + +template<typename T> +inline uint16_t +SaturateToU16(T a) +{ + return uint16_t(umin(a & -(a >= 0), INT16_MAX)); +} + +inline Scalaru16x8_t +PackAndSaturate32ToU16(Scalari32x4_t m1, Scalari32x4_t m2) +{ + Scalaru16x8_t m; + m.u16[0] = SaturateToU16(m1.i32[0]); + m.u16[1] = SaturateToU16(m1.i32[1]); + m.u16[2] = SaturateToU16(m1.i32[2]); + m.u16[3] = SaturateToU16(m1.i32[3]); + m.u16[4] = SaturateToU16(m2.i32[0]); + m.u16[5] = SaturateToU16(m2.i32[1]); + m.u16[6] = SaturateToU16(m2.i32[2]); + m.u16[7] = SaturateToU16(m2.i32[3]); + return m; +} + +template<typename T> +inline uint8_t +SaturateTo8(T a) +{ + return uint8_t(umin(a & -(a >= 0), 255)); +} + +inline Scalaru8x16_t +PackAndSaturate32To8(Scalari32x4_t m1, Scalari32x4_t m2, Scalari32x4_t m3, const Scalari32x4_t& m4) +{ + Scalaru8x16_t m; + m.u8[0] = SaturateTo8(m1.i32[0]); + m.u8[1] = SaturateTo8(m1.i32[1]); + m.u8[2] = SaturateTo8(m1.i32[2]); + m.u8[3] = SaturateTo8(m1.i32[3]); + m.u8[4] = SaturateTo8(m2.i32[0]); + m.u8[5] = SaturateTo8(m2.i32[1]); + m.u8[6] = SaturateTo8(m2.i32[2]); + m.u8[7] = SaturateTo8(m2.i32[3]); + m.u8[8] = SaturateTo8(m3.i32[0]); + m.u8[9] = SaturateTo8(m3.i32[1]); + m.u8[10] = SaturateTo8(m3.i32[2]); + m.u8[11] = SaturateTo8(m3.i32[3]); + m.u8[12] = SaturateTo8(m4.i32[0]); + m.u8[13] = SaturateTo8(m4.i32[1]); + m.u8[14] = SaturateTo8(m4.i32[2]); + m.u8[15] = SaturateTo8(m4.i32[3]); + return m; +} + +inline Scalaru8x16_t +PackAndSaturate16To8(Scalari16x8_t m1, Scalari16x8_t m2) +{ + Scalaru8x16_t m; + m.u8[0] = SaturateTo8(m1.i16[0]); + m.u8[1] = SaturateTo8(m1.i16[1]); + m.u8[2] = SaturateTo8(m1.i16[2]); + m.u8[3] = SaturateTo8(m1.i16[3]); + m.u8[4] = SaturateTo8(m1.i16[4]); + m.u8[5] = SaturateTo8(m1.i16[5]); + m.u8[6] = SaturateTo8(m1.i16[6]); + m.u8[7] = SaturateTo8(m1.i16[7]); + m.u8[8] = SaturateTo8(m2.i16[0]); + m.u8[9] = SaturateTo8(m2.i16[1]); + m.u8[10] = SaturateTo8(m2.i16[2]); + m.u8[11] = SaturateTo8(m2.i16[3]); + m.u8[12] = SaturateTo8(m2.i16[4]); + m.u8[13] = SaturateTo8(m2.i16[5]); + m.u8[14] = SaturateTo8(m2.i16[6]); + m.u8[15] = SaturateTo8(m2.i16[7]); + return m; +} + +// Fast approximate division by 255. It has the property that +// for all 0 <= n <= 255*255, FAST_DIVIDE_BY_255(n) == n/255. +// But it only uses two adds and two shifts instead of an +// integer division (which is expensive on many processors). +// +// equivalent to v/255 +template<class B, class A> +inline B FastDivideBy255(A v) +{ + return ((v << 8) + v + 255) >> 16; +} + +inline Scalaru16x8_t +FastDivideBy255_16(Scalaru16x8_t m) +{ + return FromU16<Scalaru16x8_t>(FastDivideBy255<uint16_t>(int32_t(m.u16[0])), + FastDivideBy255<uint16_t>(int32_t(m.u16[1])), + FastDivideBy255<uint16_t>(int32_t(m.u16[2])), + FastDivideBy255<uint16_t>(int32_t(m.u16[3])), + FastDivideBy255<uint16_t>(int32_t(m.u16[4])), + FastDivideBy255<uint16_t>(int32_t(m.u16[5])), + FastDivideBy255<uint16_t>(int32_t(m.u16[6])), + FastDivideBy255<uint16_t>(int32_t(m.u16[7]))); +} + +inline Scalari32x4_t +FastDivideBy255(Scalari32x4_t m) +{ + return From32<Scalari32x4_t>(FastDivideBy255<int32_t>(m.i32[0]), + FastDivideBy255<int32_t>(m.i32[1]), + FastDivideBy255<int32_t>(m.i32[2]), + FastDivideBy255<int32_t>(m.i32[3])); +} + +inline Scalaru8x16_t +Pick(Scalaru8x16_t mask, Scalaru8x16_t a, Scalaru8x16_t b) +{ + return From8<Scalaru8x16_t>((a.u8[0] & (~mask.u8[0])) | (b.u8[0] & mask.u8[0]), + (a.u8[1] & (~mask.u8[1])) | (b.u8[1] & mask.u8[1]), + (a.u8[2] & (~mask.u8[2])) | (b.u8[2] & mask.u8[2]), + (a.u8[3] & (~mask.u8[3])) | (b.u8[3] & mask.u8[3]), + (a.u8[4] & (~mask.u8[4])) | (b.u8[4] & mask.u8[4]), + (a.u8[5] & (~mask.u8[5])) | (b.u8[5] & mask.u8[5]), + (a.u8[6] & (~mask.u8[6])) | (b.u8[6] & mask.u8[6]), + (a.u8[7] & (~mask.u8[7])) | (b.u8[7] & mask.u8[7]), + (a.u8[8+0] & (~mask.u8[8+0])) | (b.u8[8+0] & mask.u8[8+0]), + (a.u8[8+1] & (~mask.u8[8+1])) | (b.u8[8+1] & mask.u8[8+1]), + (a.u8[8+2] & (~mask.u8[8+2])) | (b.u8[8+2] & mask.u8[8+2]), + (a.u8[8+3] & (~mask.u8[8+3])) | (b.u8[8+3] & mask.u8[8+3]), + (a.u8[8+4] & (~mask.u8[8+4])) | (b.u8[8+4] & mask.u8[8+4]), + (a.u8[8+5] & (~mask.u8[8+5])) | (b.u8[8+5] & mask.u8[8+5]), + (a.u8[8+6] & (~mask.u8[8+6])) | (b.u8[8+6] & mask.u8[8+6]), + (a.u8[8+7] & (~mask.u8[8+7])) | (b.u8[8+7] & mask.u8[8+7])); +} + +inline Scalari32x4_t +Pick(Scalari32x4_t mask, Scalari32x4_t a, Scalari32x4_t b) +{ + return From32<Scalari32x4_t>((a.i32[0] & (~mask.i32[0])) | (b.i32[0] & mask.i32[0]), + (a.i32[1] & (~mask.i32[1])) | (b.i32[1] & mask.i32[1]), + (a.i32[2] & (~mask.i32[2])) | (b.i32[2] & mask.i32[2]), + (a.i32[3] & (~mask.i32[3])) | (b.i32[3] & mask.i32[3])); +} + +inline Scalarf32x4_t MixF32(Scalarf32x4_t a, Scalarf32x4_t b, float t) +{ + return FromF32<Scalarf32x4_t>(a.f32[0] + (b.f32[0] - a.f32[0]) * t, + a.f32[1] + (b.f32[1] - a.f32[1]) * t, + a.f32[2] + (b.f32[2] - a.f32[2]) * t, + a.f32[3] + (b.f32[3] - a.f32[3]) * t); +} + +inline Scalarf32x4_t WSumF32(Scalarf32x4_t a, Scalarf32x4_t b, float wa, float wb) +{ + return FromF32<Scalarf32x4_t>(a.f32[0] * wa + b.f32[0] * wb, + a.f32[1] * wa + b.f32[1] * wb, + a.f32[2] * wa + b.f32[2] * wb, + a.f32[3] * wa + b.f32[3] * wb); +} + +inline Scalarf32x4_t AbsF32(Scalarf32x4_t a) +{ + return FromF32<Scalarf32x4_t>(fabs(a.f32[0]), + fabs(a.f32[1]), + fabs(a.f32[2]), + fabs(a.f32[3])); +} + +inline Scalarf32x4_t AddF32(Scalarf32x4_t a, Scalarf32x4_t b) +{ + return FromF32<Scalarf32x4_t>(a.f32[0] + b.f32[0], + a.f32[1] + b.f32[1], + a.f32[2] + b.f32[2], + a.f32[3] + b.f32[3]); +} + +inline Scalarf32x4_t MulF32(Scalarf32x4_t a, Scalarf32x4_t b) +{ + return FromF32<Scalarf32x4_t>(a.f32[0] * b.f32[0], + a.f32[1] * b.f32[1], + a.f32[2] * b.f32[2], + a.f32[3] * b.f32[3]); +} + +inline Scalarf32x4_t DivF32(Scalarf32x4_t a, Scalarf32x4_t b) +{ + return FromF32<Scalarf32x4_t>(a.f32[0] / b.f32[0], + a.f32[1] / b.f32[1], + a.f32[2] / b.f32[2], + a.f32[3] / b.f32[3]); +} + +template<uint8_t aIndex> +inline Scalarf32x4_t SplatF32(Scalarf32x4_t m) +{ + AssertIndex<aIndex>(); + return FromF32<Scalarf32x4_t>(m.f32[aIndex], + m.f32[aIndex], + m.f32[aIndex], + m.f32[aIndex]); +} + +inline Scalari32x4_t F32ToI32(Scalarf32x4_t m) +{ + return From32<Scalari32x4_t>(int32_t(floor(m.f32[0] + 0.5f)), + int32_t(floor(m.f32[1] + 0.5f)), + int32_t(floor(m.f32[2] + 0.5f)), + int32_t(floor(m.f32[3] + 0.5f))); +} + +#ifdef SIMD_COMPILE_SSE2 + +// SSE2 + +template<> +inline __m128i +Load8<__m128i>(const uint8_t* aSource) +{ + return _mm_load_si128((const __m128i*)aSource); +} + +inline void Store8(uint8_t* aTarget, __m128i aM) +{ + _mm_store_si128((__m128i*)aTarget, aM); +} + +template<> +inline __m128i FromZero8<__m128i>() +{ + return _mm_setzero_si128(); +} + +template<> +inline __m128i From8<__m128i>(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h, + uint8_t i, uint8_t j, uint8_t k, uint8_t l, uint8_t m, uint8_t n, uint8_t o, uint8_t p) +{ + return _mm_setr_epi16((b << 8) + a, (d << 8) + c, (e << 8) + f, (h << 8) + g, + (j << 8) + i, (l << 8) + k, (m << 8) + n, (p << 8) + o); +} + +template<> +inline __m128i FromI16<__m128i>(int16_t a, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g, int16_t h) +{ + return _mm_setr_epi16(a, b, c, d, e, f, g, h); +} + +template<> +inline __m128i FromU16<__m128i>(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h) +{ + return _mm_setr_epi16(a, b, c, d, e, f, g, h); +} + +template<> +inline __m128i FromI16<__m128i>(int16_t a) +{ + return _mm_set1_epi16(a); +} + +template<> +inline __m128i FromU16<__m128i>(uint16_t a) +{ + return _mm_set1_epi16((int16_t)a); +} + +template<> +inline __m128i From32<__m128i>(int32_t a, int32_t b, int32_t c, int32_t d) +{ + return _mm_setr_epi32(a, b, c, d); +} + +template<> +inline __m128i From32<__m128i>(int32_t a) +{ + return _mm_set1_epi32(a); +} + +template<> +inline __m128 FromF32<__m128>(float a, float b, float c, float d) +{ + return _mm_setr_ps(a, b, c, d); +} + +template<> +inline __m128 FromF32<__m128>(float a) +{ + return _mm_set1_ps(a); +} + +template<int32_t aNumberOfBits> +inline __m128i ShiftRight16(__m128i aM) +{ + return _mm_srli_epi16(aM, aNumberOfBits); +} + +template<int32_t aNumberOfBits> +inline __m128i ShiftRight32(__m128i aM) +{ + return _mm_srai_epi32(aM, aNumberOfBits); +} + +inline __m128i Add16(__m128i aM1, __m128i aM2) +{ + return _mm_add_epi16(aM1, aM2); +} + +inline __m128i Add32(__m128i aM1, __m128i aM2) +{ + return _mm_add_epi32(aM1, aM2); +} + +inline __m128i Sub16(__m128i aM1, __m128i aM2) +{ + return _mm_sub_epi16(aM1, aM2); +} + +inline __m128i Sub32(__m128i aM1, __m128i aM2) +{ + return _mm_sub_epi32(aM1, aM2); +} + +inline __m128i Min8(__m128i aM1, __m128i aM2) +{ + return _mm_min_epu8(aM1, aM2); +} + +inline __m128i Max8(__m128i aM1, __m128i aM2) +{ + return _mm_max_epu8(aM1, aM2); +} + +inline __m128i Min32(__m128i aM1, __m128i aM2) +{ + __m128i m1_minus_m2 = _mm_sub_epi32(aM1, aM2); + __m128i m1_greater_than_m2 = _mm_cmpgt_epi32(aM1, aM2); + return _mm_sub_epi32(aM1, _mm_and_si128(m1_minus_m2, m1_greater_than_m2)); +} + +inline __m128i Max32(__m128i aM1, __m128i aM2) +{ + __m128i m1_minus_m2 = _mm_sub_epi32(aM1, aM2); + __m128i m2_greater_than_m1 = _mm_cmpgt_epi32(aM2, aM1); + return _mm_sub_epi32(aM1, _mm_and_si128(m1_minus_m2, m2_greater_than_m1)); +} + +inline __m128i Mul16(__m128i aM1, __m128i aM2) +{ + return _mm_mullo_epi16(aM1, aM2); +} + +inline __m128i MulU16(__m128i aM1, __m128i aM2) +{ + return _mm_mullo_epi16(aM1, aM2); +} + +inline void Mul16x4x2x2To32x4x2(__m128i aFactorsA1B1, + __m128i aFactorsA2B2, + __m128i& aProductA, + __m128i& aProductB) +{ + __m128i prodAB_lo = _mm_mullo_epi16(aFactorsA1B1, aFactorsA2B2); + __m128i prodAB_hi = _mm_mulhi_epi16(aFactorsA1B1, aFactorsA2B2); + aProductA = _mm_unpacklo_epi16(prodAB_lo, prodAB_hi); + aProductB = _mm_unpackhi_epi16(prodAB_lo, prodAB_hi); +} + +inline __m128i MulAdd16x8x2To32x4(__m128i aFactorsA, + __m128i aFactorsB) +{ + return _mm_madd_epi16(aFactorsA, aFactorsB); +} + +template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> +inline __m128i Shuffle32(__m128i aM) +{ + AssertIndex<i0>(); + AssertIndex<i1>(); + AssertIndex<i2>(); + AssertIndex<i3>(); + return _mm_shuffle_epi32(aM, _MM_SHUFFLE(i0, i1, i2, i3)); +} + +template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> +inline __m128i ShuffleLo16(__m128i aM) +{ + AssertIndex<i0>(); + AssertIndex<i1>(); + AssertIndex<i2>(); + AssertIndex<i3>(); + return _mm_shufflelo_epi16(aM, _MM_SHUFFLE(i0, i1, i2, i3)); +} + +template<int8_t i0, int8_t i1, int8_t i2, int8_t i3> +inline __m128i ShuffleHi16(__m128i aM) +{ + AssertIndex<i0>(); + AssertIndex<i1>(); + AssertIndex<i2>(); + AssertIndex<i3>(); + return _mm_shufflehi_epi16(aM, _MM_SHUFFLE(i0, i1, i2, i3)); +} + +template<int8_t aIndex> +inline __m128i Splat32(__m128i aM) +{ + return Shuffle32<aIndex,aIndex,aIndex,aIndex>(aM); +} + +template<int8_t aIndex> +inline __m128i Splat32On8(__m128i aM) +{ + return Shuffle32<aIndex,aIndex,aIndex,aIndex>(aM); +} + +template<int8_t aIndexLo, int8_t aIndexHi> +inline __m128i Splat16(__m128i aM) +{ + AssertIndex<aIndexLo>(); + AssertIndex<aIndexHi>(); + return ShuffleHi16<aIndexHi,aIndexHi,aIndexHi,aIndexHi>( + ShuffleLo16<aIndexLo,aIndexLo,aIndexLo,aIndexLo>(aM)); +} + +inline __m128i +UnpackLo8x8ToI16x8(__m128i m) +{ + __m128i zero = _mm_set1_epi8(0); + return _mm_unpacklo_epi8(m, zero); +} + +inline __m128i +UnpackHi8x8ToI16x8(__m128i m) +{ + __m128i zero = _mm_set1_epi8(0); + return _mm_unpackhi_epi8(m, zero); +} + +inline __m128i +UnpackLo8x8ToU16x8(__m128i m) +{ + __m128i zero = _mm_set1_epi8(0); + return _mm_unpacklo_epi8(m, zero); +} + +inline __m128i +UnpackHi8x8ToU16x8(__m128i m) +{ + __m128i zero = _mm_set1_epi8(0); + return _mm_unpackhi_epi8(m, zero); +} + +inline __m128i +InterleaveLo8(__m128i m1, __m128i m2) +{ + return _mm_unpacklo_epi8(m1, m2); +} + +inline __m128i +InterleaveHi8(__m128i m1, __m128i m2) +{ + return _mm_unpackhi_epi8(m1, m2); +} + +inline __m128i +InterleaveLo16(__m128i m1, __m128i m2) +{ + return _mm_unpacklo_epi16(m1, m2); +} + +inline __m128i +InterleaveHi16(__m128i m1, __m128i m2) +{ + return _mm_unpackhi_epi16(m1, m2); +} + +inline __m128i +InterleaveLo32(__m128i m1, __m128i m2) +{ + return _mm_unpacklo_epi32(m1, m2); +} + +template<uint8_t aNumBytes> +inline __m128i +Rotate8(__m128i a1234, __m128i a5678) +{ + return _mm_or_si128(_mm_srli_si128(a1234, aNumBytes), _mm_slli_si128(a5678, 16 - aNumBytes)); +} + +inline __m128i +PackAndSaturate32To16(__m128i m1, __m128i m2) +{ + return _mm_packs_epi32(m1, m2); +} + +inline __m128i +PackAndSaturate32ToU16(__m128i m1, __m128i m2) +{ + return _mm_packs_epi32(m1, m2); +} + +inline __m128i +PackAndSaturate32To8(__m128i m1, __m128i m2, __m128i m3, const __m128i& m4) +{ + // Pack into 8 16bit signed integers (saturating). + __m128i m12 = _mm_packs_epi32(m1, m2); + __m128i m34 = _mm_packs_epi32(m3, m4); + + // Pack into 16 8bit unsigned integers (saturating). + return _mm_packus_epi16(m12, m34); +} + +inline __m128i +PackAndSaturate16To8(__m128i m1, __m128i m2) +{ + // Pack into 16 8bit unsigned integers (saturating). + return _mm_packus_epi16(m1, m2); +} + +inline __m128i +FastDivideBy255(__m128i m) +{ + // v = m << 8 + __m128i v = _mm_slli_epi32(m, 8); + // v = v + (m + (255,255,255,255)) + v = _mm_add_epi32(v, _mm_add_epi32(m, _mm_set1_epi32(255))); + // v = v >> 16 + return _mm_srai_epi32(v, 16); +} + +inline __m128i +FastDivideBy255_16(__m128i m) +{ + __m128i zero = _mm_set1_epi16(0); + __m128i lo = _mm_unpacklo_epi16(m, zero); + __m128i hi = _mm_unpackhi_epi16(m, zero); + return _mm_packs_epi32(FastDivideBy255(lo), FastDivideBy255(hi)); +} + +inline __m128i +Pick(__m128i mask, __m128i a, __m128i b) +{ + return _mm_or_si128(_mm_andnot_si128(mask, a), _mm_and_si128(mask, b)); +} + +inline __m128 MixF32(__m128 a, __m128 b, float t) +{ + return _mm_add_ps(a, _mm_mul_ps(_mm_sub_ps(b, a), _mm_set1_ps(t))); +} + +inline __m128 WSumF32(__m128 a, __m128 b, float wa, float wb) +{ + return _mm_add_ps(_mm_mul_ps(a, _mm_set1_ps(wa)), _mm_mul_ps(b, _mm_set1_ps(wb))); +} + +inline __m128 AbsF32(__m128 a) +{ + return _mm_max_ps(_mm_sub_ps(_mm_setzero_ps(), a), a); +} + +inline __m128 AddF32(__m128 a, __m128 b) +{ + return _mm_add_ps(a, b); +} + +inline __m128 MulF32(__m128 a, __m128 b) +{ + return _mm_mul_ps(a, b); +} + +inline __m128 DivF32(__m128 a, __m128 b) +{ + return _mm_div_ps(a, b); +} + +template<uint8_t aIndex> +inline __m128 SplatF32(__m128 m) +{ + AssertIndex<aIndex>(); + return _mm_shuffle_ps(m, m, _MM_SHUFFLE(aIndex, aIndex, aIndex, aIndex)); +} + +inline __m128i F32ToI32(__m128 m) +{ + return _mm_cvtps_epi32(m); +} + +#endif // SIMD_COMPILE_SSE2 + +} // namespace simd + +} // namespace gfx +} // namespace mozilla + +#endif // _MOZILLA_GFX_SIMD_H_ diff --git a/gfx/2d/SVGTurbulenceRenderer-inl.h b/gfx/2d/SVGTurbulenceRenderer-inl.h new file mode 100644 index 000000000..006f842e3 --- /dev/null +++ b/gfx/2d/SVGTurbulenceRenderer-inl.h @@ -0,0 +1,360 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "2D.h" +#include "Filters.h" +#include "SIMD.h" + +namespace mozilla { +namespace gfx { + +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> +class SVGTurbulenceRenderer +{ +public: + SVGTurbulenceRenderer(const Size &aBaseFrequency, int32_t aSeed, + int aNumOctaves, const Rect &aTileRect); + + TemporaryRef<DataSourceSurface> Render(const IntSize &aSize, const Point &aOffset) const; + +private: + /* The turbulence calculation code is an adapted version of what + appears in the SVG 1.1 specification: + http://www.w3.org/TR/SVG11/filters.html#feTurbulence + */ + + struct StitchInfo { + int32_t width; // How much to subtract to wrap for stitching. + int32_t height; + int32_t wrapX; // Minimum value to wrap. + int32_t wrapY; + }; + + const static int sBSize = 0x100; + const static int sBM = 0xff; + void InitFromSeed(int32_t aSeed); + void AdjustBaseFrequencyForStitch(const Rect &aTileRect); + IntPoint AdjustForStitch(IntPoint aLatticePoint, const StitchInfo& aStitchInfo) const; + StitchInfo CreateStitchInfo(const Rect &aTileRect) const; + f32x4_t Noise2(Point aVec, const StitchInfo& aStitchInfo) const; + i32x4_t Turbulence(const Point &aPoint) const; + Point EquivalentNonNegativeOffset(const Point &aOffset) const; + + Size mBaseFrequency; + int32_t mNumOctaves; + StitchInfo mStitchInfo; + bool mStitchable; + TurbulenceType mType; + uint8_t mLatticeSelector[sBSize]; + f32x4_t mGradient[sBSize][2]; +}; + +namespace { + +struct RandomNumberSource +{ + explicit RandomNumberSource(int32_t aSeed) : mLast(SetupSeed(aSeed)) {} + int32_t Next() { mLast = Random(mLast); return mLast; } + +private: + static const int32_t RAND_M = 2147483647; /* 2**31 - 1 */ + static const int32_t RAND_A = 16807; /* 7**5; primitive root of m */ + static const int32_t RAND_Q = 127773; /* m / a */ + static const int32_t RAND_R = 2836; /* m % a */ + + /* Produces results in the range [1, 2**31 - 2]. + Algorithm is: r = (a * r) mod m + where a = 16807 and m = 2**31 - 1 = 2147483647 + See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988 + To test: the algorithm should produce the result 1043618065 + as the 10,000th generated number if the original seed is 1. + */ + + static int32_t + SetupSeed(int32_t aSeed) { + if (aSeed <= 0) + aSeed = -(aSeed % (RAND_M - 1)) + 1; + if (aSeed > RAND_M - 1) + aSeed = RAND_M - 1; + return aSeed; + } + + static int32_t + Random(int32_t aSeed) + { + int32_t result = RAND_A * (aSeed % RAND_Q) - RAND_R * (aSeed / RAND_Q); + if (result <= 0) + result += RAND_M; + return result; + } + + int32_t mLast; +}; + +} // unnamed namespace + +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::SVGTurbulenceRenderer(const Size &aBaseFrequency, int32_t aSeed, + int aNumOctaves, const Rect &aTileRect) + : mBaseFrequency(aBaseFrequency) + , mNumOctaves(aNumOctaves) +{ + InitFromSeed(aSeed); + if (Stitch) { + AdjustBaseFrequencyForStitch(aTileRect); + mStitchInfo = CreateStitchInfo(aTileRect); + } +} + +template<typename T> +static void +Swap(T& a, T& b) { + T c = a; + a = b; + b = c; +} + +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> +void +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::InitFromSeed(int32_t aSeed) +{ + RandomNumberSource rand(aSeed); + + float gradient[4][sBSize][2]; + for (int32_t k = 0; k < 4; k++) { + for (int32_t i = 0; i < sBSize; i++) { + float a, b; + do { + a = float((rand.Next() % (sBSize + sBSize)) - sBSize) / sBSize; + b = float((rand.Next() % (sBSize + sBSize)) - sBSize) / sBSize; + } while (a == 0 && b == 0); + float s = sqrt(a * a + b * b); + gradient[k][i][0] = a / s; + gradient[k][i][1] = b / s; + } + } + + for (int32_t i = 0; i < sBSize; i++) { + mLatticeSelector[i] = i; + } + for (int32_t i1 = sBSize - 1; i1 > 0; i1--) { + int32_t i2 = rand.Next() % sBSize; + Swap(mLatticeSelector[i1], mLatticeSelector[i2]); + } + + for (int32_t i = 0; i < sBSize; i++) { + // Contrary to the code in the spec, we build the first lattice selector + // lookup into mGradient so that we don't need to do it again for every + // pixel. + // We also change the order of the gradient indexing so that we can process + // all four color channels at the same time. + uint8_t j = mLatticeSelector[i]; + mGradient[i][0] = simd::FromF32<f32x4_t>(gradient[2][j][0], gradient[1][j][0], + gradient[0][j][0], gradient[3][j][0]); + mGradient[i][1] = simd::FromF32<f32x4_t>(gradient[2][j][1], gradient[1][j][1], + gradient[0][j][1], gradient[3][j][1]); + } +} + +// Adjust aFreq such that aLength * AdjustForLength(aFreq, aLength) is integer +// and as close to aLength * aFreq as possible. +static inline float +AdjustForLength(float aFreq, float aLength) +{ + float lowFreq = floor(aLength * aFreq) / aLength; + float hiFreq = ceil(aLength * aFreq) / aLength; + if (aFreq / lowFreq < hiFreq / aFreq) { + return lowFreq; + } + return hiFreq; +} + +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> +void +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::AdjustBaseFrequencyForStitch(const Rect &aTileRect) +{ + mBaseFrequency = Size(AdjustForLength(mBaseFrequency.width, aTileRect.width), + AdjustForLength(mBaseFrequency.height, aTileRect.height)); +} + +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> +typename SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::StitchInfo +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::CreateStitchInfo(const Rect &aTileRect) const +{ + StitchInfo stitch; + stitch.width = int32_t(floorf(aTileRect.width * mBaseFrequency.width + 0.5f)); + stitch.height = int32_t(floorf(aTileRect.height * mBaseFrequency.height + 0.5f)); + stitch.wrapX = int32_t(aTileRect.x * mBaseFrequency.width) + stitch.width; + stitch.wrapY = int32_t(aTileRect.y * mBaseFrequency.height) + stitch.height; + return stitch; +} + +static MOZ_ALWAYS_INLINE Float +SCurve(Float t) +{ + return t * t * (3 - 2 * t); +} + +static MOZ_ALWAYS_INLINE Point +SCurve(Point t) +{ + return Point(SCurve(t.x), SCurve(t.y)); +} + +template<typename f32x4_t> +static MOZ_ALWAYS_INLINE f32x4_t +BiMix(const f32x4_t& aa, const f32x4_t& ab, + const f32x4_t& ba, const f32x4_t& bb, Point s) +{ + return simd::MixF32(simd::MixF32(aa, ab, s.x), + simd::MixF32(ba, bb, s.x), s.y); +} + +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> +IntPoint +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::AdjustForStitch(IntPoint aLatticePoint, + const StitchInfo& aStitchInfo) const +{ + if (Stitch) { + if (aLatticePoint.x >= aStitchInfo.wrapX) { + aLatticePoint.x -= aStitchInfo.width; + } + if (aLatticePoint.y >= aStitchInfo.wrapY) { + aLatticePoint.y -= aStitchInfo.height; + } + } + return aLatticePoint; +} + +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> +f32x4_t +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Noise2(Point aVec, const StitchInfo& aStitchInfo) const +{ + // aVec is guaranteed to be non-negative, so casting to int32_t always + // rounds towards negative infinity. + IntPoint topLeftLatticePoint(int32_t(aVec.x), int32_t(aVec.y)); + Point r = aVec - topLeftLatticePoint; // fractional offset + + IntPoint b0 = AdjustForStitch(topLeftLatticePoint, aStitchInfo); + IntPoint b1 = AdjustForStitch(b0 + IntPoint(1, 1), aStitchInfo); + + uint8_t i = mLatticeSelector[b0.x & sBM]; + uint8_t j = mLatticeSelector[b1.x & sBM]; + + const f32x4_t* qua = mGradient[(i + b0.y) & sBM]; + const f32x4_t* qub = mGradient[(i + b1.y) & sBM]; + const f32x4_t* qva = mGradient[(j + b0.y) & sBM]; + const f32x4_t* qvb = mGradient[(j + b1.y) & sBM]; + + return BiMix(simd::WSumF32(qua[0], qua[1], r.x, r.y), + simd::WSumF32(qva[0], qva[1], r.x - 1.f, r.y), + simd::WSumF32(qub[0], qub[1], r.x, r.y - 1.f), + simd::WSumF32(qvb[0], qvb[1], r.x - 1.f, r.y - 1.f), + SCurve(r)); +} + +template<typename f32x4_t, typename i32x4_t, typename u8x16_t> +static inline i32x4_t +ColorToBGRA(f32x4_t aUnscaledUnpreFloat) +{ + // Color is an unpremultiplied float vector where 1.0f means white. We will + // convert it into an integer vector where 255 means white. + f32x4_t alpha = simd::SplatF32<3>(aUnscaledUnpreFloat); + f32x4_t scaledUnpreFloat = simd::MulF32(aUnscaledUnpreFloat, simd::FromF32<f32x4_t>(255)); + i32x4_t scaledUnpreInt = simd::F32ToI32(scaledUnpreFloat); + + // Multiply all channels with alpha. + i32x4_t scaledPreInt = simd::F32ToI32(simd::MulF32(scaledUnpreFloat, alpha)); + + // Use the premultiplied color channels and the unpremultiplied alpha channel. + i32x4_t alphaMask = simd::From32<i32x4_t>(0, 0, 0, -1); + return simd::Pick(alphaMask, scaledPreInt, scaledUnpreInt); +} + +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> +i32x4_t +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Turbulence(const Point &aPoint) const +{ + StitchInfo stitchInfo = mStitchInfo; + f32x4_t sum = simd::FromF32<f32x4_t>(0); + Point vec(aPoint.x * mBaseFrequency.width, aPoint.y * mBaseFrequency.height); + f32x4_t ratio = simd::FromF32<f32x4_t>(1); + + for (int octave = 0; octave < mNumOctaves; octave++) { + f32x4_t thisOctave = Noise2(vec, stitchInfo); + if (Type == TURBULENCE_TYPE_TURBULENCE) { + thisOctave = simd::AbsF32(thisOctave); + } + sum = simd::AddF32(sum, simd::DivF32(thisOctave, ratio)); + vec = vec * 2; + ratio = simd::MulF32(ratio, simd::FromF32<f32x4_t>(2)); + + if (Stitch) { + stitchInfo.width *= 2; + stitchInfo.wrapX *= 2; + stitchInfo.height *= 2; + stitchInfo.wrapY *= 2; + } + } + + if (Type == TURBULENCE_TYPE_FRACTAL_NOISE) { + sum = simd::DivF32(simd::AddF32(sum, simd::FromF32<f32x4_t>(1)), simd::FromF32<f32x4_t>(2)); + } + return ColorToBGRA<f32x4_t,i32x4_t,u8x16_t>(sum); +} + +static inline Float +MakeNonNegative(Float aValue, Float aIncrementSize) +{ + if (aValue >= 0) { + return aValue; + } + return aValue + ceilf(-aValue / aIncrementSize) * aIncrementSize; +} + +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> +Point +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::EquivalentNonNegativeOffset(const Point &aOffset) const +{ + Size basePeriod = Stitch ? Size(mStitchInfo.width, mStitchInfo.height) : + Size(sBSize, sBSize); + Size repeatingSize(basePeriod.width / mBaseFrequency.width, + basePeriod.height / mBaseFrequency.height); + return Point(MakeNonNegative(aOffset.x, repeatingSize.width), + MakeNonNegative(aOffset.y, repeatingSize.height)); +} + +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> +TemporaryRef<DataSourceSurface> +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Render(const IntSize &aSize, const Point &aOffset) const +{ + RefPtr<DataSourceSurface> target = + Factory::CreateDataSourceSurface(aSize, SurfaceFormat::B8G8R8A8); + if (!target) { + return nullptr; + } + + uint8_t* targetData = target->GetData(); + uint32_t stride = target->Stride(); + + Point startOffset = EquivalentNonNegativeOffset(aOffset); + + for (int32_t y = 0; y < aSize.height; y++) { + for (int32_t x = 0; x < aSize.width; x += 4) { + int32_t targIndex = y * stride + x * 4; + i32x4_t a = Turbulence(startOffset + Point(x, y)); + i32x4_t b = Turbulence(startOffset + Point(x + 1, y)); + i32x4_t c = Turbulence(startOffset + Point(x + 2, y)); + i32x4_t d = Turbulence(startOffset + Point(x + 3, y)); + u8x16_t result1234 = simd::PackAndSaturate32To8(a, b, c, d); + simd::Store8(&targetData[targIndex], result1234); + } + } + + return target; +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/2d/Scale.cpp b/gfx/2d/Scale.cpp index 71f6f6d84..cc4710435 100644 --- a/gfx/2d/Scale.cpp +++ b/gfx/2d/Scale.cpp @@ -18,23 +18,24 @@ bool Scale(uint8_t* srcData, int32_t srcWidth, int32_t srcHeight, int32_t srcStr SurfaceFormat format) { #ifdef USE_SKIA - bool opaque; - if (format == FORMAT_B8G8R8A8) { - opaque = false; + SkAlphaType alphaType; + if (format == SurfaceFormat::B8G8R8A8) { + alphaType = kPremul_SkAlphaType; } else { - opaque = true; + alphaType = kOpaque_SkAlphaType; } - SkBitmap::Config config = GfxFormatToSkiaConfig(format); + SkImageInfo info = SkImageInfo::Make(srcWidth, + srcHeight, + GfxFormatToSkiaColorType(format), + alphaType); SkBitmap imgSrc; - imgSrc.setConfig(config, srcWidth, srcHeight, srcStride); - imgSrc.setPixels(srcData); - imgSrc.setIsOpaque(opaque); + imgSrc.installPixels(info, srcData, srcStride); // Rescaler is compatible with 32 bpp only. Convert to RGB32 if needed. - if (config != SkBitmap::kARGB_8888_Config) { - imgSrc.copyTo(&imgSrc, SkBitmap::kARGB_8888_Config); + if (format != SurfaceFormat::B8G8R8A8) { + imgSrc.copyTo(&imgSrc, kBGRA_8888_SkColorType); } // This returns an SkBitmap backed by dstData; since it also wrote to dstData, diff --git a/gfx/2d/ScaleFactor.h b/gfx/2d/ScaleFactor.h index d7eec8275..c67cb9e04 100644 --- a/gfx/2d/ScaleFactor.h +++ b/gfx/2d/ScaleFactor.h @@ -6,6 +6,8 @@ #ifndef MOZILLA_GFX_SCALEFACTOR_H_ #define MOZILLA_GFX_SCALEFACTOR_H_ +#include "mozilla/Attributes.h" + #include "gfxPoint.h" namespace mozilla { @@ -26,18 +28,14 @@ template<class src, class dst> struct ScaleFactor { float scale; - ScaleFactor() : scale(1.0) {} - ScaleFactor(const ScaleFactor<src, dst>& aCopy) : scale(aCopy.scale) {} - explicit ScaleFactor(float aScale) : scale(aScale) {} + MOZ_CONSTEXPR ScaleFactor() : scale(1.0) {} + MOZ_CONSTEXPR ScaleFactor(const ScaleFactor<src, dst>& aCopy) : scale(aCopy.scale) {} + explicit MOZ_CONSTEXPR ScaleFactor(float aScale) : scale(aScale) {} explicit ScaleFactor(float aX, float aY) : scale(aX) { MOZ_ASSERT(fabs(aX - aY) < 1e-6); } - explicit ScaleFactor(gfxSize aScale) : scale(aScale.width) { - MOZ_ASSERT(fabs(aScale.width - aScale.height) < 1e-6); - } - ScaleFactor<dst, src> Inverse() { return ScaleFactor<dst, src>(1 / scale); } @@ -50,6 +48,22 @@ struct ScaleFactor { return !(*this == aOther); } + bool operator<(const ScaleFactor<src, dst>& aOther) const { + return scale < aOther.scale; + } + + bool operator<=(const ScaleFactor<src, dst>& aOther) const { + return scale <= aOther.scale; + } + + bool operator>(const ScaleFactor<src, dst>& aOther) const { + return scale > aOther.scale; + } + + bool operator>=(const ScaleFactor<src, dst>& aOther) const { + return scale >= aOther.scale; + } + template<class other> ScaleFactor<other, dst> operator/(const ScaleFactor<src, other>& aOther) const { return ScaleFactor<other, dst>(scale / aOther.scale); diff --git a/gfx/2d/ScaledFontBase.cpp b/gfx/2d/ScaledFontBase.cpp index 1fd0e7af6..f0eb74d39 100644 --- a/gfx/2d/ScaledFontBase.cpp +++ b/gfx/2d/ScaledFontBase.cpp @@ -8,11 +8,12 @@ #ifdef USE_SKIA #include "PathSkia.h" #include "skia/SkPaint.h" -#include "skia/SkPath.h" #endif #ifdef USE_CAIRO #include "PathCairo.h" +#include "DrawTargetCairo.h" +#include "HelpersCairo.h" #endif #include <vector> @@ -28,7 +29,7 @@ ScaledFontBase::~ScaledFontBase() #ifdef USE_SKIA SkSafeUnref(mTypeface); #endif -#ifdef USE_CAIRO +#ifdef USE_CAIRO_SCALED_FONT cairo_scaled_font_destroy(mScaledFont); #endif } @@ -39,48 +40,65 @@ ScaledFontBase::ScaledFontBase(Float aSize) #ifdef USE_SKIA mTypeface = nullptr; #endif -#ifdef USE_CAIRO +#ifdef USE_CAIRO_SCALED_FONT mScaledFont = nullptr; #endif } +#ifdef USE_SKIA +SkPath +ScaledFontBase::GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer) +{ + SkTypeface *typeFace = GetSkTypeface(); + MOZ_ASSERT(typeFace); + + SkPaint paint; + paint.setTypeface(typeFace); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setTextSize(SkFloatToScalar(mSize)); + + std::vector<uint16_t> indices; + std::vector<SkPoint> offsets; + indices.resize(aBuffer.mNumGlyphs); + offsets.resize(aBuffer.mNumGlyphs); + + for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { + indices[i] = aBuffer.mGlyphs[i].mIndex; + offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x); + offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y); + } + + SkPath path; + paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path); + return path; +} +#endif + TemporaryRef<Path> ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) { #ifdef USE_SKIA - if (aTarget->GetType() == BACKEND_SKIA) { - SkPaint paint; - paint.setTypeface(GetSkTypeface()); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setTextSize(SkFloatToScalar(mSize)); - - std::vector<uint16_t> indices; - std::vector<SkPoint> offsets; - indices.resize(aBuffer.mNumGlyphs); - offsets.resize(aBuffer.mNumGlyphs); - - for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { - indices[i] = aBuffer.mGlyphs[i].mIndex; - offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x); - offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y); - } - - SkPath path; - paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path); - return new PathSkia(path, FILL_WINDING); + if (aTarget->GetBackendType() == BackendType::SKIA) { + SkPath path = GetSkiaPathForGlyphs(aBuffer); + return new PathSkia(path, FillRule::FILL_WINDING); } #endif #ifdef USE_CAIRO - if (aTarget->GetType() == BACKEND_CAIRO) { + if (aTarget->GetBackendType() == BackendType::CAIRO) { MOZ_ASSERT(mScaledFont); - RefPtr<PathBuilder> builder_iface = aTarget->CreatePathBuilder(); - PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(builder_iface.get()); + DrawTarget *dt = const_cast<DrawTarget*>(aTarget); + cairo_t *ctx = static_cast<cairo_t*>(dt->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT)); - // Manually build the path for the PathBuilder. - RefPtr<CairoPathContext> context = builder->GetPathContext(); + bool isNewContext = !ctx; + if (!ctx) { + ctx = cairo_create(DrawTargetCairo::GetDummySurface()); + cairo_matrix_t mat; + GfxMatrixToCairoMatrix(aTarget->GetTransform(), mat); + cairo_set_matrix(ctx, &mat); + } - cairo_set_scaled_font(*context, mScaledFont); + cairo_set_scaled_font(ctx, mScaledFont); // Convert our GlyphBuffer into an array of Cairo glyphs. std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs); @@ -90,28 +108,78 @@ ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *a glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y; } - cairo_glyph_path(*context, &glyphs[0], aBuffer.mNumGlyphs); + cairo_new_path(ctx); + + cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs); + + RefPtr<PathCairo> newPath = new PathCairo(ctx); + if (isNewContext) { + cairo_destroy(ctx); + } - return builder->Finish(); + return newPath.forget(); } #endif return nullptr; } void -ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder) +ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint) { - // XXX - implement me - MOZ_ASSERT(false); - return; +#ifdef USE_SKIA + if (aBackendType == BackendType::SKIA) { + PathBuilderSkia *builder = static_cast<PathBuilderSkia*>(aBuilder); + builder->AppendPath(GetSkiaPathForGlyphs(aBuffer)); + return; + } +#endif +#ifdef USE_CAIRO + if (aBackendType == BackendType::CAIRO) { + MOZ_ASSERT(mScaledFont); + + PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(aBuilder); + cairo_t *ctx = cairo_create(DrawTargetCairo::GetDummySurface()); + + if (aTransformHint) { + cairo_matrix_t mat; + GfxMatrixToCairoMatrix(*aTransformHint, mat); + cairo_set_matrix(ctx, &mat); + } + + // Convert our GlyphBuffer into an array of Cairo glyphs. + std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs); + for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) { + glyphs[i].index = aBuffer.mGlyphs[i].mIndex; + glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x; + glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y; + } + + cairo_set_scaled_font(ctx, mScaledFont); + cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs); + + RefPtr<PathCairo> cairoPath = new PathCairo(ctx); + cairo_destroy(ctx); + + cairoPath->AppendPathToBuilder(builder); + return; + } +#endif + + MOZ_CRASH("The specified backend type is not supported by CopyGlyphsToBuilder"); } -#ifdef USE_CAIRO +#ifdef USE_CAIRO_SCALED_FONT void ScaledFontBase::SetCairoScaledFont(cairo_scaled_font_t* font) { MOZ_ASSERT(!mScaledFont); + if (font == mScaledFont) + return; + + if (mScaledFont) + cairo_scaled_font_destroy(mScaledFont); + mScaledFont = font; cairo_scaled_font_reference(mScaledFont); } diff --git a/gfx/2d/ScaledFontBase.h b/gfx/2d/ScaledFontBase.h index ff5e90a8c..93f4441be 100644 --- a/gfx/2d/ScaledFontBase.h +++ b/gfx/2d/ScaledFontBase.h @@ -8,10 +8,16 @@ #include "2D.h" +// Skia uses cairo_scaled_font_t as the internal font type in ScaledFont +#if defined(USE_SKIA) || defined(USE_CAIRO) +#define USE_CAIRO_SCALED_FONT +#endif + #ifdef USE_SKIA +#include "skia/SkPath.h" #include "skia/SkTypeface.h" #endif -#ifdef USE_CAIRO +#ifdef USE_CAIRO_SCALED_FONT #include "cairo.h" #endif @@ -23,12 +29,13 @@ namespace gfx { class ScaledFontBase : public ScaledFont { public: - ScaledFontBase(Float aSize); + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontBase) + explicit ScaledFontBase(Float aSize); virtual ~ScaledFontBase(); virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget); - virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder); + virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint); float GetSize() { return mSize; } @@ -37,9 +44,9 @@ public: #endif // Not true, but required to instantiate a ScaledFontBase. - virtual FontType GetType() const { return FONT_SKIA; } + virtual FontType GetType() const { return FontType::SKIA; } -#ifdef USE_CAIRO +#ifdef USE_CAIRO_SCALED_FONT cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; } void SetCairoScaledFont(cairo_scaled_font_t* font); #endif @@ -48,8 +55,9 @@ protected: friend class DrawTargetSkia; #ifdef USE_SKIA SkTypeface* mTypeface; + SkPath GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer); #endif -#ifdef USE_CAIRO +#ifdef USE_CAIRO_SCALED_FONT cairo_scaled_font_t* mScaledFont; #endif Float mSize; diff --git a/gfx/2d/ScaledFontCairo.cpp b/gfx/2d/ScaledFontCairo.cpp new file mode 100644 index 000000000..12ab27e26 --- /dev/null +++ b/gfx/2d/ScaledFontCairo.cpp @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ScaledFontCairo.h" +#include "Logging.h" + +#ifdef MOZ_ENABLE_FREETYPE +#include <ft2build.h> +#include FT_FREETYPE_H +#include "cairo-ft.h" +#endif + +#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE) +#include "skia/SkTypeface.h" +#include "skia/SkTypeface_cairo.h" +#endif + +#include <string> + +typedef struct FT_FaceRec_* FT_Face; + +using namespace std; + +namespace mozilla { +namespace gfx { + +// On Linux and Android our "platform" font is a cairo_scaled_font_t and we use +// an SkFontHost implementation that allows Skia to render using this. +// This is mainly because FT_Face is not good for sharing between libraries, which +// is a requirement when we consider runtime switchable backends and so on +ScaledFontCairo::ScaledFontCairo(cairo_scaled_font_t* aScaledFont, Float aSize) + : ScaledFontBase(aSize) +{ + SetCairoScaledFont(aScaledFont); +} + +#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE) +SkTypeface* ScaledFontCairo::GetSkTypeface() +{ + if (!mTypeface) { + cairo_font_face_t* fontFace = cairo_scaled_font_get_font_face(mScaledFont); + FT_Face face = cairo_ft_scaled_font_lock_face(mScaledFont); + + int style = SkTypeface::kNormal; + + if (face->style_flags & FT_STYLE_FLAG_ITALIC) + style |= SkTypeface::kItalic; + + if (face->style_flags & FT_STYLE_FLAG_BOLD) + style |= SkTypeface::kBold; + + bool isFixedWidth = face->face_flags & FT_FACE_FLAG_FIXED_WIDTH; + cairo_ft_scaled_font_unlock_face(mScaledFont); + + mTypeface = SkCreateTypefaceFromCairoFont(fontFace, (SkTypeface::Style)style, isFixedWidth); + } + + return mTypeface; +} +#endif + +} +} diff --git a/gfx/2d/ScaledFontCairo.h b/gfx/2d/ScaledFontCairo.h new file mode 100644 index 000000000..9f4d227ae --- /dev/null +++ b/gfx/2d/ScaledFontCairo.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_SCALEDFONTCAIRO_H_ +#define MOZILLA_GFX_SCALEDFONTCAIRO_H_ + +#include "ScaledFontBase.h" + +#include "cairo.h" + +namespace mozilla { +namespace gfx { + +class ScaledFontCairo : public ScaledFontBase +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontCairo) + + ScaledFontCairo(cairo_scaled_font_t* aScaledFont, Float aSize); + +#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE) + virtual SkTypeface* GetSkTypeface(); +#endif +}; + +// We need to be able to tell Skia whether or not to use +// hinting when rendering text, so that the glyphs it renders +// are the same as what layout is expecting. At the moment, only +// Skia uses this class when rendering with FreeType, as gfxFT2Font +// is the only gfxFont that honours gfxPlatform::FontHintingEnabled(). +class GlyphRenderingOptionsCairo : public GlyphRenderingOptions +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptionsCairo) + GlyphRenderingOptionsCairo() + : mHinting(FontHinting::NORMAL) + , mAutoHinting(false) + { + } + + void SetHinting(FontHinting aHinting) { mHinting = aHinting; } + void SetAutoHinting(bool aAutoHinting) { mAutoHinting = aAutoHinting; } + FontHinting GetHinting() const { return mHinting; } + bool GetAutoHinting() const { return mAutoHinting; } + virtual FontType GetType() const { return FontType::CAIRO; } +private: + FontHinting mHinting; + bool mAutoHinting; +}; + +} +} + +#endif /* MOZILLA_GFX_SCALEDFONTCAIRO_H_ */ diff --git a/gfx/2d/ScaledFontDWrite.cpp b/gfx/2d/ScaledFontDWrite.cpp index 47a0d5093..e1e1b7137 100644 --- a/gfx/2d/ScaledFontDWrite.cpp +++ b/gfx/2d/ScaledFontDWrite.cpp @@ -213,7 +213,7 @@ DoGrayscale(IDWriteFontFace *aDWFace, Float ppem) return true; } -IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = NULL; +IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr; HRESULT STDMETHODCALLTYPE DWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey, @@ -275,7 +275,7 @@ DWriteFontFileStream::ReadFileFragment(const void **fragmentStart, // We should be alive for the duration of this. *fragmentStart = &mData[index]; - *fragmentContext = NULL; + *fragmentContext = nullptr; return S_OK; } @@ -309,7 +309,7 @@ ScaledFontDWrite::ScaledFontDWrite(uint8_t *aData, uint32_t aSize, TemporaryRef<Path> ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) { - if (aTarget->GetType() != BACKEND_DIRECT2D) { + if (aTarget->GetBackendType() != BackendType::DIRECT2D && aTarget->GetBackendType() != BackendType::DIRECT2D1_1) { return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget); } @@ -324,9 +324,13 @@ ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget } void -ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder) +ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint) { - // XXX - Check path builder type! + if (aBackendType != BackendType::DIRECT2D && aBackendType != BackendType::DIRECT2D1_1) { + ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint); + return; + } + PathBuilderD2D *pathBuilderD2D = static_cast<PathBuilderD2D*>(aBuilder); @@ -405,23 +409,23 @@ ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton AntialiasMode ScaledFontDWrite::GetDefaultAAMode() { - AntialiasMode defaultMode = AA_SUBPIXEL; + AntialiasMode defaultMode = AntialiasMode::SUBPIXEL; switch (GetSystemTextQuality()) { case CLEARTYPE_QUALITY: - defaultMode = AA_SUBPIXEL; + defaultMode = AntialiasMode::SUBPIXEL; break; case ANTIALIASED_QUALITY: - defaultMode = AA_GRAY; + defaultMode = AntialiasMode::GRAY; break; case DEFAULT_QUALITY: - defaultMode = AA_NONE; + defaultMode = AntialiasMode::NONE; break; } - if (defaultMode == AA_GRAY) { + if (defaultMode == AntialiasMode::GRAY) { if (!DoGrayscale(mFontFace, mSize)) { - defaultMode = AA_NONE; + defaultMode = AntialiasMode::NONE; } } return defaultMode; diff --git a/gfx/2d/ScaledFontDWrite.h b/gfx/2d/ScaledFontDWrite.h index 2f5cc94f5..941babed1 100644 --- a/gfx/2d/ScaledFontDWrite.h +++ b/gfx/2d/ScaledFontDWrite.h @@ -14,19 +14,20 @@ struct ID2D1GeometrySink; namespace mozilla { namespace gfx { -class ScaledFontDWrite MOZ_FINAL : public ScaledFontBase +class ScaledFontDWrite final : public ScaledFontBase { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontDwrite) ScaledFontDWrite(IDWriteFontFace *aFont, Float aSize) : mFontFace(aFont) , ScaledFontBase(aSize) {} ScaledFontDWrite(uint8_t *aData, uint32_t aSize, uint32_t aIndex, Float aGlyphSize); - virtual FontType GetType() const { return FONT_DWRITE; } + virtual FontType GetType() const { return FontType::DWRITE; } virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget); - virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder); + virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint); void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink); @@ -48,15 +49,17 @@ public: class GlyphRenderingOptionsDWrite : public GlyphRenderingOptions { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptionsDWrite) GlyphRenderingOptionsDWrite(IDWriteRenderingParams *aParams) : mParams(aParams) { } - virtual FontType GetType() const { return FONT_DWRITE; } + virtual FontType GetType() const { return FontType::DWRITE; } private: friend class DrawTargetD2D; + friend class DrawTargetD2D1; RefPtr<IDWriteRenderingParams> mParams; }; diff --git a/gfx/2d/ScaledFontFreetype.cpp b/gfx/2d/ScaledFontFreetype.cpp deleted file mode 100644 index 980548446..000000000 --- a/gfx/2d/ScaledFontFreetype.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "ScaledFontFreetype.h" -#include "Logging.h" - -#include "gfxFont.h" - -#ifdef USE_SKIA -#include "skia/SkTypeface.h" -#endif - -#include <string> - -using namespace std; - -namespace mozilla { -namespace gfx { - -#ifdef USE_SKIA -static SkTypeface::Style -fontStyleToSkia(FontStyle aStyle) -{ - switch (aStyle) { - case FONT_STYLE_NORMAL: - return SkTypeface::kNormal; - case FONT_STYLE_ITALIC: - return SkTypeface::kItalic; - case FONT_STYLE_BOLD: - return SkTypeface::kBold; - case FONT_STYLE_BOLD_ITALIC: - return SkTypeface::kBoldItalic; - } - - gfxWarning() << "Unknown font style"; - return SkTypeface::kNormal; -} -#endif - -// Ideally we want to use FT_Face here but as there is currently no way to get -// an SkTypeface from an FT_Face we do this. -ScaledFontFreetype::ScaledFontFreetype(FontOptions* aFont, Float aSize) - : ScaledFontBase(aSize) -{ -#ifdef USE_SKIA - mTypeface = SkTypeface::CreateFromName(aFont->mName.c_str(), fontStyleToSkia(aFont->mStyle)); -#endif -} - -} -} diff --git a/gfx/2d/ScaledFontMac.cpp b/gfx/2d/ScaledFontMac.cpp index 6add2e3a3..5babcec54 100644 --- a/gfx/2d/ScaledFontMac.cpp +++ b/gfx/2d/ScaledFontMac.cpp @@ -79,7 +79,8 @@ SkTypeface* ScaledFontMac::GetSkTypeface() TemporaryRef<Path> ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) { - if (aTarget->GetType() == BACKEND_COREGRAPHICS || aTarget->GetType() == BACKEND_COREGRAPHICS_ACCELERATED) { + if (aTarget->GetBackendType() == BackendType::COREGRAPHICS || + aTarget->GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED) { CGMutablePathRef path = CGPathCreateMutable(); for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { @@ -93,12 +94,180 @@ ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aT CGPathAddPath(path, &matrix, glyphPath); CGPathRelease(glyphPath); } - TemporaryRef<Path> ret = new PathCG(path, FILL_WINDING); + TemporaryRef<Path> ret = new PathCG(path, FillRule::FILL_WINDING); CGPathRelease(path); return ret; - } else { - return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget); } + return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget); +} + +void +ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint) +{ + if (!(aBackendType == BackendType::COREGRAPHICS || aBackendType == BackendType::COREGRAPHICS_ACCELERATED)) { + ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint); + return; + } + + PathBuilderCG *pathBuilderCG = + static_cast<PathBuilderCG*>(aBuilder); + // XXX: check builder type + for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { + // XXX: we could probably fold both of these transforms together to avoid extra work + CGAffineTransform flip = CGAffineTransformMakeScale(1, -1); + CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex); + + CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize, + aBuffer.mGlyphs[i].mPosition.x, + aBuffer.mGlyphs[i].mPosition.y); + CGPathAddPath(pathBuilderCG->mCGPath, &matrix, glyphPath); + CGPathRelease(glyphPath); + } +} + +uint32_t +CalcTableChecksum(const uint32_t *tableStart, uint32_t length, bool skipChecksumAdjust = false) +{ + uint32_t sum = 0L; + const uint32_t *table = tableStart; + const uint32_t *end = table+((length+3) & ~3) / sizeof(uint32_t); + while (table < end) { + if (skipChecksumAdjust && (table - tableStart) == 2) { + table++; + } else { + sum += CFSwapInt32BigToHost(*table++); + } + } + return sum; +} + +struct TableRecord { + uint32_t tag; + uint32_t checkSum; + uint32_t offset; + uint32_t length; + CFDataRef data; +}; + +int maxPow2LessThan(int a) +{ + int x = 1; + int shift = 0; + while ((x<<(shift+1)) < a) { + shift++; + } + return shift; +} + +struct writeBuf +{ + explicit writeBuf(int size) + { + this->data = new unsigned char [size]; + this->offset = 0; + } + ~writeBuf() { + delete this->data; + } + + template <class T> + void writeElement(T a) + { + *reinterpret_cast<T*>(&this->data[this->offset]) = a; + this->offset += sizeof(T); + } + + void writeMem(const void *data, unsigned long length) + { + memcpy(&this->data[this->offset], data, length); + this->offset += length; + } + + void align() + { + while (this->offset & 3) { + this->data[this->offset] = 0; + this->offset++; + } + } + + unsigned char *data; + int offset; +}; + +bool +ScaledFontMac::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) +{ + // We'll reconstruct a TTF font from the tables we can get from the CGFont + CFArrayRef tags = CGFontCopyTableTags(mFont); + CFIndex count = CFArrayGetCount(tags); + + TableRecord *records = new TableRecord[count]; + uint32_t offset = 0; + offset += sizeof(uint32_t)*3; + offset += sizeof(uint32_t)*4*count; + bool CFF = false; + for (CFIndex i = 0; i<count; i++) { + uint32_t tag = (uint32_t)(uintptr_t)CFArrayGetValueAtIndex(tags, i); + if (tag == 0x43464620) // 'CFF ' + CFF = true; + CFDataRef data = CGFontCopyTableForTag(mFont, tag); + records[i].tag = tag; + records[i].offset = offset; + records[i].data = data; + records[i].length = CFDataGetLength(data); + bool skipChecksumAdjust = (tag == 0x68656164); // 'head' + records[i].checkSum = CalcTableChecksum(reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(data)), + records[i].length, skipChecksumAdjust); + offset += records[i].length; + // 32 bit align the tables + offset = (offset + 3) & ~3; + } + CFRelease(tags); + + struct writeBuf buf(offset); + // write header/offset table + if (CFF) { + buf.writeElement(CFSwapInt32HostToBig(0x4f54544f)); + } else { + buf.writeElement(CFSwapInt32HostToBig(0x00010000)); + } + buf.writeElement(CFSwapInt16HostToBig(count)); + buf.writeElement(CFSwapInt16HostToBig((1<<maxPow2LessThan(count))*16)); + buf.writeElement(CFSwapInt16HostToBig(maxPow2LessThan(count))); + buf.writeElement(CFSwapInt16HostToBig(count*16-((1<<maxPow2LessThan(count))*16))); + + // write table record entries + for (CFIndex i = 0; i<count; i++) { + buf.writeElement(CFSwapInt32HostToBig(records[i].tag)); + buf.writeElement(CFSwapInt32HostToBig(records[i].checkSum)); + buf.writeElement(CFSwapInt32HostToBig(records[i].offset)); + buf.writeElement(CFSwapInt32HostToBig(records[i].length)); + } + + // write tables + int checkSumAdjustmentOffset = 0; + for (CFIndex i = 0; i<count; i++) { + if (records[i].tag == 0x68656164) { + checkSumAdjustmentOffset = buf.offset + 2*4; + } + buf.writeMem(CFDataGetBytePtr(records[i].data), CFDataGetLength(records[i].data)); + buf.align(); + CFRelease(records[i].data); + } + delete[] records; + + // clear the checksumAdjust field before checksumming the whole font + memset(&buf.data[checkSumAdjustmentOffset], 0, sizeof(uint32_t)); + uint32_t fontChecksum = CFSwapInt32HostToBig(0xb1b0afba - CalcTableChecksum(reinterpret_cast<const uint32_t*>(buf.data), offset)); + // set checkSumAdjust to the computed checksum + memcpy(&buf.data[checkSumAdjustmentOffset], &fontChecksum, sizeof(fontChecksum)); + + // we always use an index of 0 + aDataCallback(buf.data, buf.offset, 0, mSize, aBaton); + + return true; + } } diff --git a/gfx/2d/ScaledFontMac.h b/gfx/2d/ScaledFontMac.h index 456919b38..03c68add6 100644 --- a/gfx/2d/ScaledFontMac.h +++ b/gfx/2d/ScaledFontMac.h @@ -17,14 +17,17 @@ namespace gfx { class ScaledFontMac : public ScaledFontBase { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontMac) ScaledFontMac(CGFontRef aFont, Float aSize); virtual ~ScaledFontMac(); - virtual FontType GetType() const { return FONT_MAC; } + virtual FontType GetType() const { return FontType::MAC; } #ifdef USE_SKIA virtual SkTypeface* GetSkTypeface(); #endif virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget); + virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint); + virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton); private: friend class DrawTargetCG; diff --git a/gfx/2d/ScaledFontWin.h b/gfx/2d/ScaledFontWin.h index 7a99bf587..20c95ed3d 100644 --- a/gfx/2d/ScaledFontWin.h +++ b/gfx/2d/ScaledFontWin.h @@ -15,9 +15,10 @@ namespace gfx { class ScaledFontWin : public ScaledFontBase { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontWin) ScaledFontWin(LOGFONT* aFont, Float aSize); - virtual FontType GetType() const { return FONT_GDI; } + virtual FontType GetType() const { return FontType::GDI; } #ifdef USE_SKIA virtual SkTypeface* GetSkTypeface(); #endif diff --git a/gfx/2d/ShadersD2D.fx b/gfx/2d/ShadersD2D.fx index cef1b69f9..df4ced845 100644 --- a/gfx/2d/ShadersD2D.fx +++ b/gfx/2d/ShadersD2D.fx @@ -300,18 +300,27 @@ float4 SampleBlendTextureSeparablePS_1( VS_OUTPUT In) : SV_Target } else if(blendop == 5) { retval.rgb = max(output.rgb, background.rgb); } else { - if(output.r > 0)
- retval.r = 1 - min(1, (1 - background.r) / output.r);
- else
- retval.r = background.r ? 1 : 0;
- if(output.g > 0)
- retval.g = 1 - min(1, (1 - background.g) / output.g);
- else
- retval.g = background.g ? 1 : 0;
- if(output.b > 0)
- retval.b = 1 - min(1, (1 - background.b) / output.b);
- else
- retval.b = background.b ? 1 : 0; + if(background.r == 0) + retval.r = 0; + else + if(output.r == 1) + retval.r = 1; + else + retval.r = min(1, background.r / (1 - output.r)); + if(background.g == 0) + retval.g = 0; + else + if(output.g == 1) + retval.g = 1; + else + retval.g = min(1, background.g / (1 - output.g)); + if(background.b == 0) + retval.b = 0; + else + if(output.b == 1) + retval.b = 1; + else + retval.b = min(1, background.b / (1 - output.b)); } output.rgb = ((1 - background.a) * output.rgb + background.a * retval.rgb) * output.a; @@ -331,18 +340,27 @@ float4 SampleBlendTextureSeparablePS_2( VS_OUTPUT In) : SV_Target float4 retval = output; if(blendop == 7) { - if(output.r > 0) + if(background.r == 1) + retval.r = 1; + else + if(output.r == 0) + retval.r = 0; + else retval.r = 1 - min(1, (1 - background.r) / output.r); + if(background.g == 1) + retval.g = 1; + else + if(output.g == 0) + retval.g = 0; else - retval.r = background.r ? 1 : 0; - if(output.g > 0) retval.g = 1 - min(1, (1 - background.g) / output.g); + if(background.b == 1) + retval.b = 1; + else + if(output.b == 0) + retval.b = 0; else - retval.g = background.g ? 1 : 0; - if(output.b > 0) retval.b = 1 - min(1, (1 - background.b) / output.b); - else - retval.b = background.b ? 1 : 0; } else if(blendop == 8) { if(output.r <= 0.5) retval.r = 2 * output.r * background.r; diff --git a/gfx/2d/ShadersD2D.h b/gfx/2d/ShadersD2D.h index 7b8832c7d..9cc65a09b 100644 --- a/gfx/2d/ShadersD2D.h +++ b/gfx/2d/ShadersD2D.h @@ -130,7 +130,7 @@ technique10 SampleTexture RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -213,7 +213,7 @@ technique10 SampleTexture GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Resource Bindings:
@@ -278,7 +278,7 @@ technique10 SampleTextureForSeparableBlending_1 RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -361,7 +361,7 @@ technique10 SampleTextureForSeparableBlending_1 GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -429,58 +429,61 @@ technique10 SampleTextureForSeparableBlending_1 mov r0.w, c0.x
add r0.x, r0.w, c3.x
mul r0.x, r0.x, r0.x
- texld r1, t0, s0
- texld r2, t0, s1
+ texld r1, t0, s1
+ texld r2, t0, s0
rcp r0.y, r2.w
+ mad r3.xyz, r2, r0.y, -c2.x
+ mul r3.xyz, r3, r3
+ mad r4.xyz, r2, -r0.y, c2.x
+ rcp r3.w, r4.x
+ rcp r4.w, r1.w
+ mul r5.xyz, r1, r4.w
+ mad r1.xyz, r1, -r4.w, c2.z
+ mul r3.w, r3.w, r5.x
+ min r4.w, r3.w, c2.x
+ cmp r4.w, -r3.x, c2.x, r4.w
+ mul r6.xyz, r5, r5
+ cmp r7.x, -r6.x, c2.y, r4.w
+ rcp r4.w, r4.y
+ mul r4.w, r4.w, r5.y
+ min r5.w, r4.w, c2.x
+ cmp r4.w, -r3.y, c2.x, r5.w
+ cmp r7.y, -r6.y, c2.y, r4.w
+ rcp r4.w, r4.z
+ mul r4.w, r4.w, r5.z
+ min r5.w, r4.w, c2.x
+ cmp r4.w, -r3.z, c2.x, r5.w
+ cmp r7.z, -r6.z, c2.y, r4.w
mul r3.xyz, r0.y, r2
- mul r4.xyz, r3, r3
- cmp r4.xyz, -r4, c2.y, c2.x
- mad r5, r2.xyzx, -r0.y, c2.zzzx
- mad r0.yz, r2, -r0.y, c2.x
- rcp r3.w, r1.w
- mul r2.xyz, r1, r3.w
- rcp r4.w, r2.x
- mad r4.w, r5.w, -r4.w, c2.x
- max r5.w, r4.w, c2.y
- cmp r6.x, -r2.x, r4.x, r5.w
- rcp r5.w, r2.y
- mad r5.w, r0.y, -r5.w, c2.x
- max r6.w, r5.w, c2.y
- cmp r6.y, -r2.y, r4.y, r6.w
- rcp r5.w, r2.z
- mad r5.w, r0.z, -r5.w, c2.x
- max r6.w, r5.w, c2.y
- cmp r6.z, -r2.z, r4.z, r6.w
- max r4.xyz, r2, r3
- cmp r0.xyz, -r0.x, r4, r6
- add r4, r0.w, c1
- mul r4, r4, r4
- min r6.xyz, r3, r2
- cmp r0.xyz, -r4.w, r6, r0
- mad r6.xyz, r3, -c2.w, -c2.x
- add r6.xyz, -r6, c2.x
- mad r7.xyz, r1, -r3.w, c2.x
- mad r8.xyz, r1, r3.w, r3
- mad r8.xyz, r2, -r3, r8
- mad r6.xyz, r7, -r6, c2.x
- add r7.xyz, r3, r3
- mul r3.xyz, r3, r2
- mul r7.xyz, r2, r7
- cmp r5.xyz, r5, r7, r6
- cmp r0.xyz, -r4.z, r5, r0
- cmp r0.xyz, -r4.y, r8, r0
- cmp r0.xyz, -r4.x, r3, r0
- lrp r3.xyz, r2.w, r0, r2
- mul r3.w, r2.w, r2.w
- cmp r3.w, -r3.w, c2.x, c2.y
- mul r0.xyz, r1.w, r3
- mul r0.w, r1.w, r1.w
+ mad r6.xyz, r2, r0.y, r5
+ mad r6.xyz, r3, -r5, r6
+ max r8.xyz, r3, r5
+ cmp r0.xyz, -r0.x, r8, r7
+ add r7, r0.w, c1
+ mul r7, r7, r7
+ min r8.xyz, r5, r3
+ cmp r0.xyz, -r7.w, r8, r0
+ mad r8.xyz, r5, -c2.w, -c2.x
+ add r8.xyz, -r8, c2.x
+ mad r4.xyz, r4, -r8, c2.x
+ add r8.xyz, r5, r5
+ mul r5.xyz, r5, r3
+ mul r8.xyz, r3, r8
+ cmp r1.xyz, r1, r8, r4
+ cmp r0.xyz, -r7.z, r1, r0
+ cmp r0.xyz, -r7.y, r6, r0
+ cmp r0.xyz, -r7.x, r5, r0
+ lrp r4.xyz, r1.w, r0, r3
+ mul r4.w, r1.w, r1.w
+ cmp r4.w, -r4.w, c2.x, c2.y
+ mul r0.xyz, r2.w, r4
+ mul r0.w, r2.w, r2.w
cmp r0.w, -r0.w, c2.x, c2.y
- add r0.w, r3.w, r0.w
- cmp r1.xyz, -r0.w, r0, r1
- mov oC0, r1
+ add r0.w, r4.w, r0.w
+ cmp r2.xyz, -r0.w, r0, r2
+ mov oC0, r2
- // approximately 53 instruction slots used (2 texture, 51 arithmetic)
+ // approximately 56 instruction slots used (2 texture, 54 arithmetic)
ps_4_0
dcl_constantbuffer cb0[1], immediateIndexed
dcl_sampler s0, mode_default
@@ -526,16 +529,17 @@ technique10 SampleTextureForSeparableBlending_1 min r2.xyz, r0.xyzx, r1.xyzx
else
ieq r2.w, cb0[0].x, l(5)
- max r3.xyz, r0.xyzx, r1.xyzx
- lt r4.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r0.xyzx
- add r5.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
- div r5.xyz, r5.xyzx, r0.xyzx
- min r5.xyz, r5.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
- add r5.xyz, -r5.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
- ne r1.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r1.xyzx
- and r1.xyz, r1.xyzx, l(0x3f800000, 0x3f800000, 0x3f800000, 0)
- movc r1.xyz, r4.xyzx, r5.xyzx, r1.xyzx
- movc r2.xyz, r2.wwww, r3.xyzx, r1.xyzx
+ if_nz r2.w
+ max r2.xyz, r0.xyzx, r1.xyzx
+ else
+ eq r3.xyz, r1.xyzx, l(0.000000, 0.000000, 0.000000, 0.000000)
+ eq r4.xyz, r0.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ add r5.xyz, -r0.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ div r1.xyz, r1.xyzx, r5.xyzx
+ min r1.xyz, r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ movc r1.xyz, r4.xyzx, l(1.000000,1.000000,1.000000,0), r1.xyzx
+ movc r2.xyz, r3.xyzx, l(0,0,0,0), r1.xyzx
+ endif
endif
endif
endif
@@ -546,7 +550,7 @@ technique10 SampleTextureForSeparableBlending_1 mul o0.xyz, r0.wwww, r0.xyzx
mov o0.w, r0.w
ret
- // Approximately 56 instruction slots used
+ // Approximately 57 instruction slots used
};
}
@@ -560,7 +564,7 @@ technique10 SampleTextureForSeparableBlending_2 RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -643,7 +647,7 @@ technique10 SampleTextureForSeparableBlending_2 GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -703,9 +707,9 @@ technique10 SampleTextureForSeparableBlending_2 //
ps_2_x
def c1, -7, -8, -9, -10
- def c2, 1, 0, 0.25, 0.5
- def c3, 2, -1, 16, -12
- def c4, 4, 2, 1, 0
+ def c2, 1, 0, -1, 0.25
+ def c3, 0.5, 2, -1, 4
+ def c4, 16, -12, 2, 1
dcl t0
dcl_2d s0
dcl_2d s1
@@ -715,10 +719,10 @@ technique10 SampleTextureForSeparableBlending_2 texld r1, t0, s0
texld r2, t0, s1
rcp r3.w, r2.w
- mad r3.xy, r2.yzzw, -r3.w, c2.z
+ mad r3.xy, r2.yzzw, -r3.w, c2.w
mul r4.xyz, r2, r3.w
- mad r5.xyz, r4, c3.z, c3.w
- mad r5.xyz, r5, r4, c4.x
+ mad r5.xyz, r4, c4.x, c4.y
+ mad r5.xyz, r5, r4, c3.w
mul r5.xyz, r4, r5
rsq r4.w, r4.y
rcp r4.w, r4.w
@@ -726,11 +730,11 @@ technique10 SampleTextureForSeparableBlending_2 mad r4.w, r2.y, -r3.w, r4.w
rcp r3.x, r1.w
mul r6.xyz, r1, r3.x
- mad r7.xyz, r6, c3.x, c3.y
+ mad r7.xyz, r6, c3.y, c3.z
mad r4.w, r7.y, r4.w, r4.y
- mad r8.xyz, r1, -r3.x, c2.w
- mad r9, r2.xyzx, -r3.w, c2.xxxz
- mad r10.xyz, r6, -c4.y, c4.z
+ mad r8.xyz, r1, -r3.x, c3.x
+ mad r9, r2.xyzx, -r3.w, c2.xxxw
+ mad r10.xyz, r6, -c4.z, c4.w
mul r10.xyz, r4, r10
mad r10.xyz, r10, -r9, r4
cmp r11.y, r8.y, r10.y, r4.w
@@ -744,36 +748,40 @@ technique10 SampleTextureForSeparableBlending_2 rcp r4.w, r4.w
cmp r4.w, r9.w, r5.x, r4.w
mad r4.w, r2.x, -r3.w, r4.w
+ mad r2.xyz, r2, r3.w, c2.z
+ mul r2.xyz, r2, r2
mad r4.w, r7.x, r4.w, r4.x
- add r2.xyz, -r7, c2.x
- mad r2.xyz, r9, -r2, c2.x
+ add r3.yzw, -r7.xxyz, c2.x
+ mad r3.yzw, r9.xxyz, -r3, c2.x
cmp r11.x, r8.x, r10.x, r4.w
- mad r3.yzw, r1.xxyz, r3.x, -r4.xxyz
- mad r5.xyz, r1, r3.x, r4
- abs r3.xyz, r3.yzww
- mul r7.xyz, r4, r6
- mad r5.xyz, r7, -c3.x, r5
- cmp r3.xyz, -r0.w, r3, r5
- cmp r3.xyz, -r0.z, r11, r3
- add r5.xyz, r6, r6
- mul r5.xyz, r4, r5
- mul r4.xyz, r4, r4
- cmp r4.xyz, -r4, c2.y, c2.x
- cmp r2.xyz, r8, r5, r2
- cmp r0.yzw, -r0.y, r2.xxyz, r3.xxyz
- rcp r4.w, r6.x
- mad r4.w, r9.x, -r4.w, c2.x
- max r6.w, r4.w, c2.y
- cmp r2.x, -r6.x, r4.x, r6.w
- rcp r6.w, r6.y
- mad r6.w, r9.y, -r6.w, c2.x
- max r3.x, r6.w, c2.y
- cmp r2.y, -r6.y, r4.y, r3.x
- rcp r6.w, r6.z
- mad r6.w, r9.z, -r6.w, c2.x
+ mad r5.xyz, r1, r3.x, -r4
+ mad r7.xyz, r1, r3.x, r4
+ abs r5.xyz, r5
+ mul r10.xyz, r4, r6
+ mad r7.xyz, r10, -c3.y, r7
+ cmp r5.xyz, -r0.w, r5, r7
+ cmp r5.xyz, -r0.z, r11, r5
+ add r7.xyz, r6, r6
+ mul r4.xyz, r4, r7
+ cmp r3.xyz, r8, r4, r3.yzww
+ cmp r0.yzw, -r0.y, r3.xxyz, r5.xxyz
+ rcp r6.w, r6.x
+ mad r6.w, r9.x, -r6.w, c2.x
max r3.x, r6.w, c2.y
- cmp r2.z, -r6.z, r4.z, r3.x
- cmp r0.xyz, -r0.x, r2, r0.yzww
+ mul r3.yzw, r6.xxyz, r6.xxyz
+ cmp r6.w, -r3.y, c2.y, r3.x
+ cmp r4.x, -r2.x, c2.x, r6.w
+ rcp r4.w, r6.y
+ mad r4.w, r9.y, -r4.w, c2.x
+ max r6.w, r4.w, c2.y
+ cmp r4.w, -r3.z, c2.y, r6.w
+ cmp r4.y, -r2.y, c2.x, r4.w
+ rcp r4.w, r6.z
+ mad r4.w, r9.z, -r4.w, c2.x
+ max r6.w, r4.w, c2.y
+ cmp r4.w, -r3.w, c2.y, r6.w
+ cmp r4.z, -r2.z, c2.x, r4.w
+ cmp r0.xyz, -r0.x, r4, r0.yzww
lrp r3.xyz, r2.w, r0, r6
mul r3.w, r2.w, r2.w
cmp r3.w, -r3.w, c2.x, c2.y
@@ -784,7 +792,7 @@ technique10 SampleTextureForSeparableBlending_2 cmp r1.xyz, -r0.w, r0, r1
mov oC0, r1
- // approximately 74 instruction slots used (2 texture, 72 arithmetic)
+ // approximately 78 instruction slots used (2 texture, 76 arithmetic)
ps_4_0
dcl_constantbuffer cb0[1], immediateIndexed
dcl_sampler s0, mode_default
@@ -807,14 +815,14 @@ technique10 SampleTextureForSeparableBlending_2 div r1.xyz, r1.xyzx, r1.wwww
ieq r2.x, cb0[0].x, l(7)
if_nz r2.x
- lt r2.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r0.xyzx
- add r3.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
- div r3.xyz, r3.xyzx, r0.xyzx
- min r3.xyz, r3.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
- add r3.xyz, -r3.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
- ne r4.xyz, l(0.000000, 0.000000, 0.000000, 0.000000), r1.xyzx
- and r4.xyz, r4.xyzx, l(0x3f800000, 0x3f800000, 0x3f800000, 0)
- movc r2.xyz, r2.xyzx, r3.xyzx, r4.xyzx
+ eq r2.xyz, r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ eq r3.xyz, r0.xyzx, l(0.000000, 0.000000, 0.000000, 0.000000)
+ add r4.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ div r4.xyz, r4.xyzx, r0.xyzx
+ min r4.xyz, r4.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ add r4.xyz, -r4.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+ movc r3.xyz, r3.xyzx, l(0,0,0,0), r4.xyzx
+ movc r2.xyz, r2.xyzx, l(1.000000,1.000000,1.000000,0), r3.xyzx
else
ieq r2.w, cb0[0].x, l(8)
if_nz r2.w
@@ -874,7 +882,7 @@ technique10 SampleTextureForNonSeparableBlending RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -957,7 +965,7 @@ technique10 SampleTextureForNonSeparableBlending GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -1435,7 +1443,7 @@ technique10 SampleRadialGradient RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -1552,7 +1560,7 @@ technique10 SampleRadialGradient GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -1704,7 +1712,7 @@ technique10 SampleRadialGradient RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -1821,7 +1829,7 @@ technique10 SampleRadialGradient GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -1948,7 +1956,7 @@ technique10 SampleRadialGradient RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2065,7 +2073,7 @@ technique10 SampleRadialGradient GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2217,7 +2225,7 @@ technique10 SampleRadialGradient RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2334,7 +2342,7 @@ technique10 SampleRadialGradient GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2461,7 +2469,7 @@ technique10 SampleRadialGradient RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2578,7 +2586,7 @@ technique10 SampleRadialGradient GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2730,7 +2738,7 @@ technique10 SampleRadialGradient RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2847,7 +2855,7 @@ technique10 SampleRadialGradient GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -2978,7 +2986,7 @@ technique10 SampleMaskedTexture RasterizerState = TextureRast;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3061,7 +3069,7 @@ technique10 SampleMaskedTexture GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Resource Bindings:
@@ -3142,7 +3150,7 @@ technique10 SampleTextureWithShadow BlendState = ShadowBlendH;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3225,7 +3233,7 @@ technique10 SampleTextureWithShadow GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3377,7 +3385,7 @@ technique10 SampleTextureWithShadow BlendState = ShadowBlendV;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3460,7 +3468,7 @@ technique10 SampleTextureWithShadow GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3609,7 +3617,7 @@ technique10 SampleTextureWithShadow BlendState = ShadowBlendV;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3692,7 +3700,7 @@ technique10 SampleTextureWithShadow GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3857,7 +3865,7 @@ technique10 SampleTextTexture BlendState = bTextBlend;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -3940,7 +3948,7 @@ technique10 SampleTextTexture GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -4037,7 +4045,7 @@ technique10 SampleTextTexture BlendState = bTextBlend;
VertexShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -4120,7 +4128,7 @@ technique10 SampleTextTexture GeometryShader = NULL;
PixelShader = asm {
//
- // Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+ // Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
// Buffer Definitions:
@@ -4227,19 +4235,19 @@ technique10 SampleTextTexture const BYTE d2deffect[] =
{
- 68, 88, 66, 67, 161, 21,
- 0, 226, 106, 112, 70, 241,
- 114, 10, 21, 220, 141, 49,
- 31, 193, 1, 0, 0, 0,
- 190, 19, 1, 0, 1, 0,
+ 68, 88, 66, 67, 116, 210,
+ 237, 43, 26, 169, 147, 99,
+ 62, 90, 128, 241, 238, 193,
+ 236, 181, 1, 0, 0, 0,
+ 242, 19, 1, 0, 1, 0,
0, 0, 36, 0, 0, 0,
- 70, 88, 49, 48, 146, 19,
+ 70, 88, 49, 48, 198, 19,
1, 0, 1, 16, 255, 254,
4, 0, 0, 0, 16, 0,
0, 0, 13, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 8, 0, 0, 0, 10, 7,
+ 8, 0, 0, 0, 62, 7,
1, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
0, 0, 3, 0, 0, 0,
@@ -4456,16 +4464,16 @@ const BYTE d2deffect[] = 0, 0, 0, 83, 97, 109,
112, 108, 101, 84, 101, 120,
116, 117, 114, 101, 0, 80,
- 48, 0, 72, 4, 0, 0,
- 68, 88, 66, 67, 28, 248,
- 40, 83, 2, 166, 203, 194,
- 228, 163, 91, 123, 149, 165,
- 41, 212, 1, 0, 0, 0,
- 72, 4, 0, 0, 6, 0,
+ 48, 0, 68, 4, 0, 0,
+ 68, 88, 66, 67, 77, 85,
+ 167, 240, 56, 56, 155, 78,
+ 125, 96, 49, 253, 103, 100,
+ 22, 62, 1, 0, 0, 0,
+ 68, 4, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
248, 0, 0, 0, 244, 1,
0, 0, 112, 2, 0, 0,
- 164, 3, 0, 0, 216, 3,
+ 160, 3, 0, 0, 212, 3,
0, 0, 65, 111, 110, 57,
184, 0, 0, 0, 184, 0,
0, 0, 0, 2, 254, 255,
@@ -4544,7 +4552,7 @@ const BYTE d2deffect[] = 116, 0, 0, 0, 5, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 4, 0,
- 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -4554,14 +4562,14 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 82, 68, 69, 70, 44, 1,
+ 82, 68, 69, 70, 40, 1,
0, 0, 1, 0, 0, 0,
64, 0, 0, 0, 1, 0,
0, 0, 28, 0, 0, 0,
@@ -4609,97 +4617,93 @@ const BYTE d2deffect[] = 83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
- 171, 171, 73, 83, 71, 78,
- 44, 0, 0, 0, 1, 0,
- 0, 0, 8, 0, 0, 0,
- 32, 0, 0, 0, 0, 0,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 73, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 7, 3, 0, 0,
- 80, 79, 83, 73, 84, 73,
- 79, 78, 0, 171, 171, 171,
- 79, 83, 71, 78, 104, 0,
0, 0, 3, 0, 0, 0,
- 8, 0, 0, 0, 80, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 3, 0,
- 0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 92, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 12, 0, 0, 92, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 7, 3,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 12, 3, 0, 0, 83, 86,
- 95, 80, 111, 115, 105, 116,
- 105, 111, 110, 0, 84, 69,
- 88, 67, 79, 79, 82, 68,
- 0, 171, 171, 171, 232, 4,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 2, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 12, 0, 0,
+ 92, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 212, 2, 0, 0, 68, 88,
- 66, 67, 211, 98, 170, 114,
- 188, 232, 19, 214, 0, 60,
- 65, 88, 34, 156, 69, 131,
- 1, 0, 0, 0, 212, 2,
- 0, 0, 6, 0, 0, 0,
- 56, 0, 0, 0, 164, 0,
- 0, 0, 16, 1, 0, 0,
- 140, 1, 0, 0, 48, 2,
- 0, 0, 160, 2, 0, 0,
- 65, 111, 110, 57, 100, 0,
- 0, 0, 100, 0, 0, 0,
- 0, 2, 255, 255, 60, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 3, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 232, 4, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 212, 2, 0, 0,
+ 68, 88, 66, 67, 17, 106,
+ 69, 218, 119, 68, 79, 85,
+ 211, 176, 27, 183, 77, 210,
+ 131, 41, 1, 0, 0, 0,
+ 212, 2, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 164, 0, 0, 0, 16, 1,
+ 0, 0, 140, 1, 0, 0,
+ 48, 2, 0, 0, 160, 2,
+ 0, 0, 65, 111, 110, 57,
+ 100, 0, 0, 0, 100, 0,
+ 0, 0, 0, 2, 255, 255,
+ 60, 0, 0, 0, 40, 0,
+ 0, 0, 0, 0, 40, 0,
0, 0, 40, 0, 0, 0,
+ 40, 0, 1, 0, 36, 0,
0, 0, 40, 0, 0, 0,
- 40, 0, 0, 0, 40, 0,
- 1, 0, 36, 0, 0, 0,
- 40, 0, 0, 0, 0, 0,
- 1, 2, 255, 255, 31, 0,
- 0, 2, 0, 0, 0, 128,
- 0, 0, 15, 176, 31, 0,
- 0, 2, 0, 0, 0, 144,
- 0, 8, 15, 160, 66, 0,
- 0, 3, 0, 0, 15, 128,
- 0, 0, 228, 176, 0, 8,
- 228, 160, 1, 0, 0, 2,
- 0, 8, 15, 128, 0, 0,
- 228, 128, 255, 255, 0, 0,
- 83, 72, 68, 82, 100, 0,
- 0, 0, 64, 0, 0, 0,
- 25, 0, 0, 0, 90, 0,
- 0, 3, 0, 96, 16, 0,
- 0, 0, 0, 0, 88, 24,
- 0, 4, 0, 112, 16, 0,
- 0, 0, 0, 0, 85, 85,
- 0, 0, 98, 16, 0, 3,
- 50, 16, 16, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 69, 0, 0, 9,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 70, 16, 16, 0,
- 1, 0, 0, 0, 70, 126,
+ 0, 0, 1, 2, 255, 255,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 100, 0, 0, 0, 64, 0,
+ 0, 0, 25, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
16, 0, 0, 0, 0, 0,
- 0, 96, 16, 0, 0, 0,
- 0, 0, 62, 0, 0, 1,
- 83, 84, 65, 84, 116, 0,
- 0, 0, 2, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 69, 0,
+ 0, 9, 242, 32, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -4709,451 +4713,461 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 82, 68,
- 69, 70, 156, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 28, 0, 0, 0, 0, 4,
- 255, 255, 0, 1, 0, 0,
- 105, 0, 0, 0, 92, 0,
- 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 101, 0, 0, 0, 2, 0,
- 0, 0, 5, 0, 0, 0,
- 4, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 1, 0, 0, 0, 12, 0,
- 0, 0, 115, 83, 97, 109,
- 112, 108, 101, 114, 0, 116,
- 101, 120, 0, 77, 105, 99,
- 114, 111, 115, 111, 102, 116,
- 32, 40, 82, 41, 32, 72,
- 76, 83, 76, 32, 83, 104,
- 97, 100, 101, 114, 32, 67,
- 111, 109, 112, 105, 108, 101,
- 114, 32, 57, 46, 51, 48,
- 46, 57, 50, 48, 48, 46,
- 49, 54, 51, 56, 52, 0,
- 73, 83, 71, 78, 104, 0,
- 0, 0, 3, 0, 0, 0,
- 8, 0, 0, 0, 80, 0,
+ 82, 68, 69, 70, 156, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 105, 0, 0, 0,
+ 92, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 92, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 101, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
0, 0, 1, 0, 0, 0,
- 3, 3, 0, 0, 92, 0,
+ 12, 0, 0, 0, 115, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 116, 101, 120, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 73, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
0, 0, 1, 0, 0, 0,
- 12, 0, 0, 0, 83, 86,
- 95, 80, 111, 115, 105, 116,
- 105, 111, 110, 0, 84, 69,
- 88, 67, 79, 79, 82, 68,
- 0, 171, 171, 171, 79, 83,
- 71, 78, 44, 0, 0, 0,
- 1, 0, 0, 0, 8, 0,
- 0, 0, 32, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 15, 0,
- 0, 0, 83, 86, 95, 84,
- 97, 114, 103, 101, 116, 0,
- 171, 171, 72, 9, 0, 0,
- 0, 0, 0, 0, 83, 97,
- 109, 112, 108, 101, 84, 101,
- 120, 116, 117, 114, 101, 70,
- 111, 114, 83, 101, 112, 97,
- 114, 97, 98, 108, 101, 66,
- 108, 101, 110, 100, 105, 110,
- 103, 95, 49, 0, 72, 4,
- 0, 0, 68, 88, 66, 67,
- 28, 248, 40, 83, 2, 166,
- 203, 194, 228, 163, 91, 123,
- 149, 165, 41, 212, 1, 0,
- 0, 0, 72, 4, 0, 0,
- 6, 0, 0, 0, 56, 0,
- 0, 0, 248, 0, 0, 0,
- 244, 1, 0, 0, 112, 2,
- 0, 0, 164, 3, 0, 0,
- 216, 3, 0, 0, 65, 111,
- 110, 57, 184, 0, 0, 0,
- 184, 0, 0, 0, 0, 2,
- 254, 255, 132, 0, 0, 0,
- 52, 0, 0, 0, 1, 0,
- 36, 0, 0, 0, 48, 0,
- 0, 0, 48, 0, 0, 0,
- 36, 0, 1, 0, 48, 0,
0, 0, 0, 0, 3, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 2,
- 254, 255, 81, 0, 0, 5,
- 4, 0, 15, 160, 0, 0,
- 0, 0, 0, 0, 128, 63,
0, 0, 0, 0, 0, 0,
- 0, 0, 31, 0, 0, 2,
- 5, 0, 0, 128, 0, 0,
- 15, 144, 4, 0, 0, 4,
- 0, 0, 3, 224, 0, 0,
- 228, 144, 2, 0, 238, 160,
- 2, 0, 228, 160, 4, 0,
- 0, 4, 0, 0, 12, 224,
- 0, 0, 20, 144, 3, 0,
- 180, 160, 3, 0, 20, 160,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171, 68, 9,
+ 0, 0, 0, 0, 0, 0,
+ 83, 97, 109, 112, 108, 101,
+ 84, 101, 120, 116, 117, 114,
+ 101, 70, 111, 114, 83, 101,
+ 112, 97, 114, 97, 98, 108,
+ 101, 66, 108, 101, 110, 100,
+ 105, 110, 103, 95, 49, 0,
+ 68, 4, 0, 0, 68, 88,
+ 66, 67, 77, 85, 167, 240,
+ 56, 56, 155, 78, 125, 96,
+ 49, 253, 103, 100, 22, 62,
+ 1, 0, 0, 0, 68, 4,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 248, 0,
+ 0, 0, 244, 1, 0, 0,
+ 112, 2, 0, 0, 160, 3,
+ 0, 0, 212, 3, 0, 0,
+ 65, 111, 110, 57, 184, 0,
+ 0, 0, 184, 0, 0, 0,
+ 0, 2, 254, 255, 132, 0,
+ 0, 0, 52, 0, 0, 0,
+ 1, 0, 36, 0, 0, 0,
+ 48, 0, 0, 0, 48, 0,
+ 0, 0, 36, 0, 1, 0,
+ 48, 0, 0, 0, 0, 0,
+ 3, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 254, 255, 81, 0,
+ 0, 5, 4, 0, 15, 160,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 5, 0, 0, 128,
+ 0, 0, 15, 144, 4, 0,
+ 0, 4, 0, 0, 3, 224,
+ 0, 0, 228, 144, 2, 0,
+ 238, 160, 2, 0, 228, 160,
4, 0, 0, 4, 0, 0,
- 3, 128, 0, 0, 228, 144,
- 1, 0, 238, 160, 1, 0,
- 228, 160, 2, 0, 0, 3,
- 0, 0, 3, 192, 0, 0,
- 228, 128, 0, 0, 228, 160,
- 1, 0, 0, 2, 0, 0,
- 12, 192, 4, 0, 68, 160,
- 255, 255, 0, 0, 83, 72,
- 68, 82, 244, 0, 0, 0,
- 64, 0, 1, 0, 61, 0,
- 0, 0, 89, 0, 0, 4,
- 70, 142, 32, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 95, 0, 0, 3, 50, 16,
- 16, 0, 0, 0, 0, 0,
- 103, 0, 0, 4, 242, 32,
- 16, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 101, 0,
- 0, 3, 50, 32, 16, 0,
- 1, 0, 0, 0, 101, 0,
- 0, 3, 194, 32, 16, 0,
- 1, 0, 0, 0, 50, 0,
- 0, 11, 50, 32, 16, 0,
- 0, 0, 0, 0, 70, 16,
+ 12, 224, 0, 0, 20, 144,
+ 3, 0, 180, 160, 3, 0,
+ 20, 160, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 1, 0, 238, 160,
+ 1, 0, 228, 160, 2, 0,
+ 0, 3, 0, 0, 3, 192,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 12, 192, 4, 0,
+ 68, 160, 255, 255, 0, 0,
+ 83, 72, 68, 82, 244, 0,
+ 0, 0, 64, 0, 1, 0,
+ 61, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 95, 0, 0, 3,
+ 50, 16, 16, 0, 0, 0,
+ 0, 0, 103, 0, 0, 4,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 50, 32,
16, 0, 0, 0, 0, 0,
- 230, 138, 32, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
0, 0, 0, 0, 0, 0,
- 70, 128, 32, 0, 0, 0,
+ 0, 0, 70, 128, 32, 0,
0, 0, 0, 0, 0, 0,
- 54, 0, 0, 8, 194, 32,
- 16, 0, 0, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 194, 32, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 128, 63, 50, 0, 0, 11,
- 50, 32, 16, 0, 1, 0,
- 0, 0, 70, 16, 16, 0,
- 0, 0, 0, 0, 230, 138,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 70, 128,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 50, 0,
- 0, 11, 194, 32, 16, 0,
- 1, 0, 0, 0, 6, 20,
+ 0, 0, 128, 63, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
16, 0, 0, 0, 0, 0,
- 166, 142, 32, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 6, 132, 32, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 62, 0, 0, 1, 83, 84,
- 65, 84, 116, 0, 0, 0,
- 5, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 6, 20, 16, 0, 0, 0,
+ 0, 0, 166, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 6, 132, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 82, 68, 69, 70,
- 44, 1, 0, 0, 1, 0,
- 0, 0, 64, 0, 0, 0,
- 1, 0, 0, 0, 28, 0,
- 0, 0, 0, 4, 254, 255,
- 0, 1, 0, 0, 246, 0,
- 0, 0, 60, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 40, 1, 0, 0,
+ 1, 0, 0, 0, 64, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 246, 0, 0, 0, 60, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 99, 98,
- 48, 0, 60, 0, 0, 0,
- 4, 0, 0, 0, 88, 0,
- 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 184, 0, 0, 0,
- 0, 0, 0, 0, 16, 0,
- 0, 0, 2, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 212, 0, 0, 0,
- 16, 0, 0, 0, 16, 0,
- 0, 0, 2, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 222, 0, 0, 0,
- 32, 0, 0, 0, 16, 0,
- 0, 0, 2, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 236, 0, 0, 0,
- 48, 0, 0, 0, 16, 0,
+ 99, 98, 48, 0, 60, 0,
+ 0, 0, 4, 0, 0, 0,
+ 88, 0, 0, 0, 64, 0,
0, 0, 0, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 81, 117, 97, 100,
- 68, 101, 115, 99, 0, 171,
- 171, 171, 1, 0, 3, 0,
- 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
0, 0, 0, 0, 0, 0,
- 84, 101, 120, 67, 111, 111,
- 114, 100, 115, 0, 77, 97,
- 115, 107, 84, 101, 120, 67,
- 111, 111, 114, 100, 115, 0,
- 84, 101, 120, 116, 67, 111,
- 108, 111, 114, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 171, 171, 73, 83,
- 71, 78, 44, 0, 0, 0,
- 1, 0, 0, 0, 8, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 212, 0,
+ 0, 0, 16, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 222, 0,
0, 0, 32, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 236, 0,
+ 0, 0, 48, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 81, 117,
+ 97, 100, 68, 101, 115, 99,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 7, 3,
- 0, 0, 80, 79, 83, 73,
- 84, 73, 79, 78, 0, 171,
- 171, 171, 79, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
+ 0, 0, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 77, 97, 115, 107, 84, 101,
+ 120, 67, 111, 111, 114, 100,
+ 115, 0, 84, 101, 120, 116,
+ 67, 111, 108, 111, 114, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 54,
+ 46, 51, 46, 57, 54, 48,
+ 48, 46, 49, 54, 51, 56,
+ 52, 0, 73, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 0, 0, 7, 3, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 171, 171, 171,
+ 79, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 12, 0, 0,
- 92, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 3, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 76, 12, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 1, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
- 0, 0, 68, 13, 0, 0,
- 68, 88, 66, 67, 245, 249,
- 228, 50, 151, 61, 21, 152,
- 221, 49, 83, 177, 245, 217,
- 117, 158, 1, 0, 0, 0,
- 68, 13, 0, 0, 6, 0,
- 0, 0, 56, 0, 0, 0,
- 128, 4, 0, 0, 184, 10,
- 0, 0, 52, 11, 0, 0,
- 160, 12, 0, 0, 16, 13,
- 0, 0, 65, 111, 110, 57,
- 64, 4, 0, 0, 64, 4,
- 0, 0, 0, 2, 255, 255,
- 8, 4, 0, 0, 56, 0,
- 0, 0, 1, 0, 44, 0,
- 0, 0, 56, 0, 0, 0,
- 56, 0, 2, 0, 36, 0,
+ 3, 12, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 72, 12,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 72, 13, 0, 0, 68, 88,
+ 66, 67, 193, 65, 249, 15,
+ 188, 209, 36, 123, 179, 111,
+ 3, 63, 40, 10, 7, 98,
+ 1, 0, 0, 0, 72, 13,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 172, 4,
+ 0, 0, 188, 10, 0, 0,
+ 56, 11, 0, 0, 164, 12,
+ 0, 0, 20, 13, 0, 0,
+ 65, 111, 110, 57, 108, 4,
+ 0, 0, 108, 4, 0, 0,
+ 0, 2, 255, 255, 52, 4,
0, 0, 56, 0, 0, 0,
- 0, 0, 1, 1, 1, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 3, 0, 0, 0,
- 1, 2, 255, 255, 81, 0,
- 0, 5, 1, 0, 15, 160,
- 0, 0, 128, 191, 0, 0,
- 0, 192, 0, 0, 64, 192,
- 0, 0, 128, 192, 81, 0,
- 0, 5, 2, 0, 15, 160,
- 0, 0, 128, 63, 0, 0,
- 0, 0, 0, 0, 0, 63,
- 0, 0, 0, 192, 81, 0,
- 0, 5, 3, 0, 15, 160,
- 0, 0, 160, 192, 0, 0,
+ 1, 0, 44, 0, 0, 0,
+ 56, 0, 0, 0, 56, 0,
+ 2, 0, 36, 0, 0, 0,
+ 56, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 1, 0, 15, 160, 0, 0,
+ 128, 191, 0, 0, 0, 192,
+ 0, 0, 64, 192, 0, 0,
+ 128, 192, 81, 0, 0, 5,
+ 2, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 192, 81, 0, 0, 5,
+ 3, 0, 15, 160, 0, 0,
+ 160, 192, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 31, 0,
- 0, 2, 0, 0, 0, 128,
- 0, 0, 15, 176, 31, 0,
- 0, 2, 0, 0, 0, 144,
- 0, 8, 15, 160, 31, 0,
- 0, 2, 0, 0, 0, 144,
- 1, 8, 15, 160, 1, 0,
- 0, 2, 0, 0, 8, 128,
- 0, 0, 0, 160, 2, 0,
- 0, 3, 0, 0, 1, 128,
- 0, 0, 255, 128, 3, 0,
- 0, 160, 5, 0, 0, 3,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 1, 0, 0, 2,
+ 0, 0, 8, 128, 0, 0,
+ 0, 160, 2, 0, 0, 3,
0, 0, 1, 128, 0, 0,
- 0, 128, 0, 0, 0, 128,
- 66, 0, 0, 3, 1, 0,
- 15, 128, 0, 0, 228, 176,
- 0, 8, 228, 160, 66, 0,
- 0, 3, 2, 0, 15, 128,
+ 255, 128, 3, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 0, 0, 0, 128, 66, 0,
+ 0, 3, 1, 0, 15, 128,
0, 0, 228, 176, 1, 8,
- 228, 160, 6, 0, 0, 2,
- 0, 0, 2, 128, 2, 0,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 6, 0, 0, 2, 0, 0,
+ 2, 128, 2, 0, 255, 128,
+ 4, 0, 0, 4, 3, 0,
+ 7, 128, 2, 0, 228, 128,
+ 0, 0, 85, 128, 2, 0,
+ 0, 161, 5, 0, 0, 3,
+ 3, 0, 7, 128, 3, 0,
+ 228, 128, 3, 0, 228, 128,
+ 4, 0, 0, 4, 4, 0,
+ 7, 128, 2, 0, 228, 128,
+ 0, 0, 85, 129, 2, 0,
+ 0, 160, 6, 0, 0, 2,
+ 3, 0, 8, 128, 4, 0,
+ 0, 128, 6, 0, 0, 2,
+ 4, 0, 8, 128, 1, 0,
255, 128, 5, 0, 0, 3,
- 3, 0, 7, 128, 0, 0,
- 85, 128, 2, 0, 228, 128,
+ 5, 0, 7, 128, 1, 0,
+ 228, 128, 4, 0, 255, 128,
+ 4, 0, 0, 4, 1, 0,
+ 7, 128, 1, 0, 228, 128,
+ 4, 0, 255, 129, 2, 0,
+ 170, 160, 5, 0, 0, 3,
+ 3, 0, 8, 128, 3, 0,
+ 255, 128, 5, 0, 0, 128,
+ 10, 0, 0, 3, 4, 0,
+ 8, 128, 3, 0, 255, 128,
+ 2, 0, 0, 160, 88, 0,
+ 0, 4, 4, 0, 8, 128,
+ 3, 0, 0, 129, 2, 0,
+ 0, 160, 4, 0, 255, 128,
+ 5, 0, 0, 3, 6, 0,
+ 7, 128, 5, 0, 228, 128,
+ 5, 0, 228, 128, 88, 0,
+ 0, 4, 7, 0, 1, 128,
+ 6, 0, 0, 129, 2, 0,
+ 85, 160, 4, 0, 255, 128,
+ 6, 0, 0, 2, 4, 0,
+ 8, 128, 4, 0, 85, 128,
5, 0, 0, 3, 4, 0,
- 7, 128, 3, 0, 228, 128,
- 3, 0, 228, 128, 88, 0,
- 0, 4, 4, 0, 7, 128,
- 4, 0, 228, 129, 2, 0,
- 85, 160, 2, 0, 0, 160,
- 4, 0, 0, 4, 5, 0,
- 15, 128, 2, 0, 36, 128,
- 0, 0, 85, 129, 2, 0,
- 42, 160, 4, 0, 0, 4,
- 0, 0, 6, 128, 2, 0,
- 228, 128, 0, 0, 85, 129,
- 2, 0, 0, 160, 6, 0,
- 0, 2, 3, 0, 8, 128,
- 1, 0, 255, 128, 5, 0,
- 0, 3, 2, 0, 7, 128,
- 1, 0, 228, 128, 3, 0,
- 255, 128, 6, 0, 0, 2,
- 4, 0, 8, 128, 2, 0,
- 0, 128, 4, 0, 0, 4,
- 4, 0, 8, 128, 5, 0,
- 255, 128, 4, 0, 255, 129,
- 2, 0, 0, 160, 11, 0,
+ 8, 128, 4, 0, 255, 128,
+ 5, 0, 85, 128, 10, 0,
0, 3, 5, 0, 8, 128,
4, 0, 255, 128, 2, 0,
- 85, 160, 88, 0, 0, 4,
- 6, 0, 1, 128, 2, 0,
- 0, 129, 4, 0, 0, 128,
- 5, 0, 255, 128, 6, 0,
- 0, 2, 5, 0, 8, 128,
- 2, 0, 85, 128, 4, 0,
- 0, 4, 5, 0, 8, 128,
- 0, 0, 85, 128, 5, 0,
- 255, 129, 2, 0, 0, 160,
- 11, 0, 0, 3, 6, 0,
- 8, 128, 5, 0, 255, 128,
- 2, 0, 85, 160, 88, 0,
- 0, 4, 6, 0, 2, 128,
- 2, 0, 85, 129, 4, 0,
- 85, 128, 6, 0, 255, 128,
- 6, 0, 0, 2, 5, 0,
- 8, 128, 2, 0, 170, 128,
- 4, 0, 0, 4, 5, 0,
- 8, 128, 0, 0, 170, 128,
- 5, 0, 255, 129, 2, 0,
- 0, 160, 11, 0, 0, 3,
- 6, 0, 8, 128, 5, 0,
- 255, 128, 2, 0, 85, 160,
- 88, 0, 0, 4, 6, 0,
- 4, 128, 2, 0, 170, 129,
- 4, 0, 170, 128, 6, 0,
- 255, 128, 11, 0, 0, 3,
- 4, 0, 7, 128, 2, 0,
- 228, 128, 3, 0, 228, 128,
+ 0, 160, 88, 0, 0, 4,
+ 4, 0, 8, 128, 3, 0,
+ 85, 129, 2, 0, 0, 160,
+ 5, 0, 255, 128, 88, 0,
+ 0, 4, 7, 0, 2, 128,
+ 6, 0, 85, 129, 2, 0,
+ 85, 160, 4, 0, 255, 128,
+ 6, 0, 0, 2, 4, 0,
+ 8, 128, 4, 0, 170, 128,
+ 5, 0, 0, 3, 4, 0,
+ 8, 128, 4, 0, 255, 128,
+ 5, 0, 170, 128, 10, 0,
+ 0, 3, 5, 0, 8, 128,
+ 4, 0, 255, 128, 2, 0,
+ 0, 160, 88, 0, 0, 4,
+ 4, 0, 8, 128, 3, 0,
+ 170, 129, 2, 0, 0, 160,
+ 5, 0, 255, 128, 88, 0,
+ 0, 4, 7, 0, 4, 128,
+ 6, 0, 170, 129, 2, 0,
+ 85, 160, 4, 0, 255, 128,
+ 5, 0, 0, 3, 3, 0,
+ 7, 128, 0, 0, 85, 128,
+ 2, 0, 228, 128, 4, 0,
+ 0, 4, 6, 0, 7, 128,
+ 2, 0, 228, 128, 0, 0,
+ 85, 128, 5, 0, 228, 128,
+ 4, 0, 0, 4, 6, 0,
+ 7, 128, 3, 0, 228, 128,
+ 5, 0, 228, 129, 6, 0,
+ 228, 128, 11, 0, 0, 3,
+ 8, 0, 7, 128, 3, 0,
+ 228, 128, 5, 0, 228, 128,
88, 0, 0, 4, 0, 0,
7, 128, 0, 0, 0, 129,
- 4, 0, 228, 128, 6, 0,
+ 8, 0, 228, 128, 7, 0,
228, 128, 2, 0, 0, 3,
- 4, 0, 15, 128, 0, 0,
+ 7, 0, 15, 128, 0, 0,
255, 128, 1, 0, 228, 160,
- 5, 0, 0, 3, 4, 0,
- 15, 128, 4, 0, 228, 128,
- 4, 0, 228, 128, 10, 0,
- 0, 3, 6, 0, 7, 128,
- 3, 0, 228, 128, 2, 0,
+ 5, 0, 0, 3, 7, 0,
+ 15, 128, 7, 0, 228, 128,
+ 7, 0, 228, 128, 10, 0,
+ 0, 3, 8, 0, 7, 128,
+ 5, 0, 228, 128, 3, 0,
228, 128, 88, 0, 0, 4,
- 0, 0, 7, 128, 4, 0,
- 255, 129, 6, 0, 228, 128,
+ 0, 0, 7, 128, 7, 0,
+ 255, 129, 8, 0, 228, 128,
0, 0, 228, 128, 4, 0,
- 0, 4, 6, 0, 7, 128,
- 3, 0, 228, 128, 2, 0,
+ 0, 4, 8, 0, 7, 128,
+ 5, 0, 228, 128, 2, 0,
255, 161, 2, 0, 0, 161,
- 2, 0, 0, 3, 6, 0,
- 7, 128, 6, 0, 228, 129,
+ 2, 0, 0, 3, 8, 0,
+ 7, 128, 8, 0, 228, 129,
2, 0, 0, 160, 4, 0,
- 0, 4, 7, 0, 7, 128,
- 1, 0, 228, 128, 3, 0,
- 255, 129, 2, 0, 0, 160,
- 4, 0, 0, 4, 8, 0,
- 7, 128, 1, 0, 228, 128,
- 3, 0, 255, 128, 3, 0,
- 228, 128, 4, 0, 0, 4,
- 8, 0, 7, 128, 2, 0,
- 228, 128, 3, 0, 228, 129,
- 8, 0, 228, 128, 4, 0,
- 0, 4, 6, 0, 7, 128,
- 7, 0, 228, 128, 6, 0,
+ 0, 4, 4, 0, 7, 128,
+ 4, 0, 228, 128, 8, 0,
228, 129, 2, 0, 0, 160,
- 2, 0, 0, 3, 7, 0,
- 7, 128, 3, 0, 228, 128,
- 3, 0, 228, 128, 5, 0,
- 0, 3, 3, 0, 7, 128,
- 3, 0, 228, 128, 2, 0,
- 228, 128, 5, 0, 0, 3,
- 7, 0, 7, 128, 2, 0,
- 228, 128, 7, 0, 228, 128,
- 88, 0, 0, 4, 5, 0,
+ 2, 0, 0, 3, 8, 0,
7, 128, 5, 0, 228, 128,
- 7, 0, 228, 128, 6, 0,
+ 5, 0, 228, 128, 5, 0,
+ 0, 3, 5, 0, 7, 128,
+ 5, 0, 228, 128, 3, 0,
+ 228, 128, 5, 0, 0, 3,
+ 8, 0, 7, 128, 3, 0,
+ 228, 128, 8, 0, 228, 128,
+ 88, 0, 0, 4, 1, 0,
+ 7, 128, 1, 0, 228, 128,
+ 8, 0, 228, 128, 4, 0,
228, 128, 88, 0, 0, 4,
- 0, 0, 7, 128, 4, 0,
- 170, 129, 5, 0, 228, 128,
+ 0, 0, 7, 128, 7, 0,
+ 170, 129, 1, 0, 228, 128,
0, 0, 228, 128, 88, 0,
0, 4, 0, 0, 7, 128,
- 4, 0, 85, 129, 8, 0,
+ 7, 0, 85, 129, 6, 0,
228, 128, 0, 0, 228, 128,
88, 0, 0, 4, 0, 0,
- 7, 128, 4, 0, 0, 129,
- 3, 0, 228, 128, 0, 0,
+ 7, 128, 7, 0, 0, 129,
+ 5, 0, 228, 128, 0, 0,
228, 128, 18, 0, 0, 4,
- 3, 0, 7, 128, 2, 0,
+ 4, 0, 7, 128, 1, 0,
255, 128, 0, 0, 228, 128,
- 2, 0, 228, 128, 5, 0,
- 0, 3, 3, 0, 8, 128,
- 2, 0, 255, 128, 2, 0,
+ 3, 0, 228, 128, 5, 0,
+ 0, 3, 4, 0, 8, 128,
+ 1, 0, 255, 128, 1, 0,
255, 128, 88, 0, 0, 4,
- 3, 0, 8, 128, 3, 0,
+ 4, 0, 8, 128, 4, 0,
255, 129, 2, 0, 0, 160,
2, 0, 85, 160, 5, 0,
0, 3, 0, 0, 7, 128,
- 1, 0, 255, 128, 3, 0,
+ 2, 0, 255, 128, 4, 0,
228, 128, 5, 0, 0, 3,
- 0, 0, 8, 128, 1, 0,
- 255, 128, 1, 0, 255, 128,
+ 0, 0, 8, 128, 2, 0,
+ 255, 128, 2, 0, 255, 128,
88, 0, 0, 4, 0, 0,
8, 128, 0, 0, 255, 129,
2, 0, 0, 160, 2, 0,
85, 160, 2, 0, 0, 3,
- 0, 0, 8, 128, 3, 0,
+ 0, 0, 8, 128, 4, 0,
255, 128, 0, 0, 255, 128,
- 88, 0, 0, 4, 1, 0,
+ 88, 0, 0, 4, 2, 0,
7, 128, 0, 0, 255, 129,
- 0, 0, 228, 128, 1, 0,
+ 0, 0, 228, 128, 2, 0,
228, 128, 1, 0, 0, 2,
- 0, 8, 15, 128, 1, 0,
+ 0, 8, 15, 128, 2, 0,
228, 128, 255, 255, 0, 0,
- 83, 72, 68, 82, 48, 6,
+ 83, 72, 68, 82, 8, 6,
0, 0, 64, 0, 0, 0,
- 140, 1, 0, 0, 89, 0,
+ 130, 1, 0, 0, 89, 0,
0, 4, 70, 142, 32, 0,
0, 0, 0, 0, 1, 0,
0, 0, 90, 0, 0, 3,
@@ -5326,1288 +5340,1296 @@ const BYTE d2deffect[] = 10, 128, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 64, 0, 0, 5, 0,
+ 0, 0, 31, 0, 4, 3,
+ 58, 0, 16, 0, 2, 0,
0, 0, 52, 0, 0, 7,
- 114, 0, 16, 0, 3, 0,
+ 114, 0, 16, 0, 2, 0,
0, 0, 70, 2, 16, 0,
0, 0, 0, 0, 70, 2,
16, 0, 1, 0, 0, 0,
- 49, 0, 0, 10, 114, 0,
- 16, 0, 4, 0, 0, 0,
+ 18, 0, 0, 1, 24, 0,
+ 0, 10, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
2, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 24, 0, 0, 10,
+ 114, 0, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 11, 114, 0, 16, 0,
- 5, 0, 0, 0, 70, 2,
- 16, 128, 65, 0, 0, 0,
- 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 2, 64,
0, 0, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 0, 0,
- 14, 0, 0, 7, 114, 0,
- 16, 0, 5, 0, 0, 0,
- 70, 2, 16, 0, 5, 0,
- 0, 0, 70, 2, 16, 0,
- 0, 0, 0, 0, 51, 0,
- 0, 10, 114, 0, 16, 0,
- 5, 0, 0, 0, 70, 2,
+ 0, 0, 0, 11, 114, 0,
16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
2, 64, 0, 0, 0, 0,
128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
- 0, 0, 0, 0, 0, 11,
- 114, 0, 16, 0, 5, 0,
- 0, 0, 70, 2, 16, 128,
- 65, 0, 0, 0, 5, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 5, 0, 0, 0,
+ 51, 0, 0, 10, 114, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
0, 0, 2, 64, 0, 0,
0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63,
- 0, 0, 0, 0, 57, 0,
- 0, 10, 114, 0, 16, 0,
- 1, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 1, 0, 0, 10,
- 114, 0, 16, 0, 1, 0,
- 0, 0, 70, 2, 16, 0,
- 1, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 128, 63,
+ 0, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
- 128, 63, 0, 0, 0, 0,
- 55, 0, 0, 9, 114, 0,
- 16, 0, 1, 0, 0, 0,
- 70, 2, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
- 5, 0, 0, 0, 70, 2,
- 16, 0, 1, 0, 0, 0,
- 55, 0, 0, 9, 114, 0,
- 16, 0, 2, 0, 0, 0,
- 246, 15, 16, 0, 2, 0,
+ 1, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 70, 2, 16, 0,
- 3, 0, 0, 0, 70, 2,
- 16, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 21, 0,
+ 0, 1, 21, 0, 0, 1,
21, 0, 0, 1, 21, 0,
0, 1, 21, 0, 0, 1,
- 21, 0, 0, 1, 0, 0,
- 0, 8, 18, 0, 16, 0,
- 1, 0, 0, 0, 58, 0,
- 16, 128, 65, 0, 0, 0,
- 1, 0, 0, 0, 1, 64,
- 0, 0, 0, 0, 128, 63,
- 56, 0, 0, 7, 226, 0,
+ 0, 0, 0, 8, 18, 0,
16, 0, 1, 0, 0, 0,
- 246, 15, 16, 0, 1, 0,
- 0, 0, 6, 9, 16, 0,
- 2, 0, 0, 0, 50, 0,
- 0, 9, 114, 0, 16, 0,
- 0, 0, 0, 0, 6, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 56, 0, 0, 7,
+ 226, 0, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 6, 9,
+ 16, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 150, 7,
16, 0, 1, 0, 0, 0,
- 70, 2, 16, 0, 0, 0,
- 0, 0, 150, 7, 16, 0,
- 1, 0, 0, 0, 56, 0,
- 0, 7, 114, 32, 16, 0,
- 0, 0, 0, 0, 246, 15,
+ 56, 0, 0, 7, 114, 32,
16, 0, 0, 0, 0, 0,
- 70, 2, 16, 0, 0, 0,
- 0, 0, 54, 0, 0, 5,
- 130, 32, 16, 0, 0, 0,
- 0, 0, 58, 0, 16, 0,
- 0, 0, 0, 0, 62, 0,
- 0, 1, 83, 84, 65, 84,
- 116, 0, 0, 0, 56, 0,
- 0, 0, 7, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
- 0, 0, 21, 0, 0, 0,
- 5, 0, 0, 0, 2, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 57, 0, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 25, 0,
+ 0, 0, 5, 0, 0, 0,
+ 1, 0, 0, 0, 7, 0,
0, 0, 6, 0, 0, 0,
- 5, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 20, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 82, 68, 69, 70, 100, 1,
- 0, 0, 1, 0, 0, 0,
- 232, 0, 0, 0, 5, 0,
- 0, 0, 28, 0, 0, 0,
- 0, 4, 255, 255, 0, 1,
- 0, 0, 48, 1, 0, 0,
- 188, 0, 0, 0, 3, 0,
+ 0, 0, 82, 68, 69, 70,
+ 100, 1, 0, 0, 1, 0,
+ 0, 0, 232, 0, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 48, 1,
+ 0, 0, 188, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 197, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 197, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 209, 0,
- 0, 0, 2, 0, 0, 0,
- 5, 0, 0, 0, 4, 0,
- 0, 0, 255, 255, 255, 255,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 12, 0, 0, 0,
- 213, 0, 0, 0, 2, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 209, 0, 0, 0, 2, 0,
0, 0, 5, 0, 0, 0,
4, 0, 0, 0, 255, 255,
- 255, 255, 1, 0, 0, 0,
+ 255, 255, 0, 0, 0, 0,
1, 0, 0, 0, 12, 0,
- 0, 0, 220, 0, 0, 0,
+ 0, 0, 213, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 220, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 115, 83,
- 97, 109, 112, 108, 101, 114,
- 0, 115, 66, 99, 107, 83,
- 97, 109, 112, 108, 101, 114,
- 0, 116, 101, 120, 0, 98,
- 99, 107, 116, 101, 120, 0,
- 36, 71, 108, 111, 98, 97,
- 108, 115, 0, 171, 171, 171,
- 220, 0, 0, 0, 1, 0,
- 0, 0, 0, 1, 0, 0,
- 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 24, 1, 0, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 2, 0, 0, 0, 32, 1,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 115, 66, 99,
+ 107, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 101, 120,
+ 0, 98, 99, 107, 116, 101,
+ 120, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 171, 171, 220, 0, 0, 0,
+ 1, 0, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 98, 108, 101, 110, 100, 111,
- 112, 0, 0, 0, 19, 0,
- 1, 0, 1, 0, 0, 0,
+ 0, 0, 24, 1, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 32, 1, 0, 0, 0, 0,
+ 0, 0, 98, 108, 101, 110,
+ 100, 111, 112, 0, 0, 0,
+ 19, 0, 1, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 77, 105, 99, 114, 111, 115,
- 111, 102, 116, 32, 40, 82,
- 41, 32, 72, 76, 83, 76,
- 32, 83, 104, 97, 100, 101,
- 114, 32, 67, 111, 109, 112,
- 105, 108, 101, 114, 32, 57,
- 46, 51, 48, 46, 57, 50,
- 48, 48, 46, 49, 54, 51,
- 56, 52, 0, 171, 73, 83,
- 71, 78, 104, 0, 0, 0,
- 3, 0, 0, 0, 8, 0,
- 0, 0, 80, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 73, 83, 71, 78, 104, 0,
0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 15, 0,
- 0, 0, 92, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 3, 0, 0, 0,
- 1, 0, 0, 0, 3, 3,
- 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171, 164, 16, 0, 0,
+ 0, 0, 0, 0, 83, 97,
+ 109, 112, 108, 101, 84, 101,
+ 120, 116, 117, 114, 101, 70,
+ 111, 114, 83, 101, 112, 97,
+ 114, 97, 98, 108, 101, 66,
+ 108, 101, 110, 100, 105, 110,
+ 103, 95, 50, 0, 68, 4,
+ 0, 0, 68, 88, 66, 67,
+ 77, 85, 167, 240, 56, 56,
+ 155, 78, 125, 96, 49, 253,
+ 103, 100, 22, 62, 1, 0,
+ 0, 0, 68, 4, 0, 0,
+ 6, 0, 0, 0, 56, 0,
+ 0, 0, 248, 0, 0, 0,
+ 244, 1, 0, 0, 112, 2,
+ 0, 0, 160, 3, 0, 0,
+ 212, 3, 0, 0, 65, 111,
+ 110, 57, 184, 0, 0, 0,
+ 184, 0, 0, 0, 0, 2,
+ 254, 255, 132, 0, 0, 0,
+ 52, 0, 0, 0, 1, 0,
+ 36, 0, 0, 0, 48, 0,
+ 0, 0, 48, 0, 0, 0,
+ 36, 0, 1, 0, 48, 0,
+ 0, 0, 0, 0, 3, 0,
1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 254, 255, 81, 0, 0, 5,
+ 4, 0, 15, 160, 0, 0,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 5, 0, 0, 128, 0, 0,
+ 15, 144, 4, 0, 0, 4,
+ 0, 0, 3, 224, 0, 0,
+ 228, 144, 2, 0, 238, 160,
+ 2, 0, 228, 160, 4, 0,
+ 0, 4, 0, 0, 12, 224,
+ 0, 0, 20, 144, 3, 0,
+ 180, 160, 3, 0, 20, 160,
+ 4, 0, 0, 4, 0, 0,
+ 3, 128, 0, 0, 228, 144,
+ 1, 0, 238, 160, 1, 0,
+ 228, 160, 2, 0, 0, 3,
+ 0, 0, 3, 192, 0, 0,
+ 228, 128, 0, 0, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 12, 192, 4, 0, 68, 160,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 244, 0, 0, 0,
+ 64, 0, 1, 0, 61, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
0, 0, 3, 0, 0, 0,
- 1, 0, 0, 0, 12, 0,
- 0, 0, 83, 86, 95, 80,
- 111, 115, 105, 116, 105, 111,
- 110, 0, 84, 69, 88, 67,
- 79, 79, 82, 68, 0, 171,
- 171, 171, 79, 83, 71, 78,
- 44, 0, 0, 0, 1, 0,
- 0, 0, 8, 0, 0, 0,
- 32, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 50, 16,
+ 16, 0, 0, 0, 0, 0,
+ 103, 0, 0, 4, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 50, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 194, 32, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 83, 86, 95, 84, 97, 114,
- 103, 101, 116, 0, 171, 171,
- 172, 16, 0, 0, 0, 0,
- 0, 0, 83, 97, 109, 112,
- 108, 101, 84, 101, 120, 116,
- 117, 114, 101, 70, 111, 114,
- 83, 101, 112, 97, 114, 97,
- 98, 108, 101, 66, 108, 101,
- 110, 100, 105, 110, 103, 95,
- 50, 0, 72, 4, 0, 0,
- 68, 88, 66, 67, 28, 248,
- 40, 83, 2, 166, 203, 194,
- 228, 163, 91, 123, 149, 165,
- 41, 212, 1, 0, 0, 0,
- 72, 4, 0, 0, 6, 0,
- 0, 0, 56, 0, 0, 0,
- 248, 0, 0, 0, 244, 1,
- 0, 0, 112, 2, 0, 0,
- 164, 3, 0, 0, 216, 3,
- 0, 0, 65, 111, 110, 57,
- 184, 0, 0, 0, 184, 0,
- 0, 0, 0, 2, 254, 255,
- 132, 0, 0, 0, 52, 0,
- 0, 0, 1, 0, 36, 0,
- 0, 0, 48, 0, 0, 0,
- 48, 0, 0, 0, 36, 0,
- 1, 0, 48, 0, 0, 0,
- 0, 0, 3, 0, 1, 0,
+ 70, 128, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 2, 254, 255,
- 81, 0, 0, 5, 4, 0,
- 15, 160, 0, 0, 0, 0,
- 0, 0, 128, 63, 0, 0,
+ 54, 0, 0, 8, 194, 32,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 31, 0, 0, 2, 5, 0,
- 0, 128, 0, 0, 15, 144,
- 4, 0, 0, 4, 0, 0,
- 3, 224, 0, 0, 228, 144,
- 2, 0, 238, 160, 2, 0,
- 228, 160, 4, 0, 0, 4,
- 0, 0, 12, 224, 0, 0,
- 20, 144, 3, 0, 180, 160,
- 3, 0, 20, 160, 4, 0,
- 0, 4, 0, 0, 3, 128,
- 0, 0, 228, 144, 1, 0,
- 238, 160, 1, 0, 228, 160,
- 2, 0, 0, 3, 0, 0,
- 3, 192, 0, 0, 228, 128,
- 0, 0, 228, 160, 1, 0,
- 0, 2, 0, 0, 12, 192,
- 4, 0, 68, 160, 255, 255,
- 0, 0, 83, 72, 68, 82,
- 244, 0, 0, 0, 64, 0,
- 1, 0, 61, 0, 0, 0,
- 89, 0, 0, 4, 70, 142,
- 32, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 95, 0,
- 0, 3, 50, 16, 16, 0,
- 0, 0, 0, 0, 103, 0,
- 0, 4, 242, 32, 16, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 50, 0, 0, 11,
50, 32, 16, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
- 194, 32, 16, 0, 1, 0,
- 0, 0, 50, 0, 0, 11,
- 50, 32, 16, 0, 0, 0,
0, 0, 70, 16, 16, 0,
0, 0, 0, 0, 230, 138,
32, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 70, 128,
+ 1, 0, 0, 0, 70, 128,
32, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 54, 0,
- 0, 8, 194, 32, 16, 0,
- 0, 0, 0, 0, 2, 64,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 194, 32, 16, 0,
+ 1, 0, 0, 0, 6, 20,
+ 16, 0, 0, 0, 0, 0,
+ 166, 142, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 6, 132, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 5, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 128, 63,
- 50, 0, 0, 11, 50, 32,
- 16, 0, 1, 0, 0, 0,
- 70, 16, 16, 0, 0, 0,
- 0, 0, 230, 138, 32, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 70, 128, 32, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 50, 0, 0, 11,
- 194, 32, 16, 0, 1, 0,
- 0, 0, 6, 20, 16, 0,
- 0, 0, 0, 0, 166, 142,
- 32, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 6, 132,
- 32, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 62, 0,
- 0, 1, 83, 84, 65, 84,
- 116, 0, 0, 0, 5, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 82, 68, 69, 70,
+ 40, 1, 0, 0, 1, 0,
+ 0, 0, 64, 0, 0, 0,
+ 1, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 254, 255,
+ 0, 1, 0, 0, 246, 0,
+ 0, 0, 60, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 82, 68, 69, 70, 44, 1,
0, 0, 1, 0, 0, 0,
- 64, 0, 0, 0, 1, 0,
- 0, 0, 28, 0, 0, 0,
- 0, 4, 254, 255, 0, 1,
- 0, 0, 246, 0, 0, 0,
- 60, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 99, 98, 48, 0,
- 60, 0, 0, 0, 4, 0,
- 0, 0, 88, 0, 0, 0,
- 64, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 184, 0, 0, 0, 0, 0,
- 0, 0, 16, 0, 0, 0,
- 2, 0, 0, 0, 196, 0,
- 0, 0, 0, 0, 0, 0,
- 212, 0, 0, 0, 16, 0,
- 0, 0, 16, 0, 0, 0,
- 2, 0, 0, 0, 196, 0,
+ 0, 0, 0, 0, 99, 98,
+ 48, 0, 60, 0, 0, 0,
+ 4, 0, 0, 0, 88, 0,
+ 0, 0, 64, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 222, 0, 0, 0, 32, 0,
- 0, 0, 16, 0, 0, 0,
- 2, 0, 0, 0, 196, 0,
+ 0, 0, 184, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 212, 0, 0, 0,
+ 16, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 222, 0, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 236, 0, 0, 0,
+ 48, 0, 0, 0, 16, 0,
0, 0, 0, 0, 0, 0,
- 236, 0, 0, 0, 48, 0,
- 0, 0, 16, 0, 0, 0,
- 0, 0, 0, 0, 196, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 81, 117, 97, 100,
+ 68, 101, 115, 99, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 81, 117, 97, 100, 68, 101,
- 115, 99, 0, 171, 171, 171,
- 1, 0, 3, 0, 1, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 84, 101,
- 120, 67, 111, 111, 114, 100,
- 115, 0, 77, 97, 115, 107,
84, 101, 120, 67, 111, 111,
- 114, 100, 115, 0, 84, 101,
- 120, 116, 67, 111, 108, 111,
- 114, 0, 77, 105, 99, 114,
- 111, 115, 111, 102, 116, 32,
- 40, 82, 41, 32, 72, 76,
- 83, 76, 32, 83, 104, 97,
- 100, 101, 114, 32, 67, 111,
- 109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
- 171, 171, 73, 83, 71, 78,
- 44, 0, 0, 0, 1, 0,
- 0, 0, 8, 0, 0, 0,
- 32, 0, 0, 0, 0, 0,
+ 114, 100, 115, 0, 77, 97,
+ 115, 107, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 84, 101, 120, 116, 67, 111,
+ 108, 111, 114, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 73, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 7, 3, 0, 0,
- 80, 79, 83, 73, 84, 73,
- 79, 78, 0, 171, 171, 171,
- 79, 83, 71, 78, 104, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 3, 0, 0, 0,
- 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 3,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 28, 30, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 88, 17,
+ 0, 0, 68, 88, 66, 67,
+ 62, 116, 36, 238, 73, 63,
+ 158, 95, 222, 192, 91, 113,
+ 112, 55, 55, 145, 1, 0,
+ 0, 0, 88, 17, 0, 0,
+ 6, 0, 0, 0, 56, 0,
+ 0, 0, 88, 6, 0, 0,
+ 204, 14, 0, 0, 72, 15,
+ 0, 0, 180, 16, 0, 0,
+ 36, 17, 0, 0, 65, 111,
+ 110, 57, 24, 6, 0, 0,
+ 24, 6, 0, 0, 0, 2,
+ 255, 255, 224, 5, 0, 0,
+ 56, 0, 0, 0, 1, 0,
+ 44, 0, 0, 0, 56, 0,
+ 0, 0, 56, 0, 2, 0,
+ 36, 0, 0, 0, 56, 0,
+ 0, 0, 0, 0, 1, 1,
+ 1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 3, 0,
+ 0, 0, 1, 2, 255, 255,
+ 81, 0, 0, 5, 1, 0,
+ 15, 160, 0, 0, 224, 192,
+ 0, 0, 0, 193, 0, 0,
+ 16, 193, 0, 0, 32, 193,
+ 81, 0, 0, 5, 2, 0,
+ 15, 160, 0, 0, 128, 63,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 92, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 12, 0, 0, 92, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 0, 0,
- 12, 3, 0, 0, 83, 86,
- 95, 80, 111, 115, 105, 116,
- 105, 111, 110, 0, 84, 69,
- 88, 67, 79, 79, 82, 68,
- 0, 171, 171, 171, 32, 30,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 2, 0,
- 0, 0, 0, 0, 0, 0,
- 248, 16, 0, 0, 68, 88,
- 66, 67, 145, 189, 134, 89,
- 67, 31, 14, 169, 255, 63,
- 144, 149, 250, 13, 86, 17,
- 1, 0, 0, 0, 248, 16,
- 0, 0, 6, 0, 0, 0,
- 56, 0, 0, 0, 12, 6,
- 0, 0, 108, 14, 0, 0,
- 232, 14, 0, 0, 84, 16,
- 0, 0, 196, 16, 0, 0,
- 65, 111, 110, 57, 204, 5,
- 0, 0, 204, 5, 0, 0,
- 0, 2, 255, 255, 148, 5,
- 0, 0, 56, 0, 0, 0,
- 1, 0, 44, 0, 0, 0,
- 56, 0, 0, 0, 56, 0,
- 2, 0, 36, 0, 0, 0,
- 56, 0, 0, 0, 0, 0,
- 1, 1, 1, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 0, 0, 0, 1, 2,
- 255, 255, 81, 0, 0, 5,
- 1, 0, 15, 160, 0, 0,
- 224, 192, 0, 0, 0, 193,
- 0, 0, 16, 193, 0, 0,
- 32, 193, 81, 0, 0, 5,
- 2, 0, 15, 160, 0, 0,
- 128, 63, 0, 0, 0, 0,
- 0, 0, 128, 62, 0, 0,
- 0, 63, 81, 0, 0, 5,
- 3, 0, 15, 160, 0, 0,
- 0, 64, 0, 0, 128, 191,
- 0, 0, 128, 65, 0, 0,
- 64, 193, 81, 0, 0, 5,
- 4, 0, 15, 160, 0, 0,
- 128, 64, 0, 0, 0, 64,
- 0, 0, 128, 63, 0, 0,
- 0, 0, 31, 0, 0, 2,
- 0, 0, 0, 128, 0, 0,
- 15, 176, 31, 0, 0, 2,
- 0, 0, 0, 144, 0, 8,
- 15, 160, 31, 0, 0, 2,
- 0, 0, 0, 144, 1, 8,
- 15, 160, 1, 0, 0, 2,
- 0, 0, 8, 128, 0, 0,
- 0, 160, 2, 0, 0, 3,
- 0, 0, 15, 128, 0, 0,
- 255, 128, 1, 0, 228, 160,
- 5, 0, 0, 3, 0, 0,
- 15, 128, 0, 0, 228, 128,
- 0, 0, 228, 128, 66, 0,
- 0, 3, 1, 0, 15, 128,
- 0, 0, 228, 176, 0, 8,
- 228, 160, 66, 0, 0, 3,
- 2, 0, 15, 128, 0, 0,
- 228, 176, 1, 8, 228, 160,
- 6, 0, 0, 2, 3, 0,
- 8, 128, 2, 0, 255, 128,
- 4, 0, 0, 4, 3, 0,
- 3, 128, 2, 0, 233, 128,
- 3, 0, 255, 129, 2, 0,
- 170, 160, 5, 0, 0, 3,
- 4, 0, 7, 128, 2, 0,
- 228, 128, 3, 0, 255, 128,
+ 128, 191, 0, 0, 128, 62,
+ 81, 0, 0, 5, 3, 0,
+ 15, 160, 0, 0, 0, 63,
+ 0, 0, 0, 64, 0, 0,
+ 128, 191, 0, 0, 128, 64,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 0, 0, 128, 65,
+ 0, 0, 64, 193, 0, 0,
+ 0, 64, 0, 0, 128, 63,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 0, 0, 0, 160,
+ 2, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 255, 128,
+ 1, 0, 228, 160, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 0, 0,
+ 228, 128, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 6, 0,
+ 0, 2, 3, 0, 8, 128,
+ 2, 0, 255, 128, 4, 0,
+ 0, 4, 3, 0, 3, 128,
+ 2, 0, 233, 128, 3, 0,
+ 255, 129, 2, 0, 255, 160,
+ 5, 0, 0, 3, 4, 0,
+ 7, 128, 2, 0, 228, 128,
+ 3, 0, 255, 128, 4, 0,
+ 0, 4, 5, 0, 7, 128,
+ 4, 0, 228, 128, 4, 0,
+ 0, 160, 4, 0, 85, 160,
4, 0, 0, 4, 5, 0,
+ 7, 128, 5, 0, 228, 128,
+ 4, 0, 228, 128, 3, 0,
+ 255, 160, 5, 0, 0, 3,
+ 5, 0, 7, 128, 4, 0,
+ 228, 128, 5, 0, 228, 128,
+ 7, 0, 0, 2, 4, 0,
+ 8, 128, 4, 0, 85, 128,
+ 6, 0, 0, 2, 4, 0,
+ 8, 128, 4, 0, 255, 128,
+ 88, 0, 0, 4, 4, 0,
+ 8, 128, 3, 0, 0, 128,
+ 5, 0, 85, 128, 4, 0,
+ 255, 128, 4, 0, 0, 4,
+ 4, 0, 8, 128, 2, 0,
+ 85, 128, 3, 0, 255, 129,
+ 4, 0, 255, 128, 6, 0,
+ 0, 2, 3, 0, 1, 128,
+ 1, 0, 255, 128, 5, 0,
+ 0, 3, 6, 0, 7, 128,
+ 1, 0, 228, 128, 3, 0,
+ 0, 128, 4, 0, 0, 4,
+ 7, 0, 7, 128, 6, 0,
+ 228, 128, 3, 0, 85, 160,
+ 3, 0, 170, 160, 4, 0,
+ 0, 4, 4, 0, 8, 128,
+ 7, 0, 85, 128, 4, 0,
+ 255, 128, 4, 0, 85, 128,
+ 4, 0, 0, 4, 8, 0,
+ 7, 128, 1, 0, 228, 128,
+ 3, 0, 0, 129, 3, 0,
+ 0, 160, 4, 0, 0, 4,
+ 9, 0, 15, 128, 2, 0,
+ 36, 128, 3, 0, 255, 129,
+ 2, 0, 192, 160, 4, 0,
+ 0, 4, 10, 0, 7, 128,
+ 6, 0, 228, 128, 4, 0,
+ 170, 161, 4, 0, 255, 160,
+ 5, 0, 0, 3, 10, 0,
7, 128, 4, 0, 228, 128,
- 3, 0, 170, 160, 3, 0,
- 255, 160, 4, 0, 0, 4,
- 5, 0, 7, 128, 5, 0,
- 228, 128, 4, 0, 228, 128,
- 4, 0, 0, 160, 5, 0,
- 0, 3, 5, 0, 7, 128,
- 4, 0, 228, 128, 5, 0,
- 228, 128, 7, 0, 0, 2,
+ 10, 0, 228, 128, 4, 0,
+ 0, 4, 10, 0, 7, 128,
+ 10, 0, 228, 128, 9, 0,
+ 228, 129, 4, 0, 228, 128,
+ 88, 0, 0, 4, 11, 0,
+ 2, 128, 8, 0, 85, 128,
+ 10, 0, 85, 128, 4, 0,
+ 255, 128, 7, 0, 0, 2,
4, 0, 8, 128, 4, 0,
- 85, 128, 6, 0, 0, 2,
+ 170, 128, 6, 0, 0, 2,
4, 0, 8, 128, 4, 0,
255, 128, 88, 0, 0, 4,
4, 0, 8, 128, 3, 0,
- 0, 128, 5, 0, 85, 128,
+ 85, 128, 5, 0, 170, 128,
4, 0, 255, 128, 4, 0,
0, 4, 4, 0, 8, 128,
- 2, 0, 85, 128, 3, 0,
+ 2, 0, 170, 128, 3, 0,
255, 129, 4, 0, 255, 128,
- 6, 0, 0, 2, 3, 0,
- 1, 128, 1, 0, 255, 128,
- 5, 0, 0, 3, 6, 0,
- 7, 128, 1, 0, 228, 128,
- 3, 0, 0, 128, 4, 0,
- 0, 4, 7, 0, 7, 128,
- 6, 0, 228, 128, 3, 0,
- 0, 160, 3, 0, 85, 160,
4, 0, 0, 4, 4, 0,
- 8, 128, 7, 0, 85, 128,
+ 8, 128, 7, 0, 170, 128,
4, 0, 255, 128, 4, 0,
- 85, 128, 4, 0, 0, 4,
- 8, 0, 7, 128, 1, 0,
- 228, 128, 3, 0, 0, 129,
- 2, 0, 255, 160, 4, 0,
- 0, 4, 9, 0, 15, 128,
- 2, 0, 36, 128, 3, 0,
- 255, 129, 2, 0, 128, 160,
- 4, 0, 0, 4, 10, 0,
- 7, 128, 6, 0, 228, 128,
- 4, 0, 85, 161, 4, 0,
- 170, 160, 5, 0, 0, 3,
- 10, 0, 7, 128, 4, 0,
- 228, 128, 10, 0, 228, 128,
- 4, 0, 0, 4, 10, 0,
- 7, 128, 10, 0, 228, 128,
- 9, 0, 228, 129, 4, 0,
- 228, 128, 88, 0, 0, 4,
- 11, 0, 2, 128, 8, 0,
- 85, 128, 10, 0, 85, 128,
+ 170, 128, 88, 0, 0, 4,
+ 11, 0, 4, 128, 8, 0,
+ 170, 128, 10, 0, 170, 128,
4, 0, 255, 128, 7, 0,
0, 2, 4, 0, 8, 128,
- 4, 0, 170, 128, 6, 0,
+ 4, 0, 0, 128, 6, 0,
0, 2, 4, 0, 8, 128,
4, 0, 255, 128, 88, 0,
0, 4, 4, 0, 8, 128,
- 3, 0, 85, 128, 5, 0,
- 170, 128, 4, 0, 255, 128,
+ 9, 0, 255, 128, 5, 0,
+ 0, 128, 4, 0, 255, 128,
4, 0, 0, 4, 4, 0,
- 8, 128, 2, 0, 170, 128,
+ 8, 128, 2, 0, 0, 128,
3, 0, 255, 129, 4, 0,
255, 128, 4, 0, 0, 4,
+ 2, 0, 7, 128, 2, 0,
+ 228, 128, 3, 0, 255, 128,
+ 2, 0, 170, 160, 5, 0,
+ 0, 3, 2, 0, 7, 128,
+ 2, 0, 228, 128, 2, 0,
+ 228, 128, 4, 0, 0, 4,
4, 0, 8, 128, 7, 0,
- 170, 128, 4, 0, 255, 128,
- 4, 0, 170, 128, 88, 0,
- 0, 4, 11, 0, 4, 128,
- 8, 0, 170, 128, 10, 0,
- 170, 128, 4, 0, 255, 128,
- 7, 0, 0, 2, 4, 0,
- 8, 128, 4, 0, 0, 128,
- 6, 0, 0, 2, 4, 0,
- 8, 128, 4, 0, 255, 128,
- 88, 0, 0, 4, 4, 0,
- 8, 128, 9, 0, 255, 128,
- 5, 0, 0, 128, 4, 0,
- 255, 128, 4, 0, 0, 4,
- 4, 0, 8, 128, 2, 0,
- 0, 128, 3, 0, 255, 129,
- 4, 0, 255, 128, 4, 0,
- 0, 4, 4, 0, 8, 128,
- 7, 0, 0, 128, 4, 0,
- 255, 128, 4, 0, 0, 128,
- 2, 0, 0, 3, 2, 0,
- 7, 128, 7, 0, 228, 129,
- 2, 0, 0, 160, 4, 0,
- 0, 4, 2, 0, 7, 128,
- 9, 0, 228, 128, 2, 0,
- 228, 129, 2, 0, 0, 160,
- 88, 0, 0, 4, 11, 0,
- 1, 128, 8, 0, 0, 128,
- 10, 0, 0, 128, 4, 0,
- 255, 128, 4, 0, 0, 4,
- 3, 0, 14, 128, 1, 0,
- 144, 128, 3, 0, 0, 128,
- 4, 0, 144, 129, 4, 0,
- 0, 4, 5, 0, 7, 128,
- 1, 0, 228, 128, 3, 0,
- 0, 128, 4, 0, 228, 128,
- 35, 0, 0, 2, 3, 0,
- 7, 128, 3, 0, 249, 128,
- 5, 0, 0, 3, 7, 0,
- 7, 128, 4, 0, 228, 128,
- 6, 0, 228, 128, 4, 0,
+ 0, 128, 4, 0, 255, 128,
+ 4, 0, 0, 128, 2, 0,
+ 0, 3, 3, 0, 14, 128,
+ 7, 0, 144, 129, 2, 0,
+ 0, 160, 4, 0, 0, 4,
+ 3, 0, 14, 128, 9, 0,
+ 144, 128, 3, 0, 228, 129,
+ 2, 0, 0, 160, 88, 0,
+ 0, 4, 11, 0, 1, 128,
+ 8, 0, 0, 128, 10, 0,
+ 0, 128, 4, 0, 255, 128,
+ 4, 0, 0, 4, 5, 0,
+ 7, 128, 1, 0, 228, 128,
+ 3, 0, 0, 128, 4, 0,
+ 228, 129, 4, 0, 0, 4,
+ 7, 0, 7, 128, 1, 0,
+ 228, 128, 3, 0, 0, 128,
+ 4, 0, 228, 128, 35, 0,
+ 0, 2, 5, 0, 7, 128,
+ 5, 0, 228, 128, 5, 0,
+ 0, 3, 10, 0, 7, 128,
+ 4, 0, 228, 128, 6, 0,
+ 228, 128, 4, 0, 0, 4,
+ 7, 0, 7, 128, 10, 0,
+ 228, 128, 3, 0, 85, 161,
+ 7, 0, 228, 128, 88, 0,
0, 4, 5, 0, 7, 128,
- 7, 0, 228, 128, 3, 0,
- 0, 161, 5, 0, 228, 128,
- 88, 0, 0, 4, 3, 0,
- 7, 128, 0, 0, 255, 129,
- 3, 0, 228, 128, 5, 0,
- 228, 128, 88, 0, 0, 4,
- 3, 0, 7, 128, 0, 0,
- 170, 129, 11, 0, 228, 128,
- 3, 0, 228, 128, 2, 0,
- 0, 3, 5, 0, 7, 128,
- 6, 0, 228, 128, 6, 0,
- 228, 128, 5, 0, 0, 3,
- 5, 0, 7, 128, 4, 0,
- 228, 128, 5, 0, 228, 128,
+ 0, 0, 255, 129, 5, 0,
+ 228, 128, 7, 0, 228, 128,
+ 88, 0, 0, 4, 5, 0,
+ 7, 128, 0, 0, 170, 129,
+ 11, 0, 228, 128, 5, 0,
+ 228, 128, 2, 0, 0, 3,
+ 7, 0, 7, 128, 6, 0,
+ 228, 128, 6, 0, 228, 128,
5, 0, 0, 3, 4, 0,
7, 128, 4, 0, 228, 128,
- 4, 0, 228, 128, 88, 0,
- 0, 4, 4, 0, 7, 128,
- 4, 0, 228, 129, 2, 0,
- 85, 160, 2, 0, 0, 160,
- 88, 0, 0, 4, 2, 0,
- 7, 128, 8, 0, 228, 128,
- 5, 0, 228, 128, 2, 0,
- 228, 128, 88, 0, 0, 4,
- 0, 0, 14, 128, 0, 0,
- 85, 129, 2, 0, 144, 128,
- 3, 0, 144, 128, 6, 0,
+ 7, 0, 228, 128, 88, 0,
+ 0, 4, 3, 0, 7, 128,
+ 8, 0, 228, 128, 4, 0,
+ 228, 128, 3, 0, 249, 128,
+ 88, 0, 0, 4, 0, 0,
+ 14, 128, 0, 0, 85, 129,
+ 3, 0, 144, 128, 5, 0,
+ 144, 128, 6, 0, 0, 2,
+ 6, 0, 8, 128, 6, 0,
+ 0, 128, 4, 0, 0, 4,
+ 6, 0, 8, 128, 9, 0,
+ 0, 128, 6, 0, 255, 129,
+ 2, 0, 0, 160, 11, 0,
+ 0, 3, 3, 0, 1, 128,
+ 6, 0, 255, 128, 2, 0,
+ 85, 160, 5, 0, 0, 3,
+ 3, 0, 14, 128, 6, 0,
+ 144, 128, 6, 0, 144, 128,
+ 88, 0, 0, 4, 6, 0,
+ 8, 128, 3, 0, 85, 129,
+ 2, 0, 85, 160, 3, 0,
+ 0, 128, 88, 0, 0, 4,
+ 4, 0, 1, 128, 2, 0,
+ 0, 129, 2, 0, 0, 160,
+ 6, 0, 255, 128, 6, 0,
0, 2, 4, 0, 8, 128,
- 6, 0, 0, 128, 4, 0,
+ 6, 0, 85, 128, 4, 0,
0, 4, 4, 0, 8, 128,
- 9, 0, 0, 128, 4, 0,
+ 9, 0, 85, 128, 4, 0,
255, 129, 2, 0, 0, 160,
11, 0, 0, 3, 6, 0,
8, 128, 4, 0, 255, 128,
2, 0, 85, 160, 88, 0,
- 0, 4, 2, 0, 1, 128,
- 6, 0, 0, 129, 4, 0,
- 0, 128, 6, 0, 255, 128,
- 6, 0, 0, 2, 6, 0,
- 8, 128, 6, 0, 85, 128,
- 4, 0, 0, 4, 6, 0,
- 8, 128, 9, 0, 85, 128,
- 6, 0, 255, 129, 2, 0,
- 0, 160, 11, 0, 0, 3,
- 3, 0, 1, 128, 6, 0,
- 255, 128, 2, 0, 85, 160,
- 88, 0, 0, 4, 2, 0,
- 2, 128, 6, 0, 85, 129,
- 4, 0, 85, 128, 3, 0,
- 0, 128, 6, 0, 0, 2,
- 6, 0, 8, 128, 6, 0,
+ 0, 4, 4, 0, 8, 128,
+ 3, 0, 170, 129, 2, 0,
+ 85, 160, 6, 0, 255, 128,
+ 88, 0, 0, 4, 4, 0,
+ 2, 128, 2, 0, 85, 129,
+ 2, 0, 0, 160, 4, 0,
+ 255, 128, 6, 0, 0, 2,
+ 4, 0, 8, 128, 6, 0,
170, 128, 4, 0, 0, 4,
- 6, 0, 8, 128, 9, 0,
- 170, 128, 6, 0, 255, 129,
+ 4, 0, 8, 128, 9, 0,
+ 170, 128, 4, 0, 255, 129,
2, 0, 0, 160, 11, 0,
- 0, 3, 3, 0, 1, 128,
- 6, 0, 255, 128, 2, 0,
+ 0, 3, 6, 0, 8, 128,
+ 4, 0, 255, 128, 2, 0,
85, 160, 88, 0, 0, 4,
- 2, 0, 4, 128, 6, 0,
- 170, 129, 4, 0, 170, 128,
- 3, 0, 0, 128, 88, 0,
- 0, 4, 0, 0, 7, 128,
- 0, 0, 0, 129, 2, 0,
- 228, 128, 0, 0, 249, 128,
- 18, 0, 0, 4, 3, 0,
- 7, 128, 2, 0, 255, 128,
- 0, 0, 228, 128, 6, 0,
+ 4, 0, 8, 128, 3, 0,
+ 255, 129, 2, 0, 85, 160,
+ 6, 0, 255, 128, 88, 0,
+ 0, 4, 4, 0, 4, 128,
+ 2, 0, 170, 129, 2, 0,
+ 0, 160, 4, 0, 255, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 0, 0, 0, 129,
+ 4, 0, 228, 128, 0, 0,
+ 249, 128, 18, 0, 0, 4,
+ 3, 0, 7, 128, 2, 0,
+ 255, 128, 0, 0, 228, 128,
+ 6, 0, 228, 128, 5, 0,
+ 0, 3, 3, 0, 8, 128,
+ 2, 0, 255, 128, 2, 0,
+ 255, 128, 88, 0, 0, 4,
+ 3, 0, 8, 128, 3, 0,
+ 255, 129, 2, 0, 0, 160,
+ 2, 0, 85, 160, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 1, 0, 255, 128, 3, 0,
228, 128, 5, 0, 0, 3,
- 3, 0, 8, 128, 2, 0,
- 255, 128, 2, 0, 255, 128,
- 88, 0, 0, 4, 3, 0,
- 8, 128, 3, 0, 255, 129,
+ 0, 0, 8, 128, 1, 0,
+ 255, 128, 1, 0, 255, 128,
+ 88, 0, 0, 4, 0, 0,
+ 8, 128, 0, 0, 255, 129,
2, 0, 0, 160, 2, 0,
- 85, 160, 5, 0, 0, 3,
- 0, 0, 7, 128, 1, 0,
- 255, 128, 3, 0, 228, 128,
- 5, 0, 0, 3, 0, 0,
- 8, 128, 1, 0, 255, 128,
- 1, 0, 255, 128, 88, 0,
- 0, 4, 0, 0, 8, 128,
- 0, 0, 255, 129, 2, 0,
- 0, 160, 2, 0, 85, 160,
- 2, 0, 0, 3, 0, 0,
- 8, 128, 3, 0, 255, 128,
- 0, 0, 255, 128, 88, 0,
- 0, 4, 1, 0, 7, 128,
- 0, 0, 255, 129, 0, 0,
- 228, 128, 1, 0, 228, 128,
- 1, 0, 0, 2, 0, 8,
- 15, 128, 1, 0, 228, 128,
- 255, 255, 0, 0, 83, 72,
- 68, 82, 88, 8, 0, 0,
- 64, 0, 0, 0, 22, 2,
- 0, 0, 89, 0, 0, 4,
- 70, 142, 32, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 0, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 1, 0, 0, 0,
+ 85, 160, 2, 0, 0, 3,
+ 0, 0, 8, 128, 3, 0,
+ 255, 128, 0, 0, 255, 128,
+ 88, 0, 0, 4, 1, 0,
+ 7, 128, 0, 0, 255, 129,
+ 0, 0, 228, 128, 1, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 1, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 108, 8,
+ 0, 0, 64, 0, 0, 0,
+ 27, 2, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 7, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 0, 0, 0, 0,
- 85, 85, 0, 0, 88, 24,
- 0, 4, 0, 112, 16, 0,
- 1, 0, 0, 0, 85, 85,
- 0, 0, 98, 16, 0, 3,
- 50, 16, 16, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 104, 0, 0, 2,
- 7, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 0, 0, 0, 0, 70, 16,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 1, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 1, 0, 0, 0, 70, 16,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
16, 0, 1, 0, 0, 0,
- 70, 126, 16, 0, 1, 0,
- 0, 0, 0, 96, 16, 0,
- 1, 0, 0, 0, 24, 0,
- 0, 7, 18, 0, 16, 0,
+ 24, 0, 0, 7, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 24, 0,
+ 0, 7, 34, 0, 16, 0,
2, 0, 0, 0, 58, 0,
- 16, 0, 0, 0, 0, 0,
+ 16, 0, 1, 0, 0, 0,
1, 64, 0, 0, 0, 0,
- 0, 0, 24, 0, 0, 7,
- 34, 0, 16, 0, 2, 0,
- 0, 0, 58, 0, 16, 0,
- 1, 0, 0, 0, 1, 64,
- 0, 0, 0, 0, 0, 0,
- 60, 0, 0, 7, 18, 0,
+ 0, 0, 60, 0, 0, 7,
+ 18, 0, 16, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 2, 0, 0, 0, 10, 0,
16, 0, 2, 0, 0, 0,
- 26, 0, 16, 0, 2, 0,
- 0, 0, 10, 0, 16, 0,
- 2, 0, 0, 0, 31, 0,
- 4, 3, 10, 0, 16, 0,
- 2, 0, 0, 0, 54, 0,
- 0, 5, 242, 32, 16, 0,
- 0, 0, 0, 0, 70, 14,
+ 31, 0, 4, 3, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 5, 242, 32,
16, 0, 0, 0, 0, 0,
- 62, 0, 0, 1, 21, 0,
- 0, 1, 14, 0, 0, 7,
- 114, 0, 16, 0, 0, 0,
- 0, 0, 70, 2, 16, 0,
- 0, 0, 0, 0, 246, 15,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 21, 0, 0, 1, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
16, 0, 0, 0, 0, 0,
- 14, 0, 0, 7, 114, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 246, 15,
16, 0, 1, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 246, 15, 16, 0,
- 1, 0, 0, 0, 32, 0,
- 0, 8, 18, 0, 16, 0,
- 2, 0, 0, 0, 10, 128,
- 32, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 64,
- 0, 0, 7, 0, 0, 0,
- 31, 0, 4, 3, 10, 0,
- 16, 0, 2, 0, 0, 0,
- 49, 0, 0, 10, 114, 0,
+ 32, 0, 0, 8, 18, 0,
16, 0, 2, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
+ 10, 128, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 7, 0,
+ 0, 0, 31, 0, 4, 3,
+ 10, 0, 16, 0, 2, 0,
+ 0, 0, 24, 0, 0, 10,
+ 114, 0, 16, 0, 2, 0,
0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 24, 0, 0, 10, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 11, 114, 0, 16, 0,
- 3, 0, 0, 0, 70, 2,
+ 4, 0, 0, 0, 70, 2,
16, 128, 65, 0, 0, 0,
1, 0, 0, 0, 2, 64,
0, 0, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 0, 0,
14, 0, 0, 7, 114, 0,
- 16, 0, 3, 0, 0, 0,
- 70, 2, 16, 0, 3, 0,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
0, 0, 0, 0, 51, 0,
0, 10, 114, 0, 16, 0,
- 3, 0, 0, 0, 70, 2,
- 16, 0, 3, 0, 0, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 4, 0, 0, 0,
2, 64, 0, 0, 0, 0,
128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
0, 0, 0, 0, 0, 11,
- 114, 0, 16, 0, 3, 0,
+ 114, 0, 16, 0, 4, 0,
0, 0, 70, 2, 16, 128,
- 65, 0, 0, 0, 3, 0,
+ 65, 0, 0, 0, 4, 0,
0, 0, 2, 64, 0, 0,
0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63,
- 0, 0, 0, 0, 57, 0,
- 0, 10, 114, 0, 16, 0,
- 4, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 4, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 18, 0,
+ 0, 1, 32, 0, 0, 8,
+ 130, 0, 16, 0, 2, 0,
+ 0, 0, 10, 128, 32, 0,
0, 0, 0, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 1, 0, 0, 10,
+ 0, 0, 1, 64, 0, 0,
+ 8, 0, 0, 0, 31, 0,
+ 4, 3, 58, 0, 16, 0,
+ 2, 0, 0, 0, 29, 0,
+ 0, 10, 114, 0, 16, 0,
+ 3, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 63, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
114, 0, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
- 4, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 191, 0, 0,
+ 128, 191, 0, 0, 128, 191,
+ 0, 0, 0, 0, 0, 0,
+ 0, 11, 114, 0, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 11, 114, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 128, 65, 0,
+ 0, 0, 5, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 50, 0, 0, 13,
+ 114, 0, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 6, 0,
+ 0, 0, 70, 2, 16, 0,
+ 5, 0, 0, 0, 2, 64,
0, 0, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 0, 0,
55, 0, 0, 9, 114, 0,
16, 0, 2, 0, 0, 0,
- 70, 2, 16, 0, 2, 0,
+ 70, 2, 16, 0, 3, 0,
0, 0, 70, 2, 16, 0,
- 3, 0, 0, 0, 70, 2,
- 16, 0, 4, 0, 0, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 5, 0, 0, 0,
18, 0, 0, 1, 32, 0,
0, 8, 130, 0, 16, 0,
2, 0, 0, 0, 10, 128,
32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 64,
- 0, 0, 8, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
31, 0, 4, 3, 58, 0,
16, 0, 2, 0, 0, 0,
29, 0, 0, 10, 114, 0,
16, 0, 3, 0, 0, 0,
2, 64, 0, 0, 0, 0,
- 0, 63, 0, 0, 0, 63,
- 0, 0, 0, 63, 0, 0,
+ 128, 62, 0, 0, 128, 62,
+ 0, 0, 128, 62, 0, 0,
0, 0, 70, 2, 16, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 7, 114, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
4, 0, 0, 0, 70, 2,
- 16, 0, 0, 0, 0, 0,
- 70, 2, 16, 0, 0, 0,
- 0, 0, 56, 0, 0, 7,
- 114, 0, 16, 0, 4, 0,
- 0, 0, 70, 2, 16, 0,
- 1, 0, 0, 0, 70, 2,
- 16, 0, 4, 0, 0, 0,
- 50, 0, 0, 15, 114, 0,
- 16, 0, 5, 0, 0, 0,
- 70, 2, 16, 0, 0, 0,
- 0, 0, 2, 64, 0, 0,
- 0, 0, 0, 64, 0, 0,
- 0, 64, 0, 0, 0, 64,
- 0, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 128, 191,
- 0, 0, 128, 191, 0, 0,
- 128, 191, 0, 0, 0, 0,
- 0, 0, 0, 11, 114, 0,
- 16, 0, 6, 0, 0, 0,
- 70, 2, 16, 128, 65, 0,
- 0, 0, 1, 0, 0, 0,
+ 16, 0, 1, 0, 0, 0,
2, 64, 0, 0, 0, 0,
- 128, 63, 0, 0, 128, 63,
- 0, 0, 128, 63, 0, 0,
- 0, 0, 0, 0, 0, 11,
- 114, 0, 16, 0, 5, 0,
- 0, 0, 70, 2, 16, 128,
- 65, 0, 0, 0, 5, 0,
+ 128, 65, 0, 0, 128, 65,
+ 0, 0, 128, 65, 0, 0,
0, 0, 2, 64, 0, 0,
- 0, 0, 128, 63, 0, 0,
- 128, 63, 0, 0, 128, 63,
+ 0, 0, 64, 193, 0, 0,
+ 64, 193, 0, 0, 64, 193,
0, 0, 0, 0, 50, 0,
- 0, 13, 114, 0, 16, 0,
- 5, 0, 0, 0, 70, 2,
- 16, 128, 65, 0, 0, 0,
- 6, 0, 0, 0, 70, 2,
- 16, 0, 5, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 128, 63, 0, 0, 128, 63,
- 0, 0, 128, 63, 0, 0,
- 0, 0, 55, 0, 0, 9,
- 114, 0, 16, 0, 2, 0,
- 0, 0, 70, 2, 16, 0,
- 3, 0, 0, 0, 70, 2,
- 16, 0, 4, 0, 0, 0,
- 70, 2, 16, 0, 5, 0,
- 0, 0, 18, 0, 0, 1,
- 32, 0, 0, 8, 130, 0,
- 16, 0, 2, 0, 0, 0,
- 10, 128, 32, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 64, 0, 0, 9, 0,
- 0, 0, 31, 0, 4, 3,
- 58, 0, 16, 0, 2, 0,
- 0, 0, 29, 0, 0, 10,
- 114, 0, 16, 0, 3, 0,
- 0, 0, 2, 64, 0, 0,
- 0, 0, 128, 62, 0, 0,
- 128, 62, 0, 0, 128, 62,
- 0, 0, 0, 0, 70, 2,
- 16, 0, 1, 0, 0, 0,
- 50, 0, 0, 15, 114, 0,
+ 0, 12, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
16, 0, 4, 0, 0, 0,
70, 2, 16, 0, 1, 0,
0, 0, 2, 64, 0, 0,
- 0, 0, 128, 65, 0, 0,
- 128, 65, 0, 0, 128, 65,
- 0, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 64, 193,
- 0, 0, 64, 193, 0, 0,
- 64, 193, 0, 0, 0, 0,
- 50, 0, 0, 12, 114, 0,
- 16, 0, 4, 0, 0, 0,
- 70, 2, 16, 0, 4, 0,
- 0, 0, 70, 2, 16, 0,
- 1, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 128, 64,
0, 0, 128, 64, 0, 0,
- 128, 64, 0, 0, 0, 0,
- 56, 0, 0, 7, 114, 0,
- 16, 0, 4, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 70, 2, 16, 0,
- 4, 0, 0, 0, 75, 0,
- 0, 5, 114, 0, 16, 0,
- 5, 0, 0, 0, 70, 2,
+ 128, 64, 0, 0, 128, 64,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
16, 0, 1, 0, 0, 0,
- 55, 0, 0, 9, 114, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 75, 0, 0, 5,
+ 114, 0, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 55, 0,
+ 0, 9, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
16, 0, 3, 0, 0, 0,
- 70, 2, 16, 0, 3, 0,
+ 70, 2, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
- 4, 0, 0, 0, 70, 2,
- 16, 0, 5, 0, 0, 0,
- 29, 0, 0, 10, 114, 0,
- 16, 0, 4, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 0, 63, 0, 0, 0, 63,
+ 5, 0, 0, 0, 29, 0,
+ 0, 10, 114, 0, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 63,
0, 0, 0, 63, 0, 0,
- 0, 0, 70, 2, 16, 0,
- 0, 0, 0, 0, 50, 0,
- 0, 16, 114, 0, 16, 0,
- 5, 0, 0, 0, 70, 2,
- 16, 128, 65, 0, 0, 0,
- 0, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 0, 64,
+ 0, 63, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 16,
+ 114, 0, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
0, 0, 0, 64, 0, 0,
- 0, 64, 0, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 128, 63, 0, 0, 128, 63,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0,
- 0, 0, 56, 0, 0, 7,
- 114, 0, 16, 0, 5, 0,
+ 128, 63, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
0, 0, 70, 2, 16, 0,
- 1, 0, 0, 0, 70, 2,
+ 5, 0, 0, 0, 0, 0,
+ 0, 11, 114, 0, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 50, 0, 0, 10, 114, 0,
16, 0, 5, 0, 0, 0,
- 0, 0, 0, 11, 114, 0,
- 16, 0, 6, 0, 0, 0,
70, 2, 16, 128, 65, 0,
- 0, 0, 1, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 128, 63, 0, 0, 128, 63,
- 0, 0, 128, 63, 0, 0,
- 0, 0, 50, 0, 0, 10,
- 114, 0, 16, 0, 5, 0,
- 0, 0, 70, 2, 16, 128,
- 65, 0, 0, 0, 5, 0,
+ 0, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 6, 0,
0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
6, 0, 0, 0, 70, 2,
- 16, 0, 1, 0, 0, 0,
- 50, 0, 0, 15, 114, 0,
- 16, 0, 6, 0, 0, 0,
- 70, 2, 16, 0, 0, 0,
- 0, 0, 2, 64, 0, 0,
- 0, 0, 0, 64, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
0, 64, 0, 0, 0, 64,
- 0, 0, 0, 0, 2, 64,
- 0, 0, 0, 0, 128, 191,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 2, 64, 0, 0,
0, 0, 128, 191, 0, 0,
- 128, 191, 0, 0, 0, 0,
- 0, 0, 0, 8, 114, 0,
+ 128, 191, 0, 0, 128, 191,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 70, 2,
16, 0, 3, 0, 0, 0,
- 70, 2, 16, 128, 65, 0,
- 0, 0, 1, 0, 0, 0,
- 70, 2, 16, 0, 3, 0,
- 0, 0, 50, 0, 0, 9,
- 114, 0, 16, 0, 3, 0,
- 0, 0, 70, 2, 16, 0,
- 6, 0, 0, 0, 70, 2,
+ 50, 0, 0, 9, 114, 0,
16, 0, 3, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 55, 0, 0, 9,
- 114, 0, 16, 0, 2, 0,
+ 70, 2, 16, 0, 6, 0,
0, 0, 70, 2, 16, 0,
- 4, 0, 0, 0, 70, 2,
- 16, 0, 5, 0, 0, 0,
- 70, 2, 16, 0, 3, 0,
- 0, 0, 18, 0, 0, 1,
- 32, 0, 0, 8, 130, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
16, 0, 2, 0, 0, 0,
- 10, 128, 32, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 64, 0, 0, 10, 0,
- 0, 0, 0, 0, 0, 8,
- 114, 0, 16, 0, 3, 0,
+ 70, 2, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
- 0, 0, 0, 0, 70, 2,
- 16, 128, 65, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 7, 114, 0, 16, 0,
- 4, 0, 0, 0, 70, 2,
- 16, 0, 0, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 56, 0, 0, 7,
- 114, 0, 16, 0, 1, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 18, 0, 0, 1, 32, 0,
+ 0, 8, 130, 0, 16, 0,
+ 2, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 10, 0, 0, 0,
+ 0, 0, 0, 8, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 4, 0,
0, 0, 70, 2, 16, 0,
0, 0, 0, 0, 70, 2,
16, 0, 1, 0, 0, 0,
- 50, 0, 0, 13, 114, 0,
+ 56, 0, 0, 7, 114, 0,
16, 0, 1, 0, 0, 0,
- 70, 2, 16, 128, 65, 0,
- 0, 0, 1, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 0, 64, 0, 0, 0, 64,
- 0, 0, 0, 64, 0, 0,
+ 70, 2, 16, 0, 0, 0,
0, 0, 70, 2, 16, 0,
- 4, 0, 0, 0, 55, 0,
- 0, 10, 114, 0, 16, 0,
- 2, 0, 0, 0, 246, 15,
- 16, 0, 2, 0, 0, 0,
- 70, 2, 16, 128, 129, 0,
- 0, 0, 3, 0, 0, 0,
- 70, 2, 16, 0, 1, 0,
- 0, 0, 21, 0, 0, 1,
+ 1, 0, 0, 0, 50, 0,
+ 0, 13, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 64,
+ 0, 0, 0, 64, 0, 0,
+ 0, 64, 0, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 55, 0, 0, 10,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 246, 15, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 128, 129, 0, 0, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
21, 0, 0, 1, 21, 0,
- 0, 1, 0, 0, 0, 8,
- 18, 0, 16, 0, 1, 0,
- 0, 0, 58, 0, 16, 128,
- 65, 0, 0, 0, 1, 0,
- 0, 0, 1, 64, 0, 0,
- 0, 0, 128, 63, 56, 0,
- 0, 7, 226, 0, 16, 0,
- 1, 0, 0, 0, 246, 15,
+ 0, 1, 21, 0, 0, 1,
+ 0, 0, 0, 8, 18, 0,
16, 0, 1, 0, 0, 0,
- 6, 9, 16, 0, 2, 0,
- 0, 0, 50, 0, 0, 9,
- 114, 0, 16, 0, 0, 0,
- 0, 0, 6, 0, 16, 0,
- 1, 0, 0, 0, 70, 2,
- 16, 0, 0, 0, 0, 0,
- 150, 7, 16, 0, 1, 0,
- 0, 0, 56, 0, 0, 7,
- 114, 32, 16, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 56, 0, 0, 7,
+ 226, 0, 16, 0, 1, 0,
0, 0, 246, 15, 16, 0,
- 0, 0, 0, 0, 70, 2,
+ 1, 0, 0, 0, 6, 9,
+ 16, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 114, 0,
16, 0, 0, 0, 0, 0,
- 54, 0, 0, 5, 130, 32,
+ 6, 0, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 150, 7,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
16, 0, 0, 0, 0, 0,
- 58, 0, 16, 0, 0, 0,
- 0, 0, 62, 0, 0, 1,
- 83, 84, 65, 84, 116, 0,
- 0, 0, 66, 0, 0, 0,
- 7, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 27, 0, 0, 0, 4, 0,
- 0, 0, 2, 0, 0, 0,
- 5, 0, 0, 0, 4, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 66, 0, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 38, 0,
+ 0, 0, 4, 0, 0, 0,
+ 1, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 16, 0, 0, 0, 5, 0,
- 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 82, 68,
- 69, 70, 100, 1, 0, 0,
- 1, 0, 0, 0, 232, 0,
- 0, 0, 5, 0, 0, 0,
- 28, 0, 0, 0, 0, 4,
- 255, 255, 0, 1, 0, 0,
- 48, 1, 0, 0, 188, 0,
- 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 82, 68, 69, 70,
+ 100, 1, 0, 0, 1, 0,
+ 0, 0, 232, 0, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 48, 1,
+ 0, 0, 188, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 197, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 197, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 209, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 209, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 12, 0,
+ 0, 0, 213, 0, 0, 0,
2, 0, 0, 0, 5, 0,
0, 0, 4, 0, 0, 0,
- 255, 255, 255, 255, 0, 0,
+ 255, 255, 255, 255, 1, 0,
0, 0, 1, 0, 0, 0,
- 12, 0, 0, 0, 213, 0,
- 0, 0, 2, 0, 0, 0,
- 5, 0, 0, 0, 4, 0,
- 0, 0, 255, 255, 255, 255,
- 1, 0, 0, 0, 1, 0,
- 0, 0, 12, 0, 0, 0,
- 220, 0, 0, 0, 0, 0,
+ 12, 0, 0, 0, 220, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 115, 83, 97, 109,
- 112, 108, 101, 114, 0, 115,
- 66, 99, 107, 83, 97, 109,
- 112, 108, 101, 114, 0, 116,
- 101, 120, 0, 98, 99, 107,
- 116, 101, 120, 0, 36, 71,
- 108, 111, 98, 97, 108, 115,
- 0, 171, 171, 171, 220, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 1, 0, 0, 16, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 24, 1,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 2, 0,
- 0, 0, 32, 1, 0, 0,
- 0, 0, 0, 0, 98, 108,
- 101, 110, 100, 111, 112, 0,
- 0, 0, 19, 0, 1, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 73, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
- 0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 115, 66, 99,
+ 107, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 101, 120,
+ 0, 98, 99, 107, 116, 101,
+ 120, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 171, 171, 220, 0, 0, 0,
+ 1, 0, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 3, 0, 0,
- 92, 0, 0, 0, 1, 0,
+ 0, 0, 24, 1, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 32, 1, 0, 0, 0, 0,
+ 0, 0, 98, 108, 101, 110,
+ 100, 111, 112, 0, 0, 0,
+ 19, 0, 1, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 0, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 79, 83, 71, 78, 44, 0,
- 0, 0, 1, 0, 0, 0,
- 8, 0, 0, 0, 32, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 1, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 83, 86,
- 95, 84, 97, 114, 103, 101,
- 116, 0, 171, 171, 128, 34,
+ 15, 0, 0, 0, 92, 0,
0, 0, 0, 0, 0, 0,
- 83, 97, 109, 112, 108, 101,
- 84, 101, 120, 116, 117, 114,
- 101, 70, 111, 114, 78, 111,
- 110, 83, 101, 112, 97, 114,
- 97, 98, 108, 101, 66, 108,
- 101, 110, 100, 105, 110, 103,
- 0, 72, 4, 0, 0, 68,
- 88, 66, 67, 28, 248, 40,
- 83, 2, 166, 203, 194, 228,
- 163, 91, 123, 149, 165, 41,
- 212, 1, 0, 0, 0, 72,
- 4, 0, 0, 6, 0, 0,
- 0, 56, 0, 0, 0, 248,
- 0, 0, 0, 244, 1, 0,
- 0, 112, 2, 0, 0, 164,
- 3, 0, 0, 216, 3, 0,
- 0, 65, 111, 110, 57, 184,
- 0, 0, 0, 184, 0, 0,
- 0, 0, 2, 254, 255, 132,
- 0, 0, 0, 52, 0, 0,
- 0, 1, 0, 36, 0, 0,
- 0, 48, 0, 0, 0, 48,
- 0, 0, 0, 36, 0, 1,
- 0, 48, 0, 0, 0, 0,
- 0, 3, 0, 1, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 1, 2, 254, 255, 81,
- 0, 0, 5, 4, 0, 15,
- 160, 0, 0, 0, 0, 0,
- 0, 128, 63, 0, 0, 0,
- 0, 0, 0, 0, 0, 31,
- 0, 0, 2, 5, 0, 0,
- 128, 0, 0, 15, 144, 4,
- 0, 0, 4, 0, 0, 3,
- 224, 0, 0, 228, 144, 2,
- 0, 238, 160, 2, 0, 228,
- 160, 4, 0, 0, 4, 0,
- 0, 12, 224, 0, 0, 20,
- 144, 3, 0, 180, 160, 3,
- 0, 20, 160, 4, 0, 0,
- 4, 0, 0, 3, 128, 0,
- 0, 228, 144, 1, 0, 238,
- 160, 1, 0, 228, 160, 2,
- 0, 0, 3, 0, 0, 3,
- 192, 0, 0, 228, 128, 0,
- 0, 228, 160, 1, 0, 0,
- 2, 0, 0, 12, 192, 4,
- 0, 68, 160, 255, 255, 0,
- 0, 83, 72, 68, 82, 244,
- 0, 0, 0, 64, 0, 1,
- 0, 61, 0, 0, 0, 89,
- 0, 0, 4, 70, 142, 32,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171, 120, 34, 0, 0,
+ 0, 0, 0, 0, 83, 97,
+ 109, 112, 108, 101, 84, 101,
+ 120, 116, 117, 114, 101, 70,
+ 111, 114, 78, 111, 110, 83,
+ 101, 112, 97, 114, 97, 98,
+ 108, 101, 66, 108, 101, 110,
+ 100, 105, 110, 103, 0, 68,
+ 4, 0, 0, 68, 88, 66,
+ 67, 77, 85, 167, 240, 56,
+ 56, 155, 78, 125, 96, 49,
+ 253, 103, 100, 22, 62, 1,
+ 0, 0, 0, 68, 4, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 248, 0, 0,
+ 0, 244, 1, 0, 0, 112,
+ 2, 0, 0, 160, 3, 0,
+ 0, 212, 3, 0, 0, 65,
+ 111, 110, 57, 184, 0, 0,
+ 0, 184, 0, 0, 0, 0,
+ 2, 254, 255, 132, 0, 0,
+ 0, 52, 0, 0, 0, 1,
+ 0, 36, 0, 0, 0, 48,
+ 0, 0, 0, 48, 0, 0,
+ 0, 36, 0, 1, 0, 48,
0, 0, 0, 0, 0, 3,
- 0, 0, 0, 95, 0, 0,
- 3, 50, 16, 16, 0, 0,
- 0, 0, 0, 103, 0, 0,
- 4, 242, 32, 16, 0, 0,
- 0, 0, 0, 1, 0, 0,
- 0, 101, 0, 0, 3, 50,
- 32, 16, 0, 1, 0, 0,
- 0, 101, 0, 0, 3, 194,
- 32, 16, 0, 1, 0, 0,
- 0, 50, 0, 0, 11, 50,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 254, 255, 81, 0, 0,
+ 5, 4, 0, 15, 160, 0,
+ 0, 0, 0, 0, 0, 128,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 0, 0,
+ 2, 5, 0, 0, 128, 0,
+ 0, 15, 144, 4, 0, 0,
+ 4, 0, 0, 3, 224, 0,
+ 0, 228, 144, 2, 0, 238,
+ 160, 2, 0, 228, 160, 4,
+ 0, 0, 4, 0, 0, 12,
+ 224, 0, 0, 20, 144, 3,
+ 0, 180, 160, 3, 0, 20,
+ 160, 4, 0, 0, 4, 0,
+ 0, 3, 128, 0, 0, 228,
+ 144, 1, 0, 238, 160, 1,
+ 0, 228, 160, 2, 0, 0,
+ 3, 0, 0, 3, 192, 0,
+ 0, 228, 128, 0, 0, 228,
+ 160, 1, 0, 0, 2, 0,
+ 0, 12, 192, 4, 0, 68,
+ 160, 255, 255, 0, 0, 83,
+ 72, 68, 82, 244, 0, 0,
+ 0, 64, 0, 1, 0, 61,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 95, 0, 0, 3, 50,
+ 16, 16, 0, 0, 0, 0,
+ 0, 103, 0, 0, 4, 242,
32, 16, 0, 0, 0, 0,
- 0, 70, 16, 16, 0, 0,
- 0, 0, 0, 230, 138, 32,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 70, 128, 32,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 54, 0, 0,
- 8, 194, 32, 16, 0, 0,
- 0, 0, 0, 2, 64, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 128, 63, 50,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 50, 32, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 194, 32, 16,
+ 0, 1, 0, 0, 0, 50,
0, 0, 11, 50, 32, 16,
- 0, 1, 0, 0, 0, 70,
+ 0, 0, 0, 0, 0, 70,
16, 16, 0, 0, 0, 0,
0, 230, 138, 32, 0, 0,
- 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
0, 70, 128, 32, 0, 0,
- 0, 0, 0, 1, 0, 0,
- 0, 50, 0, 0, 11, 194,
- 32, 16, 0, 1, 0, 0,
- 0, 6, 20, 16, 0, 0,
- 0, 0, 0, 166, 142, 32,
- 0, 0, 0, 0, 0, 2,
- 0, 0, 0, 6, 132, 32,
- 0, 0, 0, 0, 0, 2,
- 0, 0, 0, 62, 0, 0,
- 1, 83, 84, 65, 84, 116,
- 0, 0, 0, 5, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 4, 0, 0,
+ 0, 54, 0, 0, 8, 194,
+ 32, 16, 0, 0, 0, 0,
+ 0, 2, 64, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 1, 0, 0, 0, 0,
+ 0, 128, 63, 50, 0, 0,
+ 11, 50, 32, 16, 0, 1,
+ 0, 0, 0, 70, 16, 16,
+ 0, 0, 0, 0, 0, 230,
+ 138, 32, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 70,
+ 128, 32, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 50,
+ 0, 0, 11, 194, 32, 16,
+ 0, 1, 0, 0, 0, 6,
+ 20, 16, 0, 0, 0, 0,
+ 0, 166, 142, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 6, 132, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 5, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 82,
- 68, 69, 70, 44, 1, 0,
- 0, 1, 0, 0, 0, 64,
- 0, 0, 0, 1, 0, 0,
- 0, 28, 0, 0, 0, 0,
- 4, 254, 255, 0, 1, 0,
- 0, 246, 0, 0, 0, 60,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 82, 68, 69,
+ 70, 40, 1, 0, 0, 1,
+ 0, 0, 0, 64, 0, 0,
+ 0, 1, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 254,
+ 255, 0, 1, 0, 0, 246,
+ 0, 0, 0, 60, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 99, 98, 48, 0, 60,
- 0, 0, 0, 4, 0, 0,
- 0, 88, 0, 0, 0, 64,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 184,
0, 0, 0, 0, 0, 0,
- 0, 16, 0, 0, 0, 2,
- 0, 0, 0, 196, 0, 0,
- 0, 0, 0, 0, 0, 212,
- 0, 0, 0, 16, 0, 0,
- 0, 16, 0, 0, 0, 2,
- 0, 0, 0, 196, 0, 0,
- 0, 0, 0, 0, 0, 222,
- 0, 0, 0, 32, 0, 0,
- 0, 16, 0, 0, 0, 2,
- 0, 0, 0, 196, 0, 0,
- 0, 0, 0, 0, 0, 236,
- 0, 0, 0, 48, 0, 0,
- 0, 16, 0, 0, 0, 0,
- 0, 0, 0, 196, 0, 0,
- 0, 0, 0, 0, 0, 81,
- 117, 97, 100, 68, 101, 115,
- 99, 0, 171, 171, 171, 1,
- 0, 3, 0, 1, 0, 4,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 99,
+ 98, 48, 0, 60, 0, 0,
+ 0, 4, 0, 0, 0, 88,
+ 0, 0, 0, 64, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 84, 101, 120,
+ 0, 0, 0, 184, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 212, 0, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 222, 0, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 236, 0, 0,
+ 0, 48, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 77,
+ 97, 115, 107, 84, 101, 120,
67, 111, 111, 114, 100, 115,
- 0, 77, 97, 115, 107, 84,
- 101, 120, 67, 111, 111, 114,
- 100, 115, 0, 84, 101, 120,
- 116, 67, 111, 108, 111, 114,
- 0, 77, 105, 99, 114, 111,
- 115, 111, 102, 116, 32, 40,
- 82, 41, 32, 72, 76, 83,
- 76, 32, 83, 104, 97, 100,
- 101, 114, 32, 67, 111, 109,
- 112, 105, 108, 101, 114, 32,
- 57, 46, 51, 48, 46, 57,
- 50, 48, 48, 46, 49, 54,
- 51, 56, 52, 0, 171, 171,
- 171, 73, 83, 71, 78, 44,
+ 0, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 73, 83, 71, 78, 44,
0, 0, 0, 1, 0, 0,
0, 8, 0, 0, 0, 32,
0, 0, 0, 0, 0, 0,
@@ -6634,14 +6656,14 @@ const BYTE d2deffect[] = 80, 111, 115, 105, 116, 105,
111, 110, 0, 84, 69, 88,
67, 79, 79, 82, 68, 0,
- 171, 171, 171, 169, 51, 0,
+ 171, 171, 171, 1, 52, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 216,
37, 0, 0, 68, 88, 66,
- 67, 14, 214, 69, 11, 113,
- 28, 134, 133, 126, 27, 169,
- 118, 168, 238, 179, 215, 1,
+ 67, 205, 124, 125, 227, 208,
+ 119, 203, 250, 120, 38, 135,
+ 194, 158, 189, 85, 176, 1,
0, 0, 0, 216, 37, 0,
0, 6, 0, 0, 0, 56,
0, 0, 0, 72, 13, 0,
@@ -8148,7 +8170,7 @@ const BYTE d2deffect[] = 84, 116, 0, 0, 0, 195,
0, 0, 0, 9, 0, 0,
0, 0, 0, 0, 0, 2,
- 0, 0, 0, 127, 0, 0,
+ 0, 0, 0, 128, 0, 0,
0, 3, 0, 0, 0, 1,
0, 0, 0, 7, 0, 0,
0, 6, 0, 0, 0, 0,
@@ -8158,7 +8180,7 @@ const BYTE d2deffect[] = 0, 2, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 38, 0, 0,
+ 0, 0, 0, 14, 0, 0,
0, 28, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -8223,9 +8245,9 @@ const BYTE d2deffect[] = 76, 32, 83, 104, 97, 100,
101, 114, 32, 67, 111, 109,
112, 105, 108, 101, 114, 32,
- 57, 46, 51, 48, 46, 57,
- 50, 48, 48, 46, 49, 54,
- 51, 56, 52, 0, 171, 73,
+ 54, 46, 51, 46, 57, 54,
+ 48, 48, 46, 49, 54, 51,
+ 56, 52, 0, 171, 171, 73,
83, 71, 78, 104, 0, 0,
0, 3, 0, 0, 0, 8,
0, 0, 0, 80, 0, 0,
@@ -8253,16 +8275,16 @@ const BYTE d2deffect[] = 0, 0, 0, 15, 0, 0,
0, 83, 86, 95, 84, 97,
114, 103, 101, 116, 0, 171,
- 171, 9, 56, 0, 0, 0,
+ 171, 93, 56, 0, 0, 0,
0, 0, 0, 83, 97, 109,
112, 108, 101, 82, 97, 100,
105, 97, 108, 71, 114, 97,
100, 105, 101, 110, 116, 0,
65, 80, 111, 115, 0, 44,
7, 0, 0, 68, 88, 66,
- 67, 2, 114, 191, 189, 220,
- 178, 221, 187, 160, 105, 131,
- 115, 60, 218, 164, 36, 1,
+ 67, 172, 27, 205, 113, 176,
+ 254, 27, 44, 22, 107, 179,
+ 112, 127, 38, 148, 161, 1,
0, 0, 0, 44, 7, 0,
0, 6, 0, 0, 0, 56,
0, 0, 0, 148, 1, 0,
@@ -8408,7 +8430,7 @@ const BYTE d2deffect[] = 84, 65, 84, 116, 0, 0,
0, 12, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
- 0, 4, 0, 0, 0, 6,
+ 0, 4, 0, 0, 0, 8,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
@@ -8418,7 +8440,7 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -8535,9 +8557,9 @@ const BYTE d2deffect[] = 83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
171, 73, 83, 71, 78, 44,
0, 0, 0, 1, 0, 0,
0, 8, 0, 0, 0, 32,
@@ -8565,14 +8587,14 @@ const BYTE d2deffect[] = 80, 111, 115, 105, 116, 105,
111, 110, 0, 84, 69, 88,
67, 79, 79, 82, 68, 0,
- 171, 171, 171, 7, 94, 0,
+ 171, 171, 171, 91, 94, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 224,
9, 0, 0, 68, 88, 66,
- 67, 127, 75, 112, 105, 91,
- 217, 180, 33, 240, 130, 210,
- 53, 48, 198, 20, 52, 1,
+ 67, 76, 106, 34, 250, 169,
+ 50, 124, 43, 130, 255, 198,
+ 178, 126, 127, 40, 188, 1,
0, 0, 0, 224, 9, 0,
0, 6, 0, 0, 0, 56,
0, 0, 0, 128, 2, 0,
@@ -8844,7 +8866,7 @@ const BYTE d2deffect[] = 0, 0, 0, 33, 0, 0,
0, 3, 0, 0, 0, 0,
0, 0, 0, 3, 0, 0,
- 0, 17, 0, 0, 0, 0,
+ 0, 19, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0,
0, 3, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
@@ -8854,7 +8876,7 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 8, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -8960,10 +8982,10 @@ const BYTE d2deffect[] = 72, 76, 83, 76, 32, 83,
104, 97, 100, 101, 114, 32,
67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 171, 73, 83, 71,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 171, 171, 171, 73, 83, 71,
78, 104, 0, 0, 0, 3,
0, 0, 0, 8, 0, 0,
0, 80, 0, 0, 0, 0,
@@ -8990,13 +9012,13 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 15, 0, 0, 0, 83,
86, 95, 84, 97, 114, 103,
- 101, 116, 0, 171, 171, 75,
+ 101, 116, 0, 171, 171, 159,
101, 0, 0, 0, 0, 0,
0, 65, 48, 0, 44, 7,
0, 0, 68, 88, 66, 67,
- 2, 114, 191, 189, 220, 178,
- 221, 187, 160, 105, 131, 115,
- 60, 218, 164, 36, 1, 0,
+ 172, 27, 205, 113, 176, 254,
+ 27, 44, 22, 107, 179, 112,
+ 127, 38, 148, 161, 1, 0,
0, 0, 44, 7, 0, 0,
6, 0, 0, 0, 56, 0,
0, 0, 148, 1, 0, 0,
@@ -9142,7 +9164,7 @@ const BYTE d2deffect[] = 65, 84, 116, 0, 0, 0,
12, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 6, 0,
+ 4, 0, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
@@ -9152,7 +9174,7 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -9269,9 +9291,9 @@ const BYTE d2deffect[] = 76, 32, 83, 104, 97, 100,
101, 114, 32, 67, 111, 109,
112, 105, 108, 101, 114, 32,
- 57, 46, 51, 48, 46, 57,
- 50, 48, 48, 46, 49, 54,
- 51, 56, 52, 0, 171, 171,
+ 54, 46, 51, 46, 57, 54,
+ 48, 48, 46, 49, 54, 51,
+ 56, 52, 0, 171, 171, 171,
73, 83, 71, 78, 44, 0,
0, 0, 1, 0, 0, 0,
8, 0, 0, 0, 32, 0,
@@ -9299,14 +9321,14 @@ const BYTE d2deffect[] = 111, 115, 105, 116, 105, 111,
110, 0, 84, 69, 88, 67,
79, 79, 82, 68, 0, 171,
- 171, 171, 58, 111, 0, 0,
+ 171, 171, 142, 111, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 192, 7,
0, 0, 68, 88, 66, 67,
- 6, 207, 28, 59, 230, 215,
- 167, 102, 185, 167, 244, 219,
- 74, 22, 220, 53, 1, 0,
+ 73, 174, 125, 52, 147, 212,
+ 172, 159, 223, 39, 1, 144,
+ 137, 10, 201, 206, 1, 0,
0, 0, 192, 7, 0, 0,
6, 0, 0, 0, 56, 0,
0, 0, 196, 1, 0, 0,
@@ -9487,7 +9509,7 @@ const BYTE d2deffect[] = 116, 0, 0, 0, 19, 0,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 3, 0,
- 0, 0, 9, 0, 0, 0,
+ 0, 0, 10, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
1, 0, 0, 0, 0, 0,
@@ -9497,7 +9519,7 @@ const BYTE d2deffect[] = 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 6, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -9603,10 +9625,10 @@ const BYTE d2deffect[] = 32, 72, 76, 83, 76, 32,
83, 104, 97, 100, 101, 114,
32, 67, 111, 109, 112, 105,
- 108, 101, 114, 32, 57, 46,
- 51, 48, 46, 57, 50, 48,
- 48, 46, 49, 54, 51, 56,
- 52, 0, 171, 171, 73, 83,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 171, 171, 73, 83,
71, 78, 104, 0, 0, 0,
3, 0, 0, 0, 8, 0,
0, 0, 80, 0, 0, 0,
@@ -9634,13 +9656,13 @@ const BYTE d2deffect[] = 0, 0, 15, 0, 0, 0,
83, 86, 95, 84, 97, 114,
103, 101, 116, 0, 171, 171,
- 126, 118, 0, 0, 0, 0,
+ 210, 118, 0, 0, 0, 0,
0, 0, 65, 80, 111, 115,
87, 114, 97, 112, 0, 44,
7, 0, 0, 68, 88, 66,
- 67, 2, 114, 191, 189, 220,
- 178, 221, 187, 160, 105, 131,
- 115, 60, 218, 164, 36, 1,
+ 67, 172, 27, 205, 113, 176,
+ 254, 27, 44, 22, 107, 179,
+ 112, 127, 38, 148, 161, 1,
0, 0, 0, 44, 7, 0,
0, 6, 0, 0, 0, 56,
0, 0, 0, 148, 1, 0,
@@ -9786,7 +9808,7 @@ const BYTE d2deffect[] = 84, 65, 84, 116, 0, 0,
0, 12, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
- 0, 4, 0, 0, 0, 6,
+ 0, 4, 0, 0, 0, 8,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
@@ -9796,7 +9818,7 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -9913,9 +9935,9 @@ const BYTE d2deffect[] = 83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
171, 73, 83, 71, 78, 44,
0, 0, 0, 1, 0, 0,
0, 8, 0, 0, 0, 32,
@@ -9943,14 +9965,14 @@ const BYTE d2deffect[] = 80, 111, 115, 105, 116, 105,
111, 110, 0, 84, 69, 88,
67, 79, 79, 82, 68, 0,
- 171, 171, 171, 83, 126, 0,
+ 171, 171, 171, 167, 126, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 228,
9, 0, 0, 68, 88, 66,
- 67, 200, 229, 12, 172, 236,
- 253, 99, 236, 189, 154, 200,
- 222, 235, 13, 47, 209, 1,
+ 67, 193, 68, 83, 4, 120,
+ 206, 206, 65, 213, 56, 189,
+ 186, 120, 85, 235, 59, 1,
0, 0, 0, 228, 9, 0,
0, 6, 0, 0, 0, 56,
0, 0, 0, 128, 2, 0,
@@ -10222,7 +10244,7 @@ const BYTE d2deffect[] = 0, 0, 0, 33, 0, 0,
0, 3, 0, 0, 0, 0,
0, 0, 0, 3, 0, 0,
- 0, 17, 0, 0, 0, 0,
+ 0, 19, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0,
0, 3, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
@@ -10232,7 +10254,7 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 8, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -10339,9 +10361,9 @@ const BYTE d2deffect[] = 83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
171, 73, 83, 71, 78, 104,
0, 0, 0, 3, 0, 0,
0, 8, 0, 0, 0, 80,
@@ -10369,13 +10391,13 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 15,
0, 0, 0, 83, 86, 95,
84, 97, 114, 103, 101, 116,
- 0, 171, 171, 151, 133, 0,
+ 0, 171, 171, 235, 133, 0,
0, 0, 0, 0, 0, 65,
48, 87, 114, 97, 112, 0,
44, 7, 0, 0, 68, 88,
- 66, 67, 2, 114, 191, 189,
- 220, 178, 221, 187, 160, 105,
- 131, 115, 60, 218, 164, 36,
+ 66, 67, 172, 27, 205, 113,
+ 176, 254, 27, 44, 22, 107,
+ 179, 112, 127, 38, 148, 161,
1, 0, 0, 0, 44, 7,
0, 0, 6, 0, 0, 0,
56, 0, 0, 0, 148, 1,
@@ -10522,7 +10544,7 @@ const BYTE d2deffect[] = 0, 0, 12, 0, 0, 0,
2, 0, 0, 0, 0, 0,
0, 0, 4, 0, 0, 0,
- 6, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -10532,7 +10554,7 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -10648,9 +10670,9 @@ const BYTE d2deffect[] = 76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
- 114, 32, 57, 46, 51, 48,
- 46, 57, 50, 48, 48, 46,
- 49, 54, 51, 56, 52, 0,
+ 114, 32, 54, 46, 51, 46,
+ 57, 54, 48, 48, 46, 49,
+ 54, 51, 56, 52, 0, 171,
171, 171, 73, 83, 71, 78,
44, 0, 0, 0, 1, 0,
0, 0, 8, 0, 0, 0,
@@ -10678,14 +10700,14 @@ const BYTE d2deffect[] = 95, 80, 111, 115, 105, 116,
105, 111, 110, 0, 84, 69,
88, 67, 79, 79, 82, 68,
- 0, 171, 171, 171, 142, 143,
+ 0, 171, 171, 171, 226, 143,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
196, 7, 0, 0, 68, 88,
- 66, 67, 179, 20, 248, 91,
- 13, 228, 55, 25, 123, 146,
- 28, 72, 132, 38, 155, 180,
+ 66, 67, 223, 174, 80, 104,
+ 241, 52, 44, 173, 100, 134,
+ 52, 219, 15, 210, 214, 245,
1, 0, 0, 0, 196, 7,
0, 0, 6, 0, 0, 0,
56, 0, 0, 0, 196, 1,
@@ -10866,7 +10888,7 @@ const BYTE d2deffect[] = 65, 84, 116, 0, 0, 0,
19, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 9, 0,
+ 3, 0, 0, 0, 10, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 1, 0, 0, 0,
@@ -10876,7 +10898,7 @@ const BYTE d2deffect[] = 0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 6, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -10983,10 +11005,10 @@ const BYTE d2deffect[] = 32, 72, 76, 83, 76, 32,
83, 104, 97, 100, 101, 114,
32, 67, 111, 109, 112, 105,
- 108, 101, 114, 32, 57, 46,
- 51, 48, 46, 57, 50, 48,
- 48, 46, 49, 54, 51, 56,
- 52, 0, 171, 171, 73, 83,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 171, 171, 73, 83,
71, 78, 104, 0, 0, 0,
3, 0, 0, 0, 8, 0,
0, 0, 80, 0, 0, 0,
@@ -11014,14 +11036,14 @@ const BYTE d2deffect[] = 0, 0, 15, 0, 0, 0,
83, 86, 95, 84, 97, 114,
103, 101, 116, 0, 171, 171,
- 210, 150, 0, 0, 0, 0,
+ 38, 151, 0, 0, 0, 0,
0, 0, 65, 80, 111, 115,
77, 105, 114, 114, 111, 114,
0, 44, 7, 0, 0, 68,
- 88, 66, 67, 2, 114, 191,
- 189, 220, 178, 221, 187, 160,
- 105, 131, 115, 60, 218, 164,
- 36, 1, 0, 0, 0, 44,
+ 88, 66, 67, 172, 27, 205,
+ 113, 176, 254, 27, 44, 22,
+ 107, 179, 112, 127, 38, 148,
+ 161, 1, 0, 0, 0, 44,
7, 0, 0, 6, 0, 0,
0, 56, 0, 0, 0, 148,
1, 0, 0, 104, 3, 0,
@@ -11167,7 +11189,7 @@ const BYTE d2deffect[] = 0, 0, 0, 12, 0, 0,
0, 2, 0, 0, 0, 0,
0, 0, 0, 4, 0, 0,
- 0, 6, 0, 0, 0, 0,
+ 0, 8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -11177,7 +11199,7 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 4, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -11293,10 +11315,10 @@ const BYTE d2deffect[] = 72, 76, 83, 76, 32, 83,
104, 97, 100, 101, 114, 32,
67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 171, 73, 83, 71,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 171, 171, 171, 73, 83, 71,
78, 44, 0, 0, 0, 1,
0, 0, 0, 8, 0, 0,
0, 32, 0, 0, 0, 0,
@@ -11323,15 +11345,15 @@ const BYTE d2deffect[] = 86, 95, 80, 111, 115, 105,
116, 105, 111, 110, 0, 84,
69, 88, 67, 79, 79, 82,
- 68, 0, 171, 171, 171, 173,
- 158, 0, 0, 0, 0, 0,
+ 68, 0, 171, 171, 171, 1,
+ 159, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
0, 232, 9, 0, 0, 68,
- 88, 66, 67, 212, 75, 202,
- 91, 7, 109, 80, 28, 42,
- 15, 64, 3, 93, 90, 7,
- 16, 1, 0, 0, 0, 232,
+ 88, 66, 67, 48, 133, 157,
+ 76, 135, 209, 82, 153, 49,
+ 138, 172, 57, 31, 63, 161,
+ 231, 1, 0, 0, 0, 232,
9, 0, 0, 6, 0, 0,
0, 56, 0, 0, 0, 128,
2, 0, 0, 88, 6, 0,
@@ -11602,7 +11624,7 @@ const BYTE d2deffect[] = 84, 116, 0, 0, 0, 33,
0, 0, 0, 3, 0, 0,
0, 0, 0, 0, 0, 3,
- 0, 0, 0, 17, 0, 0,
+ 0, 0, 0, 19, 0, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 3, 0, 0,
0, 2, 0, 0, 0, 0,
@@ -11612,7 +11634,7 @@ const BYTE d2deffect[] = 0, 2, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 8, 0, 0,
+ 0, 0, 0, 4, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -11720,9 +11742,9 @@ const BYTE d2deffect[] = 83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
171, 73, 83, 71, 78, 104,
0, 0, 0, 3, 0, 0,
0, 8, 0, 0, 0, 80,
@@ -11750,14 +11772,14 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 15,
0, 0, 0, 83, 86, 95,
84, 97, 114, 103, 101, 116,
- 0, 171, 171, 241, 165, 0,
+ 0, 171, 171, 69, 166, 0,
0, 0, 0, 0, 0, 65,
48, 77, 105, 114, 114, 111,
114, 0, 44, 7, 0, 0,
- 68, 88, 66, 67, 2, 114,
- 191, 189, 220, 178, 221, 187,
- 160, 105, 131, 115, 60, 218,
- 164, 36, 1, 0, 0, 0,
+ 68, 88, 66, 67, 172, 27,
+ 205, 113, 176, 254, 27, 44,
+ 22, 107, 179, 112, 127, 38,
+ 148, 161, 1, 0, 0, 0,
44, 7, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
148, 1, 0, 0, 104, 3,
@@ -11903,7 +11925,7 @@ const BYTE d2deffect[] = 116, 0, 0, 0, 12, 0,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 4, 0,
- 0, 0, 6, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -11913,7 +11935,7 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -12029,10 +12051,10 @@ const BYTE d2deffect[] = 32, 72, 76, 83, 76, 32,
83, 104, 97, 100, 101, 114,
32, 67, 111, 109, 112, 105,
- 108, 101, 114, 32, 57, 46,
- 51, 48, 46, 57, 50, 48,
- 48, 46, 49, 54, 51, 56,
- 52, 0, 171, 171, 73, 83,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 171, 171, 73, 83,
71, 78, 44, 0, 0, 0,
1, 0, 0, 0, 8, 0,
0, 0, 32, 0, 0, 0,
@@ -12060,14 +12082,14 @@ const BYTE d2deffect[] = 105, 116, 105, 111, 110, 0,
84, 69, 88, 67, 79, 79,
82, 68, 0, 171, 171, 171,
- 238, 175, 0, 0, 0, 0,
+ 66, 176, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
2, 0, 0, 0, 0, 0,
0, 0, 200, 7, 0, 0,
- 68, 88, 66, 67, 6, 226,
- 244, 224, 234, 191, 89, 63,
- 147, 234, 94, 217, 28, 153,
- 32, 29, 1, 0, 0, 0,
+ 68, 88, 66, 67, 238, 212,
+ 160, 43, 129, 11, 44, 225,
+ 62, 162, 102, 35, 9, 220,
+ 80, 177, 1, 0, 0, 0,
200, 7, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
196, 1, 0, 0, 56, 4,
@@ -12248,7 +12270,7 @@ const BYTE d2deffect[] = 0, 0, 19, 0, 0, 0,
2, 0, 0, 0, 0, 0,
0, 0, 3, 0, 0, 0,
- 9, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
@@ -12258,7 +12280,7 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 6, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -12365,10 +12387,10 @@ const BYTE d2deffect[] = 32, 72, 76, 83, 76, 32,
83, 104, 97, 100, 101, 114,
32, 67, 111, 109, 112, 105,
- 108, 101, 114, 32, 57, 46,
- 51, 48, 46, 57, 50, 48,
- 48, 46, 49, 54, 51, 56,
- 52, 0, 171, 171, 73, 83,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 171, 171, 171, 73, 83,
71, 78, 104, 0, 0, 0,
3, 0, 0, 0, 8, 0,
0, 0, 80, 0, 0, 0,
@@ -12396,21 +12418,21 @@ const BYTE d2deffect[] = 0, 0, 15, 0, 0, 0,
83, 86, 95, 84, 97, 114,
103, 101, 116, 0, 171, 171,
- 50, 183, 0, 0, 0, 0,
+ 134, 183, 0, 0, 0, 0,
0, 0, 83, 97, 109, 112,
108, 101, 77, 97, 115, 107,
101, 100, 84, 101, 120, 116,
- 117, 114, 101, 0, 72, 4,
+ 117, 114, 101, 0, 68, 4,
0, 0, 68, 88, 66, 67,
- 28, 248, 40, 83, 2, 166,
- 203, 194, 228, 163, 91, 123,
- 149, 165, 41, 212, 1, 0,
- 0, 0, 72, 4, 0, 0,
+ 77, 85, 167, 240, 56, 56,
+ 155, 78, 125, 96, 49, 253,
+ 103, 100, 22, 62, 1, 0,
+ 0, 0, 68, 4, 0, 0,
6, 0, 0, 0, 56, 0,
0, 0, 248, 0, 0, 0,
244, 1, 0, 0, 112, 2,
- 0, 0, 164, 3, 0, 0,
- 216, 3, 0, 0, 65, 111,
+ 0, 0, 160, 3, 0, 0,
+ 212, 3, 0, 0, 65, 111,
110, 57, 184, 0, 0, 0,
184, 0, 0, 0, 0, 2,
254, 255, 132, 0, 0, 0,
@@ -12488,7 +12510,7 @@ const BYTE d2deffect[] = 65, 84, 116, 0, 0, 0,
5, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
@@ -12498,7 +12520,7 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -12506,7 +12528,7 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 82, 68, 69, 70,
- 44, 1, 0, 0, 1, 0,
+ 40, 1, 0, 0, 1, 0,
0, 0, 64, 0, 0, 0,
1, 0, 0, 0, 28, 0,
0, 0, 0, 4, 254, 255,
@@ -12553,459 +12575,479 @@ const BYTE d2deffect[] = 72, 76, 83, 76, 32, 83,
104, 97, 100, 101, 114, 32,
67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 171, 171, 73, 83,
- 71, 78, 44, 0, 0, 0,
- 1, 0, 0, 0, 8, 0,
- 0, 0, 32, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 7, 3,
- 0, 0, 80, 79, 83, 73,
- 84, 73, 79, 78, 0, 171,
- 171, 171, 79, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
- 0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 73, 83, 71, 78, 44, 0,
0, 0, 1, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 12, 0, 0,
- 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 3, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 26, 191, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
+ 7, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 3,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 110, 191, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 212, 3,
+ 0, 0, 68, 88, 66, 67,
+ 98, 136, 224, 212, 103, 235,
+ 205, 77, 125, 241, 101, 150,
+ 199, 56, 208, 85, 1, 0,
0, 0, 212, 3, 0, 0,
- 68, 88, 66, 67, 206, 93,
- 214, 184, 226, 209, 206, 42,
- 139, 90, 123, 148, 119, 142,
- 142, 148, 1, 0, 0, 0,
- 212, 3, 0, 0, 6, 0,
- 0, 0, 56, 0, 0, 0,
- 224, 0, 0, 0, 188, 1,
- 0, 0, 56, 2, 0, 0,
- 48, 3, 0, 0, 160, 3,
- 0, 0, 65, 111, 110, 57,
- 160, 0, 0, 0, 160, 0,
- 0, 0, 0, 2, 255, 255,
- 116, 0, 0, 0, 44, 0,
- 0, 0, 0, 0, 44, 0,
- 0, 0, 44, 0, 0, 0,
- 44, 0, 2, 0, 36, 0,
- 0, 0, 44, 0, 0, 0,
- 0, 0, 1, 1, 1, 0,
- 1, 2, 255, 255, 31, 0,
- 0, 2, 0, 0, 0, 128,
- 0, 0, 15, 176, 31, 0,
- 0, 2, 0, 0, 0, 144,
- 0, 8, 15, 160, 31, 0,
- 0, 2, 0, 0, 0, 144,
- 1, 8, 15, 160, 1, 0,
- 0, 2, 0, 0, 3, 128,
- 0, 0, 235, 176, 66, 0,
- 0, 3, 1, 0, 15, 128,
- 0, 0, 228, 176, 0, 8,
- 228, 160, 66, 0, 0, 3,
+ 6, 0, 0, 0, 56, 0,
+ 0, 0, 224, 0, 0, 0,
+ 188, 1, 0, 0, 56, 2,
+ 0, 0, 48, 3, 0, 0,
+ 160, 3, 0, 0, 65, 111,
+ 110, 57, 160, 0, 0, 0,
+ 160, 0, 0, 0, 0, 2,
+ 255, 255, 116, 0, 0, 0,
+ 44, 0, 0, 0, 0, 0,
+ 44, 0, 0, 0, 44, 0,
+ 0, 0, 44, 0, 2, 0,
+ 36, 0, 0, 0, 44, 0,
+ 0, 0, 0, 0, 1, 1,
+ 1, 0, 1, 2, 255, 255,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 1, 0, 0, 2, 0, 0,
+ 3, 128, 0, 0, 235, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 1, 8,
+ 228, 160, 5, 0, 0, 3,
0, 0, 15, 128, 0, 0,
- 228, 128, 1, 8, 228, 160,
- 5, 0, 0, 3, 0, 0,
- 15, 128, 0, 0, 255, 128,
- 1, 0, 228, 128, 1, 0,
- 0, 2, 0, 8, 15, 128,
- 0, 0, 228, 128, 255, 255,
- 0, 0, 83, 72, 68, 82,
- 212, 0, 0, 0, 64, 0,
- 0, 0, 53, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 0, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 1, 0, 0, 0,
+ 255, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 212, 0, 0, 0,
+ 64, 0, 0, 0, 53, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 98, 16,
+ 0, 3, 194, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 2, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 0, 0, 0, 0,
- 85, 85, 0, 0, 88, 24,
- 0, 4, 0, 112, 16, 0,
- 1, 0, 0, 0, 85, 85,
- 0, 0, 98, 16, 0, 3,
- 50, 16, 16, 0, 1, 0,
- 0, 0, 98, 16, 0, 3,
- 194, 16, 16, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 104, 0, 0, 2,
- 2, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 0, 0, 0, 0, 70, 16,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 1, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 1, 0, 0, 0, 230, 26,
+ 230, 26, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
16, 0, 1, 0, 0, 0,
- 70, 126, 16, 0, 1, 0,
- 0, 0, 0, 96, 16, 0,
- 1, 0, 0, 0, 56, 0,
- 0, 7, 242, 32, 16, 0,
- 0, 0, 0, 0, 70, 14,
+ 56, 0, 0, 7, 242, 32,
16, 0, 0, 0, 0, 0,
- 246, 15, 16, 0, 1, 0,
- 0, 0, 62, 0, 0, 1,
- 83, 84, 65, 84, 116, 0,
- 0, 0, 4, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 82, 68,
- 69, 70, 240, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 28, 0, 0, 0, 0, 4,
- 255, 255, 0, 1, 0, 0,
- 187, 0, 0, 0, 156, 0,
- 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 82, 68, 69, 70, 240, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 187, 0, 0, 0,
+ 156, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 165, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0,
- 0, 0, 178, 0, 0, 0,
- 2, 0, 0, 0, 5, 0,
- 0, 0, 4, 0, 0, 0,
- 255, 255, 255, 255, 0, 0,
+ 0, 0, 165, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0,
- 12, 0, 0, 0, 182, 0,
+ 0, 0, 0, 0, 178, 0,
0, 0, 2, 0, 0, 0,
5, 0, 0, 0, 4, 0,
0, 0, 255, 255, 255, 255,
- 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 12, 0, 0, 0,
- 115, 83, 97, 109, 112, 108,
- 101, 114, 0, 115, 77, 97,
- 115, 107, 83, 97, 109, 112,
- 108, 101, 114, 0, 116, 101,
- 120, 0, 109, 97, 115, 107,
- 0, 77, 105, 99, 114, 111,
- 115, 111, 102, 116, 32, 40,
- 82, 41, 32, 72, 76, 83,
- 76, 32, 83, 104, 97, 100,
- 101, 114, 32, 67, 111, 109,
- 112, 105, 108, 101, 114, 32,
- 57, 46, 51, 48, 46, 57,
- 50, 48, 48, 46, 49, 54,
- 51, 56, 52, 0, 171, 171,
- 73, 83, 71, 78, 104, 0,
- 0, 0, 3, 0, 0, 0,
- 8, 0, 0, 0, 80, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 3, 0,
+ 182, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 1, 0, 0, 0,
+ 1, 0, 0, 0, 12, 0,
+ 0, 0, 115, 83, 97, 109,
+ 112, 108, 101, 114, 0, 115,
+ 77, 97, 115, 107, 83, 97,
+ 109, 112, 108, 101, 114, 0,
+ 116, 101, 120, 0, 109, 97,
+ 115, 107, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 54, 46, 51, 46,
+ 57, 54, 48, 48, 46, 49,
+ 54, 51, 56, 52, 0, 171,
+ 171, 171, 73, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 92, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 92, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 3, 0, 0, 92, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 12, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 0, 0,
- 12, 12, 0, 0, 83, 86,
- 95, 80, 111, 115, 105, 116,
- 105, 111, 110, 0, 84, 69,
- 88, 67, 79, 79, 82, 68,
- 0, 171, 171, 171, 79, 83,
- 71, 78, 44, 0, 0, 0,
- 1, 0, 0, 0, 8, 0,
- 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 15, 0,
- 0, 0, 83, 86, 95, 84,
- 97, 114, 103, 101, 116, 0,
- 171, 171, 122, 195, 0, 0,
- 0, 0, 0, 0, 83, 97,
- 109, 112, 108, 101, 84, 101,
- 120, 116, 117, 114, 101, 87,
- 105, 116, 104, 83, 104, 97,
- 100, 111, 119, 0, 4, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 128, 63, 1, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171, 202, 195,
+ 0, 0, 0, 0, 0, 0,
+ 83, 97, 109, 112, 108, 101,
+ 84, 101, 120, 116, 117, 114,
+ 101, 87, 105, 116, 104, 83,
+ 104, 97, 100, 111, 119, 0,
+ 4, 0, 0, 0, 1, 0,
0, 0, 0, 0, 128, 63,
1, 0, 0, 0, 0, 0,
128, 63, 1, 0, 0, 0,
0, 0, 128, 63, 1, 0,
- 0, 0, 3, 0, 0, 0,
- 255, 255, 255, 255, 72, 4,
- 0, 0, 68, 88, 66, 67,
- 28, 248, 40, 83, 2, 166,
- 203, 194, 228, 163, 91, 123,
- 149, 165, 41, 212, 1, 0,
- 0, 0, 72, 4, 0, 0,
- 6, 0, 0, 0, 56, 0,
- 0, 0, 248, 0, 0, 0,
- 244, 1, 0, 0, 112, 2,
- 0, 0, 164, 3, 0, 0,
- 216, 3, 0, 0, 65, 111,
- 110, 57, 184, 0, 0, 0,
- 184, 0, 0, 0, 0, 2,
- 254, 255, 132, 0, 0, 0,
- 52, 0, 0, 0, 1, 0,
- 36, 0, 0, 0, 48, 0,
- 0, 0, 48, 0, 0, 0,
- 36, 0, 1, 0, 48, 0,
- 0, 0, 0, 0, 3, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 2,
- 254, 255, 81, 0, 0, 5,
- 4, 0, 15, 160, 0, 0,
0, 0, 0, 0, 128, 63,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 255, 255, 255, 255,
+ 68, 4, 0, 0, 68, 88,
+ 66, 67, 77, 85, 167, 240,
+ 56, 56, 155, 78, 125, 96,
+ 49, 253, 103, 100, 22, 62,
+ 1, 0, 0, 0, 68, 4,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 248, 0,
+ 0, 0, 244, 1, 0, 0,
+ 112, 2, 0, 0, 160, 3,
+ 0, 0, 212, 3, 0, 0,
+ 65, 111, 110, 57, 184, 0,
+ 0, 0, 184, 0, 0, 0,
+ 0, 2, 254, 255, 132, 0,
+ 0, 0, 52, 0, 0, 0,
+ 1, 0, 36, 0, 0, 0,
+ 48, 0, 0, 0, 48, 0,
+ 0, 0, 36, 0, 1, 0,
+ 48, 0, 0, 0, 0, 0,
+ 3, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 31, 0, 0, 2,
- 5, 0, 0, 128, 0, 0,
- 15, 144, 4, 0, 0, 4,
- 0, 0, 3, 224, 0, 0,
- 228, 144, 2, 0, 238, 160,
- 2, 0, 228, 160, 4, 0,
- 0, 4, 0, 0, 12, 224,
- 0, 0, 20, 144, 3, 0,
- 180, 160, 3, 0, 20, 160,
+ 1, 2, 254, 255, 81, 0,
+ 0, 5, 4, 0, 15, 160,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 5, 0, 0, 128,
+ 0, 0, 15, 144, 4, 0,
+ 0, 4, 0, 0, 3, 224,
+ 0, 0, 228, 144, 2, 0,
+ 238, 160, 2, 0, 228, 160,
4, 0, 0, 4, 0, 0,
- 3, 128, 0, 0, 228, 144,
- 1, 0, 238, 160, 1, 0,
- 228, 160, 2, 0, 0, 3,
- 0, 0, 3, 192, 0, 0,
- 228, 128, 0, 0, 228, 160,
- 1, 0, 0, 2, 0, 0,
- 12, 192, 4, 0, 68, 160,
- 255, 255, 0, 0, 83, 72,
- 68, 82, 244, 0, 0, 0,
- 64, 0, 1, 0, 61, 0,
- 0, 0, 89, 0, 0, 4,
- 70, 142, 32, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 95, 0, 0, 3, 50, 16,
- 16, 0, 0, 0, 0, 0,
- 103, 0, 0, 4, 242, 32,
- 16, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 101, 0,
- 0, 3, 50, 32, 16, 0,
- 1, 0, 0, 0, 101, 0,
- 0, 3, 194, 32, 16, 0,
- 1, 0, 0, 0, 50, 0,
- 0, 11, 50, 32, 16, 0,
- 0, 0, 0, 0, 70, 16,
+ 12, 224, 0, 0, 20, 144,
+ 3, 0, 180, 160, 3, 0,
+ 20, 160, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 1, 0, 238, 160,
+ 1, 0, 228, 160, 2, 0,
+ 0, 3, 0, 0, 3, 192,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 12, 192, 4, 0,
+ 68, 160, 255, 255, 0, 0,
+ 83, 72, 68, 82, 244, 0,
+ 0, 0, 64, 0, 1, 0,
+ 61, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 95, 0, 0, 3,
+ 50, 16, 16, 0, 0, 0,
+ 0, 0, 103, 0, 0, 4,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 50, 32,
16, 0, 0, 0, 0, 0,
- 230, 138, 32, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
0, 0, 0, 0, 0, 0,
- 70, 128, 32, 0, 0, 0,
+ 0, 0, 70, 128, 32, 0,
0, 0, 0, 0, 0, 0,
- 54, 0, 0, 8, 194, 32,
- 16, 0, 0, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 194, 32, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 128, 63, 50, 0, 0, 11,
- 50, 32, 16, 0, 1, 0,
- 0, 0, 70, 16, 16, 0,
- 0, 0, 0, 0, 230, 138,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 70, 128,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 50, 0,
- 0, 11, 194, 32, 16, 0,
- 1, 0, 0, 0, 6, 20,
+ 0, 0, 128, 63, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
16, 0, 0, 0, 0, 0,
- 166, 142, 32, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 6, 132, 32, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 62, 0, 0, 1, 83, 84,
- 65, 84, 116, 0, 0, 0,
- 5, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 6, 20, 16, 0, 0, 0,
+ 0, 0, 166, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 6, 132, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 82, 68, 69, 70,
- 44, 1, 0, 0, 1, 0,
- 0, 0, 64, 0, 0, 0,
- 1, 0, 0, 0, 28, 0,
- 0, 0, 0, 4, 254, 255,
- 0, 1, 0, 0, 246, 0,
- 0, 0, 60, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 40, 1, 0, 0,
+ 1, 0, 0, 0, 64, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 246, 0, 0, 0, 60, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 99, 98,
- 48, 0, 60, 0, 0, 0,
- 4, 0, 0, 0, 88, 0,
- 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 184, 0, 0, 0,
- 0, 0, 0, 0, 16, 0,
- 0, 0, 2, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 212, 0, 0, 0,
- 16, 0, 0, 0, 16, 0,
- 0, 0, 2, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 222, 0, 0, 0,
- 32, 0, 0, 0, 16, 0,
- 0, 0, 2, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 236, 0, 0, 0,
- 48, 0, 0, 0, 16, 0,
+ 99, 98, 48, 0, 60, 0,
+ 0, 0, 4, 0, 0, 0,
+ 88, 0, 0, 0, 64, 0,
0, 0, 0, 0, 0, 0,
- 196, 0, 0, 0, 0, 0,
- 0, 0, 81, 117, 97, 100,
- 68, 101, 115, 99, 0, 171,
- 171, 171, 1, 0, 3, 0,
- 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
0, 0, 0, 0, 0, 0,
- 84, 101, 120, 67, 111, 111,
- 114, 100, 115, 0, 77, 97,
- 115, 107, 84, 101, 120, 67,
- 111, 111, 114, 100, 115, 0,
- 84, 101, 120, 116, 67, 111,
- 108, 111, 114, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 171, 171, 73, 83,
- 71, 78, 44, 0, 0, 0,
- 1, 0, 0, 0, 8, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 212, 0,
+ 0, 0, 16, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 222, 0,
0, 0, 32, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 236, 0,
+ 0, 0, 48, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 196, 0, 0, 0,
+ 0, 0, 0, 0, 81, 117,
+ 97, 100, 68, 101, 115, 99,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 7, 3,
- 0, 0, 80, 79, 83, 73,
- 84, 73, 79, 78, 0, 171,
- 171, 171, 79, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
+ 0, 0, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 77, 97, 115, 107, 84, 101,
+ 120, 67, 111, 111, 114, 100,
+ 115, 0, 84, 101, 120, 116,
+ 67, 111, 108, 111, 114, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 54,
+ 46, 51, 46, 57, 54, 48,
+ 48, 46, 49, 54, 51, 56,
+ 52, 0, 73, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 0, 0, 7, 3, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 171, 171, 171,
+ 79, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 12, 0, 0,
- 92, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 3, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 162, 199, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 1, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
- 0, 0, 232, 9, 0, 0,
- 68, 88, 66, 67, 108, 162,
- 44, 78, 210, 223, 228, 191,
- 29, 172, 165, 191, 36, 14,
- 229, 150, 1, 0, 0, 0,
- 232, 9, 0, 0, 6, 0,
- 0, 0, 56, 0, 0, 0,
- 248, 2, 0, 0, 8, 7,
- 0, 0, 132, 7, 0, 0,
- 68, 9, 0, 0, 180, 9,
- 0, 0, 65, 111, 110, 57,
- 184, 2, 0, 0, 184, 2,
- 0, 0, 0, 2, 255, 255,
- 120, 2, 0, 0, 64, 0,
- 0, 0, 2, 0, 40, 0,
- 0, 0, 64, 0, 0, 0,
- 64, 0, 1, 0, 36, 0,
+ 3, 12, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 242, 199,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 232, 9, 0, 0, 68, 88,
+ 66, 67, 128, 131, 241, 85,
+ 199, 21, 192, 89, 55, 255,
+ 82, 94, 121, 175, 16, 184,
+ 1, 0, 0, 0, 232, 9,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 248, 2,
+ 0, 0, 8, 7, 0, 0,
+ 132, 7, 0, 0, 68, 9,
+ 0, 0, 180, 9, 0, 0,
+ 65, 111, 110, 57, 184, 2,
+ 0, 0, 184, 2, 0, 0,
+ 0, 2, 255, 255, 120, 2,
0, 0, 64, 0, 0, 0,
+ 2, 0, 40, 0, 0, 0,
+ 64, 0, 0, 0, 64, 0,
+ 1, 0, 36, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 4, 0,
3, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 6, 0,
- 4, 0, 3, 0, 0, 0,
- 0, 0, 1, 2, 255, 255,
- 31, 0, 0, 2, 0, 0,
- 0, 128, 0, 0, 15, 176,
- 31, 0, 0, 2, 0, 0,
- 0, 144, 0, 8, 15, 160,
- 2, 0, 0, 3, 0, 0,
- 1, 128, 0, 0, 0, 176,
- 0, 0, 85, 160, 1, 0,
- 0, 2, 0, 0, 2, 128,
- 0, 0, 85, 176, 2, 0,
- 0, 3, 1, 0, 1, 128,
+ 1, 2, 255, 255, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 2, 0,
+ 0, 3, 0, 0, 1, 128,
0, 0, 0, 176, 0, 0,
- 0, 160, 1, 0, 0, 2,
- 1, 0, 2, 128, 0, 0,
- 85, 176, 66, 0, 0, 3,
- 0, 0, 15, 128, 0, 0,
+ 85, 160, 1, 0, 0, 2,
+ 0, 0, 2, 128, 0, 0,
+ 85, 176, 2, 0, 0, 3,
+ 1, 0, 1, 128, 0, 0,
+ 0, 176, 0, 0, 0, 160,
+ 1, 0, 0, 2, 1, 0,
+ 2, 128, 0, 0, 85, 176,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 0, 8,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 255, 128, 3, 0, 85, 160,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 3, 0, 0, 160,
+ 1, 0, 255, 128, 0, 0,
+ 0, 128, 2, 0, 0, 3,
+ 1, 0, 1, 128, 0, 0,
+ 0, 176, 0, 0, 170, 160,
+ 1, 0, 0, 2, 1, 0,
+ 2, 128, 0, 0, 85, 176,
+ 2, 0, 0, 3, 2, 0,
+ 1, 128, 0, 0, 0, 176,
+ 0, 0, 255, 160, 1, 0,
+ 0, 2, 2, 0, 2, 128,
+ 0, 0, 85, 176, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 0, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 2, 0,
228, 128, 0, 8, 228, 160,
- 66, 0, 0, 3, 1, 0,
- 15, 128, 1, 0, 228, 128,
- 0, 8, 228, 160, 5, 0,
- 0, 3, 0, 0, 1, 128,
- 0, 0, 255, 128, 3, 0,
- 85, 160, 4, 0, 0, 4,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 3, 0, 170, 160,
+ 1, 0, 255, 128, 0, 0,
+ 0, 128, 4, 0, 0, 4,
0, 0, 1, 128, 3, 0,
- 0, 160, 1, 0, 255, 128,
+ 255, 160, 2, 0, 255, 128,
0, 0, 0, 128, 2, 0,
0, 3, 1, 0, 1, 128,
- 0, 0, 0, 176, 0, 0,
- 170, 160, 1, 0, 0, 2,
+ 0, 0, 0, 176, 1, 0,
+ 0, 160, 1, 0, 0, 2,
1, 0, 2, 128, 0, 0,
85, 176, 2, 0, 0, 3,
2, 0, 1, 128, 0, 0,
- 0, 176, 0, 0, 255, 160,
+ 0, 176, 1, 0, 85, 160,
1, 0, 0, 2, 2, 0,
2, 128, 0, 0, 85, 176,
66, 0, 0, 3, 1, 0,
@@ -13014,20 +13056,20 @@ const BYTE d2deffect[] = 0, 3, 2, 0, 15, 128,
2, 0, 228, 128, 0, 8,
228, 160, 4, 0, 0, 4,
- 0, 0, 1, 128, 3, 0,
- 170, 160, 1, 0, 255, 128,
+ 0, 0, 1, 128, 4, 0,
+ 0, 160, 1, 0, 255, 128,
0, 0, 0, 128, 4, 0,
0, 4, 0, 0, 1, 128,
- 3, 0, 255, 160, 2, 0,
+ 4, 0, 85, 160, 2, 0,
255, 128, 0, 0, 0, 128,
2, 0, 0, 3, 1, 0,
1, 128, 0, 0, 0, 176,
- 1, 0, 0, 160, 1, 0,
+ 1, 0, 170, 160, 1, 0,
0, 2, 1, 0, 2, 128,
0, 0, 85, 176, 2, 0,
0, 3, 2, 0, 1, 128,
0, 0, 0, 176, 1, 0,
- 85, 160, 1, 0, 0, 2,
+ 255, 160, 1, 0, 0, 2,
2, 0, 2, 128, 0, 0,
85, 176, 66, 0, 0, 3,
1, 0, 15, 128, 1, 0,
@@ -13036,511 +13078,489 @@ const BYTE d2deffect[] = 15, 128, 2, 0, 228, 128,
0, 8, 228, 160, 4, 0,
0, 4, 0, 0, 1, 128,
- 4, 0, 0, 160, 1, 0,
+ 4, 0, 170, 160, 1, 0,
255, 128, 0, 0, 0, 128,
4, 0, 0, 4, 0, 0,
- 1, 128, 4, 0, 85, 160,
+ 1, 128, 4, 0, 255, 160,
2, 0, 255, 128, 0, 0,
0, 128, 2, 0, 0, 3,
1, 0, 1, 128, 0, 0,
- 0, 176, 1, 0, 170, 160,
+ 0, 176, 2, 0, 0, 160,
1, 0, 0, 2, 1, 0,
2, 128, 0, 0, 85, 176,
- 2, 0, 0, 3, 2, 0,
- 1, 128, 0, 0, 0, 176,
- 1, 0, 255, 160, 1, 0,
- 0, 2, 2, 0, 2, 128,
- 0, 0, 85, 176, 66, 0,
- 0, 3, 1, 0, 15, 128,
- 1, 0, 228, 128, 0, 8,
- 228, 160, 66, 0, 0, 3,
- 2, 0, 15, 128, 2, 0,
- 228, 128, 0, 8, 228, 160,
- 4, 0, 0, 4, 0, 0,
- 1, 128, 4, 0, 170, 160,
- 1, 0, 255, 128, 0, 0,
- 0, 128, 4, 0, 0, 4,
- 0, 0, 1, 128, 4, 0,
- 255, 160, 2, 0, 255, 128,
- 0, 0, 0, 128, 2, 0,
- 0, 3, 1, 0, 1, 128,
- 0, 0, 0, 176, 2, 0,
- 0, 160, 1, 0, 0, 2,
- 1, 0, 2, 128, 0, 0,
- 85, 176, 66, 0, 0, 3,
- 1, 0, 15, 128, 1, 0,
- 228, 128, 0, 8, 228, 160,
- 4, 0, 0, 4, 0, 0,
- 1, 128, 5, 0, 0, 160,
- 1, 0, 255, 128, 0, 0,
- 0, 128, 5, 0, 0, 3,
- 0, 0, 15, 128, 0, 0,
- 0, 128, 6, 0, 228, 160,
- 1, 0, 0, 2, 0, 8,
- 15, 128, 0, 0, 228, 128,
- 255, 255, 0, 0, 83, 72,
- 68, 82, 8, 4, 0, 0,
- 64, 0, 0, 0, 2, 1,
- 0, 0, 89, 0, 0, 4,
- 70, 142, 32, 0, 0, 0,
- 0, 0, 10, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 0, 0, 0, 0,
- 88, 24, 0, 4, 0, 112,
- 16, 0, 0, 0, 0, 0,
- 85, 85, 0, 0, 98, 16,
- 0, 3, 50, 16, 16, 0,
- 1, 0, 0, 0, 101, 0,
- 0, 3, 242, 32, 16, 0,
- 0, 0, 0, 0, 104, 0,
- 0, 2, 4, 0, 0, 0,
- 0, 0, 0, 8, 242, 0,
- 16, 0, 0, 0, 0, 0,
- 6, 16, 16, 0, 1, 0,
- 0, 0, 38, 135, 32, 0,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 0, 8, 228, 160, 4, 0,
+ 0, 4, 0, 0, 1, 128,
+ 5, 0, 0, 160, 1, 0,
+ 255, 128, 0, 0, 0, 128,
+ 5, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 0, 128,
+ 6, 0, 228, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 8, 4, 0, 0, 64, 0,
+ 0, 0, 2, 1, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 90, 0,
+ 0, 3, 0, 96, 16, 0,
+ 0, 0, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 0, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 4, 0, 0, 0, 0, 0,
+ 0, 8, 242, 0, 16, 0,
+ 0, 0, 0, 0, 6, 16,
+ 16, 0, 1, 0, 0, 0,
+ 38, 135, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 82, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 7, 16, 0, 0, 0,
0, 0, 54, 0, 0, 5,
- 82, 0, 16, 0, 1, 0,
- 0, 0, 86, 7, 16, 0,
- 0, 0, 0, 0, 54, 0,
+ 162, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 1, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 2, 0, 0, 0, 230, 10,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 18, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 50, 0, 0, 10, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 54, 0,
0, 5, 162, 0, 16, 0,
- 1, 0, 0, 0, 86, 21,
+ 0, 0, 0, 0, 86, 21,
16, 0, 1, 0, 0, 0,
69, 0, 0, 9, 242, 0,
16, 0, 2, 0, 0, 0,
- 230, 10, 16, 0, 1, 0,
+ 70, 0, 16, 0, 0, 0,
0, 0, 70, 126, 16, 0,
0, 0, 0, 0, 0, 96,
16, 0, 0, 0, 0, 0,
69, 0, 0, 9, 242, 0,
- 16, 0, 1, 0, 0, 0,
- 70, 0, 16, 0, 1, 0,
+ 16, 0, 0, 0, 0, 0,
+ 230, 10, 16, 0, 0, 0,
0, 0, 70, 126, 16, 0,
0, 0, 0, 0, 0, 96,
16, 0, 0, 0, 0, 0,
- 56, 0, 0, 8, 18, 0,
- 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 10, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
58, 0, 16, 0, 2, 0,
- 0, 0, 26, 128, 32, 0,
- 0, 0, 0, 0, 6, 0,
- 0, 0, 50, 0, 0, 10,
- 18, 0, 16, 0, 1, 0,
- 0, 0, 10, 128, 32, 0,
- 0, 0, 0, 0, 6, 0,
- 0, 0, 58, 0, 16, 0,
- 1, 0, 0, 0, 10, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 18, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 1, 0, 0, 0, 38, 135,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 82, 0, 16, 0,
+ 2, 0, 0, 0, 86, 7,
16, 0, 1, 0, 0, 0,
54, 0, 0, 5, 162, 0,
- 16, 0, 0, 0, 0, 0,
+ 16, 0, 2, 0, 0, 0,
86, 21, 16, 0, 1, 0,
0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 2, 0,
+ 242, 0, 16, 0, 3, 0,
0, 0, 70, 0, 16, 0,
- 0, 0, 0, 0, 70, 126,
+ 2, 0, 0, 0, 70, 126,
16, 0, 0, 0, 0, 0,
0, 96, 16, 0, 0, 0,
0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 0, 0,
+ 242, 0, 16, 0, 2, 0,
0, 0, 230, 10, 16, 0,
- 0, 0, 0, 0, 70, 126,
+ 2, 0, 0, 0, 70, 126,
16, 0, 0, 0, 0, 0,
0, 96, 16, 0, 0, 0,
0, 0, 50, 0, 0, 10,
18, 0, 16, 0, 0, 0,
- 0, 0, 42, 128, 32, 0,
- 0, 0, 0, 0, 6, 0,
+ 0, 0, 10, 128, 32, 0,
+ 0, 0, 0, 0, 7, 0,
0, 0, 58, 0, 16, 0,
- 2, 0, 0, 0, 10, 0,
- 16, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
50, 0, 0, 10, 18, 0,
16, 0, 0, 0, 0, 0,
- 58, 128, 32, 0, 0, 0,
- 0, 0, 6, 0, 0, 0,
- 58, 0, 16, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 58, 0, 16, 0, 2, 0,
0, 0, 10, 0, 16, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 8, 242, 0, 16, 0,
- 1, 0, 0, 0, 6, 16,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 162, 0, 16, 0,
+ 1, 0, 0, 0, 86, 21,
16, 0, 1, 0, 0, 0,
- 38, 135, 32, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 54, 0, 0, 5, 82, 0,
- 16, 0, 2, 0, 0, 0,
- 86, 7, 16, 0, 1, 0,
- 0, 0, 54, 0, 0, 5,
- 162, 0, 16, 0, 2, 0,
- 0, 0, 86, 21, 16, 0,
- 1, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 3, 0, 0, 0, 70, 0,
- 16, 0, 2, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 2, 0, 0, 0, 230, 10,
+ 69, 0, 0, 9, 242, 0,
16, 0, 2, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 50, 0,
- 0, 10, 18, 0, 16, 0,
- 0, 0, 0, 0, 10, 128,
- 32, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 58, 0,
- 16, 0, 3, 0, 0, 0,
- 10, 0, 16, 0, 0, 0,
- 0, 0, 50, 0, 0, 10,
- 18, 0, 16, 0, 0, 0,
- 0, 0, 26, 128, 32, 0,
- 0, 0, 0, 0, 7, 0,
- 0, 0, 58, 0, 16, 0,
- 2, 0, 0, 0, 10, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
16, 0, 0, 0, 0, 0,
- 54, 0, 0, 5, 162, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 1, 0, 0, 0,
- 86, 21, 16, 0, 1, 0,
- 0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 2, 0,
- 0, 0, 70, 0, 16, 0,
- 1, 0, 0, 0, 70, 126,
- 16, 0, 0, 0, 0, 0,
- 0, 96, 16, 0, 0, 0,
- 0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 1, 0,
- 0, 0, 230, 10, 16, 0,
- 1, 0, 0, 0, 70, 126,
- 16, 0, 0, 0, 0, 0,
- 0, 96, 16, 0, 0, 0,
- 0, 0, 50, 0, 0, 10,
- 18, 0, 16, 0, 0, 0,
- 0, 0, 42, 128, 32, 0,
- 0, 0, 0, 0, 7, 0,
- 0, 0, 58, 0, 16, 0,
- 2, 0, 0, 0, 10, 0,
+ 230, 10, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
16, 0, 0, 0, 0, 0,
50, 0, 0, 10, 18, 0,
16, 0, 0, 0, 0, 0,
- 58, 128, 32, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 58, 0, 16, 0, 1, 0,
+ 58, 0, 16, 0, 2, 0,
0, 0, 10, 0, 16, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 8, 18, 0, 16, 0,
- 1, 0, 0, 0, 10, 16,
+ 0, 0, 0, 0, 50, 0,
+ 0, 10, 18, 0, 16, 0,
+ 0, 0, 0, 0, 58, 128,
+ 32, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 58, 0,
16, 0, 1, 0, 0, 0,
- 10, 128, 32, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 54, 0, 0, 5, 34, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 18, 0, 16, 0, 1, 0,
+ 0, 0, 10, 16, 16, 0,
+ 1, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 54, 0,
+ 0, 5, 34, 0, 16, 0,
+ 1, 0, 0, 0, 26, 16,
16, 0, 1, 0, 0, 0,
- 26, 16, 16, 0, 1, 0,
- 0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 1, 0,
- 0, 0, 70, 0, 16, 0,
- 1, 0, 0, 0, 70, 126,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
16, 0, 0, 0, 0, 0,
- 0, 96, 16, 0, 0, 0,
- 0, 0, 50, 0, 0, 10,
- 18, 0, 16, 0, 0, 0,
- 0, 0, 10, 128, 32, 0,
- 0, 0, 0, 0, 8, 0,
- 0, 0, 58, 0, 16, 0,
- 1, 0, 0, 0, 10, 0,
+ 50, 0, 0, 10, 18, 0,
16, 0, 0, 0, 0, 0,
- 56, 0, 0, 8, 242, 32,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 242, 32, 16, 0,
+ 0, 0, 0, 0, 6, 0,
16, 0, 0, 0, 0, 0,
- 6, 0, 16, 0, 0, 0,
- 0, 0, 70, 142, 32, 0,
- 0, 0, 0, 0, 9, 0,
- 0, 0, 62, 0, 0, 1,
- 83, 84, 65, 84, 116, 0,
- 0, 0, 30, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
- 5, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 30, 0, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 13, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 9, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 10, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 82, 68,
- 69, 70, 184, 1, 0, 0,
- 1, 0, 0, 0, 148, 0,
- 0, 0, 3, 0, 0, 0,
- 28, 0, 0, 0, 0, 4,
- 255, 255, 0, 1, 0, 0,
- 132, 1, 0, 0, 124, 0,
- 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 0, 0, 82, 68, 69, 70,
+ 184, 1, 0, 0, 1, 0,
+ 0, 0, 148, 0, 0, 0,
+ 3, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 132, 1,
+ 0, 0, 124, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 139, 0, 0, 0, 2, 0,
- 0, 0, 5, 0, 0, 0,
- 4, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 1, 0, 0, 0, 12, 0,
- 0, 0, 143, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 139, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 143, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 115, 83,
- 104, 97, 100, 111, 119, 83,
- 97, 109, 112, 108, 101, 114,
- 0, 116, 101, 120, 0, 99,
- 98, 49, 0, 171, 143, 0,
- 0, 0, 4, 0, 0, 0,
- 172, 0, 0, 0, 160, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 12, 1,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 83, 104, 97,
+ 100, 111, 119, 83, 97, 109,
+ 112, 108, 101, 114, 0, 116,
+ 101, 120, 0, 99, 98, 49,
+ 0, 171, 143, 0, 0, 0,
+ 4, 0, 0, 0, 172, 0,
+ 0, 0, 160, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 48, 0, 0, 0, 2, 0,
- 0, 0, 28, 1, 0, 0,
- 0, 0, 0, 0, 44, 1,
- 0, 0, 48, 0, 0, 0,
- 48, 0, 0, 0, 0, 0,
- 0, 0, 60, 1, 0, 0,
- 0, 0, 0, 0, 76, 1,
- 0, 0, 96, 0, 0, 0,
- 48, 0, 0, 0, 2, 0,
- 0, 0, 88, 1, 0, 0,
- 0, 0, 0, 0, 104, 1,
- 0, 0, 144, 0, 0, 0,
- 16, 0, 0, 0, 2, 0,
- 0, 0, 116, 1, 0, 0,
+ 0, 0, 12, 1, 0, 0,
+ 0, 0, 0, 0, 48, 0,
+ 0, 0, 2, 0, 0, 0,
+ 28, 1, 0, 0, 0, 0,
+ 0, 0, 44, 1, 0, 0,
+ 48, 0, 0, 0, 48, 0,
+ 0, 0, 0, 0, 0, 0,
+ 60, 1, 0, 0, 0, 0,
+ 0, 0, 76, 1, 0, 0,
+ 96, 0, 0, 0, 48, 0,
+ 0, 0, 2, 0, 0, 0,
+ 88, 1, 0, 0, 0, 0,
+ 0, 0, 104, 1, 0, 0,
+ 144, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 116, 1, 0, 0, 0, 0,
+ 0, 0, 66, 108, 117, 114,
+ 79, 102, 102, 115, 101, 116,
+ 115, 72, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 3, 0, 0, 0,
0, 0, 0, 0, 66, 108,
117, 114, 79, 102, 102, 115,
- 101, 116, 115, 72, 0, 171,
+ 101, 116, 115, 86, 0, 171,
171, 171, 1, 0, 3, 0,
1, 0, 4, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 66, 108, 117, 114, 79, 102,
- 102, 115, 101, 116, 115, 86,
- 0, 171, 171, 171, 1, 0,
+ 66, 108, 117, 114, 87, 101,
+ 105, 103, 104, 116, 115, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 83, 104,
+ 97, 100, 111, 119, 67, 111,
+ 108, 111, 114, 0, 1, 0,
3, 0, 1, 0, 4, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 66, 108, 117, 114,
- 87, 101, 105, 103, 104, 116,
- 115, 0, 1, 0, 3, 0,
- 1, 0, 4, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 83, 104, 97, 100, 111, 119,
- 67, 111, 108, 111, 114, 0,
- 1, 0, 3, 0, 1, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 73, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
- 0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 3, 0, 0,
- 92, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 0, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 79, 83, 71, 78, 44, 0,
- 0, 0, 1, 0, 0, 0,
- 8, 0, 0, 0, 32, 0,
+ 15, 0, 0, 0, 92, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 0, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 83, 86,
- 95, 84, 97, 114, 103, 101,
- 116, 0, 171, 171, 2, 204,
- 0, 0, 0, 0, 0, 0,
- 80, 49, 0, 4, 0, 0,
- 0, 1, 0, 0, 0, 0,
- 0, 128, 63, 1, 0, 0,
- 0, 0, 0, 128, 63, 1,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171, 78, 204, 0, 0,
+ 0, 0, 0, 0, 80, 49,
+ 0, 4, 0, 0, 0, 1,
0, 0, 0, 0, 0, 128,
63, 1, 0, 0, 0, 0,
0, 128, 63, 1, 0, 0,
- 0, 3, 0, 0, 0, 255,
- 255, 255, 255, 72, 4, 0,
- 0, 68, 88, 66, 67, 28,
- 248, 40, 83, 2, 166, 203,
- 194, 228, 163, 91, 123, 149,
- 165, 41, 212, 1, 0, 0,
- 0, 72, 4, 0, 0, 6,
- 0, 0, 0, 56, 0, 0,
- 0, 248, 0, 0, 0, 244,
- 1, 0, 0, 112, 2, 0,
- 0, 164, 3, 0, 0, 216,
- 3, 0, 0, 65, 111, 110,
- 57, 184, 0, 0, 0, 184,
- 0, 0, 0, 0, 2, 254,
- 255, 132, 0, 0, 0, 52,
- 0, 0, 0, 1, 0, 36,
- 0, 0, 0, 48, 0, 0,
- 0, 48, 0, 0, 0, 36,
- 0, 1, 0, 48, 0, 0,
- 0, 0, 0, 3, 0, 1,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 2, 254,
- 255, 81, 0, 0, 5, 4,
- 0, 15, 160, 0, 0, 0,
- 0, 0, 0, 128, 63, 0,
+ 0, 0, 0, 128, 63, 1,
+ 0, 0, 0, 0, 0, 128,
+ 63, 1, 0, 0, 0, 3,
+ 0, 0, 0, 255, 255, 255,
+ 255, 68, 4, 0, 0, 68,
+ 88, 66, 67, 77, 85, 167,
+ 240, 56, 56, 155, 78, 125,
+ 96, 49, 253, 103, 100, 22,
+ 62, 1, 0, 0, 0, 68,
+ 4, 0, 0, 6, 0, 0,
+ 0, 56, 0, 0, 0, 248,
+ 0, 0, 0, 244, 1, 0,
+ 0, 112, 2, 0, 0, 160,
+ 3, 0, 0, 212, 3, 0,
+ 0, 65, 111, 110, 57, 184,
+ 0, 0, 0, 184, 0, 0,
+ 0, 0, 2, 254, 255, 132,
+ 0, 0, 0, 52, 0, 0,
+ 0, 1, 0, 36, 0, 0,
+ 0, 48, 0, 0, 0, 48,
+ 0, 0, 0, 36, 0, 1,
+ 0, 48, 0, 0, 0, 0,
+ 0, 3, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 31, 0, 0, 2, 5,
- 0, 0, 128, 0, 0, 15,
- 144, 4, 0, 0, 4, 0,
- 0, 3, 224, 0, 0, 228,
- 144, 2, 0, 238, 160, 2,
- 0, 228, 160, 4, 0, 0,
- 4, 0, 0, 12, 224, 0,
- 0, 20, 144, 3, 0, 180,
- 160, 3, 0, 20, 160, 4,
+ 0, 1, 2, 254, 255, 81,
+ 0, 0, 5, 4, 0, 15,
+ 160, 0, 0, 0, 0, 0,
+ 0, 128, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 31,
+ 0, 0, 2, 5, 0, 0,
+ 128, 0, 0, 15, 144, 4,
0, 0, 4, 0, 0, 3,
- 128, 0, 0, 228, 144, 1,
- 0, 238, 160, 1, 0, 228,
- 160, 2, 0, 0, 3, 0,
- 0, 3, 192, 0, 0, 228,
- 128, 0, 0, 228, 160, 1,
- 0, 0, 2, 0, 0, 12,
- 192, 4, 0, 68, 160, 255,
- 255, 0, 0, 83, 72, 68,
- 82, 244, 0, 0, 0, 64,
- 0, 1, 0, 61, 0, 0,
- 0, 89, 0, 0, 4, 70,
- 142, 32, 0, 0, 0, 0,
- 0, 3, 0, 0, 0, 95,
- 0, 0, 3, 50, 16, 16,
- 0, 0, 0, 0, 0, 103,
- 0, 0, 4, 242, 32, 16,
- 0, 0, 0, 0, 0, 1,
- 0, 0, 0, 101, 0, 0,
- 3, 50, 32, 16, 0, 1,
- 0, 0, 0, 101, 0, 0,
- 3, 194, 32, 16, 0, 1,
- 0, 0, 0, 50, 0, 0,
- 11, 50, 32, 16, 0, 0,
- 0, 0, 0, 70, 16, 16,
- 0, 0, 0, 0, 0, 230,
- 138, 32, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 70,
- 128, 32, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 54,
- 0, 0, 8, 194, 32, 16,
- 0, 0, 0, 0, 0, 2,
- 64, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 128,
- 63, 50, 0, 0, 11, 50,
+ 224, 0, 0, 228, 144, 2,
+ 0, 238, 160, 2, 0, 228,
+ 160, 4, 0, 0, 4, 0,
+ 0, 12, 224, 0, 0, 20,
+ 144, 3, 0, 180, 160, 3,
+ 0, 20, 160, 4, 0, 0,
+ 4, 0, 0, 3, 128, 0,
+ 0, 228, 144, 1, 0, 238,
+ 160, 1, 0, 228, 160, 2,
+ 0, 0, 3, 0, 0, 3,
+ 192, 0, 0, 228, 128, 0,
+ 0, 228, 160, 1, 0, 0,
+ 2, 0, 0, 12, 192, 4,
+ 0, 68, 160, 255, 255, 0,
+ 0, 83, 72, 68, 82, 244,
+ 0, 0, 0, 64, 0, 1,
+ 0, 61, 0, 0, 0, 89,
+ 0, 0, 4, 70, 142, 32,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 95, 0, 0,
+ 3, 50, 16, 16, 0, 0,
+ 0, 0, 0, 103, 0, 0,
+ 4, 242, 32, 16, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 101, 0, 0, 3, 50,
32, 16, 0, 1, 0, 0,
+ 0, 101, 0, 0, 3, 194,
+ 32, 16, 0, 1, 0, 0,
+ 0, 50, 0, 0, 11, 50,
+ 32, 16, 0, 0, 0, 0,
0, 70, 16, 16, 0, 0,
0, 0, 0, 230, 138, 32,
- 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 70, 128, 32,
- 0, 0, 0, 0, 0, 1,
- 0, 0, 0, 50, 0, 0,
- 11, 194, 32, 16, 0, 1,
- 0, 0, 0, 6, 20, 16,
- 0, 0, 0, 0, 0, 166,
- 142, 32, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 6,
- 132, 32, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 62,
- 0, 0, 1, 83, 84, 65,
- 84, 116, 0, 0, 0, 5,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 54, 0, 0,
+ 8, 194, 32, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 128, 63, 50,
+ 0, 0, 11, 50, 32, 16,
+ 0, 1, 0, 0, 0, 70,
+ 16, 16, 0, 0, 0, 0,
+ 0, 230, 138, 32, 0, 0,
0, 0, 0, 1, 0, 0,
+ 0, 70, 128, 32, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 50, 0, 0, 11, 194,
+ 32, 16, 0, 1, 0, 0,
+ 0, 6, 20, 16, 0, 0,
+ 0, 0, 0, 166, 142, 32,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 6, 132, 32,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 62, 0, 0,
+ 1, 83, 84, 65, 84, 116,
+ 0, 0, 0, 5, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 4, 0, 0,
+ 0, 3, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 82, 68, 69, 70, 44,
- 1, 0, 0, 1, 0, 0,
- 0, 64, 0, 0, 0, 1,
- 0, 0, 0, 28, 0, 0,
- 0, 0, 4, 254, 255, 0,
- 1, 0, 0, 246, 0, 0,
- 0, 60, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 82,
+ 68, 69, 70, 40, 1, 0,
+ 0, 1, 0, 0, 0, 64,
+ 0, 0, 0, 1, 0, 0,
+ 0, 28, 0, 0, 0, 0,
+ 4, 254, 255, 0, 1, 0,
+ 0, 246, 0, 0, 0, 60,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 1, 0, 0, 0, 0,
- 0, 0, 0, 99, 98, 48,
- 0, 60, 0, 0, 0, 4,
- 0, 0, 0, 88, 0, 0,
- 0, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 184, 0, 0, 0, 0,
- 0, 0, 0, 16, 0, 0,
- 0, 2, 0, 0, 0, 196,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
- 0, 212, 0, 0, 0, 16,
- 0, 0, 0, 16, 0, 0,
- 0, 2, 0, 0, 0, 196,
+ 0, 99, 98, 48, 0, 60,
+ 0, 0, 0, 4, 0, 0,
+ 0, 88, 0, 0, 0, 64,
0, 0, 0, 0, 0, 0,
- 0, 222, 0, 0, 0, 32,
- 0, 0, 0, 16, 0, 0,
- 0, 2, 0, 0, 0, 196,
+ 0, 0, 0, 0, 0, 184,
0, 0, 0, 0, 0, 0,
- 0, 236, 0, 0, 0, 48,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 212,
0, 0, 0, 16, 0, 0,
- 0, 0, 0, 0, 0, 196,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 222,
+ 0, 0, 0, 32, 0, 0,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 236,
+ 0, 0, 0, 48, 0, 0,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 81,
+ 117, 97, 100, 68, 101, 115,
+ 99, 0, 171, 171, 171, 1,
+ 0, 3, 0, 1, 0, 4,
0, 0, 0, 0, 0, 0,
- 0, 81, 117, 97, 100, 68,
- 101, 115, 99, 0, 171, 171,
- 171, 1, 0, 3, 0, 1,
- 0, 4, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 84,
+ 0, 0, 0, 84, 101, 120,
+ 67, 111, 111, 114, 100, 115,
+ 0, 77, 97, 115, 107, 84,
101, 120, 67, 111, 111, 114,
- 100, 115, 0, 77, 97, 115,
- 107, 84, 101, 120, 67, 111,
- 111, 114, 100, 115, 0, 84,
- 101, 120, 116, 67, 111, 108,
- 111, 114, 0, 77, 105, 99,
- 114, 111, 115, 111, 102, 116,
- 32, 40, 82, 41, 32, 72,
- 76, 83, 76, 32, 83, 104,
- 97, 100, 101, 114, 32, 67,
- 111, 109, 112, 105, 108, 101,
- 114, 32, 57, 46, 51, 48,
- 46, 57, 50, 48, 48, 46,
- 49, 54, 51, 56, 52, 0,
- 171, 171, 171, 73, 83, 71,
+ 100, 115, 0, 84, 101, 120,
+ 116, 67, 111, 108, 111, 114,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 54, 46, 51, 46, 57, 54,
+ 48, 48, 46, 49, 54, 51,
+ 56, 52, 0, 73, 83, 71,
78, 44, 0, 0, 0, 1,
0, 0, 0, 8, 0, 0,
0, 32, 0, 0, 0, 0,
@@ -13567,15 +13587,15 @@ const BYTE d2deffect[] = 86, 95, 80, 111, 115, 105,
116, 105, 111, 110, 0, 84,
69, 88, 67, 79, 79, 82,
- 68, 0, 171, 171, 171, 41,
+ 68, 0, 171, 171, 171, 117,
214, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
0, 172, 9, 0, 0, 68,
- 88, 66, 67, 59, 162, 86,
- 163, 32, 38, 131, 23, 42,
- 142, 102, 208, 154, 242, 96,
- 4, 1, 0, 0, 0, 172,
+ 88, 66, 67, 67, 47, 1,
+ 244, 0, 102, 246, 41, 38,
+ 220, 84, 204, 156, 139, 96,
+ 25, 1, 0, 0, 0, 172,
9, 0, 0, 6, 0, 0,
0, 56, 0, 0, 0, 220,
2, 0, 0, 204, 6, 0,
@@ -13865,7 +13885,7 @@ const BYTE d2deffect[] = 84, 65, 84, 116, 0, 0,
0, 29, 0, 0, 0, 4,
0, 0, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 4,
+ 0, 2, 0, 0, 0, 12,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
@@ -13875,7 +13895,7 @@ const BYTE d2deffect[] = 0, 0, 0, 9, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 10,
+ 0, 0, 0, 0, 0, 7,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -13954,9 +13974,9 @@ const BYTE d2deffect[] = 76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
- 114, 32, 57, 46, 51, 48,
- 46, 57, 50, 48, 48, 46,
- 49, 54, 51, 56, 52, 0,
+ 114, 32, 54, 46, 51, 46,
+ 57, 54, 48, 48, 46, 49,
+ 54, 51, 56, 52, 0, 171,
171, 73, 83, 71, 78, 104,
0, 0, 0, 3, 0, 0,
0, 8, 0, 0, 0, 80,
@@ -13984,7 +14004,7 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 15,
0, 0, 0, 83, 86, 95,
84, 97, 114, 103, 101, 116,
- 0, 171, 171, 137, 218, 0,
+ 0, 171, 171, 209, 218, 0,
0, 0, 0, 0, 0, 80,
50, 0, 4, 0, 0, 0,
1, 0, 0, 0, 0, 0,
@@ -13994,16 +14014,16 @@ const BYTE d2deffect[] = 1, 0, 0, 0, 0, 0,
128, 63, 1, 0, 0, 0,
3, 0, 0, 0, 255, 255,
- 255, 255, 72, 4, 0, 0,
- 68, 88, 66, 67, 28, 248,
- 40, 83, 2, 166, 203, 194,
- 228, 163, 91, 123, 149, 165,
- 41, 212, 1, 0, 0, 0,
- 72, 4, 0, 0, 6, 0,
+ 255, 255, 68, 4, 0, 0,
+ 68, 88, 66, 67, 77, 85,
+ 167, 240, 56, 56, 155, 78,
+ 125, 96, 49, 253, 103, 100,
+ 22, 62, 1, 0, 0, 0,
+ 68, 4, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
248, 0, 0, 0, 244, 1,
0, 0, 112, 2, 0, 0,
- 164, 3, 0, 0, 216, 3,
+ 160, 3, 0, 0, 212, 3,
0, 0, 65, 111, 110, 57,
184, 0, 0, 0, 184, 0,
0, 0, 0, 2, 254, 255,
@@ -14082,7 +14102,7 @@ const BYTE d2deffect[] = 116, 0, 0, 0, 5, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 4, 0,
- 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -14092,14 +14112,14 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 82, 68, 69, 70, 44, 1,
+ 82, 68, 69, 70, 40, 1,
0, 0, 1, 0, 0, 0,
64, 0, 0, 0, 1, 0,
0, 0, 28, 0, 0, 0,
@@ -14147,117 +14167,95 @@ const BYTE d2deffect[] = 83, 76, 32, 83, 104, 97,
100, 101, 114, 32, 67, 111,
109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
- 171, 171, 73, 83, 71, 78,
- 44, 0, 0, 0, 1, 0,
- 0, 0, 8, 0, 0, 0,
- 32, 0, 0, 0, 0, 0,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 73, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 7, 3, 0, 0,
- 80, 79, 83, 73, 84, 73,
- 79, 78, 0, 171, 171, 171,
- 79, 83, 71, 78, 104, 0,
0, 0, 3, 0, 0, 0,
- 8, 0, 0, 0, 80, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 3, 0,
- 0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 92, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 12, 0, 0, 92, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 7, 3,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 12, 3, 0, 0, 83, 86,
- 95, 80, 111, 115, 105, 116,
- 105, 111, 110, 0, 84, 69,
- 88, 67, 79, 79, 82, 68,
- 0, 171, 171, 171, 116, 228,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 2, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 12, 0, 0,
+ 92, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 164, 10, 0, 0, 68, 88,
- 66, 67, 219, 231, 18, 29,
- 129, 252, 34, 48, 152, 246,
- 123, 61, 199, 166, 77, 90,
- 1, 0, 0, 0, 164, 10,
- 0, 0, 6, 0, 0, 0,
- 56, 0, 0, 0, 24, 3,
- 0, 0, 112, 7, 0, 0,
- 236, 7, 0, 0, 0, 10,
- 0, 0, 112, 10, 0, 0,
- 65, 111, 110, 57, 216, 2,
- 0, 0, 216, 2, 0, 0,
- 0, 2, 255, 255, 160, 2,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 3, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 188, 228, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 164, 10, 0, 0,
+ 68, 88, 66, 67, 70, 166,
+ 174, 156, 153, 145, 163, 116,
+ 127, 37, 205, 162, 136, 116,
+ 62, 222, 1, 0, 0, 0,
+ 164, 10, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
- 1, 0, 44, 0, 0, 0,
- 56, 0, 0, 0, 56, 0,
- 2, 0, 36, 0, 0, 0,
- 56, 0, 1, 0, 0, 0,
- 0, 1, 1, 0, 0, 0,
- 3, 0, 6, 0, 0, 0,
- 0, 0, 0, 0, 1, 2,
- 255, 255, 31, 0, 0, 2,
- 0, 0, 0, 128, 0, 0,
- 15, 176, 31, 0, 0, 2,
- 0, 0, 0, 144, 0, 8,
- 15, 160, 31, 0, 0, 2,
- 0, 0, 0, 144, 1, 8,
- 15, 160, 2, 0, 0, 3,
- 0, 0, 2, 128, 0, 0,
- 85, 176, 0, 0, 85, 160,
- 1, 0, 0, 2, 0, 0,
+ 24, 3, 0, 0, 112, 7,
+ 0, 0, 236, 7, 0, 0,
+ 0, 10, 0, 0, 112, 10,
+ 0, 0, 65, 111, 110, 57,
+ 216, 2, 0, 0, 216, 2,
+ 0, 0, 0, 2, 255, 255,
+ 160, 2, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 1, 0,
+ 0, 0, 0, 1, 1, 0,
+ 0, 0, 3, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 2, 0,
+ 0, 3, 0, 0, 2, 128,
+ 0, 0, 85, 176, 0, 0,
+ 85, 160, 1, 0, 0, 2,
+ 0, 0, 1, 128, 0, 0,
+ 0, 176, 2, 0, 0, 3,
+ 1, 0, 2, 128, 0, 0,
+ 85, 176, 0, 0, 0, 160,
+ 1, 0, 0, 2, 1, 0,
1, 128, 0, 0, 0, 176,
- 2, 0, 0, 3, 1, 0,
- 2, 128, 0, 0, 85, 176,
- 0, 0, 0, 160, 1, 0,
- 0, 2, 1, 0, 1, 128,
- 0, 0, 0, 176, 66, 0,
- 0, 3, 0, 0, 15, 128,
- 0, 0, 228, 128, 1, 8,
- 228, 160, 66, 0, 0, 3,
- 1, 0, 15, 128, 1, 0,
- 228, 128, 1, 8, 228, 160,
- 5, 0, 0, 3, 0, 0,
+ 66, 0, 0, 3, 0, 0,
15, 128, 0, 0, 228, 128,
- 3, 0, 85, 160, 4, 0,
- 0, 4, 0, 0, 15, 128,
- 3, 0, 0, 160, 1, 0,
- 228, 128, 0, 0, 228, 128,
- 2, 0, 0, 3, 1, 0,
- 2, 128, 0, 0, 85, 176,
- 0, 0, 170, 160, 1, 0,
- 0, 2, 1, 0, 1, 128,
- 0, 0, 0, 176, 2, 0,
- 0, 3, 2, 0, 2, 128,
- 0, 0, 85, 176, 0, 0,
- 255, 160, 1, 0, 0, 2,
- 2, 0, 1, 128, 0, 0,
- 0, 176, 66, 0, 0, 3,
- 1, 0, 15, 128, 1, 0,
- 228, 128, 1, 8, 228, 160,
- 66, 0, 0, 3, 2, 0,
- 15, 128, 2, 0, 228, 128,
- 1, 8, 228, 160, 4, 0,
- 0, 4, 0, 0, 15, 128,
- 3, 0, 170, 160, 1, 0,
- 228, 128, 0, 0, 228, 128,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 1, 8,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 3, 0, 85, 160,
4, 0, 0, 4, 0, 0,
- 15, 128, 3, 0, 255, 160,
- 2, 0, 228, 128, 0, 0,
+ 15, 128, 3, 0, 0, 160,
+ 1, 0, 228, 128, 0, 0,
228, 128, 2, 0, 0, 3,
1, 0, 2, 128, 0, 0,
- 85, 176, 1, 0, 0, 160,
+ 85, 176, 0, 0, 170, 160,
1, 0, 0, 2, 1, 0,
1, 128, 0, 0, 0, 176,
2, 0, 0, 3, 2, 0,
2, 128, 0, 0, 85, 176,
- 1, 0, 85, 160, 1, 0,
+ 0, 0, 255, 160, 1, 0,
0, 2, 2, 0, 1, 128,
0, 0, 0, 176, 66, 0,
0, 3, 1, 0, 15, 128,
@@ -14266,19 +14264,19 @@ const BYTE d2deffect[] = 2, 0, 15, 128, 2, 0,
228, 128, 1, 8, 228, 160,
4, 0, 0, 4, 0, 0,
- 15, 128, 4, 0, 0, 160,
+ 15, 128, 3, 0, 170, 160,
1, 0, 228, 128, 0, 0,
228, 128, 4, 0, 0, 4,
- 0, 0, 15, 128, 4, 0,
- 85, 160, 2, 0, 228, 128,
+ 0, 0, 15, 128, 3, 0,
+ 255, 160, 2, 0, 228, 128,
0, 0, 228, 128, 2, 0,
0, 3, 1, 0, 2, 128,
0, 0, 85, 176, 1, 0,
- 170, 160, 1, 0, 0, 2,
+ 0, 160, 1, 0, 0, 2,
1, 0, 1, 128, 0, 0,
0, 176, 2, 0, 0, 3,
2, 0, 2, 128, 0, 0,
- 85, 176, 1, 0, 255, 160,
+ 85, 176, 1, 0, 85, 160,
1, 0, 0, 2, 2, 0,
1, 128, 0, 0, 0, 176,
66, 0, 0, 3, 1, 0,
@@ -14288,132 +14286,116 @@ const BYTE d2deffect[] = 2, 0, 228, 128, 1, 8,
228, 160, 4, 0, 0, 4,
0, 0, 15, 128, 4, 0,
- 170, 160, 1, 0, 228, 128,
+ 0, 160, 1, 0, 228, 128,
0, 0, 228, 128, 4, 0,
0, 4, 0, 0, 15, 128,
- 4, 0, 255, 160, 2, 0,
+ 4, 0, 85, 160, 2, 0,
228, 128, 0, 0, 228, 128,
2, 0, 0, 3, 1, 0,
2, 128, 0, 0, 85, 176,
- 2, 0, 0, 160, 1, 0,
+ 1, 0, 170, 160, 1, 0,
0, 2, 1, 0, 1, 128,
- 0, 0, 0, 176, 1, 0,
- 0, 2, 2, 0, 3, 128,
- 0, 0, 235, 176, 66, 0,
- 0, 3, 1, 0, 15, 128,
- 1, 0, 228, 128, 1, 8,
- 228, 160, 66, 0, 0, 3,
- 2, 0, 15, 128, 2, 0,
- 228, 128, 0, 8, 228, 160,
+ 0, 0, 0, 176, 2, 0,
+ 0, 3, 2, 0, 2, 128,
+ 0, 0, 85, 176, 1, 0,
+ 255, 160, 1, 0, 0, 2,
+ 2, 0, 1, 128, 0, 0,
+ 0, 176, 66, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 1, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 2, 0, 228, 128,
+ 1, 8, 228, 160, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 4, 0, 170, 160, 1, 0,
+ 228, 128, 0, 0, 228, 128,
4, 0, 0, 4, 0, 0,
- 15, 128, 5, 0, 0, 160,
- 1, 0, 228, 128, 0, 0,
- 228, 128, 5, 0, 0, 3,
- 0, 0, 15, 128, 2, 0,
- 255, 128, 0, 0, 228, 128,
- 1, 0, 0, 2, 0, 8,
- 15, 128, 0, 0, 228, 128,
- 255, 255, 0, 0, 83, 72,
- 68, 82, 80, 4, 0, 0,
- 64, 0, 0, 0, 20, 1,
- 0, 0, 89, 0, 0, 4,
- 70, 142, 32, 0, 0, 0,
- 0, 0, 9, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 0, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 1, 0, 0, 0,
+ 15, 128, 4, 0, 255, 160,
+ 2, 0, 228, 128, 0, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 2, 128, 0, 0,
+ 85, 176, 2, 0, 0, 160,
+ 1, 0, 0, 2, 1, 0,
+ 1, 128, 0, 0, 0, 176,
+ 1, 0, 0, 2, 2, 0,
+ 3, 128, 0, 0, 235, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 2, 0, 228, 128, 0, 8,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 5, 0,
+ 0, 160, 1, 0, 228, 128,
+ 0, 0, 228, 128, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 2, 0, 255, 128, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 80, 4,
+ 0, 0, 64, 0, 0, 0,
+ 20, 1, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 9, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
88, 24, 0, 4, 0, 112,
- 16, 0, 0, 0, 0, 0,
- 85, 85, 0, 0, 88, 24,
- 0, 4, 0, 112, 16, 0,
- 1, 0, 0, 0, 85, 85,
- 0, 0, 98, 16, 0, 3,
- 50, 16, 16, 0, 1, 0,
- 0, 0, 98, 16, 0, 3,
- 194, 16, 16, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 104, 0, 0, 2,
- 4, 0, 0, 0, 54, 0,
- 0, 5, 82, 0, 16, 0,
- 0, 0, 0, 0, 6, 16,
- 16, 0, 1, 0, 0, 0,
- 0, 0, 0, 8, 242, 0,
16, 0, 1, 0, 0, 0,
- 86, 21, 16, 0, 1, 0,
- 0, 0, 134, 141, 32, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 54, 0, 0, 5,
- 162, 0, 16, 0, 0, 0,
- 0, 0, 6, 8, 16, 0,
- 1, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 2, 0, 0, 0, 230, 10,
- 16, 0, 0, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 1, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 0, 0, 0, 0, 70, 0,
- 16, 0, 0, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 1, 0, 0, 0, 56, 0,
- 0, 8, 242, 0, 16, 0,
- 2, 0, 0, 0, 70, 14,
- 16, 0, 2, 0, 0, 0,
- 86, 133, 32, 0, 0, 0,
- 0, 0, 6, 0, 0, 0,
- 50, 0, 0, 10, 242, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 98, 16,
+ 0, 3, 194, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 82, 0,
16, 0, 0, 0, 0, 0,
- 6, 128, 32, 0, 0, 0,
- 0, 0, 6, 0, 0, 0,
- 70, 14, 16, 0, 0, 0,
- 0, 0, 70, 14, 16, 0,
- 2, 0, 0, 0, 54, 0,
- 0, 5, 82, 0, 16, 0,
- 1, 0, 0, 0, 6, 16,
+ 6, 16, 16, 0, 1, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 1, 0, 0, 0, 134, 141,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 54, 0,
+ 0, 5, 162, 0, 16, 0,
+ 0, 0, 0, 0, 6, 8,
16, 0, 1, 0, 0, 0,
69, 0, 0, 9, 242, 0,
16, 0, 2, 0, 0, 0,
- 70, 0, 16, 0, 1, 0,
+ 230, 10, 16, 0, 0, 0,
0, 0, 70, 126, 16, 0,
0, 0, 0, 0, 0, 96,
16, 0, 1, 0, 0, 0,
69, 0, 0, 9, 242, 0,
- 16, 0, 1, 0, 0, 0,
- 230, 10, 16, 0, 1, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
0, 0, 70, 126, 16, 0,
0, 0, 0, 0, 0, 96,
16, 0, 1, 0, 0, 0,
- 50, 0, 0, 10, 242, 0,
- 16, 0, 0, 0, 0, 0,
- 166, 138, 32, 0, 0, 0,
- 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 8, 242, 0,
+ 16, 0, 2, 0, 0, 0,
70, 14, 16, 0, 2, 0,
+ 0, 0, 86, 133, 32, 0,
+ 0, 0, 0, 0, 6, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 128, 32, 0,
+ 0, 0, 0, 0, 6, 0,
0, 0, 70, 14, 16, 0,
- 0, 0, 0, 0, 50, 0,
- 0, 10, 242, 0, 16, 0,
- 0, 0, 0, 0, 246, 143,
- 32, 0, 0, 0, 0, 0,
- 6, 0, 0, 0, 70, 14,
- 16, 0, 1, 0, 0, 0,
- 70, 14, 16, 0, 0, 0,
- 0, 0, 54, 0, 0, 5,
- 82, 0, 16, 0, 1, 0,
- 0, 0, 6, 16, 16, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 8, 242, 0, 16, 0,
- 2, 0, 0, 0, 86, 21,
- 16, 0, 1, 0, 0, 0,
- 134, 141, 32, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 54, 0, 0, 5, 162, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 5, 82, 0,
16, 0, 1, 0, 0, 0,
- 6, 8, 16, 0, 2, 0,
+ 6, 16, 16, 0, 1, 0,
0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 3, 0,
+ 242, 0, 16, 0, 2, 0,
0, 0, 70, 0, 16, 0,
1, 0, 0, 0, 70, 126,
16, 0, 0, 0, 0, 0,
@@ -14426,320 +14408,357 @@ const BYTE d2deffect[] = 0, 96, 16, 0, 1, 0,
0, 0, 50, 0, 0, 10,
242, 0, 16, 0, 0, 0,
- 0, 0, 6, 128, 32, 0,
- 0, 0, 0, 0, 7, 0,
+ 0, 0, 166, 138, 32, 0,
+ 0, 0, 0, 0, 6, 0,
0, 0, 70, 14, 16, 0,
- 3, 0, 0, 0, 70, 14,
+ 2, 0, 0, 0, 70, 14,
16, 0, 0, 0, 0, 0,
50, 0, 0, 10, 242, 0,
16, 0, 0, 0, 0, 0,
- 86, 133, 32, 0, 0, 0,
- 0, 0, 7, 0, 0, 0,
+ 246, 143, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
70, 14, 16, 0, 1, 0,
0, 0, 70, 14, 16, 0,
0, 0, 0, 0, 54, 0,
0, 5, 82, 0, 16, 0,
- 2, 0, 0, 0, 6, 16,
+ 1, 0, 0, 0, 6, 16,
16, 0, 1, 0, 0, 0,
- 69, 0, 0, 9, 242, 0,
+ 0, 0, 0, 8, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 1, 0,
+ 0, 0, 134, 141, 32, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 54, 0, 0, 5,
+ 162, 0, 16, 0, 1, 0,
+ 0, 0, 6, 8, 16, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 3, 0, 0, 0, 70, 0,
16, 0, 1, 0, 0, 0,
- 70, 0, 16, 0, 2, 0,
- 0, 0, 70, 126, 16, 0,
- 0, 0, 0, 0, 0, 96,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 1, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 230, 10,
16, 0, 1, 0, 0, 0,
- 69, 0, 0, 9, 242, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 0, 0, 0, 0, 6, 128,
+ 32, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 86, 133, 32, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 82, 0,
16, 0, 2, 0, 0, 0,
- 230, 10, 16, 0, 2, 0,
- 0, 0, 70, 126, 16, 0,
- 0, 0, 0, 0, 0, 96,
- 16, 0, 1, 0, 0, 0,
+ 6, 16, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 0, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 230, 10, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 166, 138, 32, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
50, 0, 0, 10, 242, 0,
16, 0, 0, 0, 0, 0,
- 166, 138, 32, 0, 0, 0,
+ 246, 143, 32, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 70, 14, 16, 0, 1, 0,
+ 70, 14, 16, 0, 2, 0,
0, 0, 70, 14, 16, 0,
- 0, 0, 0, 0, 50, 0,
- 0, 10, 242, 0, 16, 0,
- 0, 0, 0, 0, 246, 143,
- 32, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 70, 14,
- 16, 0, 2, 0, 0, 0,
- 70, 14, 16, 0, 0, 0,
- 0, 0, 0, 0, 0, 8,
- 34, 0, 16, 0, 1, 0,
- 0, 0, 26, 16, 16, 0,
- 1, 0, 0, 0, 10, 128,
- 32, 0, 0, 0, 0, 0,
- 5, 0, 0, 0, 54, 0,
- 0, 5, 18, 0, 16, 0,
- 1, 0, 0, 0, 10, 16,
- 16, 0, 1, 0, 0, 0,
- 69, 0, 0, 9, 242, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 34, 0, 16, 0,
+ 1, 0, 0, 0, 26, 16,
16, 0, 1, 0, 0, 0,
- 70, 0, 16, 0, 1, 0,
- 0, 0, 70, 126, 16, 0,
- 0, 0, 0, 0, 0, 96,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 54, 0, 0, 5, 18, 0,
16, 0, 1, 0, 0, 0,
- 50, 0, 0, 10, 242, 0,
+ 10, 16, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 0, 16, 0,
+ 1, 0, 0, 0, 70, 126,
16, 0, 0, 0, 0, 0,
- 6, 128, 32, 0, 0, 0,
- 0, 0, 8, 0, 0, 0,
- 70, 14, 16, 0, 1, 0,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 128, 32, 0,
+ 0, 0, 0, 0, 8, 0,
0, 0, 70, 14, 16, 0,
- 0, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 1, 0, 0, 0, 230, 26,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 1, 0, 0, 0,
- 70, 126, 16, 0, 1, 0,
- 0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 56, 0,
- 0, 7, 242, 32, 16, 0,
- 0, 0, 0, 0, 70, 14,
+ 230, 26, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
16, 0, 0, 0, 0, 0,
- 246, 15, 16, 0, 1, 0,
- 0, 0, 62, 0, 0, 1,
- 83, 84, 65, 84, 116, 0,
- 0, 0, 31, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 5, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 31, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 13, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 10, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 82, 68,
- 69, 70, 12, 2, 0, 0,
- 1, 0, 0, 0, 232, 0,
- 0, 0, 5, 0, 0, 0,
- 28, 0, 0, 0, 0, 4,
- 255, 255, 0, 1, 0, 0,
- 216, 1, 0, 0, 188, 0,
- 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 201, 0, 0, 0, 3, 0,
+ 82, 68, 69, 70, 12, 2,
+ 0, 0, 1, 0, 0, 0,
+ 232, 0, 0, 0, 5, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 216, 1, 0, 0,
+ 188, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0,
- 0, 0, 216, 0, 0, 0,
- 2, 0, 0, 0, 5, 0,
- 0, 0, 4, 0, 0, 0,
- 255, 255, 255, 255, 0, 0,
+ 0, 0, 201, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0,
- 12, 0, 0, 0, 220, 0,
+ 0, 0, 0, 0, 216, 0,
0, 0, 2, 0, 0, 0,
5, 0, 0, 0, 4, 0,
0, 0, 255, 255, 255, 255,
- 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 12, 0, 0, 0,
- 225, 0, 0, 0, 0, 0,
+ 220, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 1, 0, 0, 0,
+ 1, 0, 0, 0, 12, 0,
+ 0, 0, 225, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 115, 77, 97, 115,
- 107, 83, 97, 109, 112, 108,
- 101, 114, 0, 115, 83, 104,
- 97, 100, 111, 119, 83, 97,
- 109, 112, 108, 101, 114, 0,
- 116, 101, 120, 0, 109, 97,
- 115, 107, 0, 99, 98, 49,
- 0, 171, 171, 171, 225, 0,
- 0, 0, 4, 0, 0, 0,
- 0, 1, 0, 0, 160, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 115, 77,
+ 97, 115, 107, 83, 97, 109,
+ 112, 108, 101, 114, 0, 115,
+ 83, 104, 97, 100, 111, 119,
+ 83, 97, 109, 112, 108, 101,
+ 114, 0, 116, 101, 120, 0,
+ 109, 97, 115, 107, 0, 99,
+ 98, 49, 0, 171, 171, 171,
+ 225, 0, 0, 0, 4, 0,
+ 0, 0, 0, 1, 0, 0,
+ 160, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 96, 1,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 48, 0, 0, 0,
+ 0, 0, 0, 0, 112, 1,
0, 0, 0, 0, 0, 0,
- 48, 0, 0, 0, 0, 0,
- 0, 0, 112, 1, 0, 0,
- 0, 0, 0, 0, 128, 1,
+ 128, 1, 0, 0, 48, 0,
0, 0, 48, 0, 0, 0,
- 48, 0, 0, 0, 2, 0,
- 0, 0, 144, 1, 0, 0,
- 0, 0, 0, 0, 160, 1,
- 0, 0, 96, 0, 0, 0,
- 48, 0, 0, 0, 2, 0,
- 0, 0, 172, 1, 0, 0,
- 0, 0, 0, 0, 188, 1,
- 0, 0, 144, 0, 0, 0,
- 16, 0, 0, 0, 0, 0,
- 0, 0, 200, 1, 0, 0,
- 0, 0, 0, 0, 66, 108,
- 117, 114, 79, 102, 102, 115,
- 101, 116, 115, 72, 0, 171,
- 171, 171, 1, 0, 3, 0,
- 1, 0, 4, 0, 3, 0,
+ 2, 0, 0, 0, 144, 1,
+ 0, 0, 0, 0, 0, 0,
+ 160, 1, 0, 0, 96, 0,
+ 0, 0, 48, 0, 0, 0,
+ 2, 0, 0, 0, 172, 1,
+ 0, 0, 0, 0, 0, 0,
+ 188, 1, 0, 0, 144, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 200, 1,
0, 0, 0, 0, 0, 0,
66, 108, 117, 114, 79, 102,
- 102, 115, 101, 116, 115, 86,
+ 102, 115, 101, 116, 115, 72,
0, 171, 171, 171, 1, 0,
3, 0, 1, 0, 4, 0,
3, 0, 0, 0, 0, 0,
0, 0, 66, 108, 117, 114,
- 87, 101, 105, 103, 104, 116,
- 115, 0, 1, 0, 3, 0,
- 1, 0, 4, 0, 3, 0,
- 0, 0, 0, 0, 0, 0,
- 83, 104, 97, 100, 111, 119,
- 67, 111, 108, 111, 114, 0,
+ 79, 102, 102, 115, 101, 116,
+ 115, 86, 0, 171, 171, 171,
1, 0, 3, 0, 1, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 73, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
- 0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
+ 4, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 66, 108,
+ 117, 114, 87, 101, 105, 103,
+ 104, 116, 115, 0, 1, 0,
+ 3, 0, 1, 0, 4, 0,
3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 3, 0, 0,
- 92, 0, 0, 0, 1, 0,
- 0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 12, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 79, 83, 71, 78, 44, 0,
- 0, 0, 1, 0, 0, 0,
- 8, 0, 0, 0, 32, 0,
+ 0, 0, 83, 104, 97, 100,
+ 111, 119, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 54,
+ 46, 51, 46, 57, 54, 48,
+ 48, 46, 49, 54, 51, 56,
+ 52, 0, 171, 171, 73, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 83, 86,
- 95, 84, 97, 114, 103, 101,
- 116, 0, 171, 171, 212, 232,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 3,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 12,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
+ 0, 0, 8, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 83, 97, 109, 112, 108, 101,
- 84, 101, 120, 116, 84, 101,
- 120, 116, 117, 114, 101, 0,
- 85, 110, 109, 97, 115, 107,
- 101, 100, 0, 4, 0, 0,
- 0, 1, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 83, 86, 95, 84, 97, 114,
+ 103, 101, 116, 0, 171, 171,
+ 24, 233, 0, 0, 0, 0,
+ 0, 0, 83, 97, 109, 112,
+ 108, 101, 84, 101, 120, 116,
+ 84, 101, 120, 116, 117, 114,
+ 101, 0, 85, 110, 109, 97,
+ 115, 107, 101, 100, 0, 4,
0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0,
- 0, 3, 0, 0, 0, 255,
- 255, 255, 255, 72, 4, 0,
- 0, 68, 88, 66, 67, 28,
- 248, 40, 83, 2, 166, 203,
- 194, 228, 163, 91, 123, 149,
- 165, 41, 212, 1, 0, 0,
- 0, 72, 4, 0, 0, 6,
- 0, 0, 0, 56, 0, 0,
- 0, 248, 0, 0, 0, 244,
- 1, 0, 0, 112, 2, 0,
- 0, 164, 3, 0, 0, 216,
- 3, 0, 0, 65, 111, 110,
- 57, 184, 0, 0, 0, 184,
- 0, 0, 0, 0, 2, 254,
- 255, 132, 0, 0, 0, 52,
- 0, 0, 0, 1, 0, 36,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 255, 255, 255, 255, 68,
+ 4, 0, 0, 68, 88, 66,
+ 67, 77, 85, 167, 240, 56,
+ 56, 155, 78, 125, 96, 49,
+ 253, 103, 100, 22, 62, 1,
+ 0, 0, 0, 68, 4, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 248, 0, 0,
+ 0, 244, 1, 0, 0, 112,
+ 2, 0, 0, 160, 3, 0,
+ 0, 212, 3, 0, 0, 65,
+ 111, 110, 57, 184, 0, 0,
+ 0, 184, 0, 0, 0, 0,
+ 2, 254, 255, 132, 0, 0,
+ 0, 52, 0, 0, 0, 1,
+ 0, 36, 0, 0, 0, 48,
0, 0, 0, 48, 0, 0,
- 0, 48, 0, 0, 0, 36,
- 0, 1, 0, 48, 0, 0,
- 0, 0, 0, 3, 0, 1,
+ 0, 36, 0, 1, 0, 48,
+ 0, 0, 0, 0, 0, 3,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 254, 255, 81, 0, 0,
+ 5, 4, 0, 15, 160, 0,
+ 0, 0, 0, 0, 0, 128,
+ 63, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 0, 0,
+ 2, 5, 0, 0, 128, 0,
+ 0, 15, 144, 4, 0, 0,
+ 4, 0, 0, 3, 224, 0,
+ 0, 228, 144, 2, 0, 238,
+ 160, 2, 0, 228, 160, 4,
+ 0, 0, 4, 0, 0, 12,
+ 224, 0, 0, 20, 144, 3,
+ 0, 180, 160, 3, 0, 20,
+ 160, 4, 0, 0, 4, 0,
+ 0, 3, 128, 0, 0, 228,
+ 144, 1, 0, 238, 160, 1,
+ 0, 228, 160, 2, 0, 0,
+ 3, 0, 0, 3, 192, 0,
+ 0, 228, 128, 0, 0, 228,
+ 160, 1, 0, 0, 2, 0,
+ 0, 12, 192, 4, 0, 68,
+ 160, 255, 255, 0, 0, 83,
+ 72, 68, 82, 244, 0, 0,
+ 0, 64, 0, 1, 0, 61,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 95, 0, 0, 3, 50,
+ 16, 16, 0, 0, 0, 0,
+ 0, 103, 0, 0, 4, 242,
+ 32, 16, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 50, 32, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 194, 32, 16,
+ 0, 1, 0, 0, 0, 50,
+ 0, 0, 11, 50, 32, 16,
+ 0, 0, 0, 0, 0, 70,
+ 16, 16, 0, 0, 0, 0,
+ 0, 230, 138, 32, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 2, 254,
- 255, 81, 0, 0, 5, 4,
- 0, 15, 160, 0, 0, 0,
- 0, 0, 0, 128, 63, 0,
+ 0, 70, 128, 32, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 31, 0, 0, 2, 5,
- 0, 0, 128, 0, 0, 15,
- 144, 4, 0, 0, 4, 0,
- 0, 3, 224, 0, 0, 228,
- 144, 2, 0, 238, 160, 2,
- 0, 228, 160, 4, 0, 0,
- 4, 0, 0, 12, 224, 0,
- 0, 20, 144, 3, 0, 180,
- 160, 3, 0, 20, 160, 4,
- 0, 0, 4, 0, 0, 3,
- 128, 0, 0, 228, 144, 1,
- 0, 238, 160, 1, 0, 228,
- 160, 2, 0, 0, 3, 0,
- 0, 3, 192, 0, 0, 228,
- 128, 0, 0, 228, 160, 1,
- 0, 0, 2, 0, 0, 12,
- 192, 4, 0, 68, 160, 255,
- 255, 0, 0, 83, 72, 68,
- 82, 244, 0, 0, 0, 64,
- 0, 1, 0, 61, 0, 0,
- 0, 89, 0, 0, 4, 70,
- 142, 32, 0, 0, 0, 0,
- 0, 3, 0, 0, 0, 95,
- 0, 0, 3, 50, 16, 16,
- 0, 0, 0, 0, 0, 103,
- 0, 0, 4, 242, 32, 16,
- 0, 0, 0, 0, 0, 1,
- 0, 0, 0, 101, 0, 0,
- 3, 50, 32, 16, 0, 1,
- 0, 0, 0, 101, 0, 0,
- 3, 194, 32, 16, 0, 1,
- 0, 0, 0, 50, 0, 0,
- 11, 50, 32, 16, 0, 0,
+ 0, 54, 0, 0, 8, 194,
+ 32, 16, 0, 0, 0, 0,
+ 0, 2, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 128, 63, 50, 0, 0,
+ 11, 50, 32, 16, 0, 1,
0, 0, 0, 70, 16, 16,
0, 0, 0, 0, 0, 230,
138, 32, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 70,
+ 0, 1, 0, 0, 0, 70,
128, 32, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 54,
- 0, 0, 8, 194, 32, 16,
- 0, 0, 0, 0, 0, 2,
- 64, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 128,
- 63, 50, 0, 0, 11, 50,
- 32, 16, 0, 1, 0, 0,
- 0, 70, 16, 16, 0, 0,
- 0, 0, 0, 230, 138, 32,
- 0, 0, 0, 0, 0, 1,
- 0, 0, 0, 70, 128, 32,
- 0, 0, 0, 0, 0, 1,
- 0, 0, 0, 50, 0, 0,
- 11, 194, 32, 16, 0, 1,
- 0, 0, 0, 6, 20, 16,
- 0, 0, 0, 0, 0, 166,
- 142, 32, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 6,
- 132, 32, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 62,
- 0, 0, 1, 83, 84, 65,
- 84, 116, 0, 0, 0, 5,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 4,
+ 0, 1, 0, 0, 0, 50,
+ 0, 0, 11, 194, 32, 16,
+ 0, 1, 0, 0, 0, 6,
+ 20, 16, 0, 0, 0, 0,
+ 0, 166, 142, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 6, 132, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 5, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -14747,262 +14766,261 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 82, 68, 69, 70, 44,
- 1, 0, 0, 1, 0, 0,
- 0, 64, 0, 0, 0, 1,
- 0, 0, 0, 28, 0, 0,
- 0, 0, 4, 254, 255, 0,
- 1, 0, 0, 246, 0, 0,
- 0, 60, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 82, 68, 69,
+ 70, 40, 1, 0, 0, 1,
+ 0, 0, 0, 64, 0, 0,
+ 0, 1, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 254,
+ 255, 0, 1, 0, 0, 246,
+ 0, 0, 0, 60, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 1, 0, 0, 0, 0,
- 0, 0, 0, 99, 98, 48,
- 0, 60, 0, 0, 0, 4,
- 0, 0, 0, 88, 0, 0,
- 0, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 184, 0, 0, 0, 0,
- 0, 0, 0, 16, 0, 0,
- 0, 2, 0, 0, 0, 196,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 99,
+ 98, 48, 0, 60, 0, 0,
+ 0, 4, 0, 0, 0, 88,
+ 0, 0, 0, 64, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 212, 0, 0, 0, 16,
- 0, 0, 0, 16, 0, 0,
- 0, 2, 0, 0, 0, 196,
+ 0, 0, 0, 184, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 212, 0, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 222, 0, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 236, 0, 0,
+ 0, 48, 0, 0, 0, 16,
0, 0, 0, 0, 0, 0,
- 0, 222, 0, 0, 0, 32,
- 0, 0, 0, 16, 0, 0,
- 0, 2, 0, 0, 0, 196,
+ 0, 196, 0, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 236, 0, 0, 0, 48,
- 0, 0, 0, 16, 0, 0,
- 0, 0, 0, 0, 0, 196,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 77,
+ 97, 115, 107, 84, 101, 120,
+ 67, 111, 111, 114, 100, 115,
+ 0, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 73, 83, 71, 78, 44,
+ 0, 0, 0, 1, 0, 0,
+ 0, 8, 0, 0, 0, 32,
0, 0, 0, 0, 0, 0,
- 0, 81, 117, 97, 100, 68,
- 101, 115, 99, 0, 171, 171,
- 171, 1, 0, 3, 0, 1,
- 0, 4, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 84,
- 101, 120, 67, 111, 111, 114,
- 100, 115, 0, 77, 97, 115,
- 107, 84, 101, 120, 67, 111,
- 111, 114, 100, 115, 0, 84,
- 101, 120, 116, 67, 111, 108,
- 111, 114, 0, 77, 105, 99,
- 114, 111, 115, 111, 102, 116,
- 32, 40, 82, 41, 32, 72,
- 76, 83, 76, 32, 83, 104,
- 97, 100, 101, 114, 32, 67,
- 111, 109, 112, 105, 108, 101,
- 114, 32, 57, 46, 51, 48,
- 46, 57, 50, 48, 48, 46,
- 49, 54, 51, 56, 52, 0,
- 171, 171, 171, 73, 83, 71,
- 78, 44, 0, 0, 0, 1,
- 0, 0, 0, 8, 0, 0,
- 0, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
- 0, 3, 0, 0, 0, 0,
- 0, 0, 0, 7, 3, 0,
- 0, 80, 79, 83, 73, 84,
- 73, 79, 78, 0, 171, 171,
- 171, 79, 83, 71, 78, 104,
+ 0, 7, 3, 0, 0, 80,
+ 79, 83, 73, 84, 73, 79,
+ 78, 0, 171, 171, 171, 79,
+ 83, 71, 78, 104, 0, 0,
+ 0, 3, 0, 0, 0, 8,
+ 0, 0, 0, 80, 0, 0,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 3, 0, 0,
- 0, 8, 0, 0, 0, 80,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 92, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
0, 1, 0, 0, 0, 3,
+ 12, 0, 0, 92, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 12,
+ 3, 0, 0, 83, 86, 95,
+ 80, 111, 115, 105, 116, 105,
+ 111, 110, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 171, 19, 244, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 152,
+ 4, 0, 0, 68, 88, 66,
+ 67, 227, 84, 48, 176, 142,
+ 231, 109, 63, 97, 30, 1,
+ 57, 105, 137, 178, 120, 1,
+ 0, 0, 0, 152, 4, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 4, 1, 0,
+ 0, 224, 1, 0, 0, 92,
+ 2, 0, 0, 220, 3, 0,
+ 0, 76, 4, 0, 0, 65,
+ 111, 110, 57, 196, 0, 0,
+ 0, 196, 0, 0, 0, 0,
+ 2, 255, 255, 144, 0, 0,
+ 0, 52, 0, 0, 0, 1,
+ 0, 40, 0, 0, 0, 52,
+ 0, 0, 0, 52, 0, 1,
+ 0, 36, 0, 0, 0, 52,
0, 0, 0, 0, 0, 0,
- 0, 15, 0, 0, 0, 92,
+ 0, 3, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 255, 255, 81, 0, 0,
+ 5, 1, 0, 15, 160, 0,
+ 0, 128, 63, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 3,
- 0, 0, 0, 1, 0, 0,
- 0, 3, 12, 0, 0, 92,
+ 0, 0, 0, 31, 0, 0,
+ 2, 0, 0, 0, 128, 0,
+ 0, 15, 176, 31, 0, 0,
+ 2, 0, 0, 0, 144, 0,
+ 8, 15, 160, 1, 0, 0,
+ 2, 0, 0, 7, 128, 0,
+ 0, 228, 160, 4, 0, 0,
+ 4, 0, 0, 15, 128, 0,
+ 0, 36, 128, 1, 0, 64,
+ 160, 1, 0, 21, 160, 1,
+ 0, 0, 2, 0, 8, 15,
+ 128, 0, 0, 228, 128, 66,
+ 0, 0, 3, 0, 0, 15,
+ 128, 0, 0, 228, 176, 0,
+ 8, 228, 160, 5, 0, 0,
+ 3, 0, 0, 15, 128, 0,
+ 0, 70, 128, 0, 0, 255,
+ 160, 1, 0, 0, 2, 1,
+ 8, 15, 128, 0, 0, 228,
+ 128, 255, 255, 0, 0, 83,
+ 72, 68, 82, 212, 0, 0,
+ 0, 64, 0, 0, 0, 53,
+ 0, 0, 0, 89, 0, 0,
+ 4, 70, 142, 32, 0, 0,
+ 0, 0, 0, 4, 0, 0,
+ 0, 90, 0, 0, 3, 0,
+ 96, 16, 0, 0, 0, 0,
+ 0, 88, 24, 0, 4, 0,
+ 112, 16, 0, 0, 0, 0,
+ 0, 85, 85, 0, 0, 98,
+ 16, 0, 3, 50, 16, 16,
+ 0, 1, 0, 0, 0, 101,
+ 0, 0, 3, 242, 32, 16,
+ 0, 0, 0, 0, 0, 101,
+ 0, 0, 3, 242, 32, 16,
+ 0, 1, 0, 0, 0, 104,
+ 0, 0, 2, 1, 0, 0,
+ 0, 54, 0, 0, 6, 114,
+ 32, 16, 0, 0, 0, 0,
+ 0, 70, 130, 32, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 54, 0, 0, 5, 130,
+ 32, 16, 0, 0, 0, 0,
+ 0, 1, 64, 0, 0, 0,
+ 0, 128, 63, 69, 0, 0,
+ 9, 242, 0, 16, 0, 0,
+ 0, 0, 0, 70, 16, 16,
+ 0, 1, 0, 0, 0, 70,
+ 126, 16, 0, 0, 0, 0,
+ 0, 0, 96, 16, 0, 0,
+ 0, 0, 0, 56, 0, 0,
+ 8, 242, 32, 16, 0, 1,
+ 0, 0, 0, 102, 4, 16,
+ 0, 0, 0, 0, 0, 246,
+ 143, 32, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 62,
+ 0, 0, 1, 83, 84, 65,
+ 84, 116, 0, 0, 0, 5,
0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 3,
0, 0, 0, 1, 0, 0,
- 0, 12, 3, 0, 0, 83,
- 86, 95, 80, 111, 115, 105,
- 116, 105, 111, 110, 0, 84,
- 69, 88, 67, 79, 79, 82,
- 68, 0, 171, 171, 171, 207,
- 243, 0, 0, 0, 0, 0,
- 0, 1, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0,
- 0, 156, 4, 0, 0, 68,
- 88, 66, 67, 157, 57, 76,
- 176, 214, 127, 147, 239, 222,
- 22, 182, 42, 105, 157, 114,
- 166, 1, 0, 0, 0, 156,
- 4, 0, 0, 6, 0, 0,
- 0, 56, 0, 0, 0, 4,
- 1, 0, 0, 224, 1, 0,
- 0, 92, 2, 0, 0, 224,
- 3, 0, 0, 80, 4, 0,
- 0, 65, 111, 110, 57, 196,
- 0, 0, 0, 196, 0, 0,
- 0, 0, 2, 255, 255, 144,
- 0, 0, 0, 52, 0, 0,
- 0, 1, 0, 40, 0, 0,
- 0, 52, 0, 0, 0, 52,
- 0, 1, 0, 36, 0, 0,
- 0, 52, 0, 0, 0, 0,
- 0, 0, 0, 3, 0, 1,
+ 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 1, 2, 255, 255, 81,
- 0, 0, 5, 1, 0, 15,
- 160, 0, 0, 128, 63, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 31,
- 0, 0, 2, 0, 0, 0,
- 128, 0, 0, 15, 176, 31,
- 0, 0, 2, 0, 0, 0,
- 144, 0, 8, 15, 160, 1,
- 0, 0, 2, 0, 0, 7,
- 128, 0, 0, 228, 160, 4,
- 0, 0, 4, 0, 0, 15,
- 128, 0, 0, 36, 128, 1,
- 0, 64, 160, 1, 0, 21,
- 160, 1, 0, 0, 2, 0,
- 8, 15, 128, 0, 0, 228,
- 128, 66, 0, 0, 3, 0,
- 0, 15, 128, 0, 0, 228,
- 176, 0, 8, 228, 160, 5,
- 0, 0, 3, 0, 0, 15,
- 128, 0, 0, 70, 128, 0,
- 0, 255, 160, 1, 0, 0,
- 2, 1, 8, 15, 128, 0,
- 0, 228, 128, 255, 255, 0,
- 0, 83, 72, 68, 82, 212,
- 0, 0, 0, 64, 0, 0,
- 0, 53, 0, 0, 0, 89,
- 0, 0, 4, 70, 142, 32,
- 0, 0, 0, 0, 0, 4,
- 0, 0, 0, 90, 0, 0,
- 3, 0, 96, 16, 0, 0,
- 0, 0, 0, 88, 24, 0,
- 4, 0, 112, 16, 0, 0,
- 0, 0, 0, 85, 85, 0,
- 0, 98, 16, 0, 3, 50,
- 16, 16, 0, 1, 0, 0,
- 0, 101, 0, 0, 3, 242,
- 32, 16, 0, 0, 0, 0,
- 0, 101, 0, 0, 3, 242,
- 32, 16, 0, 1, 0, 0,
- 0, 104, 0, 0, 2, 1,
- 0, 0, 0, 54, 0, 0,
- 6, 114, 32, 16, 0, 0,
- 0, 0, 0, 70, 130, 32,
- 0, 0, 0, 0, 0, 3,
- 0, 0, 0, 54, 0, 0,
- 5, 130, 32, 16, 0, 0,
- 0, 0, 0, 1, 64, 0,
- 0, 0, 0, 128, 63, 69,
- 0, 0, 9, 242, 0, 16,
- 0, 0, 0, 0, 0, 70,
- 16, 16, 0, 1, 0, 0,
- 0, 70, 126, 16, 0, 0,
- 0, 0, 0, 0, 96, 16,
- 0, 0, 0, 0, 0, 56,
- 0, 0, 8, 242, 32, 16,
- 0, 1, 0, 0, 0, 102,
- 4, 16, 0, 0, 0, 0,
- 0, 246, 143, 32, 0, 0,
- 0, 0, 0, 3, 0, 0,
- 0, 62, 0, 0, 1, 83,
- 84, 65, 84, 116, 0, 0,
- 0, 5, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
- 0, 3, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 1,
+ 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 82, 68, 69, 70, 120,
+ 1, 0, 0, 1, 0, 0,
+ 0, 144, 0, 0, 0, 3,
+ 0, 0, 0, 28, 0, 0,
+ 0, 0, 4, 255, 255, 0,
+ 1, 0, 0, 70, 1, 0,
+ 0, 124, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 133, 0, 0,
+ 0, 2, 0, 0, 0, 5,
+ 0, 0, 0, 4, 0, 0,
+ 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 12, 0, 0, 0, 137,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 82, 68, 69,
- 70, 124, 1, 0, 0, 1,
- 0, 0, 0, 144, 0, 0,
- 0, 3, 0, 0, 0, 28,
- 0, 0, 0, 0, 4, 255,
- 255, 0, 1, 0, 0, 70,
- 1, 0, 0, 124, 0, 0,
- 0, 3, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 0, 0,
- 0, 0, 0, 0, 0, 133,
- 0, 0, 0, 2, 0, 0,
- 0, 5, 0, 0, 0, 4,
- 0, 0, 0, 255, 255, 255,
- 255, 0, 0, 0, 0, 1,
- 0, 0, 0, 12, 0, 0,
- 0, 137, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0,
+ 0, 115, 83, 97, 109, 112,
+ 108, 101, 114, 0, 116, 101,
+ 120, 0, 99, 98, 48, 0,
+ 171, 171, 171, 137, 0, 0,
+ 0, 4, 0, 0, 0, 168,
+ 0, 0, 0, 64, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 8, 1, 0,
+ 0, 0, 0, 0, 0, 16,
0, 0, 0, 0, 0, 0,
- 0, 1, 0, 0, 0, 0,
- 0, 0, 0, 115, 83, 97,
- 109, 112, 108, 101, 114, 0,
- 116, 101, 120, 0, 99, 98,
- 48, 0, 171, 171, 171, 137,
- 0, 0, 0, 4, 0, 0,
- 0, 168, 0, 0, 0, 64,
+ 0, 20, 1, 0, 0, 0,
+ 0, 0, 0, 36, 1, 0,
+ 0, 16, 0, 0, 0, 16,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 8,
- 1, 0, 0, 0, 0, 0,
- 0, 16, 0, 0, 0, 0,
- 0, 0, 0, 20, 1, 0,
- 0, 0, 0, 0, 0, 36,
- 1, 0, 0, 16, 0, 0,
- 0, 16, 0, 0, 0, 0,
- 0, 0, 0, 20, 1, 0,
- 0, 0, 0, 0, 0, 46,
- 1, 0, 0, 32, 0, 0,
- 0, 16, 0, 0, 0, 0,
- 0, 0, 0, 20, 1, 0,
- 0, 0, 0, 0, 0, 60,
- 1, 0, 0, 48, 0, 0,
- 0, 16, 0, 0, 0, 2,
- 0, 0, 0, 20, 1, 0,
- 0, 0, 0, 0, 0, 81,
- 117, 97, 100, 68, 101, 115,
- 99, 0, 171, 171, 171, 1,
- 0, 3, 0, 1, 0, 4,
+ 0, 20, 1, 0, 0, 0,
+ 0, 0, 0, 46, 1, 0,
+ 0, 32, 0, 0, 0, 16,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 84, 101, 120,
+ 0, 20, 1, 0, 0, 0,
+ 0, 0, 0, 60, 1, 0,
+ 0, 48, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 20, 1, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 77,
+ 97, 115, 107, 84, 101, 120,
67, 111, 111, 114, 100, 115,
- 0, 77, 97, 115, 107, 84,
- 101, 120, 67, 111, 111, 114,
- 100, 115, 0, 84, 101, 120,
- 116, 67, 111, 108, 111, 114,
- 0, 77, 105, 99, 114, 111,
- 115, 111, 102, 116, 32, 40,
- 82, 41, 32, 72, 76, 83,
- 76, 32, 83, 104, 97, 100,
- 101, 114, 32, 67, 111, 109,
- 112, 105, 108, 101, 114, 32,
- 57, 46, 51, 48, 46, 57,
- 50, 48, 48, 46, 49, 54,
- 51, 56, 52, 0, 171, 171,
- 171, 73, 83, 71, 78, 104,
+ 0, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 54, 46,
+ 51, 46, 57, 54, 48, 48,
+ 46, 49, 54, 51, 56, 52,
+ 0, 73, 83, 71, 78, 104,
0, 0, 0, 3, 0, 0,
0, 8, 0, 0, 0, 80,
0, 0, 0, 0, 0, 0,
@@ -15033,7 +15051,7 @@ const BYTE d2deffect[] = 0, 1, 0, 0, 0, 15,
0, 0, 0, 83, 86, 95,
84, 97, 114, 103, 101, 116,
- 0, 171, 171, 47, 248, 0,
+ 0, 171, 171, 111, 248, 0,
0, 0, 0, 0, 0, 77,
97, 115, 107, 101, 100, 0,
4, 0, 0, 0, 1, 0,
@@ -15044,16 +15062,16 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 3, 0,
0, 0, 255, 255, 255, 255,
- 72, 4, 0, 0, 68, 88,
- 66, 67, 28, 248, 40, 83,
- 2, 166, 203, 194, 228, 163,
- 91, 123, 149, 165, 41, 212,
- 1, 0, 0, 0, 72, 4,
+ 68, 4, 0, 0, 68, 88,
+ 66, 67, 77, 85, 167, 240,
+ 56, 56, 155, 78, 125, 96,
+ 49, 253, 103, 100, 22, 62,
+ 1, 0, 0, 0, 68, 4,
0, 0, 6, 0, 0, 0,
56, 0, 0, 0, 248, 0,
0, 0, 244, 1, 0, 0,
- 112, 2, 0, 0, 164, 3,
- 0, 0, 216, 3, 0, 0,
+ 112, 2, 0, 0, 160, 3,
+ 0, 0, 212, 3, 0, 0,
65, 111, 110, 57, 184, 0,
0, 0, 184, 0, 0, 0,
0, 2, 254, 255, 132, 0,
@@ -15132,7 +15150,7 @@ const BYTE d2deffect[] = 0, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 4, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@@ -15142,14 +15160,14 @@ const BYTE d2deffect[] = 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 82, 68,
- 69, 70, 44, 1, 0, 0,
+ 69, 70, 40, 1, 0, 0,
1, 0, 0, 0, 64, 0,
0, 0, 1, 0, 0, 0,
28, 0, 0, 0, 0, 4,
@@ -15196,559 +15214,543 @@ const BYTE d2deffect[] = 41, 32, 72, 76, 83, 76,
32, 83, 104, 97, 100, 101,
114, 32, 67, 111, 109, 112,
- 105, 108, 101, 114, 32, 57,
- 46, 51, 48, 46, 57, 50,
- 48, 48, 46, 49, 54, 51,
- 56, 52, 0, 171, 171, 171,
- 73, 83, 71, 78, 44, 0,
- 0, 0, 1, 0, 0, 0,
- 8, 0, 0, 0, 32, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 105, 108, 101, 114, 32, 54,
+ 46, 51, 46, 57, 54, 48,
+ 48, 46, 49, 54, 51, 56,
+ 52, 0, 73, 83, 71, 78,
+ 44, 0, 0, 0, 1, 0,
+ 0, 0, 8, 0, 0, 0,
+ 32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 7, 3, 0, 0, 80, 79,
- 83, 73, 84, 73, 79, 78,
- 0, 171, 171, 171, 79, 83,
- 71, 78, 104, 0, 0, 0,
- 3, 0, 0, 0, 8, 0,
- 0, 0, 80, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 3, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 171, 171, 171,
+ 79, 83, 71, 78, 104, 0,
0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 15, 0,
- 0, 0, 92, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 1, 0, 0, 0, 3, 12,
- 0, 0, 92, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 1, 0, 0, 0, 12, 3,
- 0, 0, 83, 86, 95, 80,
- 111, 115, 105, 116, 105, 111,
- 110, 0, 84, 69, 88, 67,
- 79, 79, 82, 68, 0, 171,
- 171, 171, 14, 253, 0, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 2, 0, 0, 0,
- 0, 0, 0, 0, 144, 5,
- 0, 0, 68, 88, 66, 67,
- 77, 72, 172, 53, 12, 183,
- 105, 209, 231, 49, 89, 164,
- 104, 240, 17, 81, 1, 0,
- 0, 0, 144, 5, 0, 0,
- 6, 0, 0, 0, 56, 0,
- 0, 0, 64, 1, 0, 0,
- 132, 2, 0, 0, 0, 3,
- 0, 0, 212, 4, 0, 0,
- 68, 5, 0, 0, 65, 111,
- 110, 57, 0, 1, 0, 0,
- 0, 1, 0, 0, 0, 2,
- 255, 255, 200, 0, 0, 0,
- 56, 0, 0, 0, 1, 0,
- 44, 0, 0, 0, 56, 0,
- 0, 0, 56, 0, 2, 0,
- 36, 0, 0, 0, 56, 0,
- 0, 0, 0, 0, 1, 1,
1, 0, 0, 0, 3, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 1, 2, 255, 255,
- 81, 0, 0, 5, 1, 0,
- 15, 160, 0, 0, 128, 63,
0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
0, 0, 0, 0, 0, 0,
- 31, 0, 0, 2, 0, 0,
- 0, 128, 0, 0, 15, 176,
- 31, 0, 0, 2, 0, 0,
- 0, 144, 0, 8, 15, 160,
- 31, 0, 0, 2, 0, 0,
- 0, 144, 1, 8, 15, 160,
- 1, 0, 0, 2, 0, 0,
- 7, 128, 0, 0, 228, 160,
- 4, 0, 0, 4, 0, 0,
- 15, 128, 0, 0, 36, 128,
- 1, 0, 64, 160, 1, 0,
- 21, 160, 1, 0, 0, 2,
- 0, 8, 15, 128, 0, 0,
- 228, 128, 1, 0, 0, 2,
- 0, 0, 3, 128, 0, 0,
- 235, 176, 66, 0, 0, 3,
- 1, 0, 15, 128, 0, 0,
- 228, 176, 0, 8, 228, 160,
- 66, 0, 0, 3, 0, 0,
- 15, 128, 0, 0, 228, 128,
- 1, 8, 228, 160, 5, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 12, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 12, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 74, 253,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 140, 5, 0, 0, 68, 88,
+ 66, 67, 233, 167, 4, 110,
+ 60, 182, 197, 16, 114, 252,
+ 67, 184, 217, 172, 169, 241,
+ 1, 0, 0, 0, 140, 5,
+ 0, 0, 6, 0, 0, 0,
+ 56, 0, 0, 0, 64, 1,
+ 0, 0, 132, 2, 0, 0,
+ 0, 3, 0, 0, 208, 4,
+ 0, 0, 64, 5, 0, 0,
+ 65, 111, 110, 57, 0, 1,
+ 0, 0, 0, 1, 0, 0,
+ 0, 2, 255, 255, 200, 0,
+ 0, 0, 56, 0, 0, 0,
+ 1, 0, 44, 0, 0, 0,
+ 56, 0, 0, 0, 56, 0,
+ 2, 0, 36, 0, 0, 0,
+ 56, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0,
+ 3, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 1, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 1, 0, 0, 2,
+ 0, 0, 7, 128, 0, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 0, 0,
+ 36, 128, 1, 0, 64, 160,
+ 1, 0, 21, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 1, 0,
+ 0, 2, 0, 0, 3, 128,
+ 0, 0, 235, 176, 66, 0,
0, 3, 1, 0, 15, 128,
- 1, 0, 70, 128, 0, 0,
- 255, 160, 5, 0, 0, 3,
+ 0, 0, 228, 176, 0, 8,
+ 228, 160, 66, 0, 0, 3,
0, 0, 15, 128, 0, 0,
- 255, 128, 1, 0, 228, 128,
- 1, 0, 0, 2, 1, 8,
- 15, 128, 0, 0, 228, 128,
- 255, 255, 0, 0, 83, 72,
- 68, 82, 60, 1, 0, 0,
- 64, 0, 0, 0, 79, 0,
- 0, 0, 89, 0, 0, 4,
- 70, 142, 32, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 0, 0, 0, 0,
- 90, 0, 0, 3, 0, 96,
- 16, 0, 1, 0, 0, 0,
+ 228, 128, 1, 8, 228, 160,
+ 5, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 70, 128,
+ 0, 0, 255, 160, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 255, 128, 1, 0,
+ 228, 128, 1, 0, 0, 2,
+ 1, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 60, 1,
+ 0, 0, 64, 0, 0, 0,
+ 79, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 50, 16, 16, 0,
+ 1, 0, 0, 0, 98, 16,
+ 0, 3, 194, 16, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 1, 0, 0, 0, 104, 0,
+ 0, 2, 2, 0, 0, 0,
+ 54, 0, 0, 6, 114, 32,
16, 0, 0, 0, 0, 0,
- 85, 85, 0, 0, 88, 24,
- 0, 4, 0, 112, 16, 0,
- 1, 0, 0, 0, 85, 85,
- 0, 0, 98, 16, 0, 3,
- 50, 16, 16, 0, 1, 0,
- 0, 0, 98, 16, 0, 3,
- 194, 16, 16, 0, 1, 0,
- 0, 0, 101, 0, 0, 3,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 101, 0, 0, 3,
- 242, 32, 16, 0, 1, 0,
- 0, 0, 104, 0, 0, 2,
- 2, 0, 0, 0, 54, 0,
- 0, 6, 114, 32, 16, 0,
- 0, 0, 0, 0, 70, 130,
- 32, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 54, 0,
- 0, 5, 130, 32, 16, 0,
- 0, 0, 0, 0, 1, 64,
- 0, 0, 0, 0, 128, 63,
- 69, 0, 0, 9, 242, 0,
- 16, 0, 0, 0, 0, 0,
- 70, 16, 16, 0, 1, 0,
- 0, 0, 70, 126, 16, 0,
- 0, 0, 0, 0, 0, 96,
- 16, 0, 0, 0, 0, 0,
- 56, 0, 0, 8, 242, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
16, 0, 0, 0, 0, 0,
- 102, 4, 16, 0, 0, 0,
- 0, 0, 246, 143, 32, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 69, 0, 0, 9,
- 242, 0, 16, 0, 1, 0,
- 0, 0, 230, 26, 16, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 102, 4, 16, 0,
+ 0, 0, 0, 0, 246, 143,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 230, 26,
16, 0, 1, 0, 0, 0,
- 0, 96, 16, 0, 1, 0,
- 0, 0, 56, 0, 0, 7,
- 242, 32, 16, 0, 1, 0,
- 0, 0, 70, 14, 16, 0,
- 0, 0, 0, 0, 246, 15,
- 16, 0, 1, 0, 0, 0,
- 62, 0, 0, 1, 83, 84,
- 65, 84, 116, 0, 0, 0,
- 7, 0, 0, 0, 2, 0,
- 0, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 2, 0,
+ 70, 126, 16, 0, 1, 0,
+ 0, 0, 0, 96, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 32, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 1, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 7, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 200, 1, 0, 0,
+ 1, 0, 0, 0, 224, 0,
+ 0, 0, 5, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 150, 1, 0, 0, 188, 0,
+ 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 82, 68, 69, 70,
- 204, 1, 0, 0, 1, 0,
- 0, 0, 224, 0, 0, 0,
- 5, 0, 0, 0, 28, 0,
- 0, 0, 0, 4, 255, 255,
- 0, 1, 0, 0, 150, 1,
- 0, 0, 188, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 197, 0,
- 0, 0, 3, 0, 0, 0,
+ 197, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 1, 0,
- 0, 0, 0, 0, 0, 0,
- 210, 0, 0, 0, 2, 0,
- 0, 0, 5, 0, 0, 0,
- 4, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 1, 0, 0, 0, 12, 0,
- 0, 0, 214, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 210, 0, 0, 0,
2, 0, 0, 0, 5, 0,
0, 0, 4, 0, 0, 0,
- 255, 255, 255, 255, 1, 0,
+ 255, 255, 255, 255, 0, 0,
0, 0, 1, 0, 0, 0,
- 12, 0, 0, 0, 219, 0,
- 0, 0, 0, 0, 0, 0,
+ 12, 0, 0, 0, 214, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 12, 0, 0, 0,
+ 219, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 115, 83, 97, 109, 112, 108,
- 101, 114, 0, 115, 77, 97,
- 115, 107, 83, 97, 109, 112,
- 108, 101, 114, 0, 116, 101,
- 120, 0, 109, 97, 115, 107,
- 0, 99, 98, 48, 0, 171,
- 219, 0, 0, 0, 4, 0,
- 0, 0, 248, 0, 0, 0,
- 64, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 83, 97, 109,
+ 112, 108, 101, 114, 0, 115,
+ 77, 97, 115, 107, 83, 97,
+ 109, 112, 108, 101, 114, 0,
+ 116, 101, 120, 0, 109, 97,
+ 115, 107, 0, 99, 98, 48,
+ 0, 171, 219, 0, 0, 0,
+ 4, 0, 0, 0, 248, 0,
+ 0, 0, 64, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 88, 1, 0, 0, 0, 0,
- 0, 0, 16, 0, 0, 0,
- 0, 0, 0, 0, 100, 1,
+ 0, 0, 88, 1, 0, 0,
+ 0, 0, 0, 0, 16, 0,
0, 0, 0, 0, 0, 0,
- 116, 1, 0, 0, 16, 0,
- 0, 0, 16, 0, 0, 0,
- 0, 0, 0, 0, 100, 1,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 116, 1, 0, 0,
+ 16, 0, 0, 0, 16, 0,
0, 0, 0, 0, 0, 0,
- 126, 1, 0, 0, 32, 0,
- 0, 0, 16, 0, 0, 0,
- 0, 0, 0, 0, 100, 1,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 126, 1, 0, 0,
+ 32, 0, 0, 0, 16, 0,
0, 0, 0, 0, 0, 0,
- 140, 1, 0, 0, 48, 0,
- 0, 0, 16, 0, 0, 0,
- 2, 0, 0, 0, 100, 1,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 140, 1, 0, 0,
+ 48, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 100, 1, 0, 0, 0, 0,
+ 0, 0, 81, 117, 97, 100,
+ 68, 101, 115, 99, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 81, 117, 97, 100, 68, 101,
- 115, 99, 0, 171, 171, 171,
- 1, 0, 3, 0, 1, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 84, 101,
- 120, 67, 111, 111, 114, 100,
- 115, 0, 77, 97, 115, 107,
84, 101, 120, 67, 111, 111,
- 114, 100, 115, 0, 84, 101,
- 120, 116, 67, 111, 108, 111,
- 114, 0, 77, 105, 99, 114,
- 111, 115, 111, 102, 116, 32,
- 40, 82, 41, 32, 72, 76,
- 83, 76, 32, 83, 104, 97,
- 100, 101, 114, 32, 67, 111,
- 109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
- 171, 171, 73, 83, 71, 78,
- 104, 0, 0, 0, 3, 0,
- 0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 114, 100, 115, 0, 77, 97,
+ 115, 107, 84, 101, 120, 67,
+ 111, 111, 114, 100, 115, 0,
+ 84, 101, 120, 116, 67, 111,
+ 108, 111, 114, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 3, 3, 0, 0,
- 92, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 12, 12, 0, 0,
- 83, 86, 95, 80, 111, 115,
- 105, 116, 105, 111, 110, 0,
- 84, 69, 88, 67, 79, 79,
- 82, 68, 0, 171, 171, 171,
- 79, 83, 71, 78, 68, 0,
- 0, 0, 2, 0, 0, 0,
- 8, 0, 0, 0, 56, 0,
+ 15, 0, 0, 0, 92, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
- 0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 56, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 3, 0,
0, 0, 1, 0, 0, 0,
- 15, 0, 0, 0, 83, 86,
- 95, 84, 97, 114, 103, 101,
- 116, 0, 171, 171, 110, 1,
- 1, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 16, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 46, 0, 0, 0, 18, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
+ 12, 12, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 68, 0, 0, 0,
+ 2, 0, 0, 0, 8, 0,
+ 0, 0, 56, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 54, 0,
- 0, 0, 64, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 56, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171, 166, 1, 1, 0,
0, 0, 0, 0, 4, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 255, 255, 255, 255,
- 0, 0, 0, 0, 93, 0,
- 0, 0, 65, 0, 0, 0,
+ 0, 0, 0, 0, 46, 0,
+ 0, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 102, 0, 0, 0,
+ 0, 0, 54, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 93, 0, 0, 0,
65, 0, 0, 0, 0, 0,
- 0, 0, 16, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 112, 0, 0, 0, 65, 0,
0, 0, 0, 0, 0, 0,
- 32, 0, 0, 0, 0, 0,
+ 102, 0, 0, 0, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 126, 0,
+ 0, 0, 0, 0, 112, 0,
0, 0, 65, 0, 0, 0,
- 0, 0, 0, 0, 48, 0,
+ 0, 0, 0, 0, 32, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 136, 0, 0, 0,
- 160, 0, 0, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 255, 255, 255, 255, 0, 0,
- 0, 0, 168, 0, 0, 0,
- 140, 0, 0, 0, 0, 0,
+ 0, 0, 126, 0, 0, 0,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 48, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 136, 0, 0, 0, 160, 0,
0, 0, 0, 0, 0, 0,
- 181, 0, 0, 0, 140, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 168, 0, 0, 0, 140, 0,
0, 0, 0, 0, 0, 0,
- 48, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 194, 0,
- 0, 0, 140, 0, 0, 0,
- 0, 0, 0, 0, 96, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 181, 0,
+ 0, 0, 140, 0, 0, 0,
+ 0, 0, 0, 0, 48, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 206, 0, 0, 0,
- 65, 0, 0, 0, 0, 0,
- 0, 0, 144, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 194, 0, 0, 0,
+ 140, 0, 0, 0, 0, 0,
+ 0, 0, 96, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 218, 0, 0, 0, 112, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 3, 1, 0, 0, 231, 0,
+ 206, 0, 0, 0, 65, 0,
0, 0, 0, 0, 0, 0,
+ 144, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 218, 0,
+ 0, 0, 112, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 3, 1,
+ 0, 0, 231, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 61, 1,
- 0, 0, 33, 1, 0, 0,
- 0, 0, 0, 0, 48, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 107, 1, 0, 0,
- 79, 1, 0, 0, 0, 0,
- 0, 0, 64, 0, 0, 0,
+ 0, 0, 61, 1, 0, 0,
+ 33, 1, 0, 0, 0, 0,
+ 0, 0, 48, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 112, 1, 0, 0, 33, 1,
+ 107, 1, 0, 0, 79, 1,
0, 0, 0, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 154, 1,
- 0, 0, 126, 1, 0, 0,
- 0, 0, 0, 0, 88, 0,
+ 0, 0, 0, 0, 112, 1,
+ 0, 0, 33, 1, 0, 0,
+ 0, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 156, 1, 0, 0,
+ 0, 0, 154, 1, 0, 0,
126, 1, 0, 0, 0, 0,
- 0, 0, 92, 0, 0, 0,
+ 0, 0, 88, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 164, 1, 0, 0, 126, 1,
+ 156, 1, 0, 0, 126, 1,
0, 0, 0, 0, 0, 0,
- 96, 0, 0, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 213, 1,
- 0, 0, 185, 1, 0, 0,
- 0, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 217, 1, 0, 0, 185, 1,
+ 0, 0, 0, 0, 164, 1,
+ 0, 0, 126, 1, 0, 0,
+ 0, 0, 0, 0, 96, 0,
0, 0, 0, 0, 0, 0,
- 255, 255, 255, 255, 0, 0,
- 0, 0, 224, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 213, 1, 0, 0,
185, 1, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255,
- 0, 0, 0, 0, 14, 2,
- 0, 0, 242, 1, 0, 0,
+ 0, 0, 0, 0, 217, 1,
+ 0, 0, 185, 1, 0, 0,
0, 0, 0, 0, 255, 255,
- 255, 255, 4, 0, 0, 0,
- 45, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 23, 2, 0, 0, 55, 0,
+ 255, 255, 0, 0, 0, 0,
+ 224, 1, 0, 0, 185, 1,
0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 213, 1,
- 0, 0, 46, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 35, 2, 0, 0,
- 47, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 47, 2, 0, 0, 0, 0,
- 0, 0, 59, 2, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 14, 2, 0, 0,
242, 1, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255,
4, 0, 0, 0, 45, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 71, 2,
+ 1, 0, 0, 0, 23, 2,
0, 0, 55, 0, 0, 0,
0, 0, 0, 0, 2, 0,
- 0, 0, 217, 1, 0, 0,
+ 0, 0, 213, 1, 0, 0,
46, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 83, 2, 0, 0, 47, 0,
+ 35, 2, 0, 0, 47, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 95, 2,
+ 1, 0, 0, 0, 47, 2,
0, 0, 0, 0, 0, 0,
- 107, 2, 0, 0, 242, 1,
+ 59, 2, 0, 0, 242, 1,
0, 0, 0, 0, 0, 0,
255, 255, 255, 255, 4, 0,
0, 0, 45, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 120, 2, 0, 0,
+ 0, 0, 71, 2, 0, 0,
55, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
- 213, 1, 0, 0, 46, 0,
+ 217, 1, 0, 0, 46, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 132, 2,
+ 1, 0, 0, 0, 83, 2,
0, 0, 47, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 144, 2, 0, 0,
- 0, 0, 0, 0, 156, 2,
+ 0, 0, 95, 2, 0, 0,
+ 0, 0, 0, 0, 107, 2,
0, 0, 242, 1, 0, 0,
0, 0, 0, 0, 255, 255,
255, 255, 4, 0, 0, 0,
45, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 171, 2, 0, 0, 55, 0,
+ 120, 2, 0, 0, 55, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 213, 1,
0, 0, 46, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 183, 2, 0, 0,
+ 0, 0, 132, 2, 0, 0,
47, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 195, 2, 0, 0, 0, 0,
- 0, 0, 207, 2, 0, 0,
+ 144, 2, 0, 0, 0, 0,
+ 0, 0, 156, 2, 0, 0,
242, 1, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255,
4, 0, 0, 0, 45, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 220, 2,
+ 1, 0, 0, 0, 171, 2,
0, 0, 55, 0, 0, 0,
0, 0, 0, 0, 2, 0,
- 0, 0, 224, 1, 0, 0,
+ 0, 0, 213, 1, 0, 0,
46, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 232, 2, 0, 0, 47, 0,
+ 183, 2, 0, 0, 47, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 244, 2,
+ 1, 0, 0, 0, 195, 2,
0, 0, 0, 0, 0, 0,
- 0, 3, 0, 0, 242, 1,
+ 207, 2, 0, 0, 242, 1,
0, 0, 0, 0, 0, 0,
- 255, 255, 255, 255, 5, 0,
+ 255, 255, 255, 255, 4, 0,
0, 0, 45, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 15, 3, 0, 0,
+ 0, 0, 220, 2, 0, 0,
55, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
- 213, 1, 0, 0, 46, 0,
+ 224, 1, 0, 0, 46, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 27, 3,
+ 1, 0, 0, 0, 232, 2,
0, 0, 47, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 39, 3, 0, 0,
- 52, 0, 0, 0, 0, 0,
+ 0, 0, 244, 2, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 242, 1, 0, 0,
+ 0, 0, 0, 0, 255, 255,
+ 255, 255, 5, 0, 0, 0,
+ 45, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 15, 3, 0, 0, 55, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 213, 1,
+ 0, 0, 46, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 27, 3, 0, 0,
+ 47, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 51, 3, 0, 0, 0, 0,
- 0, 0, 131, 3, 0, 0,
- 103, 3, 0, 0, 0, 0,
+ 39, 3, 0, 0, 52, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 51, 3,
+ 0, 0, 0, 0, 0, 0,
+ 131, 3, 0, 0, 103, 3,
+ 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 2, 0,
+ 0, 0, 19, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 143, 3, 0, 0,
+ 13, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 155, 3, 0, 0, 0, 0,
+ 0, 0, 206, 3, 0, 0,
+ 178, 3, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255,
- 2, 0, 0, 0, 19, 0,
+ 2, 0, 0, 0, 37, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 143, 3,
- 0, 0, 13, 0, 0, 0,
+ 1, 0, 0, 0, 219, 3,
+ 0, 0, 44, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 155, 3, 0, 0,
- 0, 0, 0, 0, 206, 3,
+ 0, 0, 231, 3, 0, 0,
+ 0, 0, 0, 0, 243, 3,
0, 0, 178, 3, 0, 0,
0, 0, 0, 0, 255, 255,
- 255, 255, 2, 0, 0, 0,
+ 255, 255, 8, 0, 0, 0,
37, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 219, 3, 0, 0, 44, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 231, 3,
+ 0, 4, 0, 0, 38, 0,
0, 0, 0, 0, 0, 0,
- 243, 3, 0, 0, 178, 3,
- 0, 0, 0, 0, 0, 0,
- 255, 255, 255, 255, 8, 0,
- 0, 0, 37, 0, 0, 0,
+ 1, 0, 0, 0, 12, 4,
+ 0, 0, 39, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 0, 4, 0, 0,
- 38, 0, 0, 0, 0, 0,
+ 0, 0, 24, 4, 0, 0,
+ 40, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 12, 4, 0, 0, 39, 0,
+ 36, 4, 0, 0, 41, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 24, 4,
- 0, 0, 40, 0, 0, 0,
+ 1, 0, 0, 0, 48, 4,
+ 0, 0, 42, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 36, 4, 0, 0,
- 41, 0, 0, 0, 0, 0,
+ 0, 0, 60, 4, 0, 0,
+ 43, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 48, 4, 0, 0, 42, 0,
+ 72, 4, 0, 0, 44, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 60, 4,
- 0, 0, 43, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 72, 4, 0, 0,
- 44, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 84, 4, 0, 0, 0, 0,
- 0, 0, 96, 4, 0, 0,
- 178, 3, 0, 0, 0, 0,
- 0, 0, 255, 255, 255, 255,
- 9, 0, 0, 0, 36, 0,
+ 1, 0, 0, 0, 84, 4,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 107, 4,
- 0, 0, 37, 0, 0, 0,
+ 96, 4, 0, 0, 178, 3,
+ 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 9, 0,
+ 0, 0, 36, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 119, 4, 0, 0,
- 38, 0, 0, 0, 0, 0,
+ 0, 0, 107, 4, 0, 0,
+ 37, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 131, 4, 0, 0, 39, 0,
+ 119, 4, 0, 0, 38, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 143, 4,
- 0, 0, 40, 0, 0, 0,
+ 1, 0, 0, 0, 131, 4,
+ 0, 0, 39, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 155, 4, 0, 0,
- 41, 0, 0, 0, 0, 0,
+ 0, 0, 143, 4, 0, 0,
+ 40, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 167, 4, 0, 0, 42, 0,
+ 155, 4, 0, 0, 41, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 179, 4,
- 0, 0, 43, 0, 0, 0,
+ 1, 0, 0, 0, 167, 4,
+ 0, 0, 42, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 191, 4, 0, 0,
- 44, 0, 0, 0, 0, 0,
+ 0, 0, 179, 4, 0, 0,
+ 43, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 203, 4, 0, 0, 0, 0,
- 0, 0, 215, 4, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 229, 4, 0, 0,
- 4, 0, 0, 0, 0, 0,
+ 191, 4, 0, 0, 44, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
- 0, 0, 131, 3, 0, 0,
- 6, 0, 0, 0, 0, 0,
- 0, 0, 7, 0, 0, 0,
- 52, 9, 0, 0, 8, 0,
+ 1, 0, 0, 0, 203, 4,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 60, 9,
- 0, 0, 7, 0, 0, 0,
- 0, 0, 0, 0, 7, 0,
- 0, 0, 32, 12, 0, 0,
- 40, 12, 0, 0, 1, 0,
+ 215, 4, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
229, 4, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0,
@@ -15756,13 +15758,13 @@ const BYTE d2deffect[] = 0, 0, 2, 0, 0, 0,
131, 3, 0, 0, 6, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 152, 16,
+ 7, 0, 0, 0, 48, 9,
0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 160, 16, 0, 0,
+ 0, 0, 56, 9, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 244, 29, 0, 0, 252, 29,
+ 28, 12, 0, 0, 36, 12,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 229, 4,
0, 0, 4, 0, 0, 0,
@@ -15771,13 +15773,13 @@ const BYTE d2deffect[] = 2, 0, 0, 0, 131, 3,
0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 108, 34, 0, 0,
+ 0, 0, 144, 16, 0, 0,
8, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 116, 34, 0, 0, 7, 0,
+ 152, 16, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 124, 51,
- 0, 0, 132, 51, 0, 0,
+ 7, 0, 0, 0, 240, 29,
+ 0, 0, 248, 29, 0, 0,
1, 0, 0, 0, 0, 0,
0, 0, 229, 4, 0, 0,
4, 0, 0, 0, 0, 0,
@@ -15786,210 +15788,225 @@ const BYTE d2deffect[] = 0, 0, 131, 3, 0, 0,
6, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 245, 55, 0, 0, 8, 0,
+ 100, 34, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 253, 55,
+ 1, 0, 0, 0, 108, 34,
0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 229, 93, 0, 0,
- 237, 93, 0, 0, 6, 0,
+ 0, 0, 212, 51, 0, 0,
+ 220, 51, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
- 2, 94, 0, 0, 4, 0,
+ 229, 4, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
131, 3, 0, 0, 6, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 55, 101,
+ 7, 0, 0, 0, 73, 56,
0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 63, 101, 0, 0,
+ 0, 0, 81, 56, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 47, 111, 0, 0, 55, 111,
+ 57, 94, 0, 0, 65, 94,
+ 0, 0, 6, 0, 0, 0,
+ 0, 0, 0, 0, 86, 94,
0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 131, 3,
0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 106, 118, 0, 0,
+ 0, 0, 139, 101, 0, 0,
8, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 114, 118, 0, 0, 7, 0,
+ 147, 101, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 66, 126,
- 0, 0, 74, 126, 0, 0,
+ 7, 0, 0, 0, 131, 111,
+ 0, 0, 139, 111, 0, 0,
4, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 131, 3, 0, 0,
6, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 131, 133, 0, 0, 8, 0,
+ 190, 118, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 139, 133,
+ 1, 0, 0, 0, 198, 118,
0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 127, 143, 0, 0,
- 135, 143, 0, 0, 4, 0,
+ 0, 0, 150, 126, 0, 0,
+ 158, 126, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
131, 3, 0, 0, 6, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 190, 150,
+ 7, 0, 0, 0, 215, 133,
0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 198, 150, 0, 0,
+ 0, 0, 223, 133, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 154, 158, 0, 0, 162, 158,
+ 211, 143, 0, 0, 219, 143,
0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 131, 3,
0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 221, 165, 0, 0,
+ 0, 0, 18, 151, 0, 0,
8, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 229, 165, 0, 0, 7, 0,
+ 26, 151, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 221, 175,
- 0, 0, 229, 175, 0, 0,
+ 7, 0, 0, 0, 238, 158,
+ 0, 0, 246, 158, 0, 0,
4, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 131, 3, 0, 0,
6, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 30, 183, 0, 0, 8, 0,
+ 49, 166, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 38, 183,
+ 1, 0, 0, 0, 57, 166,
0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 254, 190, 0, 0,
- 6, 191, 0, 0, 1, 0,
- 0, 0, 0, 0, 0, 0,
- 229, 4, 0, 0, 4, 0,
+ 0, 0, 49, 176, 0, 0,
+ 57, 176, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
131, 3, 0, 0, 6, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 102, 195,
+ 7, 0, 0, 0, 114, 183,
0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 110, 195, 0, 0,
+ 0, 0, 122, 183, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 82, 199, 0, 0, 90, 199,
- 0, 0, 3, 0, 0, 0,
+ 82, 191, 0, 0, 90, 191,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 229, 4,
- 0, 0, 7, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 131, 3,
- 0, 0, 10, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 114, 199, 0, 0,
- 11, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 150, 199, 0, 0, 2, 0,
- 0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 206, 3,
0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 238, 203, 0, 0,
+ 0, 0, 182, 195, 0, 0,
8, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 246, 203, 0, 0, 7, 0,
+ 190, 195, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 238, 213,
- 0, 0, 246, 213, 0, 0,
+ 7, 0, 0, 0, 162, 199,
+ 0, 0, 170, 199, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 229, 4, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 131, 3, 0, 0,
10, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 249, 213, 0, 0, 11, 0,
+ 194, 199, 0, 0, 11, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 29, 214,
+ 1, 0, 0, 0, 230, 199,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 2, 0,
- 0, 0, 243, 3, 0, 0,
+ 0, 0, 206, 3, 0, 0,
6, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 117, 218, 0, 0, 8, 0,
+ 58, 204, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 125, 218,
+ 1, 0, 0, 0, 66, 204,
0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 57, 228, 0, 0,
- 65, 228, 0, 0, 7, 0,
+ 0, 0, 58, 214, 0, 0,
+ 66, 214, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
131, 3, 0, 0, 10, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 68, 228,
+ 1, 0, 0, 0, 69, 214,
0, 0, 11, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 104, 228, 0, 0,
+ 0, 0, 105, 214, 0, 0,
2, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
243, 3, 0, 0, 6, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 192, 232,
+ 7, 0, 0, 0, 189, 218,
0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 200, 232, 0, 0,
+ 0, 0, 197, 218, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 124, 243, 0, 0, 132, 243,
- 0, 0, 2, 0, 0, 0,
- 0, 0, 0, 0, 150, 243,
+ 129, 228, 0, 0, 137, 228,
0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 131, 3,
0, 0, 10, 0, 0, 0,
0, 0, 0, 0, 1, 0,
- 0, 0, 159, 243, 0, 0,
+ 0, 0, 140, 228, 0, 0,
11, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 195, 243, 0, 0, 2, 0,
+ 176, 228, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 96, 4,
+ 2, 0, 0, 0, 243, 3,
0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 27, 248, 0, 0,
+ 0, 0, 4, 233, 0, 0,
8, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 35, 248, 0, 0, 7, 0,
+ 12, 233, 0, 0, 7, 0,
0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 207, 252,
- 0, 0, 215, 252, 0, 0,
+ 7, 0, 0, 0, 192, 243,
+ 0, 0, 200, 243, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 218, 243, 0, 0,
7, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 131, 3, 0, 0,
10, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
- 222, 252, 0, 0, 11, 0,
+ 227, 243, 0, 0, 11, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 2, 253,
+ 1, 0, 0, 0, 7, 244,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 96, 4, 0, 0,
6, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
- 90, 1, 1, 0, 8, 0,
+ 91, 248, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 98, 1,
- 1, 0, 7, 0, 0, 0,
+ 1, 0, 0, 0, 99, 248,
+ 0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 7, 0,
- 0, 0, 2, 7, 1, 0
+ 0, 0, 11, 253, 0, 0,
+ 19, 253, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 131, 3, 0, 0, 10, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 253,
+ 0, 0, 11, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 62, 253, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 96, 4, 0, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 146, 1,
+ 1, 0, 8, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 154, 1, 1, 0,
+ 7, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 54, 7, 1, 0
};
diff --git a/gfx/2d/ShadersD2D1.h b/gfx/2d/ShadersD2D1.h index 0a447cbe0..2d004a000 100644 --- a/gfx/2d/ShadersD2D1.h +++ b/gfx/2d/ShadersD2D1.h @@ -1,9 +1,8 @@ #if 0
//
-// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
-///
// Buffer Definitions:
//
// cbuffer constants
@@ -14,6 +13,8 @@ // float A; // Offset: 24 Size: 4
// float radius1; // Offset: 28 Size: 4
// float sq_radius1; // Offset: 32 Size: 4
+// float repeat_correct; // Offset: 36 Size: 4
+// float allow_odd; // Offset: 40 Size: 4
// float3x2 transform; // Offset: 48 Size: 28
//
// }
@@ -89,11 +90,24 @@ mul r0.xzw, r0, r1.x
mov r1.w, c1.w
mad r1.xyz, r0.xzww, c0.z, r1.w
- cmp r2.x, r1.x, r0.x, r0.w
+ cmp r1.w, r1.x, r0.x, r0.w
cmp r0.xzw, r1.xyyz, c6.xyxy, c6.zyzw
- mov r2.y, c5.x
+ frc r1.x, r1.w
+ add r1.x, -r1.x, r1.w
+ mul r1.y, r1.x, c5.x
+ abs r1.y, r1.y
+ frc r1.y, r1.y
+ cmp r1.y, r1.x, r1.y, -r1.y
+ add r1.x, -r1.x, r1.w
+ add r1.y, r1.y, r1.y
+ abs r1.y, r1.y
+ mul r1.y, r1.y, c2.z
+ frc r1.z, -r1.w
+ lrp r2.w, r1.y, r1.z, r1.x
+ lrp r3.x, c2.y, r2.w, r1.w
+ mov r3.y, c5.x
texld r1, t1, s0
- texld r2, r2, s1
+ texld r2, r3, s1
mul r2.xyz, r2.w, r2
mul r1, r1, r2
add r0.w, r0.w, r0.x
@@ -102,7 +116,7 @@ mul r0, r0.y, r1
mov oC0, r0
-// approximately 33 instruction slots used (2 texture, 31 arithmetic)
+// approximately 46 instruction slots used (2 texture, 44 arithmetic)
ps_4_0
dcl_constantbuffer cb0[5], immediateIndexed
dcl_sampler s0, mode_default
@@ -134,36 +148,52 @@ add r0.w, -r0.z, r0.x mul r1.xy, r0.xzxx, cb0[0].zzzz
ge r1.xy, r1.xyxx, -cb0[1].wwww
and r1.xy, r1.xyxx, l(0x3f800000, 0x3f800000, 0, 0)
-mad r2.x, r1.x, r0.w, r0.z
-max r0.x, r1.y, r1.x
-ge r0.x, l(0.000000), r0.x
-movc r0.x, r0.x, l(-0.000000), l(1.000000)
-mov r2.y, l(0.500000)
-sample r1.xyzw, r2.xyxx, t1.xyzw, s1
+mad r0.x, r1.x, r0.w, r0.z
+max r0.z, r1.y, r1.x
+ge r0.z, l(0.000000), r0.z
+movc r0.z, r0.z, l(-0.000000), l(1.000000)
+round_pi r0.w, r0.x
+add r0.w, -r0.x, r0.w
+round_ni r1.x, r0.x
+mul r1.y, r1.x, l(0.500000)
+add r1.x, r0.x, -r1.x
+ge r1.z, r1.y, -r1.y
+frc r1.y, |r1.y|
+movc r1.y, r1.z, r1.y, -r1.y
+add r1.y, r1.y, r1.y
+mul r1.z, |r1.y|, cb0[2].z
+mad r1.y, -|r1.y|, cb0[2].z, l(1.000000)
+mul r0.w, r0.w, r1.z
+mad r0.w, r1.x, r1.y, r0.w
+mul r0.w, r0.w, cb0[2].y
+add r1.x, l(1.000000), -cb0[2].y
+mad r1.x, r0.x, r1.x, r0.w
+mov r1.y, l(0.500000)
+sample r1.xyzw, r1.xyxx, t1.xyzw, s1
mul r1.xyz, r1.wwww, r1.xyzx
sample r2.xyzw, v2.xyxx, t0.xyzw, s0
mul r1.xyzw, r1.xyzw, r2.xyzw
-mul r1.xyzw, r0.xxxx, r1.xyzw
+mul r1.xyzw, r0.zzzz, r1.xyzw
mul o0.xyzw, r0.yyyy, r1.xyzw
ret
-// Approximately 33 instruction slots used
+// Approximately 49 instruction slots used
#endif
const BYTE SampleRadialGradientPS[] =
{
- 68, 88, 66, 67, 215, 144,
- 212, 127, 245, 34, 39, 73,
- 96, 47, 76, 239, 115, 172,
- 47, 64, 1, 0, 0, 0,
- 172, 10, 0, 0, 6, 0,
+ 68, 88, 66, 67, 20, 173,
+ 189, 124, 239, 6, 22, 67,
+ 226, 55, 243, 56, 30, 182,
+ 172, 36, 1, 0, 0, 0,
+ 180, 13, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
- 192, 2, 0, 0, 40, 7,
- 0, 0, 164, 7, 0, 0,
- 252, 9, 0, 0, 120, 10,
+ 136, 3, 0, 0, 232, 9,
+ 0, 0, 100, 10, 0, 0,
+ 4, 13, 0, 0, 128, 13,
0, 0, 65, 111, 110, 57,
- 128, 2, 0, 0, 128, 2,
+ 72, 3, 0, 0, 72, 3,
0, 0, 0, 2, 255, 255,
- 72, 2, 0, 0, 56, 0,
+ 16, 3, 0, 0, 56, 0,
0, 0, 1, 0, 44, 0,
0, 0, 56, 0, 0, 0,
56, 0, 2, 0, 36, 0,
@@ -235,385 +265,513 @@ const BYTE SampleRadialGradientPS[] = 0, 4, 1, 0, 7, 128,
0, 0, 248, 128, 0, 0,
170, 160, 1, 0, 255, 128,
- 88, 0, 0, 4, 2, 0,
- 1, 128, 1, 0, 0, 128,
+ 88, 0, 0, 4, 1, 0,
+ 8, 128, 1, 0, 0, 128,
0, 0, 0, 128, 0, 0,
255, 128, 88, 0, 0, 4,
0, 0, 13, 128, 1, 0,
148, 128, 6, 0, 68, 160,
- 6, 0, 230, 160, 1, 0,
- 0, 2, 2, 0, 2, 128,
- 5, 0, 0, 160, 66, 0,
- 0, 3, 1, 0, 15, 128,
- 1, 0, 228, 176, 0, 8,
- 228, 160, 66, 0, 0, 3,
- 2, 0, 15, 128, 2, 0,
- 228, 128, 1, 8, 228, 160,
- 5, 0, 0, 3, 2, 0,
- 7, 128, 2, 0, 255, 128,
- 2, 0, 228, 128, 5, 0,
- 0, 3, 1, 0, 15, 128,
- 1, 0, 228, 128, 2, 0,
- 228, 128, 2, 0, 0, 3,
- 0, 0, 8, 128, 0, 0,
+ 6, 0, 230, 160, 19, 0,
+ 0, 2, 1, 0, 1, 128,
+ 1, 0, 255, 128, 2, 0,
+ 0, 3, 1, 0, 1, 128,
+ 1, 0, 0, 129, 1, 0,
+ 255, 128, 5, 0, 0, 3,
+ 1, 0, 2, 128, 1, 0,
+ 0, 128, 5, 0, 0, 160,
+ 35, 0, 0, 2, 1, 0,
+ 2, 128, 1, 0, 85, 128,
+ 19, 0, 0, 2, 1, 0,
+ 2, 128, 1, 0, 85, 128,
+ 88, 0, 0, 4, 1, 0,
+ 2, 128, 1, 0, 0, 128,
+ 1, 0, 85, 128, 1, 0,
+ 85, 129, 2, 0, 0, 3,
+ 1, 0, 1, 128, 1, 0,
+ 0, 129, 1, 0, 255, 128,
+ 2, 0, 0, 3, 1, 0,
+ 2, 128, 1, 0, 85, 128,
+ 1, 0, 85, 128, 35, 0,
+ 0, 2, 1, 0, 2, 128,
+ 1, 0, 85, 128, 5, 0,
+ 0, 3, 1, 0, 2, 128,
+ 1, 0, 85, 128, 2, 0,
+ 170, 160, 19, 0, 0, 2,
+ 1, 0, 4, 128, 1, 0,
+ 255, 129, 18, 0, 0, 4,
+ 2, 0, 8, 128, 1, 0,
+ 85, 128, 1, 0, 170, 128,
+ 1, 0, 0, 128, 18, 0,
+ 0, 4, 3, 0, 1, 128,
+ 2, 0, 85, 160, 2, 0,
+ 255, 128, 1, 0, 255, 128,
+ 1, 0, 0, 2, 3, 0,
+ 2, 128, 5, 0, 0, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 3, 0, 228, 128, 1, 8,
+ 228, 160, 5, 0, 0, 3,
+ 2, 0, 7, 128, 2, 0,
+ 255, 128, 2, 0, 228, 128,
+ 5, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 2, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 8, 128,
+ 0, 0, 255, 128, 0, 0,
+ 0, 128, 88, 0, 0, 4,
+ 0, 0, 1, 128, 0, 0,
255, 128, 0, 0, 0, 128,
- 88, 0, 0, 4, 0, 0,
- 1, 128, 0, 0, 255, 128,
- 0, 0, 0, 128, 0, 0,
- 170, 128, 5, 0, 0, 3,
- 1, 0, 15, 128, 0, 0,
- 0, 128, 1, 0, 228, 128,
- 5, 0, 0, 3, 0, 0,
- 15, 128, 0, 0, 85, 128,
- 1, 0, 228, 128, 1, 0,
- 0, 2, 0, 8, 15, 128,
- 0, 0, 228, 128, 255, 255,
- 0, 0, 83, 72, 68, 82,
- 96, 4, 0, 0, 64, 0,
- 0, 0, 24, 1, 0, 0,
- 89, 0, 0, 4, 70, 142,
- 32, 0, 0, 0, 0, 0,
- 5, 0, 0, 0, 90, 0,
- 0, 3, 0, 96, 16, 0,
- 0, 0, 0, 0, 90, 0,
- 0, 3, 0, 96, 16, 0,
- 1, 0, 0, 0, 88, 24,
+ 0, 0, 170, 128, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 0, 128, 1, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 85, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 88, 6, 0, 0,
+ 64, 0, 0, 0, 150, 1,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 1, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
0, 4, 0, 112, 16, 0,
- 0, 0, 0, 0, 85, 85,
- 0, 0, 88, 24, 0, 4,
- 0, 112, 16, 0, 1, 0,
- 0, 0, 85, 85, 0, 0,
- 98, 16, 0, 3, 50, 16,
+ 1, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 3, 0, 0, 0, 15, 0,
+ 0, 8, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
16, 0, 1, 0, 0, 0,
- 98, 16, 0, 3, 50, 16,
- 16, 0, 2, 0, 0, 0,
- 101, 0, 0, 3, 242, 32,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 8, 18, 0,
16, 0, 0, 0, 0, 0,
- 104, 0, 0, 2, 3, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 128, 32, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 15, 0, 0, 8,
- 18, 0, 16, 0, 0, 0,
+ 66, 0, 16, 0, 0, 0,
0, 0, 70, 16, 16, 0,
1, 0, 0, 0, 70, 128,
32, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 8, 18, 0, 16, 0,
- 0, 0, 0, 0, 10, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 8, 34, 0, 16, 0,
+ 0, 0, 0, 0, 42, 0,
16, 0, 0, 0, 0, 0,
42, 128, 32, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 15, 0, 0, 8, 66, 0,
- 16, 0, 0, 0, 0, 0,
- 70, 16, 16, 0, 1, 0,
- 0, 0, 70, 128, 32, 0,
- 0, 0, 0, 0, 4, 0,
- 0, 0, 0, 0, 0, 8,
- 34, 0, 16, 0, 0, 0,
- 0, 0, 42, 0, 16, 0,
- 0, 0, 0, 0, 42, 128,
- 32, 0, 0, 0, 0, 0,
- 4, 0, 0, 0, 0, 0,
- 0, 9, 50, 0, 16, 0,
- 0, 0, 0, 0, 70, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 9, 50, 0,
16, 0, 0, 0, 0, 0,
- 70, 128, 32, 128, 65, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 15, 0,
- 0, 7, 130, 0, 16, 0,
- 0, 0, 0, 0, 70, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 15, 0, 0, 7, 130, 0,
16, 0, 0, 0, 0, 0,
70, 0, 16, 0, 0, 0,
- 0, 0, 0, 0, 0, 9,
- 130, 0, 16, 0, 0, 0,
- 0, 0, 58, 0, 16, 0,
- 0, 0, 0, 0, 10, 128,
- 32, 128, 65, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
- 0, 0, 56, 0, 0, 8,
- 130, 0, 16, 0, 0, 0,
- 0, 0, 58, 0, 16, 0,
- 0, 0, 0, 0, 42, 128,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 54, 0,
- 0, 6, 66, 0, 16, 0,
- 0, 0, 0, 0, 58, 128,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 16, 0,
- 0, 8, 18, 0, 16, 0,
- 0, 0, 0, 0, 70, 2,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 9, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
16, 0, 0, 0, 0, 0,
- 70, 130, 32, 0, 0, 0,
+ 10, 128, 32, 128, 65, 0,
0, 0, 0, 0, 0, 0,
- 50, 0, 0, 10, 34, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 8, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
16, 0, 0, 0, 0, 0,
- 10, 0, 16, 0, 0, 0,
+ 42, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 54, 0, 0, 6, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 16, 0, 0, 8, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 50, 0, 0, 10,
+ 34, 0, 16, 0, 0, 0,
0, 0, 10, 0, 16, 0,
- 0, 0, 0, 0, 58, 0,
- 16, 128, 65, 0, 0, 0,
- 0, 0, 0, 0, 75, 0,
- 0, 6, 18, 0, 16, 0,
- 1, 0, 0, 0, 26, 0,
- 16, 128, 129, 0, 0, 0,
- 0, 0, 0, 0, 29, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 75, 0, 0, 6, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 128, 129, 0,
+ 0, 0, 0, 0, 0, 0,
+ 29, 0, 0, 7, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 7, 34, 0, 16, 0,
0, 0, 0, 0, 26, 0,
16, 0, 0, 0, 0, 0,
1, 64, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 7,
- 34, 0, 16, 0, 0, 0,
- 0, 0, 26, 0, 16, 0,
- 0, 0, 0, 0, 1, 64,
- 0, 0, 0, 0, 128, 63,
- 54, 0, 0, 6, 34, 0,
+ 128, 63, 54, 0, 0, 6,
+ 34, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 7,
+ 82, 0, 16, 0, 0, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 6, 1,
16, 0, 1, 0, 0, 0,
- 10, 0, 16, 128, 65, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 7, 82, 0,
+ 14, 0, 0, 8, 82, 0,
16, 0, 0, 0, 0, 0,
- 6, 0, 16, 0, 0, 0,
- 0, 0, 6, 1, 16, 0,
- 1, 0, 0, 0, 14, 0,
- 0, 8, 82, 0, 16, 0,
- 0, 0, 0, 0, 6, 2,
+ 6, 2, 16, 0, 0, 0,
+ 0, 0, 166, 138, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 50, 0, 16, 0,
+ 1, 0, 0, 0, 134, 0,
16, 0, 0, 0, 0, 0,
166, 138, 32, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 8, 130, 0,
- 16, 0, 0, 0, 0, 0,
- 42, 0, 16, 128, 65, 0,
0, 0, 0, 0, 0, 0,
- 10, 0, 16, 0, 0, 0,
- 0, 0, 56, 0, 0, 8,
- 50, 0, 16, 0, 1, 0,
- 0, 0, 134, 0, 16, 0,
- 0, 0, 0, 0, 166, 138,
- 32, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 29, 0,
- 0, 9, 50, 0, 16, 0,
- 1, 0, 0, 0, 70, 0,
+ 29, 0, 0, 9, 50, 0,
16, 0, 1, 0, 0, 0,
- 246, 143, 32, 128, 65, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 1, 0,
- 0, 10, 50, 0, 16, 0,
- 1, 0, 0, 0, 70, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 246, 143, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 10, 50, 0,
16, 0, 1, 0, 0, 0,
- 2, 64, 0, 0, 0, 0,
- 128, 63, 0, 0, 128, 63,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 50, 0, 0, 9,
- 18, 0, 16, 0, 2, 0,
- 0, 0, 10, 0, 16, 0,
- 1, 0, 0, 0, 58, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 52, 0,
+ 0, 7, 66, 0, 16, 0,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 29, 0, 0, 7,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 66, 0,
16, 0, 0, 0, 0, 0,
42, 0, 16, 0, 0, 0,
- 0, 0, 52, 0, 0, 7,
- 18, 0, 16, 0, 0, 0,
- 0, 0, 26, 0, 16, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 128, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 66, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 65, 0,
+ 0, 5, 18, 0, 16, 0,
1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 34, 0,
16, 0, 1, 0, 0, 0,
- 29, 0, 0, 7, 18, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 8, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 0,
16, 0, 0, 0, 0, 0,
- 1, 64, 0, 0, 0, 0,
- 0, 0, 10, 0, 16, 0,
- 0, 0, 0, 0, 55, 0,
- 0, 9, 18, 0, 16, 0,
+ 10, 0, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 29, 0, 0, 8, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 26, 0, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 0, 6,
+ 34, 0, 16, 0, 1, 0,
+ 0, 0, 26, 0, 16, 128,
+ 129, 0, 0, 0, 1, 0,
+ 0, 0, 55, 0, 0, 10,
+ 34, 0, 16, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 9, 66, 0, 16, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 128, 129, 0, 0, 0,
+ 1, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 34, 0, 16, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 128, 193, 0, 0, 0,
+ 1, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 56, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 9, 130, 0, 16, 0,
0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
16, 0, 0, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 9, 18, 0,
+ 16, 0, 1, 0, 0, 0,
1, 64, 0, 0, 0, 0,
- 0, 128, 1, 64, 0, 0,
- 0, 0, 128, 63, 54, 0,
- 0, 5, 34, 0, 16, 0,
- 2, 0, 0, 0, 1, 64,
- 0, 0, 0, 0, 0, 63,
- 69, 0, 0, 9, 242, 0,
+ 128, 63, 26, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 18, 0,
16, 0, 1, 0, 0, 0,
- 70, 0, 16, 0, 2, 0,
- 0, 0, 70, 126, 16, 0,
- 1, 0, 0, 0, 0, 96,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 34, 0,
16, 0, 1, 0, 0, 0,
- 56, 0, 0, 7, 114, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 0, 16, 0,
+ 1, 0, 0, 0, 70, 126,
16, 0, 1, 0, 0, 0,
- 246, 15, 16, 0, 1, 0,
- 0, 0, 70, 2, 16, 0,
- 1, 0, 0, 0, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 2, 0, 0, 0, 70, 16,
+ 0, 96, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
16, 0, 2, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
- 0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 56, 0,
- 0, 7, 242, 0, 16, 0,
- 1, 0, 0, 0, 70, 14,
+ 70, 16, 16, 0, 2, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 242, 0,
16, 0, 1, 0, 0, 0,
- 70, 14, 16, 0, 2, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 7, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 10,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
0, 0, 56, 0, 0, 7,
- 242, 0, 16, 0, 1, 0,
- 0, 0, 6, 0, 16, 0,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 86, 5, 16, 0,
0, 0, 0, 0, 70, 14,
16, 0, 1, 0, 0, 0,
- 56, 0, 0, 7, 242, 32,
- 16, 0, 0, 0, 0, 0,
- 86, 5, 16, 0, 0, 0,
- 0, 0, 70, 14, 16, 0,
- 1, 0, 0, 0, 62, 0,
- 0, 1, 83, 84, 65, 84,
- 116, 0, 0, 0, 33, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
- 0, 0, 23, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
- 0, 0, 1, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 49, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 40, 0,
0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 2, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 82, 68, 69, 70, 80, 2,
- 0, 0, 1, 0, 0, 0,
- 0, 1, 0, 0, 5, 0,
- 0, 0, 28, 0, 0, 0,
- 0, 4, 255, 255, 0, 1,
- 0, 0, 28, 2, 0, 0,
- 188, 0, 0, 0, 3, 0,
+ 0, 0, 82, 68, 69, 70,
+ 152, 2, 0, 0, 1, 0,
+ 0, 0, 0, 1, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 100, 2,
+ 0, 0, 188, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 201, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 1, 0,
- 0, 0, 201, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0,
- 1, 0, 0, 0, 217, 0,
- 0, 0, 2, 0, 0, 0,
- 5, 0, 0, 0, 4, 0,
- 0, 0, 255, 255, 255, 255,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 13, 0, 0, 0,
- 230, 0, 0, 0, 2, 0,
+ 217, 0, 0, 0, 2, 0,
0, 0, 5, 0, 0, 0,
4, 0, 0, 0, 255, 255,
- 255, 255, 1, 0, 0, 0,
+ 255, 255, 0, 0, 0, 0,
1, 0, 0, 0, 13, 0,
- 0, 0, 246, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 1, 0, 0, 0, 73, 110,
- 112, 117, 116, 83, 97, 109,
- 112, 108, 101, 114, 0, 71,
- 114, 97, 100, 105, 101, 110,
- 116, 83, 97, 109, 112, 108,
- 101, 114, 0, 73, 110, 112,
- 117, 116, 84, 101, 120, 116,
- 117, 114, 101, 0, 71, 114,
- 97, 100, 105, 101, 110, 116,
- 84, 101, 120, 116, 117, 114,
- 101, 0, 99, 111, 110, 115,
- 116, 97, 110, 116, 115, 0,
- 246, 0, 0, 0, 6, 0,
- 0, 0, 24, 1, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 168, 1, 0, 0, 0, 0,
- 0, 0, 12, 0, 0, 0,
- 2, 0, 0, 0, 176, 1,
- 0, 0, 0, 0, 0, 0,
- 192, 1, 0, 0, 16, 0,
- 0, 0, 8, 0, 0, 0,
- 2, 0, 0, 0, 200, 1,
- 0, 0, 0, 0, 0, 0,
- 216, 1, 0, 0, 24, 0,
- 0, 0, 4, 0, 0, 0,
- 2, 0, 0, 0, 220, 1,
- 0, 0, 0, 0, 0, 0,
- 236, 1, 0, 0, 28, 0,
- 0, 0, 4, 0, 0, 0,
- 2, 0, 0, 0, 220, 1,
- 0, 0, 0, 0, 0, 0,
- 244, 1, 0, 0, 32, 0,
+ 0, 0, 230, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
0, 0, 4, 0, 0, 0,
- 2, 0, 0, 0, 220, 1,
- 0, 0, 0, 0, 0, 0,
- 255, 1, 0, 0, 48, 0,
- 0, 0, 28, 0, 0, 0,
- 2, 0, 0, 0, 12, 2,
+ 255, 255, 255, 255, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 246, 0,
0, 0, 0, 0, 0, 0,
- 100, 105, 102, 102, 0, 171,
- 171, 171, 1, 0, 3, 0,
- 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 99, 101, 110, 116, 101, 114,
- 49, 0, 1, 0, 3, 0,
- 1, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 65, 0, 171, 171, 0, 0,
- 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 73, 110, 112, 117, 116, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 71, 114, 97, 100, 105,
+ 101, 110, 116, 83, 97, 109,
+ 112, 108, 101, 114, 0, 73,
+ 110, 112, 117, 116, 84, 101,
+ 120, 116, 117, 114, 101, 0,
+ 71, 114, 97, 100, 105, 101,
+ 110, 116, 84, 101, 120, 116,
+ 117, 114, 101, 0, 99, 111,
+ 110, 115, 116, 97, 110, 116,
+ 115, 0, 246, 0, 0, 0,
+ 8, 0, 0, 0, 24, 1,
+ 0, 0, 80, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 114, 97, 100, 105,
- 117, 115, 49, 0, 115, 113,
- 95, 114, 97, 100, 105, 117,
- 115, 49, 0, 116, 114, 97,
- 110, 115, 102, 111, 114, 109,
- 0, 171, 171, 171, 3, 0,
- 3, 0, 3, 0, 2, 0,
+ 0, 0, 216, 1, 0, 0,
+ 0, 0, 0, 0, 12, 0,
+ 0, 0, 2, 0, 0, 0,
+ 224, 1, 0, 0, 0, 0,
+ 0, 0, 240, 1, 0, 0,
+ 16, 0, 0, 0, 8, 0,
+ 0, 0, 2, 0, 0, 0,
+ 248, 1, 0, 0, 0, 0,
+ 0, 0, 8, 2, 0, 0,
+ 24, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 28, 2, 0, 0,
+ 28, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 36, 2, 0, 0,
+ 32, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 47, 2, 0, 0,
+ 36, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 62, 2, 0, 0,
+ 40, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 12, 2, 0, 0, 0, 0,
+ 0, 0, 72, 2, 0, 0,
+ 48, 0, 0, 0, 28, 0,
+ 0, 0, 2, 0, 0, 0,
+ 84, 2, 0, 0, 0, 0,
+ 0, 0, 100, 105, 102, 102,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 3, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 77, 105, 99, 114,
- 111, 115, 111, 102, 116, 32,
- 40, 82, 41, 32, 72, 76,
- 83, 76, 32, 83, 104, 97,
- 100, 101, 114, 32, 67, 111,
- 109, 112, 105, 108, 101, 114,
- 32, 57, 46, 51, 48, 46,
- 57, 50, 48, 48, 46, 49,
- 54, 51, 56, 52, 0, 171,
- 73, 83, 71, 78, 116, 0,
- 0, 0, 3, 0, 0, 0,
- 8, 0, 0, 0, 80, 0,
+ 0, 0, 99, 101, 110, 116,
+ 101, 114, 49, 0, 1, 0,
+ 3, 0, 1, 0, 2, 0,
0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 3, 0,
+ 0, 0, 65, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 114, 97,
+ 100, 105, 117, 115, 49, 0,
+ 115, 113, 95, 114, 97, 100,
+ 105, 117, 115, 49, 0, 114,
+ 101, 112, 101, 97, 116, 95,
+ 99, 111, 114, 114, 101, 99,
+ 116, 0, 97, 108, 108, 111,
+ 119, 95, 111, 100, 100, 0,
+ 116, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 171, 171,
+ 3, 0, 3, 0, 3, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 54, 46, 51,
+ 46, 57, 54, 48, 48, 46,
+ 49, 54, 51, 56, 52, 0,
+ 171, 171, 73, 83, 71, 78,
+ 116, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 92, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 15, 3, 0, 0,
+ 107, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 3, 0, 0,
+ 83, 86, 95, 80, 79, 83,
+ 73, 84, 73, 79, 78, 0,
+ 83, 67, 69, 78, 69, 95,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 79, 83, 71, 78, 44, 0,
0, 0, 1, 0, 0, 0,
- 15, 3, 0, 0, 107, 0,
+ 8, 0, 0, 0, 32, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
- 0, 0, 2, 0, 0, 0,
- 15, 3, 0, 0, 83, 86,
- 95, 80, 79, 83, 73, 84,
- 73, 79, 78, 0, 83, 67,
- 69, 78, 69, 95, 80, 79,
- 83, 73, 84, 73, 79, 78,
- 0, 84, 69, 88, 67, 79,
- 79, 82, 68, 0, 79, 83,
- 71, 78, 44, 0, 0, 0,
- 1, 0, 0, 0, 8, 0,
- 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 0, 15, 0,
- 0, 0, 83, 86, 95, 84,
- 97, 114, 103, 101, 116, 0,
- 171, 171
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
};
#if 0
//
-// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
+// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
-///
// Buffer Definitions:
//
// cbuffer constants
@@ -624,6 +782,8 @@ const BYTE SampleRadialGradientPS[] = // float A; // Offset: 24 Size: 4 [unused]
// float radius1; // Offset: 28 Size: 4
// float sq_radius1; // Offset: 32 Size: 4 [unused]
+// float repeat_correct; // Offset: 36 Size: 4
+// float allow_odd; // Offset: 40 Size: 4
// float3x2 transform; // Offset: 48 Size: 28
//
// }
@@ -661,8 +821,7 @@ const BYTE SampleRadialGradientPS[] = //
// Target Reg Buffer Start Reg # of Regs Data Conversion
// ---------- ------- --------- --------- ----------------------
-// c0 cb0 0 2 ( FLT, FLT, FLT, FLT)
-// c2 cb0 3 2 ( FLT, FLT, FLT, FLT)
+// c0 cb0 0 5 ( FLT, FLT, FLT, FLT)
//
//
// Sampler/Resource to DX9 shader sampler mappings:
@@ -676,33 +835,46 @@ const BYTE SampleRadialGradientPS[] = // Level9 shader bytecode:
//
ps_2_x
- def c4, 0.5, -0, 1, 0
+ def c5, 0.5, -0, 1, 0
dcl t0
dcl t1
dcl_2d s0
dcl_2d s1
- dp2add r0.x, t0, c2, c2.z
- dp2add r0.y, t0, c3, c3.z
+ dp2add r0.x, t0, c3, c3.z
+ dp2add r0.y, t0, c4, c4.z
add r0.xy, r0, -c1
mul r0.w, c1.w, c1.w
dp2add r0.w, r0, r0, -r0.w
- mul r0.w, r0.w, c4.x
+ mul r0.w, r0.w, c5.x
mov r0.z, c1.w
dp3 r0.x, r0, c0
rcp r0.x, r0.x
- mul r0.x, r0.x, r0.w
- mov r0.y, c4.x
- texld r1, t1, s0
- texld r2, r0, s1
+ mul r0.y, r0.x, r0.w
+ frc r0.z, r0.y
+ add r0.z, -r0.z, r0.y
+ mul r1.w, r0.z, c5.x
+ abs r1.x, r1.w
+ frc r1.x, r1.x
+ cmp r1.x, r0.z, r1.x, -r1.x
+ mad r0.x, r0.w, r0.x, -r0.z
+ add r0.z, r1.x, r1.x
+ abs r0.z, r0.z
+ mul r0.z, r0.z, c2.z
+ frc r0.w, -r0.y
+ lrp r1.x, r0.z, r0.w, r0.x
+ lrp r2.x, c2.y, r1.x, r0.y
mov r0.w, c1.w
- mad r0.x, r0.x, -c0.z, -r0.w
- cmp r0.x, r0.x, c4.y, c4.z
+ mad r0.x, r0.y, -c0.z, -r0.w
+ cmp r0.x, r0.x, c5.y, c5.z
+ mov r2.y, c5.x
+ texld r1, t1, s0
+ texld r2, r2, s1
mul r2.xyz, r2.w, r2
mul r1, r1, r2
mul r0, r0.x, r1
mov oC0, r0
-// approximately 23 instruction slots used (2 texture, 21 arithmetic)
+// approximately 36 instruction slots used (2 texture, 34 arithmetic)
ps_4_0
dcl_constantbuffer cb0[5], immediateIndexed
dcl_sampler s0, mode_default
@@ -724,45 +896,58 @@ mul r0.w, r0.w, l(0.500000) mov r0.z, cb0[1].w
dp3 r0.x, r0.xyzx, cb0[0].xyzx
div r0.x, r0.w, r0.x
-mov r0.y, l(0.500000)
-sample r1.xyzw, r0.xyxx, t1.xyzw, s1
+round_pi r0.y, r0.x
+round_ni r0.z, r0.x
+mul r0.w, r0.z, l(0.500000)
+add r0.yz, -r0.xxzx, r0.yyxy
+ge r1.x, r0.w, -r0.w
+frc r0.w, |r0.w|
+movc r0.w, r1.x, r0.w, -r0.w
+add r0.w, r0.w, r0.w
+mul r1.x, |r0.w|, cb0[2].z
+mad r0.w, -|r0.w|, cb0[2].z, l(1.000000)
+mul r0.y, r0.y, r1.x
+mad r0.y, r0.z, r0.w, r0.y
+mul r0.y, r0.y, cb0[2].y
+add r0.z, l(1.000000), -cb0[2].y
+mad r1.x, r0.x, r0.z, r0.y
mul r0.x, r0.x, cb0[0].z
ge r0.x, -cb0[1].w, r0.x
movc r0.x, r0.x, l(-0.000000), l(1.000000)
+mov r1.y, l(0.500000)
+sample r1.xyzw, r1.xyxx, t1.xyzw, s1
mul r1.xyz, r1.wwww, r1.xyzx
sample r2.xyzw, v2.xyxx, t0.xyzw, s0
mul r1.xyzw, r1.xyzw, r2.xyzw
mul o0.xyzw, r0.xxxx, r1.xyzw
ret
-// Approximately 21 instruction slots used
+// Approximately 36 instruction slots used
#endif
const BYTE SampleRadialGradientA0PS[] =
{
- 68, 88, 66, 67, 67, 228,
- 119, 188, 222, 208, 62, 54,
- 201, 121, 212, 21, 98, 33,
- 252, 111, 1, 0, 0, 0,
- 152, 8, 0, 0, 6, 0,
+ 68, 88, 66, 67, 47, 105,
+ 118, 126, 8, 122, 228, 233,
+ 56, 98, 50, 148, 135, 10,
+ 63, 196, 1, 0, 0, 0,
+ 120, 11, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
- 20, 2, 0, 0, 20, 5,
- 0, 0, 144, 5, 0, 0,
- 232, 7, 0, 0, 100, 8,
+ 212, 2, 0, 0, 172, 7,
+ 0, 0, 40, 8, 0, 0,
+ 200, 10, 0, 0, 68, 11,
0, 0, 65, 111, 110, 57,
- 212, 1, 0, 0, 212, 1,
+ 148, 2, 0, 0, 148, 2,
0, 0, 0, 2, 255, 255,
- 144, 1, 0, 0, 68, 0,
- 0, 0, 2, 0, 44, 0,
- 0, 0, 68, 0, 0, 0,
- 68, 0, 2, 0, 36, 0,
- 0, 0, 68, 0, 0, 0,
+ 92, 2, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 0, 0,
0, 0, 1, 1, 1, 0,
- 0, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 5, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 3, 0, 2, 0,
- 2, 0, 0, 0, 0, 0,
1, 2, 255, 255, 81, 0,
- 0, 5, 4, 0, 15, 160,
+ 0, 5, 5, 0, 15, 160,
0, 0, 0, 63, 0, 0,
0, 128, 0, 0, 128, 63,
0, 0, 0, 0, 31, 0,
@@ -775,11 +960,11 @@ const BYTE SampleRadialGradientA0PS[] = 0, 2, 0, 0, 0, 144,
1, 8, 15, 160, 90, 0,
0, 4, 0, 0, 1, 128,
- 0, 0, 228, 176, 2, 0,
- 228, 160, 2, 0, 170, 160,
+ 0, 0, 228, 176, 3, 0,
+ 228, 160, 3, 0, 170, 160,
90, 0, 0, 4, 0, 0,
2, 128, 0, 0, 228, 176,
- 3, 0, 228, 160, 3, 0,
+ 4, 0, 228, 160, 4, 0,
170, 160, 2, 0, 0, 3,
0, 0, 3, 128, 0, 0,
228, 128, 1, 0, 228, 161,
@@ -791,7 +976,7 @@ const BYTE SampleRadialGradientA0PS[] = 228, 128, 0, 0, 255, 129,
5, 0, 0, 3, 0, 0,
8, 128, 0, 0, 255, 128,
- 4, 0, 0, 160, 1, 0,
+ 5, 0, 0, 160, 1, 0,
0, 2, 0, 0, 4, 128,
1, 0, 255, 160, 8, 0,
0, 3, 0, 0, 1, 128,
@@ -799,24 +984,58 @@ const BYTE SampleRadialGradientA0PS[] = 228, 160, 6, 0, 0, 2,
0, 0, 1, 128, 0, 0,
0, 128, 5, 0, 0, 3,
- 0, 0, 1, 128, 0, 0,
+ 0, 0, 2, 128, 0, 0,
0, 128, 0, 0, 255, 128,
+ 19, 0, 0, 2, 0, 0,
+ 4, 128, 0, 0, 85, 128,
+ 2, 0, 0, 3, 0, 0,
+ 4, 128, 0, 0, 170, 129,
+ 0, 0, 85, 128, 5, 0,
+ 0, 3, 1, 0, 8, 128,
+ 0, 0, 170, 128, 5, 0,
+ 0, 160, 35, 0, 0, 2,
+ 1, 0, 1, 128, 1, 0,
+ 255, 128, 19, 0, 0, 2,
+ 1, 0, 1, 128, 1, 0,
+ 0, 128, 88, 0, 0, 4,
+ 1, 0, 1, 128, 0, 0,
+ 170, 128, 1, 0, 0, 128,
+ 1, 0, 0, 129, 4, 0,
+ 0, 4, 0, 0, 1, 128,
+ 0, 0, 255, 128, 0, 0,
+ 0, 128, 0, 0, 170, 129,
+ 2, 0, 0, 3, 0, 0,
+ 4, 128, 1, 0, 0, 128,
+ 1, 0, 0, 128, 35, 0,
+ 0, 2, 0, 0, 4, 128,
+ 0, 0, 170, 128, 5, 0,
+ 0, 3, 0, 0, 4, 128,
+ 0, 0, 170, 128, 2, 0,
+ 170, 160, 19, 0, 0, 2,
+ 0, 0, 8, 128, 0, 0,
+ 85, 129, 18, 0, 0, 4,
+ 1, 0, 1, 128, 0, 0,
+ 170, 128, 0, 0, 255, 128,
+ 0, 0, 0, 128, 18, 0,
+ 0, 4, 2, 0, 1, 128,
+ 2, 0, 85, 160, 1, 0,
+ 0, 128, 0, 0, 85, 128,
1, 0, 0, 2, 0, 0,
- 2, 128, 4, 0, 0, 160,
- 66, 0, 0, 3, 1, 0,
- 15, 128, 1, 0, 228, 176,
- 0, 8, 228, 160, 66, 0,
- 0, 3, 2, 0, 15, 128,
- 0, 0, 228, 128, 1, 8,
- 228, 160, 1, 0, 0, 2,
- 0, 0, 8, 128, 1, 0,
- 255, 160, 4, 0, 0, 4,
+ 8, 128, 1, 0, 255, 160,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 0, 0, 85, 128,
+ 0, 0, 170, 161, 0, 0,
+ 255, 129, 88, 0, 0, 4,
0, 0, 1, 128, 0, 0,
- 0, 128, 0, 0, 170, 161,
- 0, 0, 255, 129, 88, 0,
- 0, 4, 0, 0, 1, 128,
- 0, 0, 0, 128, 4, 0,
- 85, 160, 4, 0, 170, 160,
+ 0, 128, 5, 0, 85, 160,
+ 5, 0, 170, 160, 1, 0,
+ 0, 2, 2, 0, 2, 128,
+ 5, 0, 0, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 176, 0, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 2, 0,
+ 228, 128, 1, 8, 228, 160,
5, 0, 0, 3, 2, 0,
7, 128, 2, 0, 255, 128,
2, 0, 228, 128, 5, 0,
@@ -828,8 +1047,8 @@ const BYTE SampleRadialGradientA0PS[] = 1, 0, 0, 2, 0, 8,
15, 128, 0, 0, 228, 128,
255, 255, 0, 0, 83, 72,
- 68, 82, 248, 2, 0, 0,
- 64, 0, 0, 0, 190, 0,
+ 68, 82, 208, 4, 0, 0,
+ 64, 0, 0, 0, 52, 1,
0, 0, 89, 0, 0, 4,
70, 142, 32, 0, 0, 0,
0, 0, 5, 0, 0, 0,
@@ -908,202 +1127,293 @@ const BYTE SampleRadialGradientA0PS[] = 0, 0, 0, 0, 58, 0,
16, 0, 0, 0, 0, 0,
10, 0, 16, 0, 0, 0,
- 0, 0, 54, 0, 0, 5,
+ 0, 0, 66, 0, 0, 5,
34, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 65, 0,
+ 0, 5, 66, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
0, 0, 1, 64, 0, 0,
- 0, 0, 0, 63, 69, 0,
- 0, 9, 242, 0, 16, 0,
- 1, 0, 0, 0, 70, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 8, 98, 0, 16, 0,
+ 0, 0, 0, 0, 6, 2,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 86, 4,
16, 0, 0, 0, 0, 0,
- 70, 126, 16, 0, 1, 0,
- 0, 0, 0, 96, 16, 0,
- 1, 0, 0, 0, 56, 0,
- 0, 8, 18, 0, 16, 0,
- 0, 0, 0, 0, 10, 0,
+ 29, 0, 0, 8, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 26, 0, 0, 6,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 128,
+ 129, 0, 0, 0, 0, 0,
+ 0, 0, 55, 0, 0, 10,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
16, 0, 0, 0, 0, 0,
- 42, 128, 32, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
0, 0, 0, 0, 0, 0,
- 29, 0, 0, 9, 18, 0,
+ 0, 0, 0, 7, 130, 0,
16, 0, 0, 0, 0, 0,
- 58, 128, 32, 128, 65, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 10, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 9, 18, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 128, 129, 0, 0, 0,
+ 0, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 128, 193, 0, 0, 0,
+ 0, 0, 0, 0, 42, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 56, 0, 0, 7, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 9, 34, 0, 16, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 34, 0, 16, 0,
+ 0, 0, 0, 0, 26, 0,
16, 0, 0, 0, 0, 0,
- 55, 0, 0, 9, 18, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 9, 66, 0,
16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 26, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 8, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 42, 128, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 29, 0, 0, 9,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 58, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
10, 0, 16, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 128,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 54, 0, 0, 5,
+ 34, 0, 16, 0, 1, 0,
0, 0, 1, 64, 0, 0,
- 0, 0, 0, 128, 1, 64,
- 0, 0, 0, 0, 128, 63,
- 56, 0, 0, 7, 114, 0,
- 16, 0, 1, 0, 0, 0,
- 246, 15, 16, 0, 1, 0,
- 0, 0, 70, 2, 16, 0,
- 1, 0, 0, 0, 69, 0,
+ 0, 0, 0, 63, 69, 0,
0, 9, 242, 0, 16, 0,
- 2, 0, 0, 0, 70, 16,
- 16, 0, 2, 0, 0, 0,
- 70, 126, 16, 0, 0, 0,
+ 1, 0, 0, 0, 70, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 1, 0,
0, 0, 0, 96, 16, 0,
- 0, 0, 0, 0, 56, 0,
- 0, 7, 242, 0, 16, 0,
- 1, 0, 0, 0, 70, 14,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 1, 0, 0, 0, 246, 15,
16, 0, 1, 0, 0, 0,
- 70, 14, 16, 0, 2, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 70, 16, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
0, 0, 56, 0, 0, 7,
- 242, 32, 16, 0, 0, 0,
- 0, 0, 6, 0, 16, 0,
- 0, 0, 0, 0, 70, 14,
- 16, 0, 1, 0, 0, 0,
- 62, 0, 0, 1, 83, 84,
- 65, 84, 116, 0, 0, 0,
- 21, 0, 0, 0, 3, 0,
- 0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 14, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
- 0, 0, 0, 0, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 56, 0, 0, 7, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 36, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 29, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 4, 0,
- 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 2, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 82, 68, 69, 70,
- 80, 2, 0, 0, 1, 0,
- 0, 0, 0, 1, 0, 0,
- 5, 0, 0, 0, 28, 0,
- 0, 0, 0, 4, 255, 255,
- 0, 1, 0, 0, 28, 2,
- 0, 0, 188, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
+ 82, 68, 69, 70, 152, 2,
0, 0, 1, 0, 0, 0,
- 1, 0, 0, 0, 201, 0,
- 0, 0, 3, 0, 0, 0,
+ 0, 1, 0, 0, 5, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 100, 2, 0, 0,
+ 188, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 1, 0,
+ 0, 0, 201, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0,
- 217, 0, 0, 0, 2, 0,
+ 1, 0, 0, 0, 217, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 230, 0, 0, 0, 2, 0,
0, 0, 5, 0, 0, 0,
4, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
+ 255, 255, 1, 0, 0, 0,
1, 0, 0, 0, 13, 0,
- 0, 0, 230, 0, 0, 0,
- 2, 0, 0, 0, 5, 0,
- 0, 0, 4, 0, 0, 0,
- 255, 255, 255, 255, 1, 0,
- 0, 0, 1, 0, 0, 0,
- 13, 0, 0, 0, 246, 0,
+ 0, 0, 246, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0,
- 73, 110, 112, 117, 116, 83,
- 97, 109, 112, 108, 101, 114,
- 0, 71, 114, 97, 100, 105,
- 101, 110, 116, 83, 97, 109,
- 112, 108, 101, 114, 0, 73,
- 110, 112, 117, 116, 84, 101,
- 120, 116, 117, 114, 101, 0,
- 71, 114, 97, 100, 105, 101,
- 110, 116, 84, 101, 120, 116,
- 117, 114, 101, 0, 99, 111,
- 110, 115, 116, 97, 110, 116,
- 115, 0, 246, 0, 0, 0,
- 6, 0, 0, 0, 24, 1,
- 0, 0, 80, 0, 0, 0,
+ 1, 0, 0, 0, 73, 110,
+ 112, 117, 116, 83, 97, 109,
+ 112, 108, 101, 114, 0, 71,
+ 114, 97, 100, 105, 101, 110,
+ 116, 83, 97, 109, 112, 108,
+ 101, 114, 0, 73, 110, 112,
+ 117, 116, 84, 101, 120, 116,
+ 117, 114, 101, 0, 71, 114,
+ 97, 100, 105, 101, 110, 116,
+ 84, 101, 120, 116, 117, 114,
+ 101, 0, 99, 111, 110, 115,
+ 116, 97, 110, 116, 115, 0,
+ 246, 0, 0, 0, 8, 0,
+ 0, 0, 24, 1, 0, 0,
+ 80, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 168, 1, 0, 0,
- 0, 0, 0, 0, 12, 0,
- 0, 0, 2, 0, 0, 0,
- 176, 1, 0, 0, 0, 0,
- 0, 0, 192, 1, 0, 0,
- 16, 0, 0, 0, 8, 0,
- 0, 0, 2, 0, 0, 0,
- 200, 1, 0, 0, 0, 0,
- 0, 0, 216, 1, 0, 0,
- 24, 0, 0, 0, 4, 0,
+ 216, 1, 0, 0, 0, 0,
+ 0, 0, 12, 0, 0, 0,
+ 2, 0, 0, 0, 224, 1,
0, 0, 0, 0, 0, 0,
- 220, 1, 0, 0, 0, 0,
- 0, 0, 236, 1, 0, 0,
- 28, 0, 0, 0, 4, 0,
- 0, 0, 2, 0, 0, 0,
- 220, 1, 0, 0, 0, 0,
- 0, 0, 244, 1, 0, 0,
- 32, 0, 0, 0, 4, 0,
+ 240, 1, 0, 0, 16, 0,
+ 0, 0, 8, 0, 0, 0,
+ 2, 0, 0, 0, 248, 1,
0, 0, 0, 0, 0, 0,
- 220, 1, 0, 0, 0, 0,
- 0, 0, 255, 1, 0, 0,
- 48, 0, 0, 0, 28, 0,
- 0, 0, 2, 0, 0, 0,
- 12, 2, 0, 0, 0, 0,
- 0, 0, 100, 105, 102, 102,
- 0, 171, 171, 171, 1, 0,
- 3, 0, 1, 0, 3, 0,
+ 8, 2, 0, 0, 24, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 12, 2,
0, 0, 0, 0, 0, 0,
- 0, 0, 99, 101, 110, 116,
- 101, 114, 49, 0, 1, 0,
- 3, 0, 1, 0, 2, 0,
+ 28, 2, 0, 0, 28, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 12, 2,
0, 0, 0, 0, 0, 0,
- 0, 0, 65, 0, 171, 171,
- 0, 0, 3, 0, 1, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 114, 97,
- 100, 105, 117, 115, 49, 0,
- 115, 113, 95, 114, 97, 100,
- 105, 117, 115, 49, 0, 116,
- 114, 97, 110, 115, 102, 111,
- 114, 109, 0, 171, 171, 171,
- 3, 0, 3, 0, 3, 0,
- 2, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 57, 46, 51,
- 48, 46, 57, 50, 48, 48,
- 46, 49, 54, 51, 56, 52,
- 0, 171, 73, 83, 71, 78,
- 116, 0, 0, 0, 3, 0,
- 0, 0, 8, 0, 0, 0,
- 80, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 3, 0, 0, 0, 0, 0,
- 0, 0, 15, 0, 0, 0,
- 92, 0, 0, 0, 0, 0,
+ 36, 2, 0, 0, 32, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 12, 2,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 1, 0,
- 0, 0, 15, 3, 0, 0,
- 107, 0, 0, 0, 0, 0,
+ 47, 2, 0, 0, 36, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 12, 2,
0, 0, 0, 0, 0, 0,
- 3, 0, 0, 0, 2, 0,
- 0, 0, 15, 3, 0, 0,
- 83, 86, 95, 80, 79, 83,
- 73, 84, 73, 79, 78, 0,
- 83, 67, 69, 78, 69, 95,
- 80, 79, 83, 73, 84, 73,
- 79, 78, 0, 84, 69, 88,
- 67, 79, 79, 82, 68, 0,
- 79, 83, 71, 78, 44, 0,
+ 62, 2, 0, 0, 40, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 12, 2,
+ 0, 0, 0, 0, 0, 0,
+ 72, 2, 0, 0, 48, 0,
+ 0, 0, 28, 0, 0, 0,
+ 2, 0, 0, 0, 84, 2,
+ 0, 0, 0, 0, 0, 0,
+ 100, 105, 102, 102, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 101, 110, 116, 101, 114,
+ 49, 0, 1, 0, 3, 0,
+ 1, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 65, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 114, 97, 100, 105,
+ 117, 115, 49, 0, 115, 113,
+ 95, 114, 97, 100, 105, 117,
+ 115, 49, 0, 114, 101, 112,
+ 101, 97, 116, 95, 99, 111,
+ 114, 114, 101, 99, 116, 0,
+ 97, 108, 108, 111, 119, 95,
+ 111, 100, 100, 0, 116, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 171, 171, 3, 0,
+ 3, 0, 3, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 54, 46, 51, 46, 57,
+ 54, 48, 48, 46, 49, 54,
+ 51, 56, 52, 0, 171, 171,
+ 73, 83, 71, 78, 116, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
0, 0, 1, 0, 0, 0,
- 8, 0, 0, 0, 32, 0,
+ 15, 3, 0, 0, 107, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 3, 0, 0, 83, 86,
+ 95, 80, 79, 83, 73, 84,
+ 73, 79, 78, 0, 83, 67,
+ 69, 78, 69, 95, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 84, 69, 88, 67, 79,
+ 79, 82, 68, 0, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 15, 0, 0, 0, 83, 86,
- 95, 84, 97, 114, 103, 101,
- 116, 0, 171, 171
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
};
diff --git a/gfx/2d/ShadersD2D1.hlsl b/gfx/2d/ShadersD2D1.hlsl index b763786c2..42337afc2 100644 --- a/gfx/2d/ShadersD2D1.hlsl +++ b/gfx/2d/ShadersD2D1.hlsl @@ -16,6 +16,13 @@ cbuffer constants : register(b0) float A : packoffset(c1.z); float radius1 : packoffset(c1.w); float sq_radius1 : packoffset(c2.x); + + // The next two values are used for a hack to compensate for an apparent + // bug in D2D where the GradientSampler SamplerState doesn't get the + // correct addressing modes. + float repeat_correct : packoffset(c2.y); + float allow_odd : packoffset(c2.z); + float3x2 transform : packoffset(c3.x); } @@ -52,7 +59,13 @@ float4 SampleRadialGradientPS( float upper_t = lerp(t.y, t.x, isValid.x); - float4 output = GradientTexture.Sample(GradientSampler, float2(upper_t, 0.5)); + // Addressing mode bug work-around.. first let's see if we should consider odd repetitions separately. + float oddeven = abs(fmod(floor(upper_t), 2)) * allow_odd; + + // Now let's calculate even or odd addressing in a branchless manner. + float upper_t_repeated = ((upper_t - floor(upper_t)) * (1.0f - oddeven)) + ((ceil(upper_t) - upper_t) * oddeven); + + float4 output = GradientTexture.Sample(GradientSampler, float2(upper_t * (1.0f - repeat_correct) + upper_t_repeated * repeat_correct, 0.5)); // Premultiply output.rgb *= output.a; // Multiply the output color by the input mask for the operation. @@ -84,7 +97,13 @@ float4 SampleRadialGradientA0PS( float t = 0.5 * C / B; - float4 output = GradientTexture.Sample(GradientSampler, float2(t, 0.5)); + // Addressing mode bug work-around.. first let's see if we should consider odd repetitions separately. + float oddeven = abs(fmod(floor(t), 2)) * allow_odd; + + // Now let's calculate even or odd addressing in a branchless manner. + float t_repeated = ((t - floor(t)) * (1.0f - oddeven)) + ((ceil(t) - t) * oddeven); + + float4 output = GradientTexture.Sample(GradientSampler, float2(t * (1.0f - repeat_correct) + t_repeated * repeat_correct, 0.5)); // Premultiply output.rgb *= output.a; // Multiply the output color by the input mask for the operation. @@ -95,3 +114,4 @@ float4 SampleRadialGradientA0PS( // -radius1 >= t * diff.z return output * abs(step(t * diff.z, -radius1) - 1.0f); }; + diff --git a/gfx/2d/SourceSurfaceCG.cpp b/gfx/2d/SourceSurfaceCG.cpp index 7be284c30..12b038c20 100644 --- a/gfx/2d/SourceSurfaceCG.cpp +++ b/gfx/2d/SourceSurfaceCG.cpp @@ -5,8 +5,12 @@ #include "SourceSurfaceCG.h" #include "DrawTargetCG.h" +#include "DataSourceSurfaceWrapper.h" +#include "DataSurfaceHelpers.h" +#include "mozilla/Types.h" // for decltype -#include "QuartzSupport.h" +#include "MacIOSurface.h" +#include "Tools.h" namespace mozilla { namespace gfx { @@ -37,22 +41,40 @@ SourceSurfaceCG::GetDataSurface() { //XXX: we should be more disciplined about who takes a reference and where CGImageRetain(mImage); - RefPtr<DataSourceSurfaceCG> dataSurf = - new DataSourceSurfaceCG(mImage); - return dataSurf; + RefPtr<DataSourceSurface> dataSurf = new DataSourceSurfaceCG(mImage); + + // We also need to make sure that the returned surface has + // surface->GetType() == SurfaceType::DATA. + return new DataSourceSurfaceWrapper(dataSurf); } static void releaseCallback(void *info, const void *data, size_t size) { free(info); } -static CGImageRef +CGImageRef CreateCGImage(void *aInfo, const void *aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat) { + return CreateCGImage(releaseCallback, + aInfo, + aData, + aSize, + aStride, + aFormat); +} + +CGImageRef +CreateCGImage(CGDataProviderReleaseDataCallback aCallback, + void *aInfo, + const void *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat) +{ //XXX: we should avoid creating this colorspace everytime CGColorSpaceRef colorSpace = nullptr; CGBitmapInfo bitinfo = 0; @@ -60,21 +82,21 @@ CreateCGImage(void *aInfo, int bitsPerPixel = 0; switch (aFormat) { - case FORMAT_B8G8R8A8: + case SurfaceFormat::B8G8R8A8: colorSpace = CGColorSpaceCreateDeviceRGB(); bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; bitsPerComponent = 8; bitsPerPixel = 32; break; - case FORMAT_B8G8R8X8: + case SurfaceFormat::B8G8R8X8: colorSpace = CGColorSpaceCreateDeviceRGB(); bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; bitsPerComponent = 8; bitsPerPixel = 32; break; - case FORMAT_A8: + case SurfaceFormat::A8: // XXX: why don't we set a colorspace here? bitsPerComponent = 8; bitsPerPixel = 8; @@ -84,13 +106,17 @@ CreateCGImage(void *aInfo, MOZ_CRASH(); } + size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height); + if (bufLen == 0) { + return nullptr; + } CGDataProviderRef dataProvider = CGDataProviderCreateWithData(aInfo, aData, - aSize.height * aStride, - releaseCallback); + bufLen, + aCallback); CGImageRef image; - if (aFormat == FORMAT_A8) { + if (aFormat == SurfaceFormat::A8) { CGFloat decode[] = {1.0, 0.0}; image = CGImageMaskCreate (aSize.width, aSize.height, bitsPerComponent, @@ -126,8 +152,16 @@ SourceSurfaceCG::InitFromData(unsigned char *aData, { assert(aSize.width >= 0 && aSize.height >= 0); - void *data = malloc(aStride * aSize.height); - memcpy(data, aData, aStride * aSize.height); + size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height); + if (bufLen == 0) { + mImage = nullptr; + return false; + } + + void *data = malloc(bufLen); + // Copy all the data except the stride padding on the very last + // row since we can't guarantee that is readable. + memcpy(data, aData, bufLen - aStride + (aSize.width * BytesPerPixel(aFormat))); mFormat = aFormat; mImage = CreateCGImage(data, data, aSize, aStride, aFormat); @@ -157,12 +191,28 @@ DataSourceSurfaceCG::InitFromData(unsigned char *aData, int32_t aStride, SurfaceFormat aFormat) { - void *data = malloc(aStride * aSize.height); - memcpy(data, aData, aStride * aSize.height); + if (aSize.width <= 0 || aSize.height <= 0) { + return false; + } + + size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height); + if (bufLen == 0) { + mImage = nullptr; + return false; + } + + void *data = malloc(bufLen); + memcpy(data, aData, bufLen - aStride + (aSize.width * BytesPerPixel(aFormat))); + mFormat = aFormat; mImage = CreateCGImage(data, data, aSize, aStride, aFormat); - return mImage; + if (!mImage) { + free(data); + return false; + } + + return true; } CGContextRef CreateBitmapContextForImage(CGImageRef image) @@ -201,6 +251,7 @@ CGContextRef CreateBitmapContextForImage(CGImageRef image) DataSourceSurfaceCG::DataSourceSurfaceCG(CGImageRef aImage) { + mFormat = SurfaceFormat::B8G8R8A8; mImage = aImage; mCg = CreateBitmapContextForImage(aImage); if (mCg == nullptr) { @@ -241,7 +292,8 @@ DataSourceSurfaceCG::GetData() SourceSurfaceCGBitmapContext::SourceSurfaceCGBitmapContext(DrawTargetCG *aDrawTarget) { mDrawTarget = aDrawTarget; - mCg = (CGContextRef)aDrawTarget->GetNativeSurface(NATIVE_SURFACE_CGCONTEXT); + mFormat = aDrawTarget->GetFormat(); + mCg = (CGContextRef)aDrawTarget->GetNativeSurface(NativeSurfaceType::CGCONTEXT); if (!mCg) abort(); @@ -263,21 +315,8 @@ void SourceSurfaceCGBitmapContext::EnsureImage() const // memcpy when the bitmap context is modified gives us more predictable // performance characteristics. if (!mImage) { - void *info; - if (mCg) { - // if we have an mCg than it owns the data - // and we don't want to tranfer ownership - // to the CGDataProviderCreateWithData - info = nullptr; - } else { - // otherwise we transfer ownership to - // the dataProvider - info = mData; - } - if (!mData) abort(); - - mImage = CreateCGImage(info, mData, mSize, mStride, FORMAT_B8G8R8A8); + mImage = CreateCGImage(nullptr, mData, mSize, mStride, mFormat); } } @@ -295,13 +334,21 @@ SourceSurfaceCGBitmapContext::DrawTargetWillChange() size_t stride = CGBitmapContextGetBytesPerRow(mCg); size_t height = CGBitmapContextGetHeight(mCg); - //XXX: infalliable malloc? - mData = malloc(stride * height); - - // copy out the data from the CGBitmapContext - // we'll maintain ownership of mData until - // we transfer it to mImage - memcpy(mData, CGBitmapContextGetData(mCg), stride*height); + size_t bufLen = BufferSizeFromStrideAndHeight(stride, height); + if (bufLen == 0) { + mDataHolder.Dealloc(); + mData = nullptr; + } else { + static_assert(sizeof(decltype(mDataHolder[0])) == 1, + "mDataHolder.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen"); + mDataHolder.Realloc(/* actually an object count */ bufLen); + mData = mDataHolder; + + // copy out the data from the CGBitmapContext + // we'll maintain ownership of mData until + // we transfer it to mImage + memcpy(mData, CGBitmapContextGetData(mCg), bufLen); + } // drop the current image for the data associated with the CGBitmapContext if (mImage) @@ -313,22 +360,37 @@ SourceSurfaceCGBitmapContext::DrawTargetWillChange() } } -SourceSurfaceCGBitmapContext::~SourceSurfaceCGBitmapContext() +void +SourceSurfaceCGBitmapContext::DrawTargetWillGoAway() { - if (!mImage && !mCg) { - // neither mImage or mCg owns the data - free(mData); + if (mDrawTarget) { + if (mDrawTarget->mData != CGBitmapContextGetData(mCg)) { + DrawTargetWillChange(); + return; + } + + // Instead of copying the data over, we can just swap it. + mDataHolder.Swap(mDrawTarget->mData); + mData = mDataHolder; + mCg = nullptr; + mDrawTarget = nullptr; + // mImage is still valid because it still points to the same data. } +} + +SourceSurfaceCGBitmapContext::~SourceSurfaceCGBitmapContext() +{ if (mImage) CGImageRelease(mImage); } SourceSurfaceCGIOSurfaceContext::SourceSurfaceCGIOSurfaceContext(DrawTargetCG *aDrawTarget) { - CGContextRef cg = (CGContextRef)aDrawTarget->GetNativeSurface(NATIVE_SURFACE_CGCONTEXT_ACCELERATED); + CGContextRef cg = (CGContextRef)aDrawTarget->GetNativeSurface(NativeSurfaceType::CGCONTEXT_ACCELERATED); RefPtr<MacIOSurface> surf = MacIOSurface::IOSurfaceContextGetSurface(cg); + mFormat = aDrawTarget->GetFormat(); mSize.width = surf->GetWidth(); mSize.height = surf->GetHeight(); @@ -361,7 +423,7 @@ void SourceSurfaceCGIOSurfaceContext::EnsureImage() const // memcpy when the bitmap context is modified gives us more predictable // performance characteristics. if (!mImage) { - mImage = CreateCGImage(mData, mData, mSize, mStride, FORMAT_B8G8R8A8); + mImage = CreateCGImage(mData, mData, mSize, mStride, SurfaceFormat::B8G8R8A8); } } diff --git a/gfx/2d/SourceSurfaceCG.h b/gfx/2d/SourceSurfaceCG.h index 63bd675eb..9e1798490 100644 --- a/gfx/2d/SourceSurfaceCG.h +++ b/gfx/2d/SourceSurfaceCG.h @@ -3,7 +3,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#pragma once +#ifndef _MOZILLA_GFX_SOURCESURFACECG_H +#define _MOZILLA_GFX_SOURCESURFACECG_H #include <ApplicationServices/ApplicationServices.h> @@ -14,16 +15,32 @@ class MacIOSurface; namespace mozilla { namespace gfx { +CGImageRef +CreateCGImage(CGDataProviderReleaseDataCallback aCallback, + void *aInfo, + const void *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat); + +CGImageRef +CreateCGImage(void *aInfo, + const void *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat); + class DrawTargetCG; class SourceSurfaceCG : public SourceSurface { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCG) SourceSurfaceCG() {} - SourceSurfaceCG(CGImageRef aImage) : mImage(aImage) {} + explicit SourceSurfaceCG(CGImageRef aImage) : mImage(aImage) {} ~SourceSurfaceCG(); - virtual SurfaceType GetType() const { return SURFACE_COREGRAPHICS_IMAGE; } + virtual SurfaceType GetType() const { return SurfaceType::COREGRAPHICS_IMAGE; } virtual IntSize GetSize() const; virtual SurfaceFormat GetFormat() const; virtual TemporaryRef<DataSourceSurface> GetDataSurface(); @@ -47,13 +64,14 @@ private: class DataSourceSurfaceCG : public DataSourceSurface { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCG) DataSourceSurfaceCG() {} - DataSourceSurfaceCG(CGImageRef aImage); + explicit DataSourceSurfaceCG(CGImageRef aImage); ~DataSourceSurfaceCG(); - virtual SurfaceType GetType() const { return SURFACE_DATA; } + virtual SurfaceType GetType() const { return SurfaceType::DATA; } virtual IntSize GetSize() const; - virtual SurfaceFormat GetFormat() const { return FORMAT_B8G8R8A8; } + virtual SurfaceFormat GetFormat() const { return mFormat; } CGImageRef GetImage() { return mImage; } @@ -70,6 +88,7 @@ public: private: CGContextRef mCg; CGImageRef mImage; + SurfaceFormat mFormat; //XXX: we don't need to store mData we can just get it from the CGContext void *mData; /* It might be better to just use the bitmap info from the CGImageRef to @@ -80,19 +99,36 @@ private: class SourceSurfaceCGContext : public DataSourceSurface { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCGContext) virtual void DrawTargetWillChange() = 0; + virtual void DrawTargetWillGoAway() = 0; virtual CGImageRef GetImage() = 0; }; class SourceSurfaceCGBitmapContext : public SourceSurfaceCGContext { public: - SourceSurfaceCGBitmapContext(DrawTargetCG *); + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCGBitmapContext) + explicit SourceSurfaceCGBitmapContext(DrawTargetCG *); ~SourceSurfaceCGBitmapContext(); - virtual SurfaceType GetType() const { return SURFACE_COREGRAPHICS_CGCONTEXT; } + virtual SurfaceType GetType() const { return SurfaceType::COREGRAPHICS_CGCONTEXT; } virtual IntSize GetSize() const; - virtual SurfaceFormat GetFormat() const { return FORMAT_B8G8R8A8; } + virtual SurfaceFormat GetFormat() const { return mFormat; } + virtual TemporaryRef<DataSourceSurface> GetDataSurface() + { + // This call to DrawTargetWillChange() is needed to make a local copy of + // the data from mDrawTarget. If we don't do that, the data can end up + // getting deleted before the CGImageRef it belongs to. + // + // Another reason we need a local copy of the data is that the data in + // mDrawTarget could change when someone touches the original DrawTargetCG + // object. But a SourceSurface object should be immutable. + // + // For more information see bug 925448. + DrawTargetWillChange(); + return this; + } CGImageRef GetImage() { EnsureImage(); return mImage; } @@ -104,12 +140,14 @@ private: //XXX: do the other backends friend their DrawTarget? friend class DrawTargetCG; virtual void DrawTargetWillChange(); + virtual void DrawTargetWillGoAway(); void EnsureImage() const; // We hold a weak reference to these two objects. // The cycle is broken by DrawTargetWillChange DrawTargetCG *mDrawTarget; CGContextRef mCg; + SurfaceFormat mFormat; mutable CGImageRef mImage; @@ -117,6 +155,9 @@ private: // mImage, mCg or SourceSurfaceCGBitmapContext void *mData; + // The image buffer, if the buffer is owned by this class. + AlignedArray<uint8_t> mDataHolder; + int32_t mStride; IntSize mSize; }; @@ -124,12 +165,13 @@ private: class SourceSurfaceCGIOSurfaceContext : public SourceSurfaceCGContext { public: - SourceSurfaceCGIOSurfaceContext(DrawTargetCG *); + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCGIOSurfaceContext) + explicit SourceSurfaceCGIOSurfaceContext(DrawTargetCG *); ~SourceSurfaceCGIOSurfaceContext(); - virtual SurfaceType GetType() const { return SURFACE_COREGRAPHICS_CGCONTEXT; } + virtual SurfaceType GetType() const { return SurfaceType::COREGRAPHICS_CGCONTEXT; } virtual IntSize GetSize() const; - virtual SurfaceFormat GetFormat() const { return FORMAT_B8G8R8A8; } + virtual SurfaceFormat GetFormat() const { return mFormat; } CGImageRef GetImage() { EnsureImage(); return mImage; } @@ -141,8 +183,10 @@ private: //XXX: do the other backends friend their DrawTarget? friend class DrawTargetCG; virtual void DrawTargetWillChange(); + virtual void DrawTargetWillGoAway() { DrawTargetWillChange(); } void EnsureImage() const; + SurfaceFormat mFormat; mutable CGImageRef mImage; MacIOSurface* mIOSurface; @@ -155,3 +199,5 @@ private: } } + +#endif // _MOZILLA_GFX_SOURCESURFACECG_H diff --git a/gfx/2d/SourceSurfaceCairo.cpp b/gfx/2d/SourceSurfaceCairo.cpp index 81438b6a4..a1ecfe51d 100644 --- a/gfx/2d/SourceSurfaceCairo.cpp +++ b/gfx/2d/SourceSurfaceCairo.cpp @@ -6,6 +6,7 @@ #include "SourceSurfaceCairo.h" #include "DrawTargetCairo.h" #include "HelpersCairo.h" +#include "DataSourceSurfaceWrapper.h" #include "cairo.h" @@ -18,13 +19,13 @@ CairoFormatToSurfaceFormat(cairo_format_t format) switch (format) { case CAIRO_FORMAT_ARGB32: - return FORMAT_B8G8R8A8; + return SurfaceFormat::B8G8R8A8; case CAIRO_FORMAT_RGB24: - return FORMAT_B8G8R8X8; + return SurfaceFormat::B8G8R8X8; case CAIRO_FORMAT_A8: - return FORMAT_A8; + return SurfaceFormat::A8; default: - return FORMAT_B8G8R8A8; + return SurfaceFormat::B8G8R8A8; } } @@ -60,7 +61,7 @@ SourceSurfaceCairo::GetFormat() const TemporaryRef<DataSourceSurface> SourceSurfaceCairo::GetDataSurface() { - RefPtr<DataSourceSurfaceCairo> dataSurf; + RefPtr<DataSourceSurface> dataSurf; if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) { dataSurf = new DataSourceSurfaceCairo(mSurface); @@ -78,7 +79,9 @@ SourceSurfaceCairo::GetDataSurface() cairo_surface_destroy(imageSurf); } - return dataSurf; + // We also need to make sure that the returned surface has + // surface->GetType() == SurfaceType::DATA. + return new DataSourceSurfaceWrapper(dataSurf); } cairo_surface_t* diff --git a/gfx/2d/SourceSurfaceCairo.h b/gfx/2d/SourceSurfaceCairo.h index 4f26de1fa..a9390e759 100644 --- a/gfx/2d/SourceSurfaceCairo.h +++ b/gfx/2d/SourceSurfaceCairo.h @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef _MOZILLA_GFX_OP_SOURCESURFACE_CAIRO_H_ +#ifndef _MOZILLA_GFX_OP_SOURCESURFACE_CAIRO_H #define _MOZILLA_GFX_OP_SOURCESURFACE_CAIRO_H #include "2D.h" @@ -16,6 +16,7 @@ class DrawTargetCairo; class SourceSurfaceCairo : public SourceSurface { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCairo) // Create a SourceSurfaceCairo. The surface will not be copied, but simply // referenced. // If aDrawTarget is non-nullptr, it is assumed that this is a snapshot source @@ -26,7 +27,7 @@ public: DrawTargetCairo* aDrawTarget = nullptr); virtual ~SourceSurfaceCairo(); - virtual SurfaceType GetType() const { return SURFACE_CAIRO; } + virtual SurfaceType GetType() const { return SurfaceType::CAIRO; } virtual IntSize GetSize() const; virtual SurfaceFormat GetFormat() const; virtual TemporaryRef<DataSourceSurface> GetDataSurface(); @@ -47,12 +48,13 @@ private: // data class DataSourceSurfaceCairo : public DataSourceSurface { public: - DataSourceSurfaceCairo(cairo_surface_t* imageSurf); + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCairo) + explicit DataSourceSurfaceCairo(cairo_surface_t* imageSurf); virtual ~DataSourceSurfaceCairo(); virtual unsigned char *GetData(); virtual int32_t Stride(); - virtual SurfaceType GetType() const { return SURFACE_CAIRO_IMAGE; } + virtual SurfaceType GetType() const { return SurfaceType::CAIRO_IMAGE; } virtual IntSize GetSize() const; virtual SurfaceFormat GetFormat() const; diff --git a/gfx/2d/SourceSurfaceD2D.cpp b/gfx/2d/SourceSurfaceD2D.cpp index 005a6901e..538f0d2bd 100644 --- a/gfx/2d/SourceSurfaceD2D.cpp +++ b/gfx/2d/SourceSurfaceD2D.cpp @@ -45,7 +45,7 @@ SourceSurfaceD2D::GetDataSurface() { RefPtr<DataSourceSurfaceD2D> result = new DataSourceSurfaceD2D(this); if (result->IsValid()) { - return result; + return result.forget(); } return nullptr; } @@ -68,12 +68,18 @@ SourceSurfaceD2D::InitFromData(unsigned char *aData, return false; } - D2D1_BITMAP_PROPERTIES props = - D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(aFormat), AlphaMode(aFormat))); - hr = aRT->CreateBitmap(D2DIntSize(aSize), aData, aStride, props, byRef(mBitmap)); + D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(aFormat)); + hr = aRT->CreateBitmap(D2DIntSize(aSize), props, byRef(mBitmap)); if (FAILED(hr)) { - gfxWarning() << "Failed to create D2D Bitmap for data. Code: " << hr; + gfxWarning() << "Failed to create D2D Bitmap for data. Code: " << hexa(hr); + return false; + } + + hr = mBitmap->CopyFromMemory(nullptr, aData, aStride); + + if (FAILED(hr)) { + gfxWarning() << "Failed to copy data to D2D bitmap. Code: " << hexa(hr); return false; } @@ -95,7 +101,7 @@ SourceSurfaceD2D::InitFromTexture(ID3D10Texture2D *aTexture, hr = aTexture->QueryInterface((IDXGISurface**)&surf); if (FAILED(hr)) { - gfxWarning() << "Failed to QI texture to surface. Code: " << hr; + gfxWarning() << "Failed to QI texture to surface. Code: " << hexa(hr); return false; } @@ -105,12 +111,11 @@ SourceSurfaceD2D::InitFromTexture(ID3D10Texture2D *aTexture, mSize = IntSize(desc.Width, desc.Height); mFormat = aFormat; - D2D1_BITMAP_PROPERTIES props = - D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(aFormat), AlphaMode(aFormat))); + D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(aFormat)); hr = aRT->CreateSharedBitmap(IID_IDXGISurface, surf, &props, byRef(mBitmap)); if (FAILED(hr)) { - gfxWarning() << "Failed to create SharedBitmap. Code: " << hr; + gfxWarning() << "Failed to create SharedBitmap. Code: " << hexa(hr); return false; } @@ -145,14 +150,14 @@ DataSourceSurfaceD2D::DataSourceSurfaceD2D(SourceSurfaceD2D* aSourceSurface) HRESULT hr = aSourceSurface->mDevice->CreateTexture2D(&desc, nullptr, byRef(sourceTexture)); if (FAILED(hr)) { - gfxWarning() << "Failed to create texture. Code: " << hr; + gfxWarning() << "Failed to create texture. Code: " << hexa(hr); return; } RefPtr<IDXGISurface> dxgiSurface; hr = sourceTexture->QueryInterface((IDXGISurface**)byRef(dxgiSurface)); if (FAILED(hr)) { - gfxWarning() << "Failed to create DXGI surface. Code: " << hr; + gfxWarning() << "Failed to create DXGI surface. Code: " << hexa(hr); return; } @@ -165,23 +170,35 @@ DataSourceSurfaceD2D::DataSourceSurfaceD2D(SourceSurfaceD2D* aSourceSurface) &rtProps, byRef(renderTarget)); if (FAILED(hr)) { - gfxWarning() << "Failed to create render target. Code: " << hr; + gfxWarning() << "Failed to create render target. Code: " << hexa(hr); return; } renderTarget->BeginDraw(); - renderTarget->DrawBitmap(aSourceSurface->mBitmap, - D2D1::RectF(0, 0, - Float(mSize.width), - Float(mSize.height))); - renderTarget->EndDraw(); + renderTarget->Clear(D2D1::ColorF(0, 0.0f)); + if (aSourceSurface->GetFormat() != SurfaceFormat::A8) { + renderTarget->DrawBitmap(aSourceSurface->mBitmap, + D2D1::RectF(0, 0, + Float(mSize.width), + Float(mSize.height))); + } else { + RefPtr<ID2D1SolidColorBrush> brush; + renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), byRef(brush)); + renderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + renderTarget->FillOpacityMask(aSourceSurface->mBitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS); + } + hr = renderTarget->EndDraw(); + if (FAILED(hr)) { + gfxWarning() << "Failed to draw bitmap. Code: " << hexa(hr); + return; + } - desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ; + desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ | D3D10_CPU_ACCESS_WRITE; desc.Usage = D3D10_USAGE_STAGING; desc.BindFlags = 0; hr = aSourceSurface->mDevice->CreateTexture2D(&desc, nullptr, byRef(mTexture)); if (FAILED(hr)) { - gfxWarning() << "Failed to create staging texture. Code: " << hr; + gfxWarning() << "Failed to create staging texture. Code: " << hexa(hr); mTexture = nullptr; return; } @@ -230,9 +247,58 @@ DataSourceSurfaceD2D::GetFormat() const return mFormat; } +bool +DataSourceSurfaceD2D::Map(MapType aMapType, MappedSurface *aMappedSurface) +{ + // DataSourceSurfaces used with the new Map API should not be used with GetData!! + MOZ_ASSERT(!mMapped); + MOZ_ASSERT(!mIsMapped); + + if (!mTexture) { + return false; + } + + D3D10_MAP mapType; + + if (aMapType == MapType::READ) { + mapType = D3D10_MAP_READ; + } else if (aMapType == MapType::WRITE) { + mapType = D3D10_MAP_WRITE; + } else { + mapType = D3D10_MAP_READ_WRITE; + } + + D3D10_MAPPED_TEXTURE2D map; + + HRESULT hr = mTexture->Map(0, mapType, 0, &map); + + if (FAILED(hr)) { + gfxWarning() << "Texture map failed with code: " << hexa(hr); + return false; + } + + aMappedSurface->mData = (uint8_t*)map.pData; + aMappedSurface->mStride = map.RowPitch; + mIsMapped = !!aMappedSurface->mData; + + return mIsMapped; +} + +void +DataSourceSurfaceD2D::Unmap() +{ + MOZ_ASSERT(mIsMapped); + + mIsMapped = false; + mTexture->Unmap(0); +} + void DataSourceSurfaceD2D::EnsureMappedTexture() { + // Do not use GetData() after having used Map! + MOZ_ASSERT(!mIsMapped); + if (mMapped || !mTexture) { return; @@ -240,7 +306,7 @@ DataSourceSurfaceD2D::EnsureMappedTexture() HRESULT hr = mTexture->Map(0, D3D10_MAP_READ, 0, &mData); if (FAILED(hr)) { - gfxWarning() << "Failed to map texture. Code: " << hr; + gfxWarning() << "Failed to map texture. Code: " << hexa(hr); mTexture = nullptr; } else { mMapped = true; diff --git a/gfx/2d/SourceSurfaceD2D.h b/gfx/2d/SourceSurfaceD2D.h index bafbd2019..c2622d76c 100644 --- a/gfx/2d/SourceSurfaceD2D.h +++ b/gfx/2d/SourceSurfaceD2D.h @@ -18,10 +18,11 @@ class DataSourceSurfaceD2D; class SourceSurfaceD2D : public SourceSurface { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceD2D) SourceSurfaceD2D(); ~SourceSurfaceD2D(); - virtual SurfaceType GetType() const { return SURFACE_D2D1_BITMAP; } + virtual SurfaceType GetType() const { return SurfaceType::D2D1_BITMAP; } virtual IntSize GetSize() const; virtual SurfaceFormat GetFormat() const; virtual bool IsValid() const; @@ -55,6 +56,7 @@ private: class DataSourceSurfaceD2D : public DataSourceSurface { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceD2D) DataSourceSurfaceD2D(SourceSurfaceD2D* aSourceSurface); virtual ~DataSourceSurfaceD2D(); @@ -62,6 +64,8 @@ public: virtual int32_t Stride(); virtual IntSize GetSize() const; virtual SurfaceFormat GetFormat() const; + virtual bool Map(MapType, MappedSurface *aMappedSurface); + virtual void Unmap(); bool IsValid() { diff --git a/gfx/2d/SourceSurfaceD2D1.cpp b/gfx/2d/SourceSurfaceD2D1.cpp new file mode 100644 index 000000000..ee742777f --- /dev/null +++ b/gfx/2d/SourceSurfaceD2D1.cpp @@ -0,0 +1,229 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SourceSurfaceD2D1.h" +#include "DrawTargetD2D1.h" +#include "Tools.h" + +namespace mozilla { +namespace gfx { + +SourceSurfaceD2D1::SourceSurfaceD2D1(ID2D1Image *aImage, ID2D1DeviceContext *aDC, + SurfaceFormat aFormat, const IntSize &aSize, + DrawTargetD2D1 *aDT) + : mImage(aImage) + , mDC(aDC) + , mDrawTarget(aDT) + , mDevice(Factory::GetD2D1Device()) +{ + aImage->QueryInterface((ID2D1Bitmap1**)byRef(mRealizedBitmap)); + + mFormat = aFormat; + mSize = aSize; +} + +SourceSurfaceD2D1::~SourceSurfaceD2D1() +{ +} + +bool +SourceSurfaceD2D1::IsValid() const +{ + return mDevice == Factory::GetD2D1Device(); +} + +TemporaryRef<DataSourceSurface> +SourceSurfaceD2D1::GetDataSurface() +{ + HRESULT hr; + + EnsureRealizedBitmap(); + + RefPtr<ID2D1Bitmap1> softwareBitmap; + D2D1_BITMAP_PROPERTIES1 props; + props.dpiX = 96; + props.dpiY = 96; + props.pixelFormat = D2DPixelFormat(mFormat); + props.colorContext = nullptr; + props.bitmapOptions = D2D1_BITMAP_OPTIONS_CANNOT_DRAW | + D2D1_BITMAP_OPTIONS_CPU_READ; + hr = mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(softwareBitmap)); + + if (FAILED(hr)) { + gfxCriticalError() << "Failed to create software bitmap: " << mSize << " Code: " << hexa(hr); + return nullptr; + } + + D2D1_POINT_2U point = D2D1::Point2U(0, 0); + D2D1_RECT_U rect = D2D1::RectU(0, 0, mSize.width, mSize.height); + + hr = softwareBitmap->CopyFromBitmap(&point, mRealizedBitmap, &rect); + + if (FAILED(hr)) { + gfxWarning() << "Failed to readback into software bitmap. Code: " << hexa(hr); + return nullptr; + } + + return new DataSourceSurfaceD2D1(softwareBitmap, mFormat); +} + +void +SourceSurfaceD2D1::EnsureRealizedBitmap() +{ + if (mRealizedBitmap) { + return; + } + + RefPtr<ID2D1DeviceContext> dc; + Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, byRef(dc)); + + D2D1_BITMAP_PROPERTIES1 props; + props.dpiX = 96; + props.dpiY = 96; + props.pixelFormat = D2DPixelFormat(mFormat); + props.colorContext = nullptr; + props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET; + dc->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mRealizedBitmap)); + + dc->SetTarget(mRealizedBitmap); + + dc->BeginDraw(); + dc->DrawImage(mImage); + dc->EndDraw(); +} + +void +SourceSurfaceD2D1::DrawTargetWillChange() +{ + // At this point in time this should always be true here. + MOZ_ASSERT(mRealizedBitmap); + + RefPtr<ID2D1Bitmap1> oldBitmap = mRealizedBitmap; + + D2D1_BITMAP_PROPERTIES1 props; + props.dpiX = 96; + props.dpiY = 96; + props.pixelFormat = D2DPixelFormat(mFormat); + props.colorContext = nullptr; + props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET; + HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mRealizedBitmap)); + + if (FAILED(hr)) { + gfxCriticalError() << "Failed to create bitmap to make DrawTarget copy. Size: " << mSize << " Code: " << hexa(hr); + MarkIndependent(); + return; + } + + D2D1_POINT_2U point = D2D1::Point2U(0, 0); + D2D1_RECT_U rect = D2D1::RectU(0, 0, mSize.width, mSize.height); + mRealizedBitmap->CopyFromBitmap(&point, oldBitmap, &rect); + mImage = mRealizedBitmap; + + DrawTargetD2D1::mVRAMUsageSS += mSize.width * mSize.height * BytesPerPixel(mFormat); + + // We now no longer depend on the source surface content remaining the same. + MarkIndependent(); +} + +void +SourceSurfaceD2D1::MarkIndependent() +{ + if (mDrawTarget) { + MOZ_ASSERT(mDrawTarget->mSnapshot == this); + mDrawTarget->mSnapshot = nullptr; + mDrawTarget = nullptr; + } +} + +DataSourceSurfaceD2D1::DataSourceSurfaceD2D1(ID2D1Bitmap1 *aMappableBitmap, SurfaceFormat aFormat) + : mBitmap(aMappableBitmap) + , mFormat(aFormat) + , mMapped(false) +{ +} + +DataSourceSurfaceD2D1::~DataSourceSurfaceD2D1() +{ + if (mMapped) { + mBitmap->Unmap(); + } +} + +IntSize +DataSourceSurfaceD2D1::GetSize() const +{ + D2D1_SIZE_F size = mBitmap->GetSize(); + + return IntSize(int32_t(size.width), int32_t(size.height)); +} + +uint8_t* +DataSourceSurfaceD2D1::GetData() +{ + EnsureMapped(); + + return mMap.bits; +} + +bool +DataSourceSurfaceD2D1::Map(MapType aMapType, MappedSurface *aMappedSurface) +{ + // DataSourceSurfaces used with the new Map API should not be used with GetData!! + MOZ_ASSERT(!mMapped); + MOZ_ASSERT(!mIsMapped); + + D2D1_MAP_OPTIONS options; + if (aMapType == MapType::READ) { + options = D2D1_MAP_OPTIONS_READ; + } else { + MOZ_CRASH("No support for Write maps on D2D1 DataSourceSurfaces yet!"); + } + + D2D1_MAPPED_RECT map; + if (FAILED(mBitmap->Map(D2D1_MAP_OPTIONS_READ, &map))) { + gfxCriticalError() << "Failed to map bitmap."; + return false; + } + aMappedSurface->mData = map.bits; + aMappedSurface->mStride = map.pitch; + + mIsMapped = !!aMappedSurface->mData; + return mIsMapped; +} + +void +DataSourceSurfaceD2D1::Unmap() +{ + MOZ_ASSERT(mIsMapped); + + mIsMapped = false; + mBitmap->Unmap(); +} + +int32_t +DataSourceSurfaceD2D1::Stride() +{ + EnsureMapped(); + + return mMap.pitch; +} + +void +DataSourceSurfaceD2D1::EnsureMapped() +{ + // Do not use GetData() after having used Map! + MOZ_ASSERT(!mIsMapped); + if (mMapped) { + return; + } + if (FAILED(mBitmap->Map(D2D1_MAP_OPTIONS_READ, &mMap))) { + gfxCriticalError() << "Failed to map bitmap."; + return; + } + mMapped = true; +} + +} +} diff --git a/gfx/2d/SourceSurfaceD2D1.h b/gfx/2d/SourceSurfaceD2D1.h new file mode 100644 index 000000000..1ecf31449 --- /dev/null +++ b/gfx/2d/SourceSurfaceD2D1.h @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_SOURCESURFACED2D1_H_ +#define MOZILLA_GFX_SOURCESURFACED2D1_H_ + +#include "2D.h" +#include "HelpersD2D.h" +#include <vector> +#include <d3d11.h> +#include <d2d1_1.h> + +namespace mozilla { +namespace gfx { + +class DrawTargetD2D1; + +class SourceSurfaceD2D1 : public SourceSurface +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceD2D1) + SourceSurfaceD2D1(ID2D1Image* aImage, ID2D1DeviceContext *aDC, + SurfaceFormat aFormat, const IntSize &aSize, + DrawTargetD2D1 *aDT = nullptr); + ~SourceSurfaceD2D1(); + + virtual SurfaceType GetType() const { return SurfaceType::D2D1_1_IMAGE; } + virtual IntSize GetSize() const { return mSize; } + virtual SurfaceFormat GetFormat() const { return mFormat; } + virtual bool IsValid() const; + virtual TemporaryRef<DataSourceSurface> GetDataSurface(); + + ID2D1Image *GetImage() { return mImage; } + + void EnsureIndependent() { if (!mDrawTarget) return; DrawTargetWillChange(); } + +private: + friend class DrawTargetD2D1; + + void EnsureRealizedBitmap(); + + // This function is called by the draw target this texture belongs to when + // it is about to be changed. The texture will be required to make a copy + // of itself when this happens. + void DrawTargetWillChange(); + + // This will mark the surface as no longer depending on its drawtarget, + // this may happen on destruction or copying. + void MarkIndependent(); + + RefPtr<ID2D1Image> mImage; + // This may be null if we were created for a non-bitmap image and have not + // had a reason yet to realize ourselves. + RefPtr<ID2D1Bitmap1> mRealizedBitmap; + RefPtr<ID2D1DeviceContext> mDC; + // Keep this around to verify whether out image is still valid in the future. + RefPtr<ID2D1Device> mDevice; + + SurfaceFormat mFormat; + IntSize mSize; + DrawTargetD2D1* mDrawTarget; +}; + +class DataSourceSurfaceD2D1 : public DataSourceSurface +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceD2D1) + DataSourceSurfaceD2D1(ID2D1Bitmap1 *aMappableBitmap, SurfaceFormat aFormat); + ~DataSourceSurfaceD2D1(); + + virtual SurfaceType GetType() const { return SurfaceType::DATA; } + virtual IntSize GetSize() const; + virtual SurfaceFormat GetFormat() const { return mFormat; } + virtual bool IsValid() const { return !!mBitmap; } + virtual uint8_t *GetData(); + virtual int32_t Stride(); + virtual bool Map(MapType, MappedSurface *aMappedSurface); + virtual void Unmap(); + +private: + friend class SourceSurfaceD2DTarget; + void EnsureMapped(); + + mutable RefPtr<ID2D1Bitmap1> mBitmap; + SurfaceFormat mFormat; + D2D1_MAPPED_RECT mMap; + bool mMapped; +}; + +} +} + +#endif /* MOZILLA_GFX_SOURCESURFACED2D2TARGET_H_ */ diff --git a/gfx/2d/SourceSurfaceD2DTarget.cpp b/gfx/2d/SourceSurfaceD2DTarget.cpp index 4f7e484cb..3cc3bbecb 100644 --- a/gfx/2d/SourceSurfaceD2DTarget.cpp +++ b/gfx/2d/SourceSurfaceD2DTarget.cpp @@ -54,7 +54,7 @@ TemporaryRef<DataSourceSurface> SourceSurfaceD2DTarget::GetDataSurface() { RefPtr<DataSourceSurfaceD2DTarget> dataSurf = - new DataSourceSurfaceD2DTarget(); + new DataSourceSurfaceD2DTarget(mFormat); D3D10_TEXTURE2D_DESC desc; mTexture->GetDesc(&desc); @@ -67,12 +67,21 @@ SourceSurfaceD2DTarget::GetDataSurface() HRESULT hr = Factory::GetDirect3D10Device()->CreateTexture2D(&desc, nullptr, byRef(dataSurf->mTexture)); if (FAILED(hr)) { - gfxDebug() << "Failed to create staging texture for SourceSurface. Code: " << hr; + gfxDebug() << "Failed to create staging texture for SourceSurface. Code: " << hexa(hr); return nullptr; } Factory::GetDirect3D10Device()->CopyResource(dataSurf->mTexture, mTexture); - return dataSurf; + return dataSurf.forget(); +} + +void* +SourceSurfaceD2DTarget::GetNativeSurface(NativeSurfaceType aType) +{ + if (aType == NativeSurfaceType::D3D10_TEXTURE) { + return static_cast<void*>(mTexture.get()); + } + return nullptr; } ID3D10ShaderResourceView* @@ -85,7 +94,7 @@ SourceSurfaceD2DTarget::GetSRView() HRESULT hr = Factory::GetDirect3D10Device()->CreateShaderResourceView(mTexture, nullptr, byRef(mSRView)); if (FAILED(hr)) { - gfxWarning() << "Failed to create ShaderResourceView. Code: " << hr; + gfxWarning() << "Failed to create ShaderResourceView. Code: " << hexa(hr); } return mSRView; @@ -134,20 +143,23 @@ SourceSurfaceD2DTarget::GetBitmap(ID2D1RenderTarget *aRT) hr = mTexture->QueryInterface((IDXGISurface**)byRef(surf)); if (FAILED(hr)) { - gfxWarning() << "Failed to query interface texture to DXGISurface. Code: " << hr; + gfxWarning() << "Failed to query interface texture to DXGISurface. Code: " << hexa(hr); return nullptr; } - D2D1_BITMAP_PROPERTIES props = - D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(mFormat), AlphaMode(mFormat))); + D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(mFormat)); hr = aRT->CreateSharedBitmap(IID_IDXGISurface, surf, &props, byRef(mBitmap)); if (FAILED(hr)) { - // This seems to happen for FORMAT_A8 sometimes... - aRT->CreateBitmap(D2D1::SizeU(desc.Width, desc.Height), - D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(mFormat), - AlphaMode(mFormat))), - byRef(mBitmap)); + // This seems to happen for SurfaceFormat::A8 sometimes... + hr = aRT->CreateBitmap(D2D1::SizeU(desc.Width, desc.Height), + D2D1::BitmapProperties(D2DPixelFormat(mFormat)), + byRef(mBitmap)); + + if (FAILED(hr)) { + gfxWarning() << "Failed in CreateBitmap. Code: " << hexa(hr); + return nullptr; + } RefPtr<ID2D1RenderTarget> rt; @@ -169,7 +181,7 @@ SourceSurfaceD2DTarget::GetBitmap(ID2D1RenderTarget *aRT) } D2D1_RENDER_TARGET_PROPERTIES props = - D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGIFormat(mFormat), AlphaMode(mFormat))); + D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2DPixelFormat(mFormat)); hr = DrawTargetD2D::factory()->CreateDxgiSurfaceRenderTarget(surface, props, byRef(rt)); if (FAILED(hr)) { @@ -195,8 +207,8 @@ SourceSurfaceD2DTarget::MarkIndependent() } } -DataSourceSurfaceD2DTarget::DataSourceSurfaceD2DTarget() - : mFormat(FORMAT_B8G8R8A8) +DataSourceSurfaceD2DTarget::DataSourceSurfaceD2DTarget(SurfaceFormat aFormat) + : mFormat(aFormat) , mMapped(false) { } @@ -238,13 +250,61 @@ DataSourceSurfaceD2DTarget::Stride() return mMap.RowPitch; } +bool +DataSourceSurfaceD2DTarget::Map(MapType aMapType, MappedSurface *aMappedSurface) +{ + // DataSourceSurfaces used with the new Map API should not be used with GetData!! + MOZ_ASSERT(!mMapped); + MOZ_ASSERT(!mIsMapped); + + if (!mTexture) { + return false; + } + + D3D10_MAP mapType; + + if (aMapType == MapType::READ) { + mapType = D3D10_MAP_READ; + } else if (aMapType == MapType::WRITE) { + mapType = D3D10_MAP_WRITE; + } else { + mapType = D3D10_MAP_READ_WRITE; + } + + D3D10_MAPPED_TEXTURE2D map; + + HRESULT hr = mTexture->Map(0, mapType, 0, &map); + + if (FAILED(hr)) { + gfxWarning() << "Texture map failed with code: " << hexa(hr); + return false; + } + + aMappedSurface->mData = (uint8_t*)map.pData; + aMappedSurface->mStride = map.RowPitch; + mIsMapped = !!aMappedSurface->mData; + + return mIsMapped; +} + +void +DataSourceSurfaceD2DTarget::Unmap() +{ + MOZ_ASSERT(mIsMapped); + + mIsMapped = false; + mTexture->Unmap(0); +} + void DataSourceSurfaceD2DTarget::EnsureMapped() { + // Do not use GetData() after having used Map! + MOZ_ASSERT(!mIsMapped); if (!mMapped) { HRESULT hr = mTexture->Map(0, D3D10_MAP_READ, 0, &mMap); if (FAILED(hr)) { - gfxWarning() << "Failed to map texture to memory. Code: " << hr; + gfxWarning() << "Failed to map texture to memory. Code: " << hexa(hr); return; } mMapped = true; diff --git a/gfx/2d/SourceSurfaceD2DTarget.h b/gfx/2d/SourceSurfaceD2DTarget.h index 2be02df97..a66505047 100644 --- a/gfx/2d/SourceSurfaceD2DTarget.h +++ b/gfx/2d/SourceSurfaceD2DTarget.h @@ -19,17 +19,23 @@ class DrawTargetD2D; class SourceSurfaceD2DTarget : public SourceSurface { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceD2DTarget) SourceSurfaceD2DTarget(DrawTargetD2D* aDrawTarget, ID3D10Texture2D* aTexture, SurfaceFormat aFormat); ~SourceSurfaceD2DTarget(); - virtual SurfaceType GetType() const { return SURFACE_D2D1_DRAWTARGET; } + virtual SurfaceType GetType() const { return SurfaceType::D2D1_DRAWTARGET; } virtual IntSize GetSize() const; virtual SurfaceFormat GetFormat() const; virtual TemporaryRef<DataSourceSurface> GetDataSurface(); + virtual void *GetNativeSurface(NativeSurfaceType aType); + + DrawTargetD2D* GetDT() { return mDrawTarget; } + ID2D1Bitmap *GetBitmap(ID2D1RenderTarget *aRT); private: friend class DrawTargetD2D; + ID3D10ShaderResourceView *GetSRView(); // This function is called by the draw target this texture belongs to when @@ -41,8 +47,6 @@ private: // this may happen on destruction or copying. void MarkIndependent(); - ID2D1Bitmap *GetBitmap(ID2D1RenderTarget *aRT); - RefPtr<ID3D10ShaderResourceView> mSRView; RefPtr<ID2D1Bitmap> mBitmap; // Non-null if this is a "lazy copy" of the given draw target. @@ -58,14 +62,17 @@ private: class DataSourceSurfaceD2DTarget : public DataSourceSurface { public: - DataSourceSurfaceD2DTarget(); + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceD2DTarget) + DataSourceSurfaceD2DTarget(SurfaceFormat aFormat); ~DataSourceSurfaceD2DTarget(); - virtual SurfaceType GetType() const { return SURFACE_DATA; } + virtual SurfaceType GetType() const { return SurfaceType::DATA; } virtual IntSize GetSize() const; virtual SurfaceFormat GetFormat() const; virtual uint8_t *GetData(); virtual int32_t Stride(); + virtual bool Map(MapType, MappedSurface *aMappedSurface); + virtual void Unmap(); private: friend class SourceSurfaceD2DTarget; diff --git a/gfx/2d/SourceSurfaceDual.h b/gfx/2d/SourceSurfaceDual.h index 0b783cbb4..aaa85dcc8 100644 --- a/gfx/2d/SourceSurfaceDual.h +++ b/gfx/2d/SourceSurfaceDual.h @@ -17,12 +17,13 @@ class DualPattern; class SourceSurfaceDual : public SourceSurface { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceDual) SourceSurfaceDual(DrawTarget *aDTA, DrawTarget *aDTB) : mA(aDTA->Snapshot()) , mB(aDTB->Snapshot()) { } - virtual SurfaceType GetType() const { return SURFACE_DUAL_DT; } + virtual SurfaceType GetType() const { return SurfaceType::DUAL_DT; } virtual IntSize GetSize() const { return mA->GetSize(); } virtual SurfaceFormat GetFormat() const { return mA->GetFormat(); } diff --git a/gfx/2d/SourceSurfaceRawData.cpp b/gfx/2d/SourceSurfaceRawData.cpp index 7f775e5c8..901a5f567 100644 --- a/gfx/2d/SourceSurfaceRawData.cpp +++ b/gfx/2d/SourceSurfaceRawData.cpp @@ -4,8 +4,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SourceSurfaceRawData.h" + +#include "DataSurfaceHelpers.h" #include "Logging.h" -#include "Tools.h" +#include "mozilla/Types.h" // for decltype namespace mozilla { namespace gfx { @@ -26,5 +28,64 @@ SourceSurfaceRawData::InitWrappingData(uint8_t *aData, return true; } +void +SourceSurfaceRawData::GuaranteePersistance() +{ + if (mOwnData) { + return; + } + + uint8_t* oldData = mRawData; + mRawData = new uint8_t[mStride * mSize.height]; + + memcpy(mRawData, oldData, mStride * mSize.height); + mOwnData = true; +} + +bool +SourceSurfaceAlignedRawData::Init(const IntSize &aSize, + SurfaceFormat aFormat, + bool aZero) +{ + mFormat = aFormat; + mStride = GetAlignedStride<16>(aSize.width * BytesPerPixel(aFormat)); + + size_t bufLen = BufferSizeFromStrideAndHeight(mStride, aSize.height); + if (bufLen > 0) { + static_assert(sizeof(decltype(mArray[0])) == 1, + "mArray.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen"); + mArray.Realloc(/* actually an object count */ bufLen, aZero); + mSize = aSize; + } else { + mArray.Dealloc(); + mSize.SizeTo(0, 0); + } + + return mArray != nullptr; +} + +bool +SourceSurfaceAlignedRawData::InitWithStride(const IntSize &aSize, + SurfaceFormat aFormat, + int32_t aStride, + bool aZero) +{ + mFormat = aFormat; + mStride = aStride; + + size_t bufLen = BufferSizeFromStrideAndHeight(mStride, aSize.height); + if (bufLen > 0) { + static_assert(sizeof(decltype(mArray[0])) == 1, + "mArray.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen"); + mArray.Realloc(/* actually an object count */ bufLen, aZero); + mSize = aSize; + } else { + mArray.Dealloc(); + mSize.SizeTo(0, 0); + } + + return mArray != nullptr; +} + } } diff --git a/gfx/2d/SourceSurfaceRawData.h b/gfx/2d/SourceSurfaceRawData.h index 4b7a33fb3..13a84e5d0 100644 --- a/gfx/2d/SourceSurfaceRawData.h +++ b/gfx/2d/SourceSurfaceRawData.h @@ -7,6 +7,7 @@ #define MOZILLA_GFX_SOURCESURFACERAWDATA_H_ #include "2D.h" +#include "Tools.h" namespace mozilla { namespace gfx { @@ -14,13 +15,14 @@ namespace gfx { class SourceSurfaceRawData : public DataSourceSurface { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceRawData) SourceSurfaceRawData() {} ~SourceSurfaceRawData() { if(mOwnData) delete [] mRawData; } virtual uint8_t *GetData() { return mRawData; } virtual int32_t Stride() { return mStride; } - virtual SurfaceType GetType() const { return SURFACE_DATA; } + virtual SurfaceType GetType() const { return SurfaceType::DATA; } virtual IntSize GetSize() const { return mSize; } virtual SurfaceFormat GetFormat() const { return mFormat; } @@ -30,6 +32,8 @@ public: SurfaceFormat aFormat, bool aOwnData); + virtual void GuaranteePersistance(); + private: uint8_t *mRawData; int32_t mStride; @@ -38,6 +42,34 @@ private: bool mOwnData; }; +class SourceSurfaceAlignedRawData : public DataSourceSurface +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceAlignedRawData) + SourceSurfaceAlignedRawData() {} + + virtual uint8_t *GetData() { return mArray; } + virtual int32_t Stride() { return mStride; } + + virtual SurfaceType GetType() const { return SurfaceType::DATA; } + virtual IntSize GetSize() const { return mSize; } + virtual SurfaceFormat GetFormat() const { return mFormat; } + + bool Init(const IntSize &aSize, + SurfaceFormat aFormat, + bool aZero); + bool InitWithStride(const IntSize &aSize, + SurfaceFormat aFormat, + int32_t aStride, + bool aZero); + +private: + AlignedArray<uint8_t> mArray; + int32_t mStride; + SurfaceFormat mFormat; + IntSize mSize; +}; + } } diff --git a/gfx/2d/SourceSurfaceSkia.cpp b/gfx/2d/SourceSurfaceSkia.cpp index fcee181cc..d7e071448 100644 --- a/gfx/2d/SourceSurfaceSkia.cpp +++ b/gfx/2d/SourceSurfaceSkia.cpp @@ -10,6 +10,8 @@ #include "skia/SkDevice.h" #include "HelpersSkia.h" #include "DrawTargetSkia.h" +#include "DataSurfaceHelpers.h" +#include "skia/SkGrPixelRef.h" namespace mozilla { namespace gfx { @@ -22,7 +24,10 @@ SourceSurfaceSkia::SourceSurfaceSkia() SourceSurfaceSkia::~SourceSurfaceSkia() { MaybeUnlock(); - MarkIndependent(); + if (mDrawTarget) { + mDrawTarget->SnapshotDestroyed(); + mDrawTarget = nullptr; + } } IntSize @@ -45,6 +50,7 @@ SourceSurfaceSkia::InitFromCanvas(SkCanvas* aCanvas, SkISize size = aCanvas->getDeviceSize(); mBitmap = (SkBitmap)aCanvas->getDevice()->accessBitmap(false); + mFormat = aFormat; mSize = IntSize(size.fWidth, size.fHeight); @@ -61,25 +67,55 @@ SourceSurfaceSkia::InitFromData(unsigned char* aData, SurfaceFormat aFormat) { SkBitmap temp; - temp.setConfig(GfxFormatToSkiaConfig(aFormat), aSize.width, aSize.height, aStride); + SkAlphaType alphaType = (aFormat == SurfaceFormat::B8G8R8X8) ? + kOpaque_SkAlphaType : kPremul_SkAlphaType; + + SkImageInfo info = SkImageInfo::Make(aSize.width, + aSize.height, + GfxFormatToSkiaColorType(aFormat), + alphaType); + temp.setInfo(info, aStride); temp.setPixels(aData); - if (!temp.copyTo(&mBitmap, GfxFormatToSkiaConfig(aFormat))) { + if (!temp.copyTo(&mBitmap, GfxFormatToSkiaColorType(aFormat))) { return false; } - if (aFormat == FORMAT_B8G8R8X8) { - mBitmap.lockPixels(); - // We have to manually set the A channel to be 255 as Skia doesn't understand BGRX - ConvertBGRXToBGRA(reinterpret_cast<unsigned char*>(mBitmap.getPixels()), aSize, aStride); - mBitmap.unlockPixels(); - mBitmap.notifyPixelsChanged(); - mBitmap.setIsOpaque(true); + if (aFormat == SurfaceFormat::B8G8R8X8) { + mBitmap.setAlphaType(kIgnore_SkAlphaType); } mSize = aSize; mFormat = aFormat; - mStride = aStride; + mStride = mBitmap.rowBytes(); + return true; +} + +bool +SourceSurfaceSkia::InitFromTexture(DrawTargetSkia* aOwner, + unsigned int aTexture, + const IntSize &aSize, + SurfaceFormat aFormat) +{ + MOZ_ASSERT(aOwner, "null GrContext"); + GrBackendTextureDesc skiaTexGlue; + mSize.width = skiaTexGlue.fWidth = aSize.width; + mSize.height = skiaTexGlue.fHeight = aSize.height; + skiaTexGlue.fFlags = kNone_GrBackendTextureFlag; + skiaTexGlue.fOrigin = kBottomLeft_GrSurfaceOrigin; + skiaTexGlue.fConfig = GfxFormatToGrConfig(aFormat); + skiaTexGlue.fSampleCnt = 0; + skiaTexGlue.fTextureHandle = aTexture; + + GrTexture *skiaTexture = aOwner->mGrContext->wrapBackendTexture(skiaTexGlue); + SkImageInfo imgInfo = SkImageInfo::Make(aSize.width, aSize.height, GfxFormatToSkiaColorType(aFormat), kOpaque_SkAlphaType); + SkGrPixelRef *texRef = new SkGrPixelRef(imgInfo, skiaTexture, false); + mBitmap.setInfo(imgInfo); + mBitmap.setPixelRef(texRef); + mFormat = aFormat; + mStride = mBitmap.rowBytes(); + + mDrawTarget = aOwner; return true; } @@ -104,22 +140,7 @@ SourceSurfaceSkia::DrawTargetWillChange() mDrawTarget = nullptr; SkBitmap temp = mBitmap; mBitmap.reset(); - temp.copyTo(&mBitmap, temp.getConfig()); - } -} - -void -SourceSurfaceSkia::DrawTargetDestroyed() -{ - mDrawTarget = nullptr; -} - -void -SourceSurfaceSkia::MarkIndependent() -{ - if (mDrawTarget) { - mDrawTarget->RemoveSnapshot(this); - mDrawTarget = nullptr; + temp.copyTo(&mBitmap, temp.colorType()); } } diff --git a/gfx/2d/SourceSurfaceSkia.h b/gfx/2d/SourceSurfaceSkia.h index 0e8f7fab6..de57602b4 100644 --- a/gfx/2d/SourceSurfaceSkia.h +++ b/gfx/2d/SourceSurfaceSkia.h @@ -11,7 +11,10 @@ #include "skia/SkCanvas.h" #include "skia/SkBitmap.h" +class GrContext; + namespace mozilla { + namespace gfx { class DrawTargetSkia; @@ -19,10 +22,11 @@ class DrawTargetSkia; class SourceSurfaceSkia : public DataSourceSurface { public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceSkia) SourceSurfaceSkia(); ~SourceSurfaceSkia(); - virtual SurfaceType GetType() const { return SURFACE_SKIA; } + virtual SurfaceType GetType() const { return SurfaceType::SKIA; } virtual IntSize GetSize() const; virtual SurfaceFormat GetFormat() const; @@ -37,6 +41,15 @@ public: SurfaceFormat aFormat, DrawTargetSkia* aOwner); + /** + * NOTE: While wrapping a Texture for SkiaGL, the texture *must* be created + * with the same GLcontext of DrawTargetSkia + */ + bool InitFromTexture(DrawTargetSkia* aOwner, + unsigned int aTexture, + const IntSize &aSize, + SurfaceFormat aFormat); + virtual unsigned char *GetData(); virtual int32_t Stride() { return mStride; } @@ -45,15 +58,13 @@ private: friend class DrawTargetSkia; void DrawTargetWillChange(); - void DrawTargetDestroyed(); - void MarkIndependent(); void MaybeUnlock(); SkBitmap mBitmap; SurfaceFormat mFormat; IntSize mSize; int32_t mStride; - DrawTargetSkia* mDrawTarget; + RefPtr<DrawTargetSkia> mDrawTarget; bool mLocked; }; diff --git a/gfx/2d/StackArray.h b/gfx/2d/StackArray.h new file mode 100644 index 000000000..e3c2684a0 --- /dev/null +++ b/gfx/2d/StackArray.h @@ -0,0 +1,30 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A handy class that will allocate data for size*T objects on the stack and + * otherwise allocate them on the heap. It is similar in purpose to nsAutoTArray */ + +template <class T, size_t size> +class StackArray +{ +public: + StackArray(size_t count) { + if (count > size) { + mData = new T[count]; + } else { + mData = mStackData; + } + } + ~StackArray() { + if (mData != mStackData) { + delete[] mData; + } + } + T& operator[](size_t n) { return mData[n]; } + const T& operator[](size_t n) const { return mData[n]; } + T* data() { return mData; }; +private: + T mStackData[size]; + T* mData; +}; diff --git a/gfx/2d/Tools.h b/gfx/2d/Tools.h index 0aca4eddf..d7405c75b 100644 --- a/gfx/2d/Tools.h +++ b/gfx/2d/Tools.h @@ -6,6 +6,9 @@ #ifndef MOZILLA_GFX_TOOLS_H_ #define MOZILLA_GFX_TOOLS_H_ +#include "mozilla/CheckedInt.h" +#include "mozilla/Move.h" +#include "mozilla/TypeTraits.h" #include "Types.h" #include "Point.h" #include <math.h> @@ -19,11 +22,11 @@ namespace gfx { static inline bool IsOperatorBoundByMask(CompositionOp aOp) { switch (aOp) { - case OP_IN: - case OP_OUT: - case OP_DEST_IN: - case OP_DEST_ATOP: - case OP_SOURCE: + case CompositionOp::OP_IN: + case CompositionOp::OP_OUT: + case CompositionOp::OP_DEST_IN: + case CompositionOp::OP_DEST_ATOP: + case CompositionOp::OP_SOURCE: return false; default: return true; @@ -62,6 +65,15 @@ NudgeToInteger(float *aVal) } } +static inline void +NudgeToInteger(float *aVal, float aErr) +{ + float r = floorf(*aVal + 0.5f); + if (FuzzyEqual(r, *aVal, aErr)) { + *aVal = r; + } +} + static inline Float Distance(Point aA, Point aB) { @@ -72,9 +84,9 @@ static inline int BytesPerPixel(SurfaceFormat aFormat) { switch (aFormat) { - case FORMAT_A8: + case SurfaceFormat::A8: return 1; - case FORMAT_R5G6B5: + case SurfaceFormat::R5G6B5: return 2; default: return 4; @@ -84,40 +96,94 @@ BytesPerPixel(SurfaceFormat aFormat) template<typename T, int alignment = 16> struct AlignedArray { + typedef T value_type; + AlignedArray() - : mStorage(nullptr) - , mPtr(nullptr) + : mPtr(nullptr) + , mStorage(nullptr) { } - MOZ_ALWAYS_INLINE AlignedArray(size_t aSize) + explicit MOZ_ALWAYS_INLINE AlignedArray(size_t aCount, bool aZero = false) : mStorage(nullptr) + , mCount(0) { - Realloc(aSize); + Realloc(aCount, aZero); } MOZ_ALWAYS_INLINE ~AlignedArray() { - delete [] mStorage; + Dealloc(); } void Dealloc() { - delete [] mStorage; - mStorage = mPtr = nullptr; + // If we fail this assert we'll need to uncomment the loop below to make + // sure dtors are properly invoked. If we do that, we should check that the + // comment about compiler dead code elimination is in fact true for all the + // compilers that we care about. + static_assert(mozilla::IsPod<T>::value, + "Destructors must be invoked for this type"); +#if 0 + for (size_t i = 0; i < mCount; ++i) { + // Since we used the placement |operator new| function to construct the + // elements of this array we need to invoke their destructors manually. + // For types where the destructor does nothing the compiler's dead code + // elimination step should optimize this loop away. + mPtr[i].~T(); + } +#endif + + moz_free(mStorage); + mStorage = nullptr; + mPtr = nullptr; } - MOZ_ALWAYS_INLINE void Realloc(size_t aSize) + MOZ_ALWAYS_INLINE void Realloc(size_t aCount, bool aZero = false) { - delete [] mStorage; - mStorage = new (std::nothrow) T[aSize + (alignment - 1)]; + moz_free(mStorage); + CheckedInt32 storageByteCount = + CheckedInt32(sizeof(T)) * aCount + (alignment - 1); + if (!storageByteCount.isValid()) { + mStorage = nullptr; + mPtr = nullptr; + mCount = 0; + return; + } + // We don't create an array of T here, since we don't want ctors to be + // invoked at the wrong places if we realign below. + if (aZero) { + // calloc can be more efficient than new[] for large chunks, + // so we use calloc/malloc/free for everything. + mStorage = static_cast<uint8_t *>(moz_calloc(1, storageByteCount.value())); + } else { + mStorage = static_cast<uint8_t *>(moz_malloc(storageByteCount.value())); + } + if (!mStorage) { + mStorage = nullptr; + mPtr = nullptr; + mCount = 0; + return; + } if (uintptr_t(mStorage) % alignment) { - // Our storage does not start at a <alignment>-byte boundary. Make sure mData does! - mPtr = (uint32_t*)(uintptr_t(mStorage) + - (alignment - (uintptr_t(mStorage) % alignment))); + // Our storage does not start at a <alignment>-byte boundary. Make sure mPtr does! + mPtr = (T*)(uintptr_t(mStorage) + alignment - (uintptr_t(mStorage) % alignment)); } else { - mPtr = mStorage; + mPtr = (T*)(mStorage); } + // Now that mPtr is pointing to the aligned position we can use placement + // |operator new| to invoke any ctors at the correct positions. For types + // that have a no-op default constructor the compiler's dead code + // elimination step should optimize this away. + mPtr = new (mPtr) T[aCount]; + mCount = aCount; + } + + void Swap(AlignedArray<T, alignment>& aOther) + { + mozilla::Swap(mPtr, aOther.mPtr); + mozilla::Swap(mStorage, aOther.mStorage); + mozilla::Swap(mCount, aOther.mCount); } MOZ_ALWAYS_INLINE operator T*() @@ -125,18 +191,28 @@ struct AlignedArray return mPtr; } - T *mStorage; T *mPtr; + +private: + uint8_t *mStorage; + size_t mCount; }; +/** + * Returns aStride increased, if necessary, so that it divides exactly into + * |alignment|. + * + * Note that currently |alignment| must be a power-of-2. If for some reason we + * want to support NPOT alignment we can revert back to this functions old + * implementation. + */ template<int alignment> int32_t GetAlignedStride(int32_t aStride) { - if (aStride % alignment) { - return aStride + (alignment - (aStride % alignment)); - } - - return aStride; + static_assert(alignment > 0 && (alignment & (alignment-1)) == 0, + "This implementation currently require power-of-two alignment"); + const int32_t mask = alignment - 1; + return (aStride + mask) & ~mask; } } diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h index 8a3a19586..29cf37e0b 100644 --- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -6,99 +6,219 @@ #ifndef MOZILLA_GFX_TYPES_H_ #define MOZILLA_GFX_TYPES_H_ -#include "mozilla/StandardInteger.h" -#include "mozilla/NullPtr.h" - #include <stddef.h> +#include <stdint.h> namespace mozilla { namespace gfx { typedef float Float; -enum SurfaceType -{ - SURFACE_DATA, /* Data surface - bitmap in memory */ - SURFACE_D2D1_BITMAP, /* Surface wrapping a ID2D1Bitmap */ - SURFACE_D2D1_DRAWTARGET, /* Surface made from a D2D draw target */ - SURFACE_CAIRO, /* Surface wrapping a cairo surface */ - SURFACE_CAIRO_IMAGE, /* Data surface wrapping a cairo image surface */ - SURFACE_COREGRAPHICS_IMAGE, /* Surface wrapping a CoreGraphics Image */ - SURFACE_COREGRAPHICS_CGCONTEXT, /* Surface wrapping a CG context */ - SURFACE_SKIA, /* Surface wrapping a Skia bitmap */ - SURFACE_DUAL_DT, /* Snapshot of a dual drawtarget */ - SURFACE_RECORDING /* Surface used for recording */ -}; - -enum SurfaceFormat -{ - FORMAT_B8G8R8A8, - FORMAT_B8G8R8X8, - FORMAT_R8G8B8A8, - FORMAT_R8G8B8X8, - FORMAT_R5G6B5, - FORMAT_A8, - FORMAT_YUV, - FORMAT_UNKNOWN +enum class SurfaceType : int8_t { + DATA, /* Data surface - bitmap in memory */ + D2D1_BITMAP, /* Surface wrapping a ID2D1Bitmap */ + D2D1_DRAWTARGET, /* Surface made from a D2D draw target */ + CAIRO, /* Surface wrapping a cairo surface */ + CAIRO_IMAGE, /* Data surface wrapping a cairo image surface */ + COREGRAPHICS_IMAGE, /* Surface wrapping a CoreGraphics Image */ + COREGRAPHICS_CGCONTEXT, /* Surface wrapping a CG context */ + SKIA, /* Surface wrapping a Skia bitmap */ + DUAL_DT, /* Snapshot of a dual drawtarget */ + D2D1_1_IMAGE, /* A D2D 1.1 ID2D1Image SourceSurface */ + RECORDING, /* Surface used for recording */ + TILED /* Surface from a tiled DrawTarget */ }; -enum BackendType -{ - BACKEND_NONE = 0, - BACKEND_DIRECT2D, - BACKEND_COREGRAPHICS, - BACKEND_COREGRAPHICS_ACCELERATED, - BACKEND_CAIRO, - BACKEND_SKIA, - BACKEND_RECORDING +enum class SurfaceFormat : int8_t { + B8G8R8A8, + B8G8R8X8, + R8G8B8A8, + R8G8B8X8, + R5G6B5, + A8, + YUV, + UNKNOWN }; -enum FontType +inline bool IsOpaque(SurfaceFormat aFormat) { - FONT_DWRITE, - FONT_GDI, - FONT_MAC, - FONT_SKIA, - FONT_CAIRO, - FONT_COREGRAPHICS + switch (aFormat) { + case SurfaceFormat::B8G8R8X8: + case SurfaceFormat::R8G8B8X8: + case SurfaceFormat::R5G6B5: + case SurfaceFormat::YUV: + return true; + default: + return false; + } +} + +enum class FilterType : int8_t { + BLEND = 0, + TRANSFORM, + MORPHOLOGY, + COLOR_MATRIX, + FLOOD, + TILE, + TABLE_TRANSFER, + DISCRETE_TRANSFER, + LINEAR_TRANSFER, + GAMMA_TRANSFER, + CONVOLVE_MATRIX, + DISPLACEMENT_MAP, + TURBULENCE, + ARITHMETIC_COMBINE, + COMPOSITE, + DIRECTIONAL_BLUR, + GAUSSIAN_BLUR, + POINT_DIFFUSE, + POINT_SPECULAR, + SPOT_DIFFUSE, + SPOT_SPECULAR, + DISTANT_DIFFUSE, + DISTANT_SPECULAR, + CROP, + PREMULTIPLY, + UNPREMULTIPLY }; -enum NativeSurfaceType -{ - NATIVE_SURFACE_D3D10_TEXTURE, - NATIVE_SURFACE_CAIRO_SURFACE, - NATIVE_SURFACE_CGCONTEXT, - NATIVE_SURFACE_CGCONTEXT_ACCELERATED +enum class DrawTargetType : int8_t { + SOFTWARE_RASTER = 0, + HARDWARE_RASTER, + VECTOR }; -enum NativeFontType -{ - NATIVE_FONT_DWRITE_FONT_FACE, - NATIVE_FONT_GDI_FONT_FACE, - NATIVE_FONT_MAC_FONT_FACE, - NATIVE_FONT_SKIA_FONT_FACE, - NATIVE_FONT_CAIRO_FONT_FACE +enum class BackendType : int8_t { + NONE = 0, + DIRECT2D, + COREGRAPHICS, + COREGRAPHICS_ACCELERATED, + CAIRO, + SKIA, + RECORDING, + DIRECT2D1_1 }; -enum FontStyle -{ - FONT_STYLE_NORMAL, - FONT_STYLE_ITALIC, - FONT_STYLE_BOLD, - FONT_STYLE_BOLD_ITALIC -}; - -enum CompositionOp { OP_OVER, OP_ADD, OP_ATOP, OP_OUT, OP_IN, OP_SOURCE, OP_DEST_IN, OP_DEST_OUT, OP_DEST_OVER, OP_DEST_ATOP, OP_XOR, - OP_MULTIPLY, OP_SCREEN, OP_OVERLAY, OP_DARKEN, OP_LIGHTEN, OP_COLOR_DODGE, OP_COLOR_BURN, OP_HARD_LIGHT, OP_SOFT_LIGHT, OP_DIFFERENCE, OP_EXCLUSION, OP_HUE, OP_SATURATION, OP_COLOR, OP_LUMINOSITY, OP_COUNT }; -enum ExtendMode { EXTEND_CLAMP, EXTEND_REPEAT, EXTEND_REFLECT }; -enum FillRule { FILL_WINDING, FILL_EVEN_ODD }; -enum AntialiasMode { AA_NONE, AA_GRAY, AA_SUBPIXEL, AA_DEFAULT }; -enum Snapping { SNAP_NONE, SNAP_ALIGNED }; -enum Filter { FILTER_LINEAR, FILTER_POINT }; -enum PatternType { PATTERN_COLOR, PATTERN_SURFACE, PATTERN_LINEAR_GRADIENT, PATTERN_RADIAL_GRADIENT }; -enum JoinStyle { JOIN_BEVEL, JOIN_ROUND, JOIN_MITER, JOIN_MITER_OR_BEVEL }; -enum CapStyle { CAP_BUTT, CAP_ROUND, CAP_SQUARE }; -enum SamplingBounds { SAMPLING_UNBOUNDED, SAMPLING_BOUNDED }; +enum class FontType : int8_t { + DWRITE, + GDI, + MAC, + SKIA, + CAIRO, + COREGRAPHICS +}; + +enum class NativeSurfaceType : int8_t { + D3D10_TEXTURE, + CAIRO_SURFACE, + CAIRO_CONTEXT, + CGCONTEXT, + CGCONTEXT_ACCELERATED, + OPENGL_TEXTURE +}; + +enum class NativeFontType : int8_t { + DWRITE_FONT_FACE, + GDI_FONT_FACE, + MAC_FONT_FACE, + SKIA_FONT_FACE, + CAIRO_FONT_FACE +}; + +enum class FontStyle : int8_t { + NORMAL, + ITALIC, + BOLD, + BOLD_ITALIC +}; + +enum class FontHinting : int8_t { + NONE, + LIGHT, + NORMAL, + FULL +}; + +enum class CompositionOp : int8_t { + OP_OVER, + OP_ADD, + OP_ATOP, + OP_OUT, + OP_IN, + OP_SOURCE, + OP_DEST_IN, + OP_DEST_OUT, + OP_DEST_OVER, + OP_DEST_ATOP, + OP_XOR, + OP_MULTIPLY, + OP_SCREEN, + OP_OVERLAY, + OP_DARKEN, + OP_LIGHTEN, + OP_COLOR_DODGE, + OP_COLOR_BURN, + OP_HARD_LIGHT, + OP_SOFT_LIGHT, + OP_DIFFERENCE, + OP_EXCLUSION, + OP_HUE, + OP_SATURATION, + OP_COLOR, + OP_LUMINOSITY, + OP_COUNT +}; + +enum class ExtendMode : int8_t { + CLAMP, + REPEAT, + REFLECT +}; + +enum class FillRule : int8_t { + FILL_WINDING, + FILL_EVEN_ODD +}; + +enum class AntialiasMode : int8_t { + NONE, + GRAY, + SUBPIXEL, + DEFAULT +}; + +enum class Filter : int8_t { + GOOD, + LINEAR, + POINT +}; + +enum class PatternType : int8_t { + COLOR, + SURFACE, + LINEAR_GRADIENT, + RADIAL_GRADIENT +}; + +enum class JoinStyle : int8_t { + BEVEL, + ROUND, + MITER, //!< Mitered if within the miter limit, else, if the backed supports + //!< it (D2D), the miter is clamped. If the backend does not support + //!< miter clamping the behavior is as for MITER_OR_BEVEL. + MITER_OR_BEVEL //!< Mitered if within the miter limit, else beveled. +}; + +enum class CapStyle : int8_t { + BUTT, + ROUND, + SQUARE +}; + +enum class SamplingBounds : int8_t { + UNBOUNDED, + BOUNDED +}; /* Color is stored in non-premultiplied form */ struct Color @@ -124,12 +244,28 @@ public: return newColor; } + static Color FromARGB(uint32_t aColor) + { + Color newColor(((aColor >> 16) & 0xff) * (1.0f / 255.0f), + ((aColor >> 8) & 0xff) * (1.0f / 255.0f), + ((aColor >> 0) & 0xff) * (1.0f / 255.0f), + ((aColor >> 24) & 0xff) * (1.0f / 255.0f)); + + return newColor; + } + uint32_t ToABGR() const { return uint32_t(r * 255.0f) | uint32_t(g * 255.0f) << 8 | uint32_t(b * 255.0f) << 16 | uint32_t(a * 255.0f) << 24; } + uint32_t ToARGB() const + { + return uint32_t(b * 255.0f) | uint32_t(g * 255.0f) << 8 | + uint32_t(r * 255.0f) << 16 | uint32_t(a * 255.0f) << 24; + } + Float r, g, b, a; }; @@ -156,17 +292,41 @@ struct GradientStop #define GFX2D_API #endif -// Side constants for use in various places namespace mozilla { - namespace css { - enum Side {eSideTop, eSideRight, eSideBottom, eSideLeft}; - } -} -// XXX - These don't really belong here. But for now this is where they go. -#define NS_SIDE_TOP mozilla::css::eSideTop -#define NS_SIDE_RIGHT mozilla::css::eSideRight -#define NS_SIDE_BOTTOM mozilla::css::eSideBottom -#define NS_SIDE_LEFT mozilla::css::eSideLeft +// We can't use MOZ_BEGIN_ENUM_CLASS here because that prevents the enum +// values from being used for indexing. Wrapping the enum in a struct does at +// least gives us name scoping. +struct RectCorner { + enum { + // This order is important since Rect::AtCorner, AppendRoundedRectToPath + // and other code depends on it! + TopLeft = 0, + TopRight = 1, + BottomRight = 2, + BottomLeft = 3, + Count = 4 + }; +}; + +// Side constants for use in various places. +enum Side { eSideTop, eSideRight, eSideBottom, eSideLeft }; + +enum SideBits { + eSideBitsNone = 0, + eSideBitsTop = 1 << eSideTop, + eSideBitsRight = 1 << eSideRight, + eSideBitsBottom = 1 << eSideBottom, + eSideBitsLeft = 1 << eSideLeft, + eSideBitsTopBottom = eSideBitsTop | eSideBitsBottom, + eSideBitsLeftRight = eSideBitsLeft | eSideBitsRight, + eSideBitsAll = eSideBitsTopBottom | eSideBitsLeftRight +}; +} // namespace mozilla + +#define NS_SIDE_TOP mozilla::eSideTop +#define NS_SIDE_RIGHT mozilla::eSideRight +#define NS_SIDE_BOTTOM mozilla::eSideBottom +#define NS_SIDE_LEFT mozilla::eSideLeft #endif /* MOZILLA_GFX_TYPES_H_ */ diff --git a/gfx/2d/UserData.h b/gfx/2d/UserData.h index c9a22534b..e803dc04e 100644 --- a/gfx/2d/UserData.h +++ b/gfx/2d/UserData.h @@ -73,7 +73,7 @@ public: } /* Retrives the userData for the associated key */ - void *Get(UserDataKey *key) + void *Get(UserDataKey *key) const { for (int i=0; i<count; i++) { if (key == entries[i].key) { diff --git a/gfx/2d/convolver.cpp b/gfx/2d/convolver.cpp index 948ce73ea..f1480aaa4 100644 --- a/gfx/2d/convolver.cpp +++ b/gfx/2d/convolver.cpp @@ -26,19 +26,20 @@ // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. +#include "2D.h" #include "convolver.h" #include <algorithm> -#include "nsAlgorithm.h" #include "skia/SkTypes.h" -// note: SIMD_SSE2 is not enabled because of bugs, apparently -#if defined(SIMD_SSE2) -#include <emmintrin.h> // ARCH_CPU_X86_FAMILY was defined in build/config.h +#if defined(USE_SSE2) +#include "convolverSSE2.h" #endif +using mozilla::gfx::Factory; + #if defined(SK_CPU_LENDIAN) #define R_OFFSET_IDX 0 #define G_OFFSET_IDX 1 @@ -152,15 +153,17 @@ class CircularRowBuffer { std::vector<unsigned char*> row_addresses_; }; +} // namespace + // Convolves horizontally along a single row. The row data is given in -// |src_data| and continues for the num_values() of the filter. +// |src_data| and continues for the [begin, end) of the filter. template<bool has_alpha> void ConvolveHorizontally(const unsigned char* src_data, + int begin, int end, const ConvolutionFilter1D& filter, unsigned char* out_row) { // Loop over each pixel on this row in the output image. - int num_values = filter.num_values(); - for (int out_x = 0; out_x < num_values; out_x++) { + for (int out_x = begin; out_x < end; out_x++) { // Get the filter that determines the current output pixel. int filter_offset, filter_length; const ConvolutionFilter1D::Fixed* filter_values = @@ -201,18 +204,17 @@ void ConvolveHorizontally(const unsigned char* src_data, // Does vertical convolution to produce one output row. The filter values and // length are given in the first two parameters. These are applied to each // of the rows pointed to in the |source_data_rows| array, with each row -// being |pixel_width| wide. +// being |end - begin| wide. // -// The output must have room for |pixel_width * 4| bytes. +// The output must have room for |(end - begin) * 4| bytes. template<bool has_alpha> void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values, int filter_length, unsigned char* const* source_data_rows, - int pixel_width, - unsigned char* out_row) { + int begin, int end, unsigned char* out_row) { // We go through each column in the output and do a vertical convolution, // generating one output pixel each time. - for (int out_x = 0; out_x < pixel_width; out_x++) { + for (int out_x = begin; out_x < end; out_x++) { // Compute the number of bytes over in each row that the current column // we're convolving starts at. The pixel will cover the next 4 bytes. int byte_offset = out_x * 4; @@ -267,435 +269,61 @@ void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values, } } - -// Convolves horizontally along a single row. The row data is given in -// |src_data| and continues for the num_values() of the filter. -void ConvolveHorizontally_SSE2(const unsigned char* src_data, - const ConvolutionFilter1D& filter, - unsigned char* out_row) { -#if defined(SIMD_SSE2) - int num_values = filter.num_values(); - - int filter_offset, filter_length; - __m128i zero = _mm_setzero_si128(); - __m128i mask[4]; - // |mask| will be used to decimate all extra filter coefficients that are - // loaded by SIMD when |filter_length| is not divisible by 4. - // mask[0] is not used in following algorithm. - mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1); - mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1); - mask[3] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1); - - // Output one pixel each iteration, calculating all channels (RGBA) together. - for (int out_x = 0; out_x < num_values; out_x++) { - const ConvolutionFilter1D::Fixed* filter_values = - filter.FilterForValue(out_x, &filter_offset, &filter_length); - - __m128i accum = _mm_setzero_si128(); - - // Compute the first pixel in this row that the filter affects. It will - // touch |filter_length| pixels (4 bytes each) after this. - const __m128i* row_to_filter = - reinterpret_cast<const __m128i*>(&src_data[filter_offset << 2]); - - // We will load and accumulate with four coefficients per iteration. - for (int filter_x = 0; filter_x < filter_length >> 2; filter_x++) { - - // Load 4 coefficients => duplicate 1st and 2nd of them for all channels. - __m128i coeff, coeff16; - // [16] xx xx xx xx c3 c2 c1 c0 - coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values)); - // [16] xx xx xx xx c1 c1 c0 c0 - coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0)); - // [16] c1 c1 c1 c1 c0 c0 c0 c0 - coeff16 = _mm_unpacklo_epi16(coeff16, coeff16); - - // Load four pixels => unpack the first two pixels to 16 bits => - // multiply with coefficients => accumulate the convolution result. - // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0 - __m128i src8 = _mm_loadu_si128(row_to_filter); - // [16] a1 b1 g1 r1 a0 b0 g0 r0 - __m128i src16 = _mm_unpacklo_epi8(src8, zero); - __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16); - __m128i mul_lo = _mm_mullo_epi16(src16, coeff16); - // [32] a0*c0 b0*c0 g0*c0 r0*c0 - __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi); - accum = _mm_add_epi32(accum, t); - // [32] a1*c1 b1*c1 g1*c1 r1*c1 - t = _mm_unpackhi_epi16(mul_lo, mul_hi); - accum = _mm_add_epi32(accum, t); - - // Duplicate 3rd and 4th coefficients for all channels => - // unpack the 3rd and 4th pixels to 16 bits => multiply with coefficients - // => accumulate the convolution results. - // [16] xx xx xx xx c3 c3 c2 c2 - coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2)); - // [16] c3 c3 c3 c3 c2 c2 c2 c2 - coeff16 = _mm_unpacklo_epi16(coeff16, coeff16); - // [16] a3 g3 b3 r3 a2 g2 b2 r2 - src16 = _mm_unpackhi_epi8(src8, zero); - mul_hi = _mm_mulhi_epi16(src16, coeff16); - mul_lo = _mm_mullo_epi16(src16, coeff16); - // [32] a2*c2 b2*c2 g2*c2 r2*c2 - t = _mm_unpacklo_epi16(mul_lo, mul_hi); - accum = _mm_add_epi32(accum, t); - // [32] a3*c3 b3*c3 g3*c3 r3*c3 - t = _mm_unpackhi_epi16(mul_lo, mul_hi); - accum = _mm_add_epi32(accum, t); - - // Advance the pixel and coefficients pointers. - row_to_filter += 1; - filter_values += 4; - } - - // When |filter_length| is not divisible by 4, we need to decimate some of - // the filter coefficient that was loaded incorrectly to zero; Other than - // that the algorithm is same with above, exceot that the 4th pixel will be - // always absent. - int r = filter_length&3; - if (r) { - // Note: filter_values must be padded to align_up(filter_offset, 8). - __m128i coeff, coeff16; - coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values)); - // Mask out extra filter taps. - coeff = _mm_and_si128(coeff, mask[r]); - coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0)); - coeff16 = _mm_unpacklo_epi16(coeff16, coeff16); - - // Note: line buffer must be padded to align_up(filter_offset, 16). - // We resolve this by use C-version for the last horizontal line. - __m128i src8 = _mm_loadu_si128(row_to_filter); - __m128i src16 = _mm_unpacklo_epi8(src8, zero); - __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16); - __m128i mul_lo = _mm_mullo_epi16(src16, coeff16); - __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi); - accum = _mm_add_epi32(accum, t); - t = _mm_unpackhi_epi16(mul_lo, mul_hi); - accum = _mm_add_epi32(accum, t); - - src16 = _mm_unpackhi_epi8(src8, zero); - coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2)); - coeff16 = _mm_unpacklo_epi16(coeff16, coeff16); - mul_hi = _mm_mulhi_epi16(src16, coeff16); - mul_lo = _mm_mullo_epi16(src16, coeff16); - t = _mm_unpacklo_epi16(mul_lo, mul_hi); - accum = _mm_add_epi32(accum, t); - } - - // Shift right for fixed point implementation. - accum = _mm_srai_epi32(accum, ConvolutionFilter1D::kShiftBits); - - // Packing 32 bits |accum| to 16 bits per channel (signed saturation). - accum = _mm_packs_epi32(accum, zero); - // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation). - accum = _mm_packus_epi16(accum, zero); - - // Store the pixel value of 32 bits. - *(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum); - out_row += 4; - } -#endif -} - -// Convolves horizontally along four rows. The row data is given in -// |src_data| and continues for the num_values() of the filter. -// The algorithm is almost same as |ConvolveHorizontally_SSE2|. Please -// refer to that function for detailed comments. -void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4], - const ConvolutionFilter1D& filter, - unsigned char* out_row[4]) { -#if defined(SIMD_SSE2) - int num_values = filter.num_values(); - - int filter_offset, filter_length; - __m128i zero = _mm_setzero_si128(); - __m128i mask[4]; - // |mask| will be used to decimate all extra filter coefficients that are - // loaded by SIMD when |filter_length| is not divisible by 4. - // mask[0] is not used in following algorithm. - mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1); - mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1); - mask[3] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1); - - // Output one pixel each iteration, calculating all channels (RGBA) together. - for (int out_x = 0; out_x < num_values; out_x++) { - const ConvolutionFilter1D::Fixed* filter_values = - filter.FilterForValue(out_x, &filter_offset, &filter_length); - - // four pixels in a column per iteration. - __m128i accum0 = _mm_setzero_si128(); - __m128i accum1 = _mm_setzero_si128(); - __m128i accum2 = _mm_setzero_si128(); - __m128i accum3 = _mm_setzero_si128(); - int start = (filter_offset<<2); - // We will load and accumulate with four coefficients per iteration. - for (int filter_x = 0; filter_x < (filter_length >> 2); filter_x++) { - __m128i coeff, coeff16lo, coeff16hi; - // [16] xx xx xx xx c3 c2 c1 c0 - coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values)); - // [16] xx xx xx xx c1 c1 c0 c0 - coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0)); - // [16] c1 c1 c1 c1 c0 c0 c0 c0 - coeff16lo = _mm_unpacklo_epi16(coeff16lo, coeff16lo); - // [16] xx xx xx xx c3 c3 c2 c2 - coeff16hi = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2)); - // [16] c3 c3 c3 c3 c2 c2 c2 c2 - coeff16hi = _mm_unpacklo_epi16(coeff16hi, coeff16hi); - - __m128i src8, src16, mul_hi, mul_lo, t; - -#define ITERATION(src, accum) \ - src8 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src)); \ - src16 = _mm_unpacklo_epi8(src8, zero); \ - mul_hi = _mm_mulhi_epi16(src16, coeff16lo); \ - mul_lo = _mm_mullo_epi16(src16, coeff16lo); \ - t = _mm_unpacklo_epi16(mul_lo, mul_hi); \ - accum = _mm_add_epi32(accum, t); \ - t = _mm_unpackhi_epi16(mul_lo, mul_hi); \ - accum = _mm_add_epi32(accum, t); \ - src16 = _mm_unpackhi_epi8(src8, zero); \ - mul_hi = _mm_mulhi_epi16(src16, coeff16hi); \ - mul_lo = _mm_mullo_epi16(src16, coeff16hi); \ - t = _mm_unpacklo_epi16(mul_lo, mul_hi); \ - accum = _mm_add_epi32(accum, t); \ - t = _mm_unpackhi_epi16(mul_lo, mul_hi); \ - accum = _mm_add_epi32(accum, t) - - ITERATION(src_data[0] + start, accum0); - ITERATION(src_data[1] + start, accum1); - ITERATION(src_data[2] + start, accum2); - ITERATION(src_data[3] + start, accum3); - - start += 16; - filter_values += 4; - } - - int r = filter_length & 3; - if (r) { - // Note: filter_values must be padded to align_up(filter_offset, 8); - __m128i coeff; - coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values)); - // Mask out extra filter taps. - coeff = _mm_and_si128(coeff, mask[r]); - - __m128i coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0)); - /* c1 c1 c1 c1 c0 c0 c0 c0 */ - coeff16lo = _mm_unpacklo_epi16(coeff16lo, coeff16lo); - __m128i coeff16hi = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2)); - coeff16hi = _mm_unpacklo_epi16(coeff16hi, coeff16hi); - - __m128i src8, src16, mul_hi, mul_lo, t; - - ITERATION(src_data[0] + start, accum0); - ITERATION(src_data[1] + start, accum1); - ITERATION(src_data[2] + start, accum2); - ITERATION(src_data[3] + start, accum3); - } - - accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits); - accum0 = _mm_packs_epi32(accum0, zero); - accum0 = _mm_packus_epi16(accum0, zero); - accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits); - accum1 = _mm_packs_epi32(accum1, zero); - accum1 = _mm_packus_epi16(accum1, zero); - accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits); - accum2 = _mm_packs_epi32(accum2, zero); - accum2 = _mm_packus_epi16(accum2, zero); - accum3 = _mm_srai_epi32(accum3, ConvolutionFilter1D::kShiftBits); - accum3 = _mm_packs_epi32(accum3, zero); - accum3 = _mm_packus_epi16(accum3, zero); - - *(reinterpret_cast<int*>(out_row[0])) = _mm_cvtsi128_si32(accum0); - *(reinterpret_cast<int*>(out_row[1])) = _mm_cvtsi128_si32(accum1); - *(reinterpret_cast<int*>(out_row[2])) = _mm_cvtsi128_si32(accum2); - *(reinterpret_cast<int*>(out_row[3])) = _mm_cvtsi128_si32(accum3); - - out_row[0] += 4; - out_row[1] += 4; - out_row[2] += 4; - out_row[3] += 4; +void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values, + int filter_length, + unsigned char* const* source_data_rows, + int width, unsigned char* out_row, + bool has_alpha, bool use_sse2) { + int processed = 0; + +#if defined(USE_SSE2) + // If the binary was not built with SSE2 support, we had to fallback to C version. + int simd_width = width & ~3; + if (use_sse2 && simd_width) { + ConvolveVertically_SSE2(filter_values, filter_length, + source_data_rows, 0, simd_width, + out_row, has_alpha); + processed = simd_width; } #endif -} - -// Does vertical convolution to produce one output row. The filter values and -// length are given in the first two parameters. These are applied to each -// of the rows pointed to in the |source_data_rows| array, with each row -// being |pixel_width| wide. -// -// The output must have room for |pixel_width * 4| bytes. -template<bool has_alpha> -void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values, - int filter_length, - unsigned char* const* source_data_rows, - int pixel_width, - unsigned char* out_row) { -#if defined(SIMD_SSE2) - int width = pixel_width & ~3; - - __m128i zero = _mm_setzero_si128(); - __m128i accum0, accum1, accum2, accum3, coeff16; - const __m128i* src; - // Output four pixels per iteration (16 bytes). - for (int out_x = 0; out_x < width; out_x += 4) { - - // Accumulated result for each pixel. 32 bits per RGBA channel. - accum0 = _mm_setzero_si128(); - accum1 = _mm_setzero_si128(); - accum2 = _mm_setzero_si128(); - accum3 = _mm_setzero_si128(); - - // Convolve with one filter coefficient per iteration. - for (int filter_y = 0; filter_y < filter_length; filter_y++) { - - // Duplicate the filter coefficient 8 times. - // [16] cj cj cj cj cj cj cj cj - coeff16 = _mm_set1_epi16(filter_values[filter_y]); - - // Load four pixels (16 bytes) together. - // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0 - src = reinterpret_cast<const __m128i*>( - &source_data_rows[filter_y][out_x << 2]); - __m128i src8 = _mm_loadu_si128(src); - - // Unpack 1st and 2nd pixels from 8 bits to 16 bits for each channels => - // multiply with current coefficient => accumulate the result. - // [16] a1 b1 g1 r1 a0 b0 g0 r0 - __m128i src16 = _mm_unpacklo_epi8(src8, zero); - __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16); - __m128i mul_lo = _mm_mullo_epi16(src16, coeff16); - // [32] a0 b0 g0 r0 - __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi); - accum0 = _mm_add_epi32(accum0, t); - // [32] a1 b1 g1 r1 - t = _mm_unpackhi_epi16(mul_lo, mul_hi); - accum1 = _mm_add_epi32(accum1, t); - - // Unpack 3rd and 4th pixels from 8 bits to 16 bits for each channels => - // multiply with current coefficient => accumulate the result. - // [16] a3 b3 g3 r3 a2 b2 g2 r2 - src16 = _mm_unpackhi_epi8(src8, zero); - mul_hi = _mm_mulhi_epi16(src16, coeff16); - mul_lo = _mm_mullo_epi16(src16, coeff16); - // [32] a2 b2 g2 r2 - t = _mm_unpacklo_epi16(mul_lo, mul_hi); - accum2 = _mm_add_epi32(accum2, t); - // [32] a3 b3 g3 r3 - t = _mm_unpackhi_epi16(mul_lo, mul_hi); - accum3 = _mm_add_epi32(accum3, t); - } - - // Shift right for fixed point implementation. - accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits); - accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits); - accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits); - accum3 = _mm_srai_epi32(accum3, ConvolutionFilter1D::kShiftBits); - - // Packing 32 bits |accum| to 16 bits per channel (signed saturation). - // [16] a1 b1 g1 r1 a0 b0 g0 r0 - accum0 = _mm_packs_epi32(accum0, accum1); - // [16] a3 b3 g3 r3 a2 b2 g2 r2 - accum2 = _mm_packs_epi32(accum2, accum3); - - // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation). - // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0 - accum0 = _mm_packus_epi16(accum0, accum2); - + + if (width > processed) { if (has_alpha) { - // Compute the max(ri, gi, bi) for each pixel. - // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0 - __m128i a = _mm_srli_epi32(accum0, 8); - // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0 - __m128i b = _mm_max_epu8(a, accum0); // Max of r and g. - // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0 - a = _mm_srli_epi32(accum0, 16); - // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0 - b = _mm_max_epu8(a, b); // Max of r and g and b. - // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00 - b = _mm_slli_epi32(b, 24); - - // Make sure the value of alpha channel is always larger than maximum - // value of color channels. - accum0 = _mm_max_epu8(b, accum0); + ConvolveVertically<true>(filter_values, filter_length, source_data_rows, + processed, width, out_row); } else { - // Set value of alpha channels to 0xFF. - __m128i mask = _mm_set1_epi32(0xff000000); - accum0 = _mm_or_si128(accum0, mask); + ConvolveVertically<false>(filter_values, filter_length, source_data_rows, + processed, width, out_row); } - - // Store the convolution result (16 bytes) and advance the pixel pointers. - _mm_storeu_si128(reinterpret_cast<__m128i*>(out_row), accum0); - out_row += 16; } +} - // When the width of the output is not divisible by 4, We need to save one - // pixel (4 bytes) each time. And also the fourth pixel is always absent. - if (pixel_width & 3) { - accum0 = _mm_setzero_si128(); - accum1 = _mm_setzero_si128(); - accum2 = _mm_setzero_si128(); - for (int filter_y = 0; filter_y < filter_length; ++filter_y) { - coeff16 = _mm_set1_epi16(filter_values[filter_y]); - // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0 - src = reinterpret_cast<const __m128i*>( - &source_data_rows[filter_y][width<<2]); - __m128i src8 = _mm_loadu_si128(src); - // [16] a1 b1 g1 r1 a0 b0 g0 r0 - __m128i src16 = _mm_unpacklo_epi8(src8, zero); - __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16); - __m128i mul_lo = _mm_mullo_epi16(src16, coeff16); - // [32] a0 b0 g0 r0 - __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi); - accum0 = _mm_add_epi32(accum0, t); - // [32] a1 b1 g1 r1 - t = _mm_unpackhi_epi16(mul_lo, mul_hi); - accum1 = _mm_add_epi32(accum1, t); - // [16] a3 b3 g3 r3 a2 b2 g2 r2 - src16 = _mm_unpackhi_epi8(src8, zero); - mul_hi = _mm_mulhi_epi16(src16, coeff16); - mul_lo = _mm_mullo_epi16(src16, coeff16); - // [32] a2 b2 g2 r2 - t = _mm_unpacklo_epi16(mul_lo, mul_hi); - accum2 = _mm_add_epi32(accum2, t); - } +void ConvolveHorizontally(const unsigned char* src_data, + const ConvolutionFilter1D& filter, + unsigned char* out_row, + bool has_alpha, bool use_sse2) { + int width = filter.num_values(); + int processed = 0; +#if defined(USE_SSE2) + int simd_width = width & ~3; + if (use_sse2 && simd_width) { + // SIMD implementation works with 4 pixels at a time. + // Therefore we process as much as we can using SSE and then use + // C implementation for leftovers + ConvolveHorizontally_SSE2(src_data, 0, simd_width, filter, out_row); + processed = simd_width; + } +#endif - accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits); - accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits); - accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits); - // [16] a1 b1 g1 r1 a0 b0 g0 r0 - accum0 = _mm_packs_epi32(accum0, accum1); - // [16] a3 b3 g3 r3 a2 b2 g2 r2 - accum2 = _mm_packs_epi32(accum2, zero); - // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0 - accum0 = _mm_packus_epi16(accum0, accum2); + if (width > processed) { if (has_alpha) { - // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0 - __m128i a = _mm_srli_epi32(accum0, 8); - // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0 - __m128i b = _mm_max_epu8(a, accum0); // Max of r and g. - // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0 - a = _mm_srli_epi32(accum0, 16); - // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0 - b = _mm_max_epu8(a, b); // Max of r and g and b. - // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00 - b = _mm_slli_epi32(b, 24); - accum0 = _mm_max_epu8(b, accum0); + ConvolveHorizontally<true>(src_data, processed, width, filter, out_row); } else { - __m128i mask = _mm_set1_epi32(0xff000000); - accum0 = _mm_or_si128(accum0, mask); - } - - for (int out_x = width; out_x < pixel_width; out_x++) { - *(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum0); - accum0 = _mm_srli_si128(accum0, 4); - out_row += 4; + ConvolveHorizontally<false>(src_data, processed, width, filter, out_row); } } -#endif } -} // namespace - // ConvolutionFilter1D --------------------------------------------------------- ConvolutionFilter1D::ConvolutionFilter1D() @@ -765,14 +393,16 @@ void BGRAConvolve2D(const unsigned char* source_data, const ConvolutionFilter1D& filter_x, const ConvolutionFilter1D& filter_y, int output_byte_row_stride, - unsigned char* output, - bool use_sse2) { -#if !defined(SIMD_SSE2) + unsigned char* output) { + bool use_sse2 = Factory::HasSSE2(); + +#if !defined(USE_SSE2) // Even we have runtime support for SSE2 instructions, since the binary // was not built with SSE2 support, we had to fallback to C version. use_sse2 = false; #endif + int max_y_filter_size = filter_y.max_filter(); // The next row in the input that we will generate a horizontally @@ -805,6 +435,7 @@ void BGRAConvolve2D(const unsigned char* source_data, // convolutions to run each subsequent vertical convolution. SkASSERT(output_byte_row_stride >= filter_x.num_values() * 4); int num_output_rows = filter_y.num_values(); + int pixel_width = filter_x.num_values(); // We need to check which is the last line to convolve before we advance 4 // lines in one iteration. @@ -818,47 +449,56 @@ void BGRAConvolve2D(const unsigned char* source_data, // Generate output rows until we have enough to run the current filter. if (use_sse2) { +#if defined(USE_SSE2) + // We don't want to process too much rows in batches of 4 because + // we can go out-of-bounds at the end while (next_x_row < filter_offset + filter_length) { - if (next_x_row + 3 < last_filter_offset + last_filter_length - 1) { + if (next_x_row + 3 < last_filter_offset + last_filter_length - 3) { const unsigned char* src[4]; unsigned char* out_row[4]; for (int i = 0; i < 4; ++i) { src[i] = &source_data[(next_x_row + i) * source_byte_row_stride]; out_row[i] = row_buffer.AdvanceRow(); } - ConvolveHorizontally4_SSE2(src, filter_x, out_row); + ConvolveHorizontally4_SSE2(src, 0, pixel_width, filter_x, out_row); next_x_row += 4; } else { - // For the last row, SSE2 load possibly to access data beyond the - // image area. therefore we use C version here. - if (next_x_row == last_filter_offset + last_filter_length - 1) { + unsigned char* buffer = row_buffer.AdvanceRow(); + + // For last rows, SSE2 load possibly to access data beyond the + // image area. therefore we use cobined C+SSE version here + int simd_width = pixel_width & ~3; + if (simd_width) { + ConvolveHorizontally_SSE2( + &source_data[next_x_row * source_byte_row_stride], + 0, simd_width, filter_x, buffer); + } + + if (pixel_width > simd_width) { if (source_has_alpha) { ConvolveHorizontally<true>( &source_data[next_x_row * source_byte_row_stride], - filter_x, row_buffer.AdvanceRow()); + simd_width, pixel_width, filter_x, buffer); } else { ConvolveHorizontally<false>( &source_data[next_x_row * source_byte_row_stride], - filter_x, row_buffer.AdvanceRow()); + simd_width, pixel_width, filter_x, buffer); } - } else { - ConvolveHorizontally_SSE2( - &source_data[next_x_row * source_byte_row_stride], - filter_x, row_buffer.AdvanceRow()); } next_x_row++; } } +#endif } else { while (next_x_row < filter_offset + filter_length) { if (source_has_alpha) { ConvolveHorizontally<true>( &source_data[next_x_row * source_byte_row_stride], - filter_x, row_buffer.AdvanceRow()); + 0, pixel_width, filter_x, row_buffer.AdvanceRow()); } else { ConvolveHorizontally<false>( &source_data[next_x_row * source_byte_row_stride], - filter_x, row_buffer.AdvanceRow()); + 0, pixel_width, filter_x, row_buffer.AdvanceRow()); } next_x_row++; } @@ -877,27 +517,9 @@ void BGRAConvolve2D(const unsigned char* source_data, unsigned char* const* first_row_for_filter = &rows_to_convolve[filter_offset - first_row_in_circular_buffer]; - if (source_has_alpha) { - if (use_sse2) { - ConvolveVertically_SSE2<true>(filter_values, filter_length, - first_row_for_filter, - filter_x.num_values(), cur_output_row); - } else { - ConvolveVertically<true>(filter_values, filter_length, - first_row_for_filter, - filter_x.num_values(), cur_output_row); - } - } else { - if (use_sse2) { - ConvolveVertically_SSE2<false>(filter_values, filter_length, - first_row_for_filter, - filter_x.num_values(), cur_output_row); - } else { - ConvolveVertically<false>(filter_values, filter_length, - first_row_for_filter, - filter_x.num_values(), cur_output_row); - } - } + ConvolveVertically(filter_values, filter_length, + first_row_for_filter, pixel_width, + cur_output_row, source_has_alpha, use_sse2); } } diff --git a/gfx/2d/convolver.h b/gfx/2d/convolver.h index d7e83789e..6ef882f1c 100644 --- a/gfx/2d/convolver.h +++ b/gfx/2d/convolver.h @@ -74,8 +74,8 @@ class ConvolutionFilter1D { // The cast relies on Fixed being a short, implying that on // the platforms we care about all (16) bits will fit into // the mantissa of a (32-bit) float. - MOZ_STATIC_ASSERT(sizeof(Fixed) == 2, - "fixed type should fit in float mantissa"); + static_assert(sizeof(Fixed) == 2, + "fixed type should fit in float mantissa"); float raw = static_cast<float>(x); return ldexpf(raw, -kShiftBits); } @@ -184,8 +184,19 @@ void BGRAConvolve2D(const unsigned char* source_data, const ConvolutionFilter1D& xfilter, const ConvolutionFilter1D& yfilter, int output_byte_row_stride, - unsigned char* output, - bool use_sse2); + unsigned char* output); + +void ConvolveHorizontally(const unsigned char* src_data, + const ConvolutionFilter1D& filter, + unsigned char* out_row, + bool has_alpha, bool use_sse2); + +void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values, + int filter_length, + unsigned char* const* source_data_rows, + int pixel_width, unsigned char* out_row, + bool has_alpha, bool use_sse2); + } // namespace skia #endif // SKIA_EXT_CONVOLVER_H_ diff --git a/gfx/2d/convolverSSE2.cpp b/gfx/2d/convolverSSE2.cpp new file mode 100644 index 000000000..4a177f1de --- /dev/null +++ b/gfx/2d/convolverSSE2.cpp @@ -0,0 +1,476 @@ +// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google, Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#include "convolver.h" +#include <algorithm> +#include "skia/SkTypes.h" + +#include <emmintrin.h> // ARCH_CPU_X86_FAMILY was defined in build/config.h + +namespace skia { + +// Convolves horizontally along a single row. The row data is given in +// |src_data| and continues for the [begin, end) of the filter. +void ConvolveHorizontally_SSE2(const unsigned char* src_data, + int begin, int end, + const ConvolutionFilter1D& filter, + unsigned char* out_row) { + + int filter_offset, filter_length; + __m128i zero = _mm_setzero_si128(); + __m128i mask[3]; + // |mask| will be used to decimate all extra filter coefficients that are + // loaded by SIMD when |filter_length| is not divisible by 4. + mask[0] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1); + mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1); + mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1); + + // This buffer is used for tails + __m128i buffer; + + // Output one pixel each iteration, calculating all channels (RGBA) together. + for (int out_x = begin; out_x < end; out_x++) { + const ConvolutionFilter1D::Fixed* filter_values = + filter.FilterForValue(out_x, &filter_offset, &filter_length); + + __m128i accum = _mm_setzero_si128(); + + // Compute the first pixel in this row that the filter affects. It will + // touch |filter_length| pixels (4 bytes each) after this. + const __m128i* row_to_filter = + reinterpret_cast<const __m128i*>(&src_data[filter_offset << 2]); + + // We will load and accumulate with four coefficients per iteration. + for (int filter_x = 0; filter_x < filter_length >> 2; filter_x++) { + + // Load 4 coefficients => duplicate 1st and 2nd of them for all channels. + __m128i coeff, coeff16; + // [16] xx xx xx xx c3 c2 c1 c0 + coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values)); + // [16] xx xx xx xx c1 c1 c0 c0 + coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0)); + // [16] c1 c1 c1 c1 c0 c0 c0 c0 + coeff16 = _mm_unpacklo_epi16(coeff16, coeff16); + + // Load four pixels => unpack the first two pixels to 16 bits => + // multiply with coefficients => accumulate the convolution result. + // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0 + __m128i src8 = _mm_loadu_si128(row_to_filter); + // [16] a1 b1 g1 r1 a0 b0 g0 r0 + __m128i src16 = _mm_unpacklo_epi8(src8, zero); + __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16); + __m128i mul_lo = _mm_mullo_epi16(src16, coeff16); + // [32] a0*c0 b0*c0 g0*c0 r0*c0 + __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi); + accum = _mm_add_epi32(accum, t); + // [32] a1*c1 b1*c1 g1*c1 r1*c1 + t = _mm_unpackhi_epi16(mul_lo, mul_hi); + accum = _mm_add_epi32(accum, t); + + // Duplicate 3rd and 4th coefficients for all channels => + // unpack the 3rd and 4th pixels to 16 bits => multiply with coefficients + // => accumulate the convolution results. + // [16] xx xx xx xx c3 c3 c2 c2 + coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2)); + // [16] c3 c3 c3 c3 c2 c2 c2 c2 + coeff16 = _mm_unpacklo_epi16(coeff16, coeff16); + // [16] a3 g3 b3 r3 a2 g2 b2 r2 + src16 = _mm_unpackhi_epi8(src8, zero); + mul_hi = _mm_mulhi_epi16(src16, coeff16); + mul_lo = _mm_mullo_epi16(src16, coeff16); + // [32] a2*c2 b2*c2 g2*c2 r2*c2 + t = _mm_unpacklo_epi16(mul_lo, mul_hi); + accum = _mm_add_epi32(accum, t); + // [32] a3*c3 b3*c3 g3*c3 r3*c3 + t = _mm_unpackhi_epi16(mul_lo, mul_hi); + accum = _mm_add_epi32(accum, t); + + // Advance the pixel and coefficients pointers. + row_to_filter += 1; + filter_values += 4; + } + + // When |filter_length| is not divisible by 4, we need to decimate some of + // the filter coefficient that was loaded incorrectly to zero; Other than + // that the algorithm is same with above, except that the 4th pixel will be + // always absent. + int r = filter_length & 3; + if (r) { + memcpy(&buffer, row_to_filter, r * 4); + // Note: filter_values must be padded to align_up(filter_offset, 8). + __m128i coeff, coeff16; + coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values)); + // Mask out extra filter taps. + coeff = _mm_and_si128(coeff, mask[r-1]); + coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0)); + coeff16 = _mm_unpacklo_epi16(coeff16, coeff16); + + // Note: line buffer must be padded to align_up(filter_offset, 16). + // We resolve this by temporary buffer + __m128i src8 = _mm_loadu_si128(&buffer); + __m128i src16 = _mm_unpacklo_epi8(src8, zero); + __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16); + __m128i mul_lo = _mm_mullo_epi16(src16, coeff16); + __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi); + accum = _mm_add_epi32(accum, t); + t = _mm_unpackhi_epi16(mul_lo, mul_hi); + accum = _mm_add_epi32(accum, t); + + src16 = _mm_unpackhi_epi8(src8, zero); + coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2)); + coeff16 = _mm_unpacklo_epi16(coeff16, coeff16); + mul_hi = _mm_mulhi_epi16(src16, coeff16); + mul_lo = _mm_mullo_epi16(src16, coeff16); + t = _mm_unpacklo_epi16(mul_lo, mul_hi); + accum = _mm_add_epi32(accum, t); + } + + // Shift right for fixed point implementation. + accum = _mm_srai_epi32(accum, ConvolutionFilter1D::kShiftBits); + + // Packing 32 bits |accum| to 16 bits per channel (signed saturation). + accum = _mm_packs_epi32(accum, zero); + // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation). + accum = _mm_packus_epi16(accum, zero); + + // Store the pixel value of 32 bits. + *(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum); + out_row += 4; + } +} + +// Convolves horizontally along four rows. The row data is given in +// |src_data| and continues for the [begin, end) of the filter. +// The algorithm is almost same as |ConvolveHorizontally_SSE2|. Please +// refer to that function for detailed comments. +void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4], + int begin, int end, + const ConvolutionFilter1D& filter, + unsigned char* out_row[4]) { + int filter_offset, filter_length; + __m128i zero = _mm_setzero_si128(); + __m128i mask[3]; + // |mask| will be used to decimate all extra filter coefficients that are + // loaded by SIMD when |filter_length| is not divisible by 4. + mask[0] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1); + mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1); + mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1); + + // Output one pixel each iteration, calculating all channels (RGBA) together. + for (int out_x = begin; out_x < end; out_x++) { + const ConvolutionFilter1D::Fixed* filter_values = + filter.FilterForValue(out_x, &filter_offset, &filter_length); + + // four pixels in a column per iteration. + __m128i accum0 = _mm_setzero_si128(); + __m128i accum1 = _mm_setzero_si128(); + __m128i accum2 = _mm_setzero_si128(); + __m128i accum3 = _mm_setzero_si128(); + int start = (filter_offset<<2); + // We will load and accumulate with four coefficients per iteration. + for (int filter_x = 0; filter_x < (filter_length >> 2); filter_x++) { + __m128i coeff, coeff16lo, coeff16hi; + // [16] xx xx xx xx c3 c2 c1 c0 + coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values)); + // [16] xx xx xx xx c1 c1 c0 c0 + coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0)); + // [16] c1 c1 c1 c1 c0 c0 c0 c0 + coeff16lo = _mm_unpacklo_epi16(coeff16lo, coeff16lo); + // [16] xx xx xx xx c3 c3 c2 c2 + coeff16hi = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2)); + // [16] c3 c3 c3 c3 c2 c2 c2 c2 + coeff16hi = _mm_unpacklo_epi16(coeff16hi, coeff16hi); + + __m128i src8, src16, mul_hi, mul_lo, t; + +#define ITERATION(src, accum) \ + src8 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src)); \ + src16 = _mm_unpacklo_epi8(src8, zero); \ + mul_hi = _mm_mulhi_epi16(src16, coeff16lo); \ + mul_lo = _mm_mullo_epi16(src16, coeff16lo); \ + t = _mm_unpacklo_epi16(mul_lo, mul_hi); \ + accum = _mm_add_epi32(accum, t); \ + t = _mm_unpackhi_epi16(mul_lo, mul_hi); \ + accum = _mm_add_epi32(accum, t); \ + src16 = _mm_unpackhi_epi8(src8, zero); \ + mul_hi = _mm_mulhi_epi16(src16, coeff16hi); \ + mul_lo = _mm_mullo_epi16(src16, coeff16hi); \ + t = _mm_unpacklo_epi16(mul_lo, mul_hi); \ + accum = _mm_add_epi32(accum, t); \ + t = _mm_unpackhi_epi16(mul_lo, mul_hi); \ + accum = _mm_add_epi32(accum, t) + + ITERATION(src_data[0] + start, accum0); + ITERATION(src_data[1] + start, accum1); + ITERATION(src_data[2] + start, accum2); + ITERATION(src_data[3] + start, accum3); + + start += 16; + filter_values += 4; + } + + int r = filter_length & 3; + if (r) { + // Note: filter_values must be padded to align_up(filter_offset, 8); + __m128i coeff; + coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values)); + // Mask out extra filter taps. + coeff = _mm_and_si128(coeff, mask[r-1]); + + __m128i coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0)); + /* c1 c1 c1 c1 c0 c0 c0 c0 */ + coeff16lo = _mm_unpacklo_epi16(coeff16lo, coeff16lo); + __m128i coeff16hi = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2)); + coeff16hi = _mm_unpacklo_epi16(coeff16hi, coeff16hi); + + __m128i src8, src16, mul_hi, mul_lo, t; + + ITERATION(src_data[0] + start, accum0); + ITERATION(src_data[1] + start, accum1); + ITERATION(src_data[2] + start, accum2); + ITERATION(src_data[3] + start, accum3); + } + + accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits); + accum0 = _mm_packs_epi32(accum0, zero); + accum0 = _mm_packus_epi16(accum0, zero); + accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits); + accum1 = _mm_packs_epi32(accum1, zero); + accum1 = _mm_packus_epi16(accum1, zero); + accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits); + accum2 = _mm_packs_epi32(accum2, zero); + accum2 = _mm_packus_epi16(accum2, zero); + accum3 = _mm_srai_epi32(accum3, ConvolutionFilter1D::kShiftBits); + accum3 = _mm_packs_epi32(accum3, zero); + accum3 = _mm_packus_epi16(accum3, zero); + + *(reinterpret_cast<int*>(out_row[0])) = _mm_cvtsi128_si32(accum0); + *(reinterpret_cast<int*>(out_row[1])) = _mm_cvtsi128_si32(accum1); + *(reinterpret_cast<int*>(out_row[2])) = _mm_cvtsi128_si32(accum2); + *(reinterpret_cast<int*>(out_row[3])) = _mm_cvtsi128_si32(accum3); + + out_row[0] += 4; + out_row[1] += 4; + out_row[2] += 4; + out_row[3] += 4; + } +} + +// Does vertical convolution to produce one output row. The filter values and +// length are given in the first two parameters. These are applied to each +// of the rows pointed to in the |source_data_rows| array, with each row +// being |end - begin| wide. +// +// The output must have room for |(end - begin) * 4| bytes. +template<bool has_alpha> +void ConvolveVertically_SSE2_impl(const ConvolutionFilter1D::Fixed* filter_values, + int filter_length, + unsigned char* const* source_data_rows, + int begin, int end, + unsigned char* out_row) { + __m128i zero = _mm_setzero_si128(); + __m128i accum0, accum1, accum2, accum3, coeff16; + const __m128i* src; + int out_x; + // Output four pixels per iteration (16 bytes). + for (out_x = begin; out_x + 3 < end; out_x += 4) { + + // Accumulated result for each pixel. 32 bits per RGBA channel. + accum0 = _mm_setzero_si128(); + accum1 = _mm_setzero_si128(); + accum2 = _mm_setzero_si128(); + accum3 = _mm_setzero_si128(); + + // Convolve with one filter coefficient per iteration. + for (int filter_y = 0; filter_y < filter_length; filter_y++) { + + // Duplicate the filter coefficient 8 times. + // [16] cj cj cj cj cj cj cj cj + coeff16 = _mm_set1_epi16(filter_values[filter_y]); + + // Load four pixels (16 bytes) together. + // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0 + src = reinterpret_cast<const __m128i*>( + &source_data_rows[filter_y][out_x << 2]); + __m128i src8 = _mm_loadu_si128(src); + + // Unpack 1st and 2nd pixels from 8 bits to 16 bits for each channels => + // multiply with current coefficient => accumulate the result. + // [16] a1 b1 g1 r1 a0 b0 g0 r0 + __m128i src16 = _mm_unpacklo_epi8(src8, zero); + __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16); + __m128i mul_lo = _mm_mullo_epi16(src16, coeff16); + // [32] a0 b0 g0 r0 + __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi); + accum0 = _mm_add_epi32(accum0, t); + // [32] a1 b1 g1 r1 + t = _mm_unpackhi_epi16(mul_lo, mul_hi); + accum1 = _mm_add_epi32(accum1, t); + + // Unpack 3rd and 4th pixels from 8 bits to 16 bits for each channels => + // multiply with current coefficient => accumulate the result. + // [16] a3 b3 g3 r3 a2 b2 g2 r2 + src16 = _mm_unpackhi_epi8(src8, zero); + mul_hi = _mm_mulhi_epi16(src16, coeff16); + mul_lo = _mm_mullo_epi16(src16, coeff16); + // [32] a2 b2 g2 r2 + t = _mm_unpacklo_epi16(mul_lo, mul_hi); + accum2 = _mm_add_epi32(accum2, t); + // [32] a3 b3 g3 r3 + t = _mm_unpackhi_epi16(mul_lo, mul_hi); + accum3 = _mm_add_epi32(accum3, t); + } + + // Shift right for fixed point implementation. + accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits); + accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits); + accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits); + accum3 = _mm_srai_epi32(accum3, ConvolutionFilter1D::kShiftBits); + + // Packing 32 bits |accum| to 16 bits per channel (signed saturation). + // [16] a1 b1 g1 r1 a0 b0 g0 r0 + accum0 = _mm_packs_epi32(accum0, accum1); + // [16] a3 b3 g3 r3 a2 b2 g2 r2 + accum2 = _mm_packs_epi32(accum2, accum3); + + // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation). + // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0 + accum0 = _mm_packus_epi16(accum0, accum2); + + if (has_alpha) { + // Compute the max(ri, gi, bi) for each pixel. + // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0 + __m128i a = _mm_srli_epi32(accum0, 8); + // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0 + __m128i b = _mm_max_epu8(a, accum0); // Max of r and g. + // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0 + a = _mm_srli_epi32(accum0, 16); + // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0 + b = _mm_max_epu8(a, b); // Max of r and g and b. + // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00 + b = _mm_slli_epi32(b, 24); + + // Make sure the value of alpha channel is always larger than maximum + // value of color channels. + accum0 = _mm_max_epu8(b, accum0); + } else { + // Set value of alpha channels to 0xFF. + __m128i mask = _mm_set1_epi32(0xff000000); + accum0 = _mm_or_si128(accum0, mask); + } + + // Store the convolution result (16 bytes) and advance the pixel pointers. + _mm_storeu_si128(reinterpret_cast<__m128i*>(out_row), accum0); + out_row += 16; + } + + // When the width of the output is not divisible by 4, We need to save one + // pixel (4 bytes) each time. And also the fourth pixel is always absent. + int r = end - out_x; + if (r > 0) { + // Since accum3 is never used here, we'll use it as a buffer + __m128i *buffer = &accum3; + + accum0 = _mm_setzero_si128(); + accum1 = _mm_setzero_si128(); + accum2 = _mm_setzero_si128(); + for (int filter_y = 0; filter_y < filter_length; ++filter_y) { + coeff16 = _mm_set1_epi16(filter_values[filter_y]); + // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0 + src = reinterpret_cast<const __m128i*>( + &source_data_rows[filter_y][out_x * 4]); + memcpy(buffer, src, r * 4); + __m128i src8 = _mm_loadu_si128(buffer); + // [16] a1 b1 g1 r1 a0 b0 g0 r0 + __m128i src16 = _mm_unpacklo_epi8(src8, zero); + __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16); + __m128i mul_lo = _mm_mullo_epi16(src16, coeff16); + // [32] a0 b0 g0 r0 + __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi); + accum0 = _mm_add_epi32(accum0, t); + // [32] a1 b1 g1 r1 + t = _mm_unpackhi_epi16(mul_lo, mul_hi); + accum1 = _mm_add_epi32(accum1, t); + // [16] a3 b3 g3 r3 a2 b2 g2 r2 + src16 = _mm_unpackhi_epi8(src8, zero); + mul_hi = _mm_mulhi_epi16(src16, coeff16); + mul_lo = _mm_mullo_epi16(src16, coeff16); + // [32] a2 b2 g2 r2 + t = _mm_unpacklo_epi16(mul_lo, mul_hi); + accum2 = _mm_add_epi32(accum2, t); + } + + accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits); + accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits); + accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits); + // [16] a1 b1 g1 r1 a0 b0 g0 r0 + accum0 = _mm_packs_epi32(accum0, accum1); + // [16] a3 b3 g3 r3 a2 b2 g2 r2 + accum2 = _mm_packs_epi32(accum2, zero); + // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0 + accum0 = _mm_packus_epi16(accum0, accum2); + if (has_alpha) { + // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0 + __m128i a = _mm_srli_epi32(accum0, 8); + // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0 + __m128i b = _mm_max_epu8(a, accum0); // Max of r and g. + // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0 + a = _mm_srli_epi32(accum0, 16); + // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0 + b = _mm_max_epu8(a, b); // Max of r and g and b. + // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00 + b = _mm_slli_epi32(b, 24); + accum0 = _mm_max_epu8(b, accum0); + } else { + __m128i mask = _mm_set1_epi32(0xff000000); + accum0 = _mm_or_si128(accum0, mask); + } + + for (; out_x < end; out_x++) { + *(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum0); + accum0 = _mm_srli_si128(accum0, 4); + out_row += 4; + } + } +} + +void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values, + int filter_length, + unsigned char* const* source_data_rows, + int begin, int end, + unsigned char* out_row, bool has_alpha) { + if (has_alpha) { + ConvolveVertically_SSE2_impl<true>(filter_values, filter_length, + source_data_rows, begin, end, out_row); + } else { + ConvolveVertically_SSE2_impl<false>(filter_values, filter_length, + source_data_rows, begin, end, out_row); + } +} + +} // namespace skia diff --git a/gfx/2d/convolverSSE2.h b/gfx/2d/convolverSSE2.h new file mode 100644 index 000000000..d1ba5d698 --- /dev/null +++ b/gfx/2d/convolverSSE2.h @@ -0,0 +1,70 @@ +// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google, Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#ifndef SKIA_EXT_CONVOLVER_SSE_H_ +#define SKIA_EXT_CONVOLVER_SSE_H_ + +#include "convolver.h" + +#include <algorithm> + +#include "skia/SkTypes.h" + +namespace skia { + +// Convolves horizontally along a single row. The row data is given in +// |src_data| and continues for the [begin, end) of the filter. +void ConvolveHorizontally_SSE2(const unsigned char* src_data, + int begin, int end, + const ConvolutionFilter1D& filter, + unsigned char* out_row); + +// Convolves horizontally along four rows. The row data is given in +// |src_data| and continues for the [begin, end) of the filter. +// The algorithm is almost same as |ConvolveHorizontally_SSE2|. Please +// refer to that function for detailed comments. +void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4], + int begin, int end, + const ConvolutionFilter1D& filter, + unsigned char* out_row[4]); + +// Does vertical convolution to produce one output row. The filter values and +// length are given in the first two parameters. These are applied to each +// of the rows pointed to in the |source_data_rows| array, with each row +// being |pixel_width| wide. +// +// The output must have room for |pixel_width * 4| bytes. +void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values, + int filter_length, + unsigned char* const* source_data_rows, + int begin, int end, + unsigned char* out_row, bool has_alpha); + +} // namespace skia + +#endif // SKIA_EXT_CONVOLVER_SSE_H_ diff --git a/gfx/2d/genshaders.sh b/gfx/2d/genshaders.sh index 5c399b822..74b1a3d7b 100755 --- a/gfx/2d/genshaders.sh +++ b/gfx/2d/genshaders.sh @@ -3,8 +3,8 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. fxc ShadersD2D.fx -nologo -FhShadersD2D.h -Tfx_4_0 -Vn d2deffect -fxc ShadersD2D1.hlsl -ESampleRadialGradientPS -nologo -Tps_4_0_level_9_3 -Fhtmpfile -VnSampleRadialGradientPS
-cat tmpfile > ShadersD2D1.h
-fxc ShadersD2D1.hlsl -ESampleRadialGradientA0PS -nologo -Tps_4_0_level_9_3 -Fhtmpfile -VnSampleRadialGradientA0PS
-cat tmpfile >> ShadersD2D1.h
-rm tmpfile
+fxc ShadersD2D1.hlsl -ESampleRadialGradientPS -nologo -Tps_4_0_level_9_3 -Fhtmpfile -VnSampleRadialGradientPS +cat tmpfile > ShadersD2D1.h +fxc ShadersD2D1.hlsl -ESampleRadialGradientA0PS -nologo -Tps_4_0_level_9_3 -Fhtmpfile -VnSampleRadialGradientA0PS +cat tmpfile >> ShadersD2D1.h +rm tmpfile diff --git a/gfx/2d/gfx2d.vcxproj b/gfx/2d/gfx2d.vcxproj index dfd0a1f1b..746856a10 100644 --- a/gfx/2d/gfx2d.vcxproj +++ b/gfx/2d/gfx2d.vcxproj @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
@@ -42,7 +42,7 @@ </PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
- <PreprocessorDefinitions>INITGUID;USE_SSE2;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);GFX_LOG_DEBUG;GFX_LOG_WARNING;MFBT_STAND_ALONE;XP_WIN</PreprocessorDefinitions>
+ <PreprocessorDefinitions>USE_SSE2;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);GFX_LOG_DEBUG;GFX_LOG_WARNING;MFBT_STAND_ALONE;XP_WIN</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
@@ -62,7 +62,7 @@ </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
- <PreprocessorDefinitions>INITGUID;USE_SSE2;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>USE_SSE2;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);GFX_LOG_WARNING</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
@@ -92,6 +92,7 @@ <ClInclude Include="Logging.h" />
<ClInclude Include="Matrix.h" />
<ClInclude Include="PathD2D.h" />
+ <ClInclude Include="PathHelpers.h" />
<ClInclude Include="PathRecording.h" />
<ClInclude Include="Point.h" />
<ClInclude Include="RecordedEvent.h" />
@@ -135,4 +136,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project>
\ No newline at end of file +</Project>
diff --git a/gfx/2d/image_operations.cpp b/gfx/2d/image_operations.cpp index 532be2d98..59861bb9b 100644 --- a/gfx/2d/image_operations.cpp +++ b/gfx/2d/image_operations.cpp @@ -35,7 +35,6 @@ #include "image_operations.h" -#include "nsAlgorithm.h" #include "base/stack_container.h" #include "convolver.h" #include "skia/SkColorPriv.h" @@ -45,184 +44,7 @@ namespace skia { -namespace { - -// Returns the ceiling/floor as an integer. -inline int CeilInt(float val) { - return static_cast<int>(ceil(val)); -} -inline int FloorInt(float val) { - return static_cast<int>(floor(val)); -} - -// Filter function computation ------------------------------------------------- - -// Evaluates the box filter, which goes from -0.5 to +0.5. -float EvalBox(float x) { - return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f; -} - -// Evaluates the Lanczos filter of the given filter size window for the given -// position. -// -// |filter_size| is the width of the filter (the "window"), outside of which -// the value of the function is 0. Inside of the window, the value is the -// normalized sinc function: -// lanczos(x) = sinc(x) * sinc(x / filter_size); -// where -// sinc(x) = sin(pi*x) / (pi*x); -float EvalLanczos(int filter_size, float x) { - if (x <= -filter_size || x >= filter_size) - return 0.0f; // Outside of the window. - if (x > -std::numeric_limits<float>::epsilon() && - x < std::numeric_limits<float>::epsilon()) - return 1.0f; // Special case the discontinuity at the origin. - float xpi = x * static_cast<float>(M_PI); - return (sin(xpi) / xpi) * // sinc(x) - sin(xpi / filter_size) / (xpi / filter_size); // sinc(x/filter_size) -} - -// Evaluates the Hamming filter of the given filter size window for the given -// position. -// -// The filter covers [-filter_size, +filter_size]. Outside of this window -// the value of the function is 0. Inside of the window, the value is sinus -// cardinal multiplied by a recentered Hamming function. The traditional -// Hamming formula for a window of size N and n ranging in [0, N-1] is: -// hamming(n) = 0.54 - 0.46 * cos(2 * pi * n / (N-1))) -// In our case we want the function centered for x == 0 and at its minimum -// on both ends of the window (x == +/- filter_size), hence the adjusted -// formula: -// hamming(x) = (0.54 - -// 0.46 * cos(2 * pi * (x - filter_size)/ (2 * filter_size))) -// = 0.54 - 0.46 * cos(pi * x / filter_size - pi) -// = 0.54 + 0.46 * cos(pi * x / filter_size) -float EvalHamming(int filter_size, float x) { - if (x <= -filter_size || x >= filter_size) - return 0.0f; // Outside of the window. - if (x > -std::numeric_limits<float>::epsilon() && - x < std::numeric_limits<float>::epsilon()) - return 1.0f; // Special case the sinc discontinuity at the origin. - const float xpi = x * static_cast<float>(M_PI); - - return ((sin(xpi) / xpi) * // sinc(x) - (0.54f + 0.46f * cos(xpi / filter_size))); // hamming(x) -} - -// ResizeFilter ---------------------------------------------------------------- - -// Encapsulates computation and storage of the filters required for one complete -// resize operation. -class ResizeFilter { - public: - ResizeFilter(ImageOperations::ResizeMethod method, - int src_full_width, int src_full_height, - int dest_width, int dest_height, - const SkIRect& dest_subset); - - // Returns the filled filter values. - const ConvolutionFilter1D& x_filter() { return x_filter_; } - const ConvolutionFilter1D& y_filter() { return y_filter_; } - - private: - // Returns the number of pixels that the filer spans, in filter space (the - // destination image). - float GetFilterSupport(float scale) { - switch (method_) { - case ImageOperations::RESIZE_BOX: - // The box filter just scales with the image scaling. - return 0.5f; // Only want one side of the filter = /2. - case ImageOperations::RESIZE_HAMMING1: - // The Hamming filter takes as much space in the source image in - // each direction as the size of the window = 1 for Hamming1. - return 1.0f; - case ImageOperations::RESIZE_LANCZOS2: - // The Lanczos filter takes as much space in the source image in - // each direction as the size of the window = 2 for Lanczos2. - return 2.0f; - case ImageOperations::RESIZE_LANCZOS3: - // The Lanczos filter takes as much space in the source image in - // each direction as the size of the window = 3 for Lanczos3. - return 3.0f; - default: - return 1.0f; - } - } - - // Computes one set of filters either horizontally or vertically. The caller - // will specify the "min" and "max" rather than the bottom/top and - // right/bottom so that the same code can be re-used in each dimension. - // - // |src_depend_lo| and |src_depend_size| gives the range for the source - // depend rectangle (horizontally or vertically at the caller's discretion - // -- see above for what this means). - // - // Likewise, the range of destination values to compute and the scale factor - // for the transform is also specified. - void ComputeFilters(int src_size, - int dest_subset_lo, int dest_subset_size, - float scale, float src_support, - ConvolutionFilter1D* output); - - // Computes the filter value given the coordinate in filter space. - inline float ComputeFilter(float pos) { - switch (method_) { - case ImageOperations::RESIZE_BOX: - return EvalBox(pos); - case ImageOperations::RESIZE_HAMMING1: - return EvalHamming(1, pos); - case ImageOperations::RESIZE_LANCZOS2: - return EvalLanczos(2, pos); - case ImageOperations::RESIZE_LANCZOS3: - return EvalLanczos(3, pos); - default: - return 0; - } - } - - ImageOperations::ResizeMethod method_; - - // Size of the filter support on one side only in the destination space. - // See GetFilterSupport. - float x_filter_support_; - float y_filter_support_; - - // Subset of scaled destination bitmap to compute. - SkIRect out_bounds_; - - ConvolutionFilter1D x_filter_; - ConvolutionFilter1D y_filter_; - - DISALLOW_COPY_AND_ASSIGN(ResizeFilter); -}; - -ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method, - int src_full_width, int src_full_height, - int dest_width, int dest_height, - const SkIRect& dest_subset) - : method_(method), - out_bounds_(dest_subset) { - // method_ will only ever refer to an "algorithm method". - SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) && - (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD)); - - float scale_x = static_cast<float>(dest_width) / - static_cast<float>(src_full_width); - float scale_y = static_cast<float>(dest_height) / - static_cast<float>(src_full_height); - - x_filter_support_ = GetFilterSupport(scale_x); - y_filter_support_ = GetFilterSupport(scale_y); - - // Support of the filter in source space. - float src_x_support = x_filter_support_ / scale_x; - float src_y_support = y_filter_support_ / scale_y; - - ComputeFilters(src_full_width, dest_subset.fLeft, dest_subset.width(), - scale_x, src_x_support, &x_filter_); - ComputeFilters(src_full_height, dest_subset.fTop, dest_subset.height(), - scale_y, src_y_support, &y_filter_); -} +namespace resize { // TODO(egouriou): Take advantage of periods in the convolution. // Practical resizing filters are periodic outside of the border area. @@ -235,10 +57,16 @@ ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method, // Small periods reduce computational load and improve cache usage if // the coefficients can be shared. For periods of 1 we can consider // loading the factors only once outside the borders. -void ResizeFilter::ComputeFilters(int src_size, - int dest_subset_lo, int dest_subset_size, - float scale, float src_support, - ConvolutionFilter1D* output) { +void ComputeFilters(ImageOperations::ResizeMethod method, + int src_size, int dst_size, + int dest_subset_lo, int dest_subset_size, + ConvolutionFilter1D* output) { + // method_ will only ever refer to an "algorithm method". + SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) && + (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD)); + + float scale = static_cast<float>(dst_size) / static_cast<float>(src_size); + int dest_subset_hi = dest_subset_lo + dest_subset_size; // [lo, hi) // When we're doing a magnification, the scale will be larger than one. This @@ -248,6 +76,8 @@ void ResizeFilter::ComputeFilters(int src_size, // some computations. float clamped_scale = std::min(1.0f, scale); + float src_support = GetFilterSupport(method, clamped_scale) / clamped_scale; + // Speed up the divisions below by turning them into multiplies. float inv_scale = 1.0f / scale; @@ -294,7 +124,7 @@ void ResizeFilter::ComputeFilters(int src_size, float dest_filter_dist = src_filter_dist * clamped_scale; // Compute the filter value at that location. - float filter_value = ComputeFilter(dest_filter_dist); + float filter_value = ComputeFilter(method, dest_filter_dist); filter_values->push_back(filter_value); filter_sum += filter_value; @@ -325,6 +155,8 @@ void ResizeFilter::ComputeFilters(int src_size, output->PaddingForSIMD(8); } +} + ImageOperations::ResizeMethod ResizeMethodToAlgorithmMethod( ImageOperations::ResizeMethod method) { // Convert any "Quality Method" into an "Algorithm Method" @@ -336,22 +168,24 @@ ImageOperations::ResizeMethod ResizeMethodToAlgorithmMethod( // GPU-acceleration in the cases where it is possible. So now we just // pick the appropriate software method for each resize quality. switch (method) { + // Users of RESIZE_GOOD are willing to trade a lot of quality to + // get speed, allowing the use of linear resampling to get hardware + // acceleration (SRB). Hence any of our "good" software filters + // will be acceptable, and we use the fastest one, Hamming-1. case ImageOperations::RESIZE_GOOD: - // In visual tests we see that Hamming-1 is not as good as + // Users of RESIZE_BETTER are willing to trade some quality in order + // to improve performance, but are guaranteed not to devolve to a linear + // resampling. In visual tests we see that Hamming-1 is not as good as // Lanczos-2, however it is about 40% faster and Lanczos-2 itself is // about 30% faster than Lanczos-3. The use of Hamming-1 has been deemed - // an unacceptable trade-off between quality and speed due to the limited - // pixel space it operates in before switching to HQ scaling becomes - // necessary to retain fidelity of images. + // an acceptable trade-off between quality and speed. case ImageOperations::RESIZE_BETTER: - return ImageOperations::RESIZE_LANCZOS2; + return ImageOperations::RESIZE_HAMMING1; default: return ImageOperations::RESIZE_LANCZOS3; } } -} // namespace - // Resize ---------------------------------------------------------------------- // static @@ -405,9 +239,13 @@ SkBitmap ImageOperations::ResizeSubpixel(const SkBitmap& source, // Render into subpixels. SkBitmap result; - result.setConfig(SkBitmap::kARGB_8888_Config, dest_subset.width(), - dest_subset.height()); - result.allocPixels(); + SkImageInfo info = SkImageInfo::Make(dest_subset.width(), + dest_subset.height(), + kBGRA_8888_SkColorType, + kPremul_SkAlphaType); + + + result.allocPixels(info); if (!result.readyToDraw()) return img; @@ -467,7 +305,7 @@ SkBitmap ImageOperations::ResizeSubpixel(const SkBitmap& source, src_row += h * row_words; dst_row += result.rowBytes() / 4; } - result.setIsOpaque(img.isOpaque()); + result.setAlphaType(img.alphaType()); return result; #else return SkBitmap(); @@ -501,8 +339,11 @@ SkBitmap ImageOperations::ResizeBasic(const SkBitmap& source, if (!source.readyToDraw()) return SkBitmap(); - ResizeFilter filter(method, source.width(), source.height(), - dest_width, dest_height, dest_subset); + ConvolutionFilter1D x_filter; + ConvolutionFilter1D y_filter; + + resize::ComputeFilters(method, source.width(), dest_width, dest_subset.fLeft, dest_subset.width(), &x_filter); + resize::ComputeFilters(method, source.height(), dest_height, dest_subset.fTop, dest_subset.height(), &y_filter); // Get a source bitmap encompassing this touched area. We construct the // offsets and row strides such that it looks like a new bitmap, while @@ -512,26 +353,27 @@ SkBitmap ImageOperations::ResizeBasic(const SkBitmap& source, // Convolve into the result. SkBitmap result; - result.setConfig(SkBitmap::kARGB_8888_Config, - dest_subset.width(), dest_subset.height()); + SkImageInfo info = SkImageInfo::Make(dest_subset.width(), + dest_subset.height(), + kBGRA_8888_SkColorType, + kPremul_SkAlphaType); if (dest_pixels) { - result.setPixels(dest_pixels); + result.installPixels(info, dest_pixels, info.minRowBytes()); } else { - result.allocPixels(); + result.allocPixels(info); } if (!result.readyToDraw()) return SkBitmap(); BGRAConvolve2D(source_subset, static_cast<int>(source.rowBytes()), - !source.isOpaque(), filter.x_filter(), filter.y_filter(), + !source.isOpaque(), x_filter, y_filter, static_cast<int>(result.rowBytes()), - static_cast<unsigned char*>(result.getPixels()), - /* sse = */ false); + static_cast<unsigned char*>(result.getPixels())); // Preserve the "opaque" flag for use as an optimization later. - result.setIsOpaque(source.isOpaque()); + result.setAlphaType(source.alphaType()); return result; } diff --git a/gfx/2d/image_operations.h b/gfx/2d/image_operations.h index 4713133d6..b2a792fde 100644 --- a/gfx/2d/image_operations.h +++ b/gfx/2d/image_operations.h @@ -31,6 +31,8 @@ #include "skia/SkTypes.h" #include "Types.h" +#include "convolver.h" +#include "skia/SkRect.h" class SkBitmap; struct SkIRect; @@ -152,6 +154,132 @@ class ImageOperations { const SkIRect& dest_subset); }; +// Returns the ceiling/floor as an integer. +inline int CeilInt(float val) { + return static_cast<int>(ceil(val)); +} +inline int FloorInt(float val) { + return static_cast<int>(floor(val)); +} + +// Filter function computation ------------------------------------------------- + +// Evaluates the box filter, which goes from -0.5 to +0.5. +inline float EvalBox(float x) { + return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f; +} + +// Evaluates the Lanczos filter of the given filter size window for the given +// position. +// +// |filter_size| is the width of the filter (the "window"), outside of which +// the value of the function is 0. Inside of the window, the value is the +// normalized sinc function: +// lanczos(x) = sinc(x) * sinc(x / filter_size); +// where +// sinc(x) = sin(pi*x) / (pi*x); +inline float EvalLanczos(int filter_size, float x) { + if (x <= -filter_size || x >= filter_size) + return 0.0f; // Outside of the window. + if (x > -std::numeric_limits<float>::epsilon() && + x < std::numeric_limits<float>::epsilon()) + return 1.0f; // Special case the discontinuity at the origin. + float xpi = x * static_cast<float>(M_PI); + return (sin(xpi) / xpi) * // sinc(x) + sin(xpi / filter_size) / (xpi / filter_size); // sinc(x/filter_size) +} + +// Evaluates the Hamming filter of the given filter size window for the given +// position. +// +// The filter covers [-filter_size, +filter_size]. Outside of this window +// the value of the function is 0. Inside of the window, the value is sinus +// cardinal multiplied by a recentered Hamming function. The traditional +// Hamming formula for a window of size N and n ranging in [0, N-1] is: +// hamming(n) = 0.54 - 0.46 * cos(2 * pi * n / (N-1))) +// In our case we want the function centered for x == 0 and at its minimum +// on both ends of the window (x == +/- filter_size), hence the adjusted +// formula: +// hamming(x) = (0.54 - +// 0.46 * cos(2 * pi * (x - filter_size)/ (2 * filter_size))) +// = 0.54 - 0.46 * cos(pi * x / filter_size - pi) +// = 0.54 + 0.46 * cos(pi * x / filter_size) +inline float EvalHamming(int filter_size, float x) { + if (x <= -filter_size || x >= filter_size) + return 0.0f; // Outside of the window. + if (x > -std::numeric_limits<float>::epsilon() && + x < std::numeric_limits<float>::epsilon()) + return 1.0f; // Special case the sinc discontinuity at the origin. + const float xpi = x * static_cast<float>(M_PI); + + return ((sin(xpi) / xpi) * // sinc(x) + (0.54f + 0.46f * cos(xpi / filter_size))); // hamming(x) +} + +// ResizeFilter ---------------------------------------------------------------- + +// Encapsulates computation and storage of the filters required for one complete +// resize operation. + +namespace resize { + + // Returns the number of pixels that the filer spans, in filter space (the + // destination image). + inline float GetFilterSupport(ImageOperations::ResizeMethod method, + float scale) { + switch (method) { + case ImageOperations::RESIZE_BOX: + // The box filter just scales with the image scaling. + return 0.5f; // Only want one side of the filter = /2. + case ImageOperations::RESIZE_HAMMING1: + // The Hamming filter takes as much space in the source image in + // each direction as the size of the window = 1 for Hamming1. + return 1.0f; + case ImageOperations::RESIZE_LANCZOS2: + // The Lanczos filter takes as much space in the source image in + // each direction as the size of the window = 2 for Lanczos2. + return 2.0f; + case ImageOperations::RESIZE_LANCZOS3: + // The Lanczos filter takes as much space in the source image in + // each direction as the size of the window = 3 for Lanczos3. + return 3.0f; + default: + return 1.0f; + } + } + + // Computes one set of filters either horizontally or vertically. The caller + // will specify the "min" and "max" rather than the bottom/top and + // right/bottom so that the same code can be re-used in each dimension. + // + // |src_depend_lo| and |src_depend_size| gives the range for the source + // depend rectangle (horizontally or vertically at the caller's discretion + // -- see above for what this means). + // + // Likewise, the range of destination values to compute and the scale factor + // for the transform is also specified. + void ComputeFilters(ImageOperations::ResizeMethod method, + int src_size, int dst_size, + int dest_subset_lo, int dest_subset_size, + ConvolutionFilter1D* output); + + // Computes the filter value given the coordinate in filter space. + inline float ComputeFilter(ImageOperations::ResizeMethod method, float pos) { + switch (method) { + case ImageOperations::RESIZE_BOX: + return EvalBox(pos); + case ImageOperations::RESIZE_HAMMING1: + return EvalHamming(1, pos); + case ImageOperations::RESIZE_LANCZOS2: + return EvalLanczos(2, pos); + case ImageOperations::RESIZE_LANCZOS3: + return EvalLanczos(3, pos); + default: + return 0; + } + } +} + } // namespace skia #endif // SKIA_EXT_IMAGE_OPERATIONS_H_ diff --git a/gfx/2d/moz.build b/gfx/2d/moz.build index 96413cbd7..9ce208ec9 100644 --- a/gfx/2d/moz.build +++ b/gfx/2d/moz.build @@ -4,10 +4,13 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -MODULE = 'gfx2d' +EXPORTS.mozilla += [ + 'GenericRefCounted.h', +] EXPORTS.mozilla.gfx += [ '2D.h', + 'BaseCoord.h', 'BaseMargin.h', 'BasePoint.h', 'BasePoint3D.h', @@ -15,12 +18,21 @@ EXPORTS.mozilla.gfx += [ 'BaseRect.h', 'BaseSize.h', 'Blur.h', + 'BorrowedContext.h', + 'Coord.h', + 'DataSurfaceHelpers.h', + 'DrawTargetTiled.h', + 'Filters.h', + 'Helpers.h', + 'Logging.h', 'Matrix.h', 'PathHelpers.h', + 'PatternHelpers.h', 'Point.h', 'Rect.h', 'Scale.h', 'ScaleFactor.h', + 'SourceSurfaceCairo.h', 'Tools.h', 'Types.h', 'UserData.h', @@ -31,67 +43,130 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': 'MacIOSurface.h', 'QuartzSupport.h', ] - CPP_SOURCES += [ - 'SourceSurfaceCG.cpp', + UNIFIED_SOURCES += [ 'DrawTargetCG.cpp', 'PathCG.cpp', 'ScaledFontMac.cpp', + 'SourceSurfaceCG.cpp', ] elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': - CPP_SOURCES += [ + SOURCES += [ 'DrawTargetD2D.cpp', - 'SourceSurfaceD2D.cpp', - 'SourceSurfaceD2DTarget.cpp', + 'DrawTargetD2D1.cpp', + 'ExtendInputEffectD2D1.cpp', + 'FilterNodeD2D1.cpp', 'PathD2D.cpp', + 'RadialGradientEffectD2D1.cpp', 'ScaledFontDWrite.cpp', + 'ScaledFontWin.cpp', + 'SourceSurfaceD2D.cpp', + 'SourceSurfaceD2D1.cpp', + 'SourceSurfaceD2DTarget.cpp', ] - if CONFIG['MOZ_WINSDK_MAXVER'] >= '0x06020000': - CPP_SOURCES += [ - 'RadialGradientEffectD2D1.cpp' - ] - if CONFIG['MOZ_ENABLE_SKIA']: - CPP_SOURCES += [ - 'ScaledFontWin.cpp', - ] + DEFINES['WIN32'] = True if CONFIG['MOZ_ENABLE_SKIA']: - CPP_SOURCES += [ - 'SourceSurfaceSkia.cpp', + UNIFIED_SOURCES += [ + 'convolver.cpp', 'DrawTargetSkia.cpp', 'PathSkia.cpp', - 'convolver.cpp', - 'image_operations.cpp', + 'SourceSurfaceSkia.cpp', ] - -if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3', 'gonk', 'qt'): - CPP_SOURCES += [ - 'ScaledFontFreetype.cpp', + SOURCES += [ + 'image_operations.cpp', # Uses _USE_MATH_DEFINES + ] + EXPORTS.mozilla.gfx += [ + 'HelpersSkia.h', ] # Are we targeting x86 or x64? If so, build SSE2 files. if CONFIG['INTEL_ARCHITECTURE']: - # VC2005 doesn't support _mm_castsi128_ps, so SSE2 is turned off - if CONFIG['_MSC_VER'] != '1400': - CPP_SOURCES += [ - 'ImageScalingSSE2.cpp', - 'BlurSSE2.cpp', + SOURCES += [ + 'BlurSSE2.cpp', + 'FilterProcessingSSE2.cpp', + 'ImageScalingSSE2.cpp', + ] + if CONFIG['MOZ_ENABLE_SKIA']: + SOURCES += [ + 'convolverSSE2.cpp', ] + DEFINES['USE_SSE2'] = True + # The file uses SSE2 intrinsics, so it needs special compile flags on some + # compilers. + SOURCES['BlurSSE2.cpp'].flags += CONFIG['SSE2_FLAGS'] + SOURCES['FilterProcessingSSE2.cpp'].flags += CONFIG['SSE2_FLAGS'] + SOURCES['ImageScalingSSE2.cpp'].flags += CONFIG['SSE2_FLAGS'] + if CONFIG['MOZ_ENABLE_SKIA']: + SOURCES['convolverSSE2.cpp'].flags += CONFIG['SSE2_FLAGS'] -CPP_SOURCES += [ +UNIFIED_SOURCES += [ 'Blur.cpp', + 'DataSourceSurface.cpp', + 'DataSurfaceHelpers.cpp', 'DrawEventRecorder.cpp', + 'DrawTarget.cpp', 'DrawTargetCairo.cpp', + 'DrawTargetCapture.cpp', 'DrawTargetDual.cpp', 'DrawTargetRecording.cpp', 'Factory.cpp', + 'FilterNodeSoftware.cpp', + 'FilterProcessing.cpp', + 'FilterProcessingScalar.cpp', 'ImageScaling.cpp', 'Matrix.cpp', + 'Path.cpp', 'PathCairo.cpp', + 'PathHelpers.cpp', 'PathRecording.cpp', 'RecordedEvent.cpp', 'Scale.cpp', 'ScaledFontBase.cpp', + 'ScaledFontCairo.cpp', 'SourceSurfaceCairo.cpp', 'SourceSurfaceRawData.cpp', ] +SOURCES += [ + 'DrawTargetTiled.cpp', +] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + SOURCES += [ + 'MacIOSurface.cpp', + 'QuartzSupport.mm', + ] + +if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']: + SOURCES += ['BlurNEON.cpp'] + SOURCES['BlurNEON.cpp'].flags += ['-mfpu=neon'] + +FAIL_ON_WARNINGS = True + +MSVC_ENABLE_PGO = True + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' + +for var in ('USE_CAIRO', 'MOZ2D_HAS_MOZ_CAIRO'): + DEFINES[var] = True + +if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3', 'gonk', 'qt'): + DEFINES['MOZ_ENABLE_FREETYPE'] = True + +if CONFIG['MOZ_DEBUG']: + DEFINES['GFX_LOG_DEBUG'] = True + DEFINES['GFX_LOG_WARNING'] = True + +# Define the GFX_LOG_WARNING in release builds (available, but controlled by a +# preference), though we may want to consider only doing it in the nightly +# build, if the size of gfxWarning() code ends up making a difference. +# See bug 1074952. +# if CONFIG['NIGHTLY_BUILD']: +DEFINES['GFX_LOG_WARNING'] = True + +CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3', 'gonk', 'qt'): + CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS'] diff --git a/gfx/2d/unittest/Main.cpp b/gfx/2d/unittest/Main.cpp index 8439c76f4..46a1af5b6 100644 --- a/gfx/2d/unittest/Main.cpp +++ b/gfx/2d/unittest/Main.cpp @@ -6,6 +6,7 @@ #include "SanityChecks.h" #include "TestPoint.h" #include "TestScaling.h" +#include "TestBugs.h" #ifdef WIN32 #include "TestDrawTargetD2D.h" #endif @@ -32,6 +33,7 @@ main() #endif { new TestPoint(), "Point Tests" }, { new TestScaling(), "Scaling Tests" } + { new TestBugs(), "Bug Tests" } }; int totalFailures = 0; diff --git a/gfx/2d/unittest/SanityChecks.cpp b/gfx/2d/unittest/SanityChecks.cpp index 428c19854..7f109facd 100644 --- a/gfx/2d/unittest/SanityChecks.cpp +++ b/gfx/2d/unittest/SanityChecks.cpp @@ -2,18 +2,18 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -
-#include "SanityChecks.h"
-
-SanityChecks::SanityChecks()
-{
- REGISTER_TEST(SanityChecks, AlwaysPasses);
-}
-
-void
-SanityChecks::AlwaysPasses()
-{
- bool testMustPass = true;
-
- VERIFY(testMustPass);
-}
+ +#include "SanityChecks.h" + +SanityChecks::SanityChecks() +{ + REGISTER_TEST(SanityChecks, AlwaysPasses); +} + +void +SanityChecks::AlwaysPasses() +{ + bool testMustPass = true; + + VERIFY(testMustPass); +} diff --git a/gfx/2d/unittest/SanityChecks.h b/gfx/2d/unittest/SanityChecks.h index 365c8bf1c..f2a4a0b59 100644 --- a/gfx/2d/unittest/SanityChecks.h +++ b/gfx/2d/unittest/SanityChecks.h @@ -2,15 +2,15 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -
-#pragma once
-
-#include "TestBase.h"
-
-class SanityChecks : public TestBase
-{
-public:
- SanityChecks();
-
- void AlwaysPasses();
-};
+ +#pragma once + +#include "TestBase.h" + +class SanityChecks : public TestBase +{ +public: + SanityChecks(); + + void AlwaysPasses(); +}; diff --git a/gfx/2d/unittest/TestBase.cpp b/gfx/2d/unittest/TestBase.cpp index be8a8e04d..5818a7299 100644 --- a/gfx/2d/unittest/TestBase.cpp +++ b/gfx/2d/unittest/TestBase.cpp @@ -2,47 +2,47 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -
-#include "TestBase.h"
-
-#include <sstream>
-
-using namespace std;
-
-int
-TestBase::RunTests(int *aFailures)
-{
- int testsRun = 0;
- *aFailures = 0;
-
- for(unsigned int i = 0; i < mTests.size(); i++) {
- stringstream stream;
- stream << "Test (" << mTests[i].name << "): ";
- LogMessage(stream.str());
- stream.str("");
-
- mTestFailed = false;
-
- // Don't try this at home! We know these are actually pointers to members
- // of child clases, so we reinterpret cast those child class pointers to
- // TestBase and then call the functions. Because the compiler believes
- // these function calls are members of TestBase.
- ((*reinterpret_cast<TestBase*>((mTests[i].implPointer))).*(mTests[i].funcCall))();
-
- if (!mTestFailed) {
- LogMessage("PASSED\n");
- } else {
- LogMessage("FAILED\n");
- (*aFailures)++;
- }
- testsRun++;
- }
-
- return testsRun;
-}
-
-void
-TestBase::LogMessage(string aMessage)
-{
- printf("%s", aMessage.c_str());
-}
+ +#include "TestBase.h" + +#include <sstream> + +using namespace std; + +int +TestBase::RunTests(int *aFailures) +{ + int testsRun = 0; + *aFailures = 0; + + for(unsigned int i = 0; i < mTests.size(); i++) { + stringstream stream; + stream << "Test (" << mTests[i].name << "): "; + LogMessage(stream.str()); + stream.str(""); + + mTestFailed = false; + + // Don't try this at home! We know these are actually pointers to members + // of child clases, so we reinterpret cast those child class pointers to + // TestBase and then call the functions. Because the compiler believes + // these function calls are members of TestBase. + ((*reinterpret_cast<TestBase*>((mTests[i].implPointer))).*(mTests[i].funcCall))(); + + if (!mTestFailed) { + LogMessage("PASSED\n"); + } else { + LogMessage("FAILED\n"); + (*aFailures)++; + } + testsRun++; + } + + return testsRun; +} + +void +TestBase::LogMessage(string aMessage) +{ + printf("%s", aMessage.c_str()); +} diff --git a/gfx/2d/unittest/TestBase.h b/gfx/2d/unittest/TestBase.h index 97f37883e..a57d6a730 100644 --- a/gfx/2d/unittest/TestBase.h +++ b/gfx/2d/unittest/TestBase.h @@ -26,7 +26,7 @@ class TestBase public: TestBase() {} - typedef void (TestBase::*TestCall)();
+ typedef void (TestBase::*TestCall)(); int RunTests(int *aFailures); diff --git a/gfx/2d/unittest/TestBugs.cpp b/gfx/2d/unittest/TestBugs.cpp new file mode 100644 index 000000000..f127eed8b --- /dev/null +++ b/gfx/2d/unittest/TestBugs.cpp @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "TestBugs.h" +#include "2D.h" +#include <string.h> + +using namespace mozilla; +using namespace mozilla::gfx; + +TestBugs::TestBugs() +{ + REGISTER_TEST(TestBugs, CairoClip918671); + REGISTER_TEST(TestBugs, PushPopClip950550); +} + +void +TestBugs::CairoClip918671() +{ + RefPtr<DrawTarget> dt = Factory::CreateDrawTarget(BackendType::CAIRO, + IntSize(100, 100), + SurfaceFormat::B8G8R8A8); + RefPtr<DrawTarget> ref = Factory::CreateDrawTarget(BackendType::CAIRO, + IntSize(100, 100), + SurfaceFormat::B8G8R8A8); + // Create a path that extends around the center rect but doesn't intersect it. + RefPtr<PathBuilder> pb1 = dt->CreatePathBuilder(); + pb1->MoveTo(Point(10, 10)); + pb1->LineTo(Point(90, 10)); + pb1->LineTo(Point(90, 20)); + pb1->LineTo(Point(10, 20)); + pb1->Close(); + pb1->MoveTo(Point(90, 90)); + pb1->LineTo(Point(91, 90)); + pb1->LineTo(Point(91, 91)); + pb1->LineTo(Point(91, 90)); + pb1->Close(); + + RefPtr<Path> path1 = pb1->Finish(); + dt->PushClip(path1); + + // This center rect must NOT be rectilinear! + RefPtr<PathBuilder> pb2 = dt->CreatePathBuilder(); + pb2->MoveTo(Point(50, 50)); + pb2->LineTo(Point(55, 51)); + pb2->LineTo(Point(54, 55)); + pb2->LineTo(Point(50, 56)); + pb2->Close(); + + RefPtr<Path> path2 = pb2->Finish(); + dt->PushClip(path2); + + dt->FillRect(Rect(0, 0, 100, 100), ColorPattern(Color(1,0,0))); + + RefPtr<SourceSurface> surf1 = dt->Snapshot(); + RefPtr<SourceSurface> surf2 = ref->Snapshot(); + + RefPtr<DataSourceSurface> dataSurf1 = surf1->GetDataSurface(); + RefPtr<DataSourceSurface> dataSurf2 = surf2->GetDataSurface(); + + for (int y = 0; y < dt->GetSize().height; y++) { + VERIFY(memcmp(dataSurf1->GetData() + y * dataSurf1->Stride(), + dataSurf2->GetData() + y * dataSurf2->Stride(), + dataSurf1->GetSize().width * 4) == 0); + } + +} + +void +TestBugs::PushPopClip950550() +{ + RefPtr<DrawTarget> dt = Factory::CreateDrawTarget(BackendType::CAIRO, + IntSize(500, 500), + SurfaceFormat::B8G8R8A8); + dt->PushClipRect(Rect(0, 0, 100, 100)); + Matrix m(1, 0, 0, 1, 45, -100); + dt->SetTransform(m); + dt->PopClip(); + + // We fail the test if we assert in this call because our draw target's + // transforms are out of sync. + dt->FillRect(Rect(50, 50, 50, 50), ColorPattern(Color(0.5f, 0, 0, 1.0f))); +} + diff --git a/gfx/2d/unittest/TestBugs.h b/gfx/2d/unittest/TestBugs.h new file mode 100644 index 000000000..0c715df44 --- /dev/null +++ b/gfx/2d/unittest/TestBugs.h @@ -0,0 +1,18 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include "TestBase.h" + +class TestBugs : public TestBase +{ +public: + TestBugs(); + + void CairoClip918671(); + void PushPopClip950550(); +}; + diff --git a/gfx/2d/unittest/TestDrawTargetBase.cpp b/gfx/2d/unittest/TestDrawTargetBase.cpp index 020da024a..2a0d95ed6 100644 --- a/gfx/2d/unittest/TestDrawTargetBase.cpp +++ b/gfx/2d/unittest/TestDrawTargetBase.cpp @@ -2,108 +2,108 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -
-#include "TestDrawTargetBase.h"
-#include <sstream>
-
-using namespace mozilla;
-using namespace mozilla::gfx;
-using namespace std;
-
-TestDrawTargetBase::TestDrawTargetBase()
-{
- REGISTER_TEST(TestDrawTargetBase, Initialized);
- REGISTER_TEST(TestDrawTargetBase, FillCompletely);
- REGISTER_TEST(TestDrawTargetBase, FillRect);
-}
-
-void
-TestDrawTargetBase::Initialized()
-{
- VERIFY(mDT);
-}
-
-void
-TestDrawTargetBase::FillCompletely()
-{
- mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f)));
-
- RefreshSnapshot();
-
- VerifyAllPixels(Color(0, 0.5f, 0, 1.0f));
-}
-
-void
-TestDrawTargetBase::FillRect()
-{
- mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f)));
- mDT->FillRect(Rect(50, 50, 50, 50), ColorPattern(Color(0.5f, 0, 0, 1.0f)));
-
- RefreshSnapshot();
-
- VerifyPixel(IntPoint(49, 49), Color(0, 0.5f, 0, 1.0f));
- VerifyPixel(IntPoint(50, 50), Color(0.5f, 0, 0, 1.0f));
- VerifyPixel(IntPoint(99, 99), Color(0.5f, 0, 0, 1.0f));
- VerifyPixel(IntPoint(100, 100), Color(0, 0.5f, 0, 1.0f));
-}
-
-void
-TestDrawTargetBase::RefreshSnapshot()
-{
- RefPtr<SourceSurface> snapshot = mDT->Snapshot();
- mDataSnapshot = snapshot->GetDataSurface();
-}
-
-void
-TestDrawTargetBase::VerifyAllPixels(const Color &aColor)
-{
- uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData();
-
- uint32_t expected = RGBAPixelFromColor(aColor);
-
- for (int y = 0; y < DT_HEIGHT; y++) {
- for (int x = 0; x < DT_WIDTH; x++) {
- if (colVal[y * (mDataSnapshot->Stride() / 4) + x] != expected) {
- LogMessage("VerifyAllPixels Failed\n");
- mTestFailed = true;
- return;
- }
- }
- }
-}
-
-void
-TestDrawTargetBase::VerifyPixel(const IntPoint &aPoint, mozilla::gfx::Color &aColor)
-{
- uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData();
-
- uint32_t expected = RGBAPixelFromColor(aColor);
- uint32_t rawActual = colVal[aPoint.y * (mDataSnapshot->Stride() / 4) + aPoint.x];
-
- if (rawActual != expected) {
- stringstream message;
- uint32_t actb = rawActual & 0xFF;
- uint32_t actg = (rawActual & 0xFF00) >> 8;
- uint32_t actr = (rawActual & 0xFF0000) >> 16;
- uint32_t acta = (rawActual & 0xFF000000) >> 24;
- uint32_t expb = expected & 0xFF;
- uint32_t expg = (expected & 0xFF00) >> 8;
- uint32_t expr = (expected & 0xFF0000) >> 16;
- uint32_t expa = (expected & 0xFF000000) >> 24;
-
- message << "Verify Pixel (" << aPoint.x << "x" << aPoint.y << ") Failed."
- " Expected (" << expr << "," << expg << "," << expb << "," << expa << ") "
- " Got (" << actr << "," << actg << "," << actb << "," << acta << ")\n";
-
- LogMessage(message.str());
- mTestFailed = true;
- return;
- }
-}
-
-uint32_t
-TestDrawTargetBase::RGBAPixelFromColor(const Color &aColor)
-{
- return uint8_t((aColor.b * 255) + 0.5f) | uint8_t((aColor.g * 255) + 0.5f) << 8 |
- uint8_t((aColor.r * 255) + 0.5f) << 16 | uint8_t((aColor.a * 255) + 0.5f) << 24;
-}
+ +#include "TestDrawTargetBase.h" +#include <sstream> + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace std; + +TestDrawTargetBase::TestDrawTargetBase() +{ + REGISTER_TEST(TestDrawTargetBase, Initialized); + REGISTER_TEST(TestDrawTargetBase, FillCompletely); + REGISTER_TEST(TestDrawTargetBase, FillRect); +} + +void +TestDrawTargetBase::Initialized() +{ + VERIFY(mDT); +} + +void +TestDrawTargetBase::FillCompletely() +{ + mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f))); + + RefreshSnapshot(); + + VerifyAllPixels(Color(0, 0.5f, 0, 1.0f)); +} + +void +TestDrawTargetBase::FillRect() +{ + mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f))); + mDT->FillRect(Rect(50, 50, 50, 50), ColorPattern(Color(0.5f, 0, 0, 1.0f))); + + RefreshSnapshot(); + + VerifyPixel(IntPoint(49, 49), Color(0, 0.5f, 0, 1.0f)); + VerifyPixel(IntPoint(50, 50), Color(0.5f, 0, 0, 1.0f)); + VerifyPixel(IntPoint(99, 99), Color(0.5f, 0, 0, 1.0f)); + VerifyPixel(IntPoint(100, 100), Color(0, 0.5f, 0, 1.0f)); +} + +void +TestDrawTargetBase::RefreshSnapshot() +{ + RefPtr<SourceSurface> snapshot = mDT->Snapshot(); + mDataSnapshot = snapshot->GetDataSurface(); +} + +void +TestDrawTargetBase::VerifyAllPixels(const Color &aColor) +{ + uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData(); + + uint32_t expected = RGBAPixelFromColor(aColor); + + for (int y = 0; y < DT_HEIGHT; y++) { + for (int x = 0; x < DT_WIDTH; x++) { + if (colVal[y * (mDataSnapshot->Stride() / 4) + x] != expected) { + LogMessage("VerifyAllPixels Failed\n"); + mTestFailed = true; + return; + } + } + } +} + +void +TestDrawTargetBase::VerifyPixel(const IntPoint &aPoint, mozilla::gfx::Color &aColor) +{ + uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData(); + + uint32_t expected = RGBAPixelFromColor(aColor); + uint32_t rawActual = colVal[aPoint.y * (mDataSnapshot->Stride() / 4) + aPoint.x]; + + if (rawActual != expected) { + stringstream message; + uint32_t actb = rawActual & 0xFF; + uint32_t actg = (rawActual & 0xFF00) >> 8; + uint32_t actr = (rawActual & 0xFF0000) >> 16; + uint32_t acta = (rawActual & 0xFF000000) >> 24; + uint32_t expb = expected & 0xFF; + uint32_t expg = (expected & 0xFF00) >> 8; + uint32_t expr = (expected & 0xFF0000) >> 16; + uint32_t expa = (expected & 0xFF000000) >> 24; + + message << "Verify Pixel (" << aPoint.x << "x" << aPoint.y << ") Failed." + " Expected (" << expr << "," << expg << "," << expb << "," << expa << ") " + " Got (" << actr << "," << actg << "," << actb << "," << acta << ")\n"; + + LogMessage(message.str()); + mTestFailed = true; + return; + } +} + +uint32_t +TestDrawTargetBase::RGBAPixelFromColor(const Color &aColor) +{ + return uint8_t((aColor.b * 255) + 0.5f) | uint8_t((aColor.g * 255) + 0.5f) << 8 | + uint8_t((aColor.r * 255) + 0.5f) << 16 | uint8_t((aColor.a * 255) + 0.5f) << 24; +} diff --git a/gfx/2d/unittest/TestDrawTargetBase.h b/gfx/2d/unittest/TestDrawTargetBase.h index d373666b8..1e51f3082 100644 --- a/gfx/2d/unittest/TestDrawTargetBase.h +++ b/gfx/2d/unittest/TestDrawTargetBase.h @@ -2,37 +2,37 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -
-#pragma once
-
-#include "2D.h"
-#include "TestBase.h"
-
-#define DT_WIDTH 500
-#define DT_HEIGHT 500
-
-/* This general DrawTarget test class can be reimplemented by a child class
- * with optional additional drawtarget-specific tests. And is intended to run
- * on a 500x500 32 BPP drawtarget.
- */
-class TestDrawTargetBase : public TestBase
-{
-public:
- void Initialized();
- void FillCompletely();
- void FillRect();
-
-protected:
- TestDrawTargetBase();
-
- void RefreshSnapshot();
-
- void VerifyAllPixels(const mozilla::gfx::Color &aColor);
- void VerifyPixel(const mozilla::gfx::IntPoint &aPoint,
- mozilla::gfx::Color &aColor);
-
- uint32_t RGBAPixelFromColor(const mozilla::gfx::Color &aColor);
-
- mozilla::RefPtr<mozilla::gfx::DrawTarget> mDT;
- mozilla::RefPtr<mozilla::gfx::DataSourceSurface> mDataSnapshot;
-};
+ +#pragma once + +#include "2D.h" +#include "TestBase.h" + +#define DT_WIDTH 500 +#define DT_HEIGHT 500 + +/* This general DrawTarget test class can be reimplemented by a child class + * with optional additional drawtarget-specific tests. And is intended to run + * on a 500x500 32 BPP drawtarget. + */ +class TestDrawTargetBase : public TestBase +{ +public: + void Initialized(); + void FillCompletely(); + void FillRect(); + +protected: + TestDrawTargetBase(); + + void RefreshSnapshot(); + + void VerifyAllPixels(const mozilla::gfx::Color &aColor); + void VerifyPixel(const mozilla::gfx::IntPoint &aPoint, + mozilla::gfx::Color &aColor); + + uint32_t RGBAPixelFromColor(const mozilla::gfx::Color &aColor); + + mozilla::RefPtr<mozilla::gfx::DrawTarget> mDT; + mozilla::RefPtr<mozilla::gfx::DataSourceSurface> mDataSnapshot; +}; diff --git a/gfx/2d/unittest/TestDrawTargetD2D.cpp b/gfx/2d/unittest/TestDrawTargetD2D.cpp index fd93e93c8..27c25767d 100644 --- a/gfx/2d/unittest/TestDrawTargetD2D.cpp +++ b/gfx/2d/unittest/TestDrawTargetD2D.cpp @@ -2,12 +2,12 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -
-#include "TestDrawTargetD2D.h"
-
-using namespace mozilla::gfx;
-TestDrawTargetD2D::TestDrawTargetD2D()
-{
+ +#include "TestDrawTargetD2D.h" + +using namespace mozilla::gfx; +TestDrawTargetD2D::TestDrawTargetD2D() +{ ::D3D10CreateDevice1(nullptr, D3D10_DRIVER_TYPE_HARDWARE, nullptr, @@ -15,9 +15,9 @@ TestDrawTargetD2D::TestDrawTargetD2D() D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, D3D10_FEATURE_LEVEL_10_0, D3D10_1_SDK_VERSION, - byRef(mDevice));
-
- Factory::SetDirect3D10Device(mDevice);
-
- mDT = Factory::CreateDrawTarget(BACKEND_DIRECT2D, IntSize(DT_WIDTH, DT_HEIGHT), FORMAT_B8G8R8A8);
-}
+ byRef(mDevice)); + + Factory::SetDirect3D10Device(mDevice); + + mDT = Factory::CreateDrawTarget(BackendType::DIRECT2D, IntSize(DT_WIDTH, DT_HEIGHT), SurfaceFormat::B8G8R8A8); +} diff --git a/gfx/2d/unittest/TestDrawTargetD2D.h b/gfx/2d/unittest/TestDrawTargetD2D.h index 40f5f83b9..e4647ba74 100644 --- a/gfx/2d/unittest/TestDrawTargetD2D.h +++ b/gfx/2d/unittest/TestDrawTargetD2D.h @@ -2,18 +2,18 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -
-#pragma once
-
-#include "TestDrawTargetBase.h"
-
-#include <d3d10_1.h>
-
-class TestDrawTargetD2D : public TestDrawTargetBase
-{
-public:
- TestDrawTargetD2D();
-
-private:
- mozilla::RefPtr<ID3D10Device1> mDevice;
+ +#pragma once + +#include "TestDrawTargetBase.h" + +#include <d3d10_1.h> + +class TestDrawTargetD2D : public TestDrawTargetBase +{ +public: + TestDrawTargetD2D(); + +private: + mozilla::RefPtr<ID3D10Device1> mDevice; }; diff --git a/gfx/2d/unittest/TestPoint.cpp b/gfx/2d/unittest/TestPoint.cpp index 21d9dfeb2..6aa2b6a35 100644 --- a/gfx/2d/unittest/TestPoint.cpp +++ b/gfx/2d/unittest/TestPoint.cpp @@ -2,45 +2,45 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -
-#include "TestPoint.h"
-
-#include "Point.h"
-
-using namespace mozilla::gfx;
-
-TestPoint::TestPoint()
-{
- REGISTER_TEST(TestPoint, Addition);
- REGISTER_TEST(TestPoint, Subtraction);
-}
-
-void
-TestPoint::Addition()
-{
- Point a, b;
- a.x = 2;
- a.y = 2;
- b.x = 5;
- b.y = -5;
-
- a += b;
-
- VERIFY(a.x == 7);
- VERIFY(a.y == -3);
-}
-
-void
-TestPoint::Subtraction()
-{
- Point a, b;
- a.x = 2;
- a.y = 2;
- b.x = 5;
- b.y = -5;
-
- a -= b;
-
- VERIFY(a.x == -3);
- VERIFY(a.y == 7);
-}
+ +#include "TestPoint.h" + +#include "Point.h" + +using namespace mozilla::gfx; + +TestPoint::TestPoint() +{ + REGISTER_TEST(TestPoint, Addition); + REGISTER_TEST(TestPoint, Subtraction); +} + +void +TestPoint::Addition() +{ + Point a, b; + a.x = 2; + a.y = 2; + b.x = 5; + b.y = -5; + + a += b; + + VERIFY(a.x == 7.f); + VERIFY(a.y == -3.f); +} + +void +TestPoint::Subtraction() +{ + Point a, b; + a.x = 2; + a.y = 2; + b.x = 5; + b.y = -5; + + a -= b; + + VERIFY(a.x == -3.f); + VERIFY(a.y == 7.f); +} diff --git a/gfx/2d/unittest/TestPoint.h b/gfx/2d/unittest/TestPoint.h index c62e0afdf..813d66c2f 100644 --- a/gfx/2d/unittest/TestPoint.h +++ b/gfx/2d/unittest/TestPoint.h @@ -2,16 +2,16 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -
-#pragma once
-
-#include "TestBase.h"
-
-class TestPoint : public TestBase
-{
-public:
- TestPoint();
-
- void Addition();
- void Subtraction();
-};
+ +#pragma once + +#include "TestBase.h" + +class TestPoint : public TestBase +{ +public: + TestPoint(); + + void Addition(); + void Subtraction(); +}; diff --git a/gfx/2d/unittest/TestScaling.cpp b/gfx/2d/unittest/TestScaling.cpp index 1515c13fe..60e1acbf0 100644 --- a/gfx/2d/unittest/TestScaling.cpp +++ b/gfx/2d/unittest/TestScaling.cpp @@ -2,248 +2,248 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -
-#include "TestScaling.h"
-
-#include "ImageScaling.h"
-
-using namespace mozilla::gfx;
-
-TestScaling::TestScaling()
-{
- REGISTER_TEST(TestScaling, BasicHalfScale);
- REGISTER_TEST(TestScaling, DoubleHalfScale);
- REGISTER_TEST(TestScaling, UnevenHalfScale);
- REGISTER_TEST(TestScaling, OddStrideHalfScale);
- REGISTER_TEST(TestScaling, VerticalHalfScale);
- REGISTER_TEST(TestScaling, HorizontalHalfScale);
- REGISTER_TEST(TestScaling, MixedHalfScale);
-}
-
-void
-TestScaling::BasicHalfScale()
-{
- std::vector<uint8_t> data;
- data.resize(500 * 500 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y += 2) {
- for (int x = 0; x < 500; x += 2) {
- pixels[y * 500 + x] = 0xff00ff00;
- pixels[y * 500 + x + 1] = 0xff00ffff;
- pixels[(y + 1) * 500 + x] = 0xff000000;
- pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
- }
- }
- ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
-
- scaler.ScaleForSize(IntSize(220, 240));
-
- VERIFY(scaler.GetSize().width == 250);
- VERIFY(scaler.GetSize().height == 250);
-
- pixels = (uint32_t*)scaler.GetScaledData();
-
- for (int y = 0; y < 250; y++) {
- for (int x = 0; x < 250; x++) {
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
- }
- }
-}
-
-void
-TestScaling::DoubleHalfScale()
-{
- std::vector<uint8_t> data;
- data.resize(500 * 500 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y += 2) {
- for (int x = 0; x < 500; x += 2) {
- pixels[y * 500 + x] = 0xff00ff00;
- pixels[y * 500 + x + 1] = 0xff00ffff;
- pixels[(y + 1) * 500 + x] = 0xff000000;
- pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
- }
- }
- ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
-
- scaler.ScaleForSize(IntSize(120, 110));
- VERIFY(scaler.GetSize().width == 125);
- VERIFY(scaler.GetSize().height == 125);
-
- pixels = (uint32_t*)scaler.GetScaledData();
-
- for (int y = 0; y < 125; y++) {
- for (int x = 0; x < 125; x++) {
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
- }
- }
-}
-
-void
-TestScaling::UnevenHalfScale()
-{
- std::vector<uint8_t> data;
- // Use a 16-byte aligned stride still, we test none-aligned strides
- // separately.
- data.resize(499 * 500 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y += 2) {
- for (int x = 0; x < 500; x += 2) {
- pixels[y * 500 + x] = 0xff00ff00;
- if (x < 498) {
- pixels[y * 500 + x + 1] = 0xff00ffff;
- }
- if (y < 498) {
- pixels[(y + 1) * 500 + x] = 0xff000000;
- if (x < 498) {
- pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
- }
- }
- }
- }
- ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(499, 499));
-
- scaler.ScaleForSize(IntSize(220, 220));
- VERIFY(scaler.GetSize().width == 249);
- VERIFY(scaler.GetSize().height == 249);
-
- pixels = (uint32_t*)scaler.GetScaledData();
-
- for (int y = 0; y < 249; y++) {
- for (int x = 0; x < 249; x++) {
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
- }
- }
-}
-
-void
-TestScaling::OddStrideHalfScale()
-{
- std::vector<uint8_t> data;
- // Use a 4-byte aligned stride to test if that doesn't cause any issues.
- data.resize(499 * 499 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y += 2) {
- for (int x = 0; x < 500; x += 2) {
- pixels[y * 499 + x] = 0xff00ff00;
- if (x < 498) {
- pixels[y * 499 + x + 1] = 0xff00ffff;
- }
- if (y < 498) {
- pixels[(y + 1) * 499 + x] = 0xff000000;
- if (x < 498) {
- pixels[(y + 1) * 499 + x + 1] = 0xff0000ff;
- }
- }
- }
- }
- ImageHalfScaler scaler(&data.front(), 499 * 4, IntSize(499, 499));
-
- scaler.ScaleForSize(IntSize(220, 220));
- VERIFY(scaler.GetSize().width == 249);
- VERIFY(scaler.GetSize().height == 249);
-
- pixels = (uint32_t*)scaler.GetScaledData();
-
- for (int y = 0; y < 249; y++) {
- for (int x = 0; x < 249; x++) {
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
- }
- }
-}
-void
-TestScaling::VerticalHalfScale()
-{
- std::vector<uint8_t> data;
- data.resize(500 * 500 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y += 2) {
- for (int x = 0; x < 500; x += 2) {
- pixels[y * 500 + x] = 0xff00ff00;
- pixels[y * 500 + x + 1] = 0xff00ffff;
- pixels[(y + 1) * 500 + x] = 0xff000000;
- pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
- }
- }
- ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
-
- scaler.ScaleForSize(IntSize(400, 240));
- VERIFY(scaler.GetSize().width == 500);
- VERIFY(scaler.GetSize().height == 250);
-
- pixels = (uint32_t*)scaler.GetScaledData();
-
- for (int y = 0; y < 250; y++) {
- for (int x = 0; x < 500; x += 2) {
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f00);
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 1] == 0xff007fff);
- }
- }
-}
-
-void
-TestScaling::HorizontalHalfScale()
-{
- std::vector<uint8_t> data;
- data.resize(520 * 500 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y ++) {
- for (int x = 0; x < 520; x += 8) {
- pixels[y * 520 + x] = 0xff00ff00;
- pixels[y * 520 + x + 1] = 0xff00ffff;
- pixels[y * 520 + x + 2] = 0xff000000;
- pixels[y * 520 + x + 3] = 0xff0000ff;
- pixels[y * 520 + x + 4] = 0xffff00ff;
- pixels[y * 520 + x + 5] = 0xff0000ff;
- pixels[y * 520 + x + 6] = 0xffffffff;
- pixels[y * 520 + x + 7] = 0xff0000ff;
- }
- }
- ImageHalfScaler scaler(&data.front(), 520 * 4, IntSize(520, 500));
-
- scaler.ScaleForSize(IntSize(240, 400));
- VERIFY(scaler.GetSize().width == 260);
- VERIFY(scaler.GetSize().height == 500);
-
- pixels = (uint32_t*)scaler.GetScaledData();
-
- for (int y = 0; y < 500; y++) {
- for (int x = 0; x < 260; x += 4) {
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff00ff7f);
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 1] == 0xff00007f);
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 2] == 0xff7f00ff);
- VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 3] == 0xff7f7fff);
- }
- }
-}
-
-void
-TestScaling::MixedHalfScale()
-{
- std::vector<uint8_t> data;
- data.resize(500 * 500 * 4);
-
- uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front());
- for (int y = 0; y < 500; y += 2) {
- for (int x = 0; x < 500; x += 2) {
- pixels[y * 500 + x] = 0xff00ff00;
- pixels[y * 500 + x + 1] = 0xff00ffff;
- pixels[(y + 1) * 500 + x] = 0xff000000;
- pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
- }
- }
- ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500));
-
- scaler.ScaleForSize(IntSize(120, 240));
- VERIFY(scaler.GetSize().width == 125);
- VERIFY(scaler.GetSize().height == 250);
- scaler.ScaleForSize(IntSize(240, 120));
- VERIFY(scaler.GetSize().width == 250);
- VERIFY(scaler.GetSize().height == 125);
-}
+ +#include "TestScaling.h" + +#include "ImageScaling.h" + +using namespace mozilla::gfx; + +TestScaling::TestScaling() +{ + REGISTER_TEST(TestScaling, BasicHalfScale); + REGISTER_TEST(TestScaling, DoubleHalfScale); + REGISTER_TEST(TestScaling, UnevenHalfScale); + REGISTER_TEST(TestScaling, OddStrideHalfScale); + REGISTER_TEST(TestScaling, VerticalHalfScale); + REGISTER_TEST(TestScaling, HorizontalHalfScale); + REGISTER_TEST(TestScaling, MixedHalfScale); +} + +void +TestScaling::BasicHalfScale() +{ + std::vector<uint8_t> data; + data.resize(500 * 500 * 4); + + uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front()); + for (int y = 0; y < 500; y += 2) { + for (int x = 0; x < 500; x += 2) { + pixels[y * 500 + x] = 0xff00ff00; + pixels[y * 500 + x + 1] = 0xff00ffff; + pixels[(y + 1) * 500 + x] = 0xff000000; + pixels[(y + 1) * 500 + x + 1] = 0xff0000ff; + } + } + ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500)); + + scaler.ScaleForSize(IntSize(220, 240)); + + VERIFY(scaler.GetSize().width == 250); + VERIFY(scaler.GetSize().height == 250); + + pixels = (uint32_t*)scaler.GetScaledData(); + + for (int y = 0; y < 250; y++) { + for (int x = 0; x < 250; x++) { + VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f); + } + } +} + +void +TestScaling::DoubleHalfScale() +{ + std::vector<uint8_t> data; + data.resize(500 * 500 * 4); + + uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front()); + for (int y = 0; y < 500; y += 2) { + for (int x = 0; x < 500; x += 2) { + pixels[y * 500 + x] = 0xff00ff00; + pixels[y * 500 + x + 1] = 0xff00ffff; + pixels[(y + 1) * 500 + x] = 0xff000000; + pixels[(y + 1) * 500 + x + 1] = 0xff0000ff; + } + } + ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500)); + + scaler.ScaleForSize(IntSize(120, 110)); + VERIFY(scaler.GetSize().width == 125); + VERIFY(scaler.GetSize().height == 125); + + pixels = (uint32_t*)scaler.GetScaledData(); + + for (int y = 0; y < 125; y++) { + for (int x = 0; x < 125; x++) { + VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f); + } + } +} + +void +TestScaling::UnevenHalfScale() +{ + std::vector<uint8_t> data; + // Use a 16-byte aligned stride still, we test none-aligned strides + // separately. + data.resize(499 * 500 * 4); + + uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front()); + for (int y = 0; y < 500; y += 2) { + for (int x = 0; x < 500; x += 2) { + pixels[y * 500 + x] = 0xff00ff00; + if (x < 498) { + pixels[y * 500 + x + 1] = 0xff00ffff; + } + if (y < 498) { + pixels[(y + 1) * 500 + x] = 0xff000000; + if (x < 498) { + pixels[(y + 1) * 500 + x + 1] = 0xff0000ff; + } + } + } + } + ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(499, 499)); + + scaler.ScaleForSize(IntSize(220, 220)); + VERIFY(scaler.GetSize().width == 249); + VERIFY(scaler.GetSize().height == 249); + + pixels = (uint32_t*)scaler.GetScaledData(); + + for (int y = 0; y < 249; y++) { + for (int x = 0; x < 249; x++) { + VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f); + } + } +} + +void +TestScaling::OddStrideHalfScale() +{ + std::vector<uint8_t> data; + // Use a 4-byte aligned stride to test if that doesn't cause any issues. + data.resize(499 * 499 * 4); + + uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front()); + for (int y = 0; y < 500; y += 2) { + for (int x = 0; x < 500; x += 2) { + pixels[y * 499 + x] = 0xff00ff00; + if (x < 498) { + pixels[y * 499 + x + 1] = 0xff00ffff; + } + if (y < 498) { + pixels[(y + 1) * 499 + x] = 0xff000000; + if (x < 498) { + pixels[(y + 1) * 499 + x + 1] = 0xff0000ff; + } + } + } + } + ImageHalfScaler scaler(&data.front(), 499 * 4, IntSize(499, 499)); + + scaler.ScaleForSize(IntSize(220, 220)); + VERIFY(scaler.GetSize().width == 249); + VERIFY(scaler.GetSize().height == 249); + + pixels = (uint32_t*)scaler.GetScaledData(); + + for (int y = 0; y < 249; y++) { + for (int x = 0; x < 249; x++) { + VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f); + } + } +} +void +TestScaling::VerticalHalfScale() +{ + std::vector<uint8_t> data; + data.resize(500 * 500 * 4); + + uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front()); + for (int y = 0; y < 500; y += 2) { + for (int x = 0; x < 500; x += 2) { + pixels[y * 500 + x] = 0xff00ff00; + pixels[y * 500 + x + 1] = 0xff00ffff; + pixels[(y + 1) * 500 + x] = 0xff000000; + pixels[(y + 1) * 500 + x + 1] = 0xff0000ff; + } + } + ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500)); + + scaler.ScaleForSize(IntSize(400, 240)); + VERIFY(scaler.GetSize().width == 500); + VERIFY(scaler.GetSize().height == 250); + + pixels = (uint32_t*)scaler.GetScaledData(); + + for (int y = 0; y < 250; y++) { + for (int x = 0; x < 500; x += 2) { + VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f00); + VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 1] == 0xff007fff); + } + } +} + +void +TestScaling::HorizontalHalfScale() +{ + std::vector<uint8_t> data; + data.resize(520 * 500 * 4); + + uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front()); + for (int y = 0; y < 500; y ++) { + for (int x = 0; x < 520; x += 8) { + pixels[y * 520 + x] = 0xff00ff00; + pixels[y * 520 + x + 1] = 0xff00ffff; + pixels[y * 520 + x + 2] = 0xff000000; + pixels[y * 520 + x + 3] = 0xff0000ff; + pixels[y * 520 + x + 4] = 0xffff00ff; + pixels[y * 520 + x + 5] = 0xff0000ff; + pixels[y * 520 + x + 6] = 0xffffffff; + pixels[y * 520 + x + 7] = 0xff0000ff; + } + } + ImageHalfScaler scaler(&data.front(), 520 * 4, IntSize(520, 500)); + + scaler.ScaleForSize(IntSize(240, 400)); + VERIFY(scaler.GetSize().width == 260); + VERIFY(scaler.GetSize().height == 500); + + pixels = (uint32_t*)scaler.GetScaledData(); + + for (int y = 0; y < 500; y++) { + for (int x = 0; x < 260; x += 4) { + VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff00ff7f); + VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 1] == 0xff00007f); + VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 2] == 0xff7f00ff); + VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 3] == 0xff7f7fff); + } + } +} + +void +TestScaling::MixedHalfScale() +{ + std::vector<uint8_t> data; + data.resize(500 * 500 * 4); + + uint32_t *pixels = reinterpret_cast<uint32_t*>(&data.front()); + for (int y = 0; y < 500; y += 2) { + for (int x = 0; x < 500; x += 2) { + pixels[y * 500 + x] = 0xff00ff00; + pixels[y * 500 + x + 1] = 0xff00ffff; + pixels[(y + 1) * 500 + x] = 0xff000000; + pixels[(y + 1) * 500 + x + 1] = 0xff0000ff; + } + } + ImageHalfScaler scaler(&data.front(), 500 * 4, IntSize(500, 500)); + + scaler.ScaleForSize(IntSize(120, 240)); + VERIFY(scaler.GetSize().width == 125); + VERIFY(scaler.GetSize().height == 250); + scaler.ScaleForSize(IntSize(240, 120)); + VERIFY(scaler.GetSize().width == 250); + VERIFY(scaler.GetSize().height == 125); +} diff --git a/gfx/2d/unittest/TestScaling.h b/gfx/2d/unittest/TestScaling.h index 928dda45c..e9bd1a8e0 100644 --- a/gfx/2d/unittest/TestScaling.h +++ b/gfx/2d/unittest/TestScaling.h @@ -2,21 +2,21 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -
-#pragma once
-
-#include "TestBase.h"
-
-class TestScaling : public TestBase
-{
-public:
- TestScaling();
-
- void BasicHalfScale();
- void DoubleHalfScale();
- void UnevenHalfScale();
- void OddStrideHalfScale();
- void VerticalHalfScale();
- void HorizontalHalfScale();
- void MixedHalfScale();
-};
+ +#pragma once + +#include "TestBase.h" + +class TestScaling : public TestBase +{ +public: + TestScaling(); + + void BasicHalfScale(); + void DoubleHalfScale(); + void UnevenHalfScale(); + void OddStrideHalfScale(); + void VerticalHalfScale(); + void HorizontalHalfScale(); + void MixedHalfScale(); +}; |