author | Mason Chang <mchang@mozilla.com> |
Mon, 23 Nov 2015 08:17:35 -0800 | |
changeset 273860 | cae5c087063d54c80a13fc3f4914320a17febd97 |
parent 273859 | 16e98ad8c97b2232a228e67cda6d76f7cd66dab2 |
child 273861 | cd5c09ba9561cd035827aa2cb75567fe91a4d2bd |
push id | 29715 |
push user | kwierso@gmail.com |
push date | Tue, 24 Nov 2015 21:54:25 +0000 |
treeherder | mozilla-central@d9243e369c22 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | seth |
bugs | 1221840 |
milestone | 45.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/gfx/2d/DrawTargetCG.cpp +++ b/gfx/2d/DrawTargetCG.cpp @@ -709,17 +709,17 @@ DrawGradient(CGColorSpaceRef aColorSpace 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) { + } else { 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()); @@ -729,17 +729,17 @@ DrawGradient(CGColorSpaceRef aColorSpace 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) { + } else { DrawRadialRepeatingGradient(aColorSpace, cg, pat, extents, stops->mExtend == ExtendMode::REFLECT); } } else { assert(0); } } @@ -770,18 +770,24 @@ static bool isGradient(const Pattern &aPattern) { 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; + if (aPattern.GetType() != PatternType::SURFACE) { + return false; + } + + const SurfacePattern& surfacePattern = static_cast<const SurfacePattern&>(aPattern); + return surfacePattern.mExtendMode != ExtendMode::REPEAT && + surfacePattern.mExtendMode != ExtendMode::REPEAT_X && + surfacePattern.mExtendMode != ExtendMode::REPEAT_Y; } /* CoreGraphics patterns ignore the userspace transform so * we need to multiply it in */ static CGPatternRef CreateCGPattern(const Pattern &aPattern, CGAffineTransform aUserSpace) { const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); @@ -810,16 +816,25 @@ CreateCGPattern(const Pattern &aPattern, // this is done to avoid pixel-cracking along pattern boundaries // (see https://bugs.webkit.org/show_bug.cgi?id=53055) // typedef enum { // wkPatternTilingNoDistortion, // wkPatternTilingConstantSpacingMinimalDistortion, // wkPatternTilingConstantSpacing // } wkPatternTiling; // extern CGPatternRef (*wkCGPatternCreateWithImageAndTransform)(CGImageRef, CGAffineTransform, int); + break; + case ExtendMode::REPEAT_X: + xStep = static_cast<CGFloat>(CGImageGetWidth(image)); + yStep = static_cast<CGFloat>(1 << 22); + break; + case ExtendMode::REPEAT_Y: + yStep = static_cast<CGFloat>(CGImageGetHeight(image)); + xStep = static_cast<CGFloat>(1 << 22); + break; } //XXX: We should be using CGContextDrawTiledImage when we can. Even though it // creates a pattern, it seems to go down a faster path than using a delegate // like we do below CGRect bounds = { {0, 0,}, {static_cast<CGFloat>(CGImageGetWidth(image)), static_cast<CGFloat>(CGImageGetHeight(image))}
--- a/gfx/2d/DrawTargetD2D.cpp +++ b/gfx/2d/DrawTargetD2D.cpp @@ -1313,17 +1313,17 @@ DrawTargetD2D::CreateGradientStops(Gradi stops[i].position = rawStops[i].offset; stops[i].color = D2DColor(rawStops[i].color); } RefPtr<ID2D1GradientStopCollection> stopCollection; HRESULT hr = mRT->CreateGradientStopCollection(stops, aNumStops, - D2D1_GAMMA_2_2, D2DExtend(aExtendMode), + D2D1_GAMMA_2_2, D2DExtend(aExtendMode, Axis::BOTH), getter_AddRefs(stopCollection)); delete [] stops; if (FAILED(hr)) { gfxWarning() << "Failed to create GradientStopCollection. Code: " << hexa(hr); return nullptr; } @@ -2440,19 +2440,21 @@ DrawTargetD2D::CreateBrushForPattern(con RefPtr<ID2D1SolidColorBrush> colBrush; mRT->CreateSolidColorBrush(D2D1::ColorF(0, 0), getter_AddRefs(colBrush)); return colBrush.forget(); } } break; } + D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS); + D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS); HRESULT hr = mRT->CreateBitmapBrush(bitmap, - D2D1::BitmapBrushProperties(D2DExtend(pat->mExtendMode), - D2DExtend(pat->mExtendMode), + D2D1::BitmapBrushProperties(xRepeat, + yRepeat, D2DFilter(pat->mFilter)), D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), getter_AddRefs(bmBrush)); if (FAILED(hr)) { gfxCriticalError() << "[D2D] 1CreateBitmapBrush failure: " << hexa(hr); return nullptr; } return bmBrush.forget();
--- a/gfx/2d/DrawTargetD2D1.cpp +++ b/gfx/2d/DrawTargetD2D1.cpp @@ -800,17 +800,17 @@ DrawTargetD2D1::CreateGradientStops(Grad 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), + D2D1_GAMMA_2_2, D2DExtend(aExtendMode, Axis::BOTH), getter_AddRefs(stopCollection)); delete [] stops; if (FAILED(hr)) { gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: " << hexa(hr); return nullptr; } @@ -1463,20 +1463,25 @@ DrawTargetD2D1::CreateBrushForPattern(co if (!image) { return CreateTransparentBlackBrush(); } if (pat->mSamplingRect.IsEmpty()) { RefPtr<ID2D1Bitmap> bitmap; image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap)); if (bitmap) { + /** + * Create the brush with the proper repeat modes. + */ RefPtr<ID2D1BitmapBrush> bitmapBrush; + D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS); + D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS); + mDC->CreateBitmapBrush(bitmap, - D2D1::BitmapBrushProperties(D2DExtend(pat->mExtendMode), - D2DExtend(pat->mExtendMode), + D2D1::BitmapBrushProperties(xRepeat, yRepeat, D2DFilter(pat->mFilter)), D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), getter_AddRefs(bitmapBrush)); if (!bitmapBrush) { gfxWarning() << "Couldn't create bitmap brush!"; return CreateTransparentBlackBrush(); } return bitmapBrush.forget(); @@ -1490,20 +1495,24 @@ DrawTargetD2D1::CreateBrushForPattern(co Float(pat->mSurface->GetSize().height)); } else if (pat->mSurface->GetType() == SurfaceType::D2D1_1_IMAGE) { samplingBounds = D2DRect(pat->mSamplingRect); mat.PreTranslate(pat->mSamplingRect.x, pat->mSamplingRect.y); } else { // We will do a partial upload of the sampling restricted area from GetImageForSurface. samplingBounds = D2D1::RectF(0, 0, pat->mSamplingRect.width, pat->mSamplingRect.height); } + + D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS); + D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS); + mDC->CreateImageBrush(image, D2D1::ImageBrushProperties(samplingBounds, - D2DExtend(pat->mExtendMode), - D2DExtend(pat->mExtendMode), + xRepeat, + yRepeat, D2DInterpolationMode(pat->mFilter)), D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), getter_AddRefs(imageBrush)); if (!imageBrush) { gfxWarning() << "Couldn't create image brush!"; return CreateTransparentBlackBrush(); }
--- a/gfx/2d/DrawTargetSkia.cpp +++ b/gfx/2d/DrawTargetSkia.cpp @@ -181,17 +181,17 @@ SetPaintPattern(SkPaint& aPaint, const P case PatternType::COLOR: { Color color = static_cast<const ColorPattern&>(aPattern).mColor; aPaint.setColor(ColorToSkColor(color, aAlpha)); break; } case PatternType::LINEAR_GRADIENT: { const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern); GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get()); - SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode); + SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH); if (stops->mCount >= 2) { SkPoint points[2]; points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x), SkFloatToScalar(pat.mBegin.y)); points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x), SkFloatToScalar(pat.mEnd.y)); SkShader* shader = SkGradientShader::CreateLinear(points, &stops->mColors.front(), @@ -210,17 +210,17 @@ SetPaintPattern(SkPaint& aPaint, const P } else { aPaint.setColor(SkColorSetARGB(0, 0, 0, 0)); } break; } 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); + SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH); if (stops->mCount >= 2) { SkPoint points[2]; points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x), SkFloatToScalar(pat.mCenter1.y)); points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x), SkFloatToScalar(pat.mCenter2.y)); SkShader* shader = SkGradientShader::CreateTwoPointConical(points[0], SkFloatToScalar(pat.mRadius1), @@ -252,18 +252,20 @@ SetPaintPattern(SkPaint& aPaint, const P GfxMatrixToSkiaMatrix(pat.mMatrix, mat); 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::TileMode xTileMode = ExtendModeToTileMode(pat.mExtendMode, Axis::X_AXIS); + SkShader::TileMode yTileMode = ExtendModeToTileMode(pat.mExtendMode, Axis::Y_AXIS); + + SkShader* shader = SkShader::CreateBitmapShader(bitmap, xTileMode, yTileMode); SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat); SkSafeUnref(shader); SkSafeUnref(aPaint.setShader(matrixShader)); if (pat.mFilter == Filter::POINT) { aPaint.setFilterLevel(SkPaint::kNone_FilterLevel); } break; }
--- a/gfx/2d/HelpersCairo.h +++ b/gfx/2d/HelpersCairo.h @@ -128,16 +128,20 @@ GfxFilterToCairoFilter(Filter filter) static inline cairo_extend_t GfxExtendToCairoExtend(ExtendMode extend) { switch (extend) { case ExtendMode::CLAMP: return CAIRO_EXTEND_PAD; + // Cairo doesn't support tiling in only 1 direction, + // So we have to fallback and tile in both. + case ExtendMode::REPEAT_X: + case ExtendMode::REPEAT_Y: case ExtendMode::REPEAT: return CAIRO_EXTEND_REPEAT; case ExtendMode::REFLECT: return CAIRO_EXTEND_REFLECT; } return CAIRO_EXTEND_PAD; }
--- a/gfx/2d/HelpersD2D.h +++ b/gfx/2d/HelpersD2D.h @@ -40,23 +40,37 @@ static inline D2D1_SIZE_U D2DIntSize(con } template <typename T> static inline D2D1_RECT_F D2DRect(const T &aRect) { return D2D1::RectF(aRect.x, aRect.y, aRect.XMost(), aRect.YMost()); } -static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode) +static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode, Axis aAxis) { D2D1_EXTEND_MODE extend; switch (aExtendMode) { case ExtendMode::REPEAT: extend = D2D1_EXTEND_MODE_WRAP; break; + case ExtendMode::REPEAT_X: + { + extend = aAxis == Axis::X_AXIS + ? D2D1_EXTEND_MODE_WRAP + : D2D1_EXTEND_MODE_CLAMP; + break; + } + case ExtendMode::REPEAT_Y: + { + extend = aAxis == Axis::Y_AXIS + ? D2D1_EXTEND_MODE_WRAP + : D2D1_EXTEND_MODE_CLAMP; + break; + } case ExtendMode::REFLECT: extend = D2D1_EXTEND_MODE_MIRROR; break; default: extend = D2D1_EXTEND_MODE_CLAMP; } return extend;
--- a/gfx/2d/HelpersSkia.h +++ b/gfx/2d/HelpersSkia.h @@ -286,26 +286,38 @@ SkPointToPoint(const SkPoint &aPoint) 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) +ExtendModeToTileMode(ExtendMode aMode, Axis aAxis) { switch (aMode) { case ExtendMode::CLAMP: return SkShader::kClamp_TileMode; case ExtendMode::REPEAT: return SkShader::kRepeat_TileMode; case ExtendMode::REFLECT: return SkShader::kMirror_TileMode; + case ExtendMode::REPEAT_X: + { + return aAxis == Axis::X_AXIS + ? SkShader::kRepeat_TileMode + : SkShader::kClamp_TileMode; + } + case ExtendMode::REPEAT_Y: + { + return aAxis == Axis::Y_AXIS + ? SkShader::kRepeat_TileMode + : SkShader::kClamp_TileMode; + } } return SkShader::kClamp_TileMode; } static inline SkPaint::Hinting GfxHintingToSkiaHinting(FontHinting aHinting) { switch (aHinting) {
--- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -197,20 +197,28 @@ enum class CompositionOp : int8_t { OP_EXCLUSION, OP_HUE, OP_SATURATION, OP_COLOR, OP_LUMINOSITY, OP_COUNT }; +enum class Axis : int8_t { + X_AXIS, + Y_AXIS, + BOTH +}; + enum class ExtendMode : int8_t { - CLAMP, - REPEAT, - REFLECT + CLAMP, // Do not repeat + REPEAT, // Repeat in both axis + REPEAT_X, // Only X axis + REPEAT_Y, // Only Y axis + REFLECT // Mirror the image }; enum class FillRule : int8_t { FILL_WINDING, FILL_EVEN_ODD }; enum class AntialiasMode : int8_t {
--- a/gfx/thebes/gfxDrawable.cpp +++ b/gfx/thebes/gfxDrawable.cpp @@ -28,17 +28,17 @@ gfxSurfaceDrawable::gfxSurfaceDrawable(S gfxWarning() << "Creating gfxSurfaceDrawable with null SourceSurface"; } } bool gfxSurfaceDrawable::DrawWithSamplingRect(gfxContext* aContext, const gfxRect& aFillRect, const gfxRect& aSamplingRect, - bool aRepeat, + ExtendMode aExtendMode, const Filter& aFilter, gfxFloat aOpacity) { if (!mSourceSurface) { return true; } // When drawing with CLAMP we can expand the sampling rect to the nearest pixel @@ -47,55 +47,50 @@ gfxSurfaceDrawable::DrawWithSamplingRect samplingRect.RoundOut(); IntRect intRect(samplingRect.x, samplingRect.y, samplingRect.width, samplingRect.height); IntSize size = mSourceSurface->GetSize(); if (!IntRect(0, 0, size.width, size.height).Contains(intRect)) { return false; } - DrawInternal(aContext, aFillRect, intRect, false, aFilter, aOpacity, gfxMatrix()); + DrawInternal(aContext, aFillRect, intRect, ExtendMode::CLAMP, aFilter, aOpacity, gfxMatrix()); return true; } bool gfxSurfaceDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect, - bool aRepeat, + ExtendMode aExtendMode, const Filter& aFilter, gfxFloat aOpacity, const gfxMatrix& aTransform) + { if (!mSourceSurface) { return true; } - DrawInternal(aContext, aFillRect, IntRect(), aRepeat, aFilter, aOpacity, aTransform); + DrawInternal(aContext, aFillRect, IntRect(), aExtendMode, aFilter, aOpacity, aTransform); return true; } void gfxSurfaceDrawable::DrawInternal(gfxContext* aContext, const gfxRect& aFillRect, const IntRect& aSamplingRect, - bool aRepeat, + ExtendMode aExtendMode, const Filter& aFilter, gfxFloat aOpacity, const gfxMatrix& aTransform) { - ExtendMode extend = ExtendMode::CLAMP; - - if (aRepeat) { - extend = ExtendMode::REPEAT; - } - Matrix patternTransform = ToMatrix(aTransform * mTransform); patternTransform.Invert(); - SurfacePattern pattern(mSourceSurface, extend, + SurfacePattern pattern(mSourceSurface, aExtendMode, patternTransform, aFilter, aSamplingRect); Rect fillRect = ToRect(aFillRect); DrawTarget* dt = aContext->GetDrawTarget(); if (aContext->CurrentOp() == CompositionOp::OP_SOURCE && aOpacity == 1.0) { // Emulate cairo operator source which is bound by mask! @@ -122,40 +117,53 @@ gfxCallbackDrawable::MakeSurfaceDrawable gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR_ALPHA); RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mSize, format); if (!dt) return nullptr; RefPtr<gfxContext> ctx = new gfxContext(dt); - Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), false, aFilter); + Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), ExtendMode::CLAMP, aFilter); RefPtr<SourceSurface> surface = dt->Snapshot(); if (surface) { RefPtr<gfxSurfaceDrawable> drawable = new gfxSurfaceDrawable(surface, mSize); return drawable.forget(); } return nullptr; } +static bool +IsRepeatingExtendMode(ExtendMode aExtendMode) +{ + switch (aExtendMode) { + case ExtendMode::REPEAT: + case ExtendMode::REPEAT_X: + case ExtendMode::REPEAT_Y: + return true; + default: + return false; + } +} + bool gfxCallbackDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect, - bool aRepeat, + ExtendMode aExtendMode, const Filter& aFilter, gfxFloat aOpacity, const gfxMatrix& aTransform) { - if ((aRepeat || aOpacity != 1.0) && !mSurfaceDrawable) { + if ((IsRepeatingExtendMode(aExtendMode) || aOpacity != 1.0) && !mSurfaceDrawable) { mSurfaceDrawable = MakeSurfaceDrawable(aFilter); } if (mSurfaceDrawable) - return mSurfaceDrawable->Draw(aContext, aFillRect, aRepeat, aFilter, + return mSurfaceDrawable->Draw(aContext, aFillRect, aExtendMode, aFilter, aOpacity, aTransform); if (mCallback) return (*mCallback)(aContext, aFillRect, aFilter, aTransform); return false; } @@ -179,17 +187,17 @@ public: virtual ~DrawingCallbackFromDrawable() {} virtual bool operator()(gfxContext* aContext, const gfxRect& aFillRect, const Filter& aFilter, const gfxMatrix& aTransform = gfxMatrix()) { - return mDrawable->Draw(aContext, aFillRect, false, aFilter, 1.0, + return mDrawable->Draw(aContext, aFillRect, ExtendMode::CLAMP, aFilter, 1.0, aTransform); } private: RefPtr<gfxDrawable> mDrawable; }; already_AddRefed<gfxCallbackDrawable> gfxPatternDrawable::MakeCallbackDrawable() @@ -199,36 +207,36 @@ gfxPatternDrawable::MakeCallbackDrawable RefPtr<gfxCallbackDrawable> callbackDrawable = new gfxCallbackDrawable(callback, mSize); return callbackDrawable.forget(); } bool gfxPatternDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect, - bool aRepeat, + ExtendMode aExtendMode, const Filter& aFilter, gfxFloat aOpacity, const gfxMatrix& aTransform) { DrawTarget& aDrawTarget = *aContext->GetDrawTarget(); if (!mPattern) return false; - if (aRepeat) { + if (IsRepeatingExtendMode(aExtendMode)) { // We can't use mPattern directly: We want our repeated tiles to have // the size mSize, which might not be the case in mPattern. // So we need to draw mPattern into a surface of size mSize, create // a pattern from the surface and draw that pattern. // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do // those things, so we use them here. Drawing mPattern into the surface // will happen through this Draw() method with aRepeat = false. RefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable(); - return callbackDrawable->Draw(aContext, aFillRect, true, aFilter, + return callbackDrawable->Draw(aContext, aFillRect, aExtendMode, aFilter, aOpacity, aTransform); } gfxMatrix oldMatrix = mPattern->GetMatrix(); mPattern->SetMatrix(aTransform * oldMatrix); DrawOptions drawOptions(aOpacity); aDrawTarget.FillRect(ToRect(aFillRect), *mPattern->GetPattern(&aDrawTarget), drawOptions);
--- a/gfx/thebes/gfxDrawable.h +++ b/gfx/thebes/gfxDrawable.h @@ -5,16 +5,17 @@ #ifndef GFX_DRAWABLE_H #define GFX_DRAWABLE_H #include "nsAutoPtr.h" #include "gfxRect.h" #include "gfxMatrix.h" #include "mozilla/gfx/2D.h" +#include "mozilla/gfx/Types.h" class gfxContext; class gfxPattern; /** * gfxDrawable * An Interface representing something that has an intrinsic size and can draw * itself repeatedly. @@ -29,24 +30,25 @@ public: * Draw into aContext filling aFillRect, possibly repeating, using aFilter. * aTransform is a userspace to "image"space matrix. For example, if Draw * draws using a gfxPattern, this is the matrix that should be set on the * pattern prior to rendering it. * @return whether drawing was successful */ virtual bool Draw(gfxContext* aContext, const gfxRect& aFillRect, - bool aRepeat, + mozilla::gfx::ExtendMode aExtendMode, const mozilla::gfx::Filter& aFilter, gfxFloat aOpacity = 1.0, const gfxMatrix& aTransform = gfxMatrix()) = 0; + virtual bool DrawWithSamplingRect(gfxContext* aContext, const gfxRect& aFillRect, const gfxRect& aSamplingRect, - bool aRepeat, + mozilla::gfx::ExtendMode aExtendMode, const mozilla::gfx::Filter& aFilter, gfxFloat aOpacity = 1.0) { return false; } virtual mozilla::gfx::IntSize Size() { return mSize; } @@ -64,32 +66,33 @@ protected: class gfxSurfaceDrawable : public gfxDrawable { public: gfxSurfaceDrawable(mozilla::gfx::SourceSurface* aSurface, const mozilla::gfx::IntSize aSize, const gfxMatrix aTransform = gfxMatrix()); virtual ~gfxSurfaceDrawable() {} virtual bool Draw(gfxContext* aContext, const gfxRect& aFillRect, - bool aRepeat, + mozilla::gfx::ExtendMode aExtendMode, const mozilla::gfx::Filter& aFilter, gfxFloat aOpacity = 1.0, const gfxMatrix& aTransform = gfxMatrix()); + virtual bool DrawWithSamplingRect(gfxContext* aContext, const gfxRect& aFillRect, const gfxRect& aSamplingRect, - bool aRepeat, + mozilla::gfx::ExtendMode aExtendMode, const mozilla::gfx::Filter& aFilter, gfxFloat aOpacity = 1.0); - + protected: void DrawInternal(gfxContext* aContext, const gfxRect& aFillRect, const mozilla::gfx::IntRect& aSamplingRect, - bool aRepeat, + mozilla::gfx::ExtendMode aExtendMode, const mozilla::gfx::Filter& aFilter, gfxFloat aOpacity, const gfxMatrix& aTransform = gfxMatrix()); RefPtr<mozilla::gfx::SourceSurface> mSourceSurface; const gfxMatrix mTransform; }; @@ -124,17 +127,17 @@ public: */ class gfxCallbackDrawable : public gfxDrawable { public: gfxCallbackDrawable(gfxDrawingCallback* aCallback, const mozilla::gfx::IntSize aSize); virtual ~gfxCallbackDrawable() {} virtual bool Draw(gfxContext* aContext, const gfxRect& aFillRect, - bool aRepeat, + mozilla::gfx::ExtendMode aExtendMode, const mozilla::gfx::Filter& aFilter, gfxFloat aOpacity = 1.0, const gfxMatrix& aTransform = gfxMatrix()); protected: already_AddRefed<gfxSurfaceDrawable> MakeSurfaceDrawable(mozilla::gfx::Filter aFilter = mozilla::gfx::Filter::LINEAR); RefPtr<gfxDrawingCallback> mCallback; @@ -148,20 +151,21 @@ protected: class gfxPatternDrawable : public gfxDrawable { public: gfxPatternDrawable(gfxPattern* aPattern, const mozilla::gfx::IntSize aSize); virtual ~gfxPatternDrawable(); virtual bool Draw(gfxContext* aContext, const gfxRect& aFillRect, - bool aRepeat, + mozilla::gfx::ExtendMode aExtendMode, const mozilla::gfx::Filter& aFilter, gfxFloat aOpacity = 1.0, const gfxMatrix& aTransform = gfxMatrix()); + protected: already_AddRefed<gfxCallbackDrawable> MakeCallbackDrawable(); RefPtr<gfxPattern> mPattern; }; #endif /* GFX_DRAWABLE_H */
--- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -454,17 +454,17 @@ CreateSamplingRestrictedDrawable(gfxDraw RefPtr<DrawTarget> target = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(size, aFormat); if (!target) { return nullptr; } RefPtr<gfxContext> tmpCtx = new gfxContext(target); tmpCtx->SetOp(OptimalFillOp()); - aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), true, Filter::LINEAR, + aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), ExtendMode::REPEAT, Filter::LINEAR, 1.0, gfxMatrix::Translation(needed.TopLeft())); RefPtr<SourceSurface> surface = target->Snapshot(); RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, size, gfxMatrix::Translation(-needed.TopLeft())); return drawable.forget(); } #endif // !MOZ_GFX_OPTIMIZE_MOBILE @@ -678,17 +678,17 @@ PrescaleAndTileDrawable(gfxDrawable* aDr gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(scaledImageSize, aFormat); if (!scaledDT) { return false; } RefPtr<gfxContext> tmpCtx = new gfxContext(scaledDT); scaledDT->SetTransform(ToMatrix(scaleMatrix)); gfxRect gfxImageRect(aImageRect.x, aImageRect.y, aImageRect.width, aImageRect.height); - aDrawable->Draw(tmpCtx, gfxImageRect, true, aFilter, 1.0, gfxMatrix()); + aDrawable->Draw(tmpCtx, gfxImageRect, ExtendMode::REPEAT, aFilter, 1.0, gfxMatrix()); RefPtr<SourceSurface> scaledImage = scaledDT->Snapshot(); { gfxContextMatrixAutoSaveRestore autoSR(aContext); Matrix withoutScale = ToMatrix(aContext->CurrentMatrix()); DrawTarget* destDrawTarget = aContext->GetDrawTarget(); @@ -717,19 +717,17 @@ gfxUtils::DrawPixelSnapped(gfxContext* uint32_t aImageFlags, gfxFloat aOpacity) { PROFILER_LABEL("gfxUtils", "DrawPixelSnapped", js::ProfileEntry::Category::GRAPHICS); gfxRect imageRect(gfxPoint(0, 0), aImageSize); gfxRect region(aRegion.Rect()); - - bool doTile = !imageRect.Contains(region) && - !(aImageFlags & imgIContainer::FLAG_CLAMP); + ExtendMode extendMode = aRegion.GetExtendMode(); RefPtr<gfxASurface> currentTarget = aContext->CurrentSurface(); gfxMatrix deviceSpaceToImageSpace = DeviceToImageTransform(aContext); AutoCairoPixmanBugWorkaround workaround(aContext, deviceSpaceToImageSpace, region, currentTarget); if (!workaround.Succeeded()) return; @@ -740,20 +738,21 @@ gfxUtils::DrawPixelSnapped(gfxContext* imageRect.Width(), imageRect.Height(), region.Width(), region.Height()); // OK now, the hard part left is to account for the subimage sampling // restriction. If all the transforms involved are just integer // translations, then we assume no resampling will occur so there's // nothing to do. // XXX if only we had source-clipping in cairo! + if (aContext->CurrentMatrix().HasNonIntegerTranslation()) { - if (doTile || !aRegion.RestrictionContains(imageRect)) { + if ((extendMode != ExtendMode::CLAMP) || !aRegion.RestrictionContains(imageRect)) { if (drawable->DrawWithSamplingRect(aContext, aRegion.Rect(), aRegion.Restriction(), - doTile, aFilter, aOpacity)) { + extendMode, aFilter, aOpacity)) { return; } #ifdef MOZ_WIDGET_COCOA if (PrescaleAndTileDrawable(aDrawable, aContext, aRegion, ToRect(imageRect), aFilter, aFormat, aOpacity)) { return; @@ -768,23 +767,23 @@ gfxUtils::DrawPixelSnapped(gfxContext* CreateSamplingRestrictedDrawable(aDrawable, aContext, aRegion, aFormat); if (restrictedDrawable) { drawable.swap(restrictedDrawable); // We no longer need to tile: Either we never needed to, or we already // filled a surface with the tiled pattern; this surface can now be // drawn without tiling. - doTile = false; + extendMode = ExtendMode::CLAMP; } #endif } } - drawable->Draw(aContext, aRegion.Rect(), doTile, aFilter, aOpacity); + drawable->Draw(aContext, aRegion.Rect(), extendMode, aFilter, aOpacity, gfxMatrix()); } /* static */ int gfxUtils::ImageFormatToDepth(gfxImageFormat aFormat) { switch (aFormat) { case gfxImageFormat::ARGB32: return 32;
--- a/image/ImageRegion.h +++ b/image/ImageRegion.h @@ -2,57 +2,64 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.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_image_ImageRegion_h #define mozilla_image_ImageRegion_h #include "gfxRect.h" +#include "mozilla/gfx/Types.h" namespace mozilla { namespace image { /** * An axis-aligned rectangle in tiled image space, with an optional sampling * restriction rect. The drawing code ensures that if a sampling restriction * rect is present, any pixels sampled during the drawing process are found * within that rect. * * The sampling restriction rect exists primarily for callers which perform * pixel snapping. Other callers should generally use one of the Create() * overloads. */ class ImageRegion { + typedef mozilla::gfx::ExtendMode ExtendMode; + public: static ImageRegion Empty() { - return ImageRegion(gfxRect()); + return ImageRegion(gfxRect(), ExtendMode::CLAMP); } - static ImageRegion Create(const gfxRect& aRect) + static ImageRegion Create(const gfxRect& aRect, + ExtendMode aExtendMode = ExtendMode::CLAMP) { - return ImageRegion(aRect); + return ImageRegion(aRect, aExtendMode); } - static ImageRegion Create(const gfxSize& aSize) + static ImageRegion Create(const gfxSize& aSize, + ExtendMode aExtendMode = ExtendMode::CLAMP) { - return ImageRegion(gfxRect(0, 0, aSize.width, aSize.height)); + return ImageRegion(gfxRect(0, 0, aSize.width, aSize.height), aExtendMode); } - static ImageRegion Create(const nsIntSize& aSize) + static ImageRegion Create(const nsIntSize& aSize, + ExtendMode aExtendMode = ExtendMode::CLAMP) { - return ImageRegion(gfxRect(0, 0, aSize.width, aSize.height)); + return ImageRegion(gfxRect(0, 0, aSize.width, aSize.height), aExtendMode); } static ImageRegion CreateWithSamplingRestriction(const gfxRect& aRect, - const gfxRect& aRestriction) + const gfxRect& aRestriction, + ExtendMode aExtendMode = ExtendMode::CLAMP) { - return ImageRegion(aRect, aRestriction); + return ImageRegion(aRect, aRestriction, aExtendMode); } bool IsRestricted() const { return mIsRestricted; } const gfxRect& Rect() const { return mRect; } const gfxRect& Restriction() const { MOZ_ASSERT(mIsRestricted); @@ -128,31 +135,39 @@ public: ImageRegion operator+(const gfxPoint& aPt) const { if (mIsRestricted) { return CreateWithSamplingRestriction(mRect + aPt, mRestriction + aPt); } return Create(mRect + aPt); } + gfx::ExtendMode GetExtendMode() const + { + return mExtendMode; + } + /* ImageRegion() : mIsRestricted(false) { } */ private: - explicit ImageRegion(const gfxRect& aRect) + explicit ImageRegion(const gfxRect& aRect, ExtendMode aExtendMode) : mRect(aRect) + , mExtendMode(aExtendMode) , mIsRestricted(false) { } - ImageRegion(const gfxRect& aRect, const gfxRect& aRestriction) + ImageRegion(const gfxRect& aRect, const gfxRect& aRestriction, ExtendMode aExtendMode) : mRect(aRect) , mRestriction(aRestriction) + , mExtendMode(aExtendMode) , mIsRestricted(true) { } gfxRect mRect; gfxRect mRestriction; + ExtendMode mExtendMode; bool mIsRestricted; }; } // namespace image } // namespace mozilla #endif // mozilla_image_ImageRegion_h
--- a/image/imgFrame.cpp +++ b/image/imgFrame.cpp @@ -517,17 +517,17 @@ imgFrame::SurfaceForDrawing(bool // Fill 'available' with whatever we've got if (mSinglePixel) { target->FillRect(ToRect(aRegion.Intersect(available).Rect()), ColorPattern(mSinglePixelColor), DrawOptions(1.0f, CompositionOp::OP_SOURCE)); } else { SurfacePattern pattern(aSurface, - ExtendMode::REPEAT, + aRegion.GetExtendMode(), Matrix::Translation(mDecoded.x, mDecoded.y)); target->FillRect(ToRect(aRegion.Intersect(available).Rect()), pattern); } RefPtr<SourceSurface> newsurf = target->Snapshot(); return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf, size), target->GetFormat()); } @@ -581,16 +581,17 @@ bool imgFrame::Draw(gfxContext* aContext RefPtr<SourceSurface> surf = GetSurfaceInternal(); if (!surf && !mSinglePixel) { return false; } gfxRect imageRect(0, 0, mImageSize.width, mImageSize.height); bool doTile = !imageRect.Contains(aRegion.Rect()) && !(aImageFlags & imgIContainer::FLAG_CLAMP); + ImageRegion region(aRegion); // SurfaceForDrawing changes the current transform, and we need it to still // be changed when we call gfxUtils::DrawPixelSnapped. We still need to // restore it before returning though. // XXXjwatt In general having functions require someone further up the stack // to undo transform changes that they make is bad practice. We should // change how this code works. gfxContextMatrixAutoSaveRestore autoSR(aContext);
--- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -3330,24 +3330,40 @@ nsCSSRendering::PrepareBackgroundLayer(n &imageTopLeft, &state.mAnchor); imageTopLeft += bgPositioningArea.TopLeft(); state.mAnchor += bgPositioningArea.TopLeft(); state.mDestArea = nsRect(imageTopLeft + aBorderArea.TopLeft(), imageSize); state.mFillArea = state.mDestArea; int repeatX = aLayer.mRepeat.mXRepeat; int repeatY = aLayer.mRepeat.mYRepeat; + + ExtendMode repeatMode = ExtendMode::CLAMP; if (repeatX == NS_STYLE_BG_REPEAT_REPEAT) { state.mFillArea.x = bgClipRect.x; state.mFillArea.width = bgClipRect.width; + repeatMode = ExtendMode::REPEAT_X; } if (repeatY == NS_STYLE_BG_REPEAT_REPEAT) { state.mFillArea.y = bgClipRect.y; state.mFillArea.height = bgClipRect.height; - } + + /*** + * We're repeating on the X axis already, + * so if we have to repeat in the Y axis, + * we really need to repeat in both directions. + */ + if (repeatMode == ExtendMode::REPEAT_X) { + repeatMode = ExtendMode::REPEAT; + } else { + repeatMode = ExtendMode::REPEAT_Y; + } + } + state.mImageRenderer.SetExtendMode(repeatMode); + state.mFillArea.IntersectRect(state.mFillArea, bgClipRect); state.mCompositionOp = GetGFXBlendMode(aLayer.mBlendMode); return state; } nsRect @@ -4643,16 +4659,17 @@ nsImageRenderer::nsImageRenderer(nsIFram , mImage(aImage) , mType(aImage->GetType()) , mImageContainer(nullptr) , mGradientData(nullptr) , mPaintServerFrame(nullptr) , mPrepareResult(DrawResult::NOT_READY) , mSize(0, 0) , mFlags(aFlags) + , mExtendMode(ExtendMode::CLAMP) { } nsImageRenderer::~nsImageRenderer() { } static bool @@ -5031,17 +5048,18 @@ nsImageRenderer::Draw(nsPresContext* { CSSIntSize imageSize(nsPresContext::AppUnitsToIntCSSPixels(mSize.width), nsPresContext::AppUnitsToIntCSSPixels(mSize.height)); return nsLayoutUtils::DrawBackgroundImage(*aRenderingContext.ThebesContext(), aPresContext, mImageContainer, imageSize, filter, aDest, aFill, aAnchor, aDirtyRect, - ConvertImageRendererToDrawFlags(mFlags)); + ConvertImageRendererToDrawFlags(mFlags), + mExtendMode); } case eStyleImageType_Gradient: { nsCSSRendering::PaintGradient(aPresContext, aRenderingContext, mGradientData, aDirtyRect, aDest, aFill, aSrc, mSize); return DrawResult::SUCCESS; }
--- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -256,16 +256,17 @@ public: bool IsContainerAvailable(LayerManager* aManager, nsDisplayListBuilder* aBuilder); /// Retrieves the image associated with this nsImageRenderer, if there is one. already_AddRefed<imgIContainer> GetImage(); bool IsReady() const { return mPrepareResult == DrawResult::SUCCESS; } DrawResult PrepareResult() const { return mPrepareResult; } + void SetExtendMode(mozilla::gfx::ExtendMode aMode) { mExtendMode = aMode; } private: /** * Draws the image to the target rendering context. * aSrc is a rect on the source image which will be mapped to aDest; it's * currently only used for gradients. * * @see nsLayoutUtils::DrawImage() for other parameters. @@ -292,16 +293,17 @@ private: nsStyleImageType mType; nsCOMPtr<imgIContainer> mImageContainer; RefPtr<nsStyleGradient> mGradientData; nsIFrame* mPaintServerFrame; nsLayoutUtils::SurfaceFromElementResult mImageElementSurface; DrawResult mPrepareResult; nsSize mSize; // unscaled size of the image, in app units uint32_t mFlags; + mozilla::gfx::ExtendMode mExtendMode; }; /** * A struct representing all the information needed to paint a background * image to some target, taking into account all CSS background-* properties. * See PrepareBackgroundLayer. */ struct nsBackgroundLayerState {
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -6214,17 +6214,18 @@ static SnappedImageDrawingParameters ComputeSnappedImageDrawingParameters(gfxContext* aCtx, int32_t aAppUnitsPerDevPixel, const nsRect aDest, const nsRect aFill, const nsPoint aAnchor, const nsRect aDirty, imgIContainer* aImage, Filter aGraphicsFilter, - uint32_t aImageFlags) + uint32_t aImageFlags, + ExtendMode aExtendMode) { if (aDest.IsEmpty() || aFill.IsEmpty()) return SnappedImageDrawingParameters(); // Avoid unnecessarily large offsets. bool doTile = !aDest.Contains(aFill); nsRect appUnitDest = doTile ? TileNearRect(aDest, aFill.Intersect(aDirty)) : aDest; @@ -6383,35 +6384,45 @@ ComputeSnappedImageDrawingParameters(gfx // If we didn't snap, we need to post-multiply the matrix on the context to // get the final matrix we'll draw with, because we didn't take it into // account when computing the matrices above. if (!didSnap) { transform = transform * currentMatrix; } + ExtendMode extendMode = (aImageFlags & imgIContainer::FLAG_CLAMP) + ? ExtendMode::CLAMP + : aExtendMode; + // We were passed in the default extend mode but need to tile. + if (extendMode == ExtendMode::CLAMP && doTile) { + MOZ_ASSERT(!(aImageFlags & imgIContainer::FLAG_CLAMP)); + extendMode = ExtendMode::REPEAT; + } + ImageRegion region = - ImageRegion::CreateWithSamplingRestriction(imageSpaceFill, subimage); + ImageRegion::CreateWithSamplingRestriction(imageSpaceFill, subimage, extendMode); return SnappedImageDrawingParameters(transform, intImageSize, region, svgViewportSize); } static DrawResult DrawImageInternal(gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, Filter aGraphicsFilter, const nsRect& aDest, const nsRect& aFill, const nsPoint& aAnchor, const nsRect& aDirty, const SVGImageContext* aSVGContext, - uint32_t aImageFlags) + uint32_t aImageFlags, + ExtendMode aExtendMode = ExtendMode::CLAMP) { DrawResult result = DrawResult::SUCCESS; if (aPresContext->Type() == nsPresContext::eContext_Print) { // We want vector images to be passed on as vector commands, not a raster // image. aImageFlags |= imgIContainer::FLAG_BYPASS_SURFACE_CACHE; } @@ -6419,17 +6430,17 @@ DrawImageInternal(gfxContext& aImageFlags |= imgIContainer::FLAG_CLAMP; } int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel(); SnappedImageDrawingParameters params = ComputeSnappedImageDrawingParameters(&aContext, appUnitsPerDevPixel, aDest, aFill, aAnchor, aDirty, aImage, - aGraphicsFilter, aImageFlags); + aGraphicsFilter, aImageFlags, aExtendMode); if (!params.shouldDraw) { return result; } { gfxContextMatrixAutoSaveRestore contextMatrixRestorer(&aContext); @@ -6629,30 +6640,31 @@ nsLayoutUtils::DrawBackgroundImage(gfxCo nsPresContext* aPresContext, imgIContainer* aImage, const CSSIntSize& aImageSize, Filter aGraphicsFilter, const nsRect& aDest, const nsRect& aFill, const nsPoint& aAnchor, const nsRect& aDirty, - uint32_t aImageFlags) + uint32_t aImageFlags, + ExtendMode aExtendMode) { PROFILER_LABEL("layout", "nsLayoutUtils::DrawBackgroundImage", js::ProfileEntry::Category::GRAPHICS); if (UseBackgroundNearestFiltering()) { aGraphicsFilter = Filter::POINT; } SVGImageContext svgContext(aImageSize, Nothing()); return DrawImageInternal(aContext, aPresContext, aImage, aGraphicsFilter, aDest, aFill, aAnchor, - aDirty, &svgContext, aImageFlags); + aDirty, &svgContext, aImageFlags, aExtendMode); } /* static */ DrawResult nsLayoutUtils::DrawImage(gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, Filter aGraphicsFilter, const nsRect& aDest,
--- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -114,16 +114,17 @@ class nsLayoutUtils { typedef mozilla::dom::DOMRectList DOMRectList; typedef mozilla::layers::Layer Layer; typedef mozilla::ContainerLayerParameters ContainerLayerParameters; typedef mozilla::IntrinsicSize IntrinsicSize; typedef mozilla::gfx::SourceSurface SourceSurface; typedef mozilla::gfx::Color Color; typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::gfx::ExtendMode ExtendMode; typedef mozilla::gfx::Filter Filter; typedef mozilla::gfx::Float Float; typedef mozilla::gfx::Point Point; typedef mozilla::gfx::Rect Rect; typedef mozilla::gfx::RectDouble RectDouble; typedef mozilla::gfx::Matrix4x4 Matrix4x4; typedef mozilla::gfx::RectCornerRadii RectCornerRadii; typedef mozilla::gfx::StrokeOptions StrokeOptions; @@ -1764,28 +1765,30 @@ public: * the image is a vector image being rendered at * that size.) * @param aDest The position and scaled area where one copy of * the image should be drawn. * @param aFill The area to be filled with copies of the image. * @param aAnchor A point in aFill which we will ensure is * pixel-aligned in the output. * @param aDirty Pixels outside this area may be skipped. - * @param aImageFlags Image flags of the imgIContainer::FLAG_* variety + * @param aImageFlags Image flags of the imgIContainer::FLAG_* variety. + * @param aExtendMode How to extend the image over the dest rect. */ static DrawResult DrawBackgroundImage(gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, const CSSIntSize& aImageSize, Filter aGraphicsFilter, const nsRect& aDest, const nsRect& aFill, const nsPoint& aAnchor, const nsRect& aDirty, - uint32_t aImageFlags); + uint32_t aImageFlags, + ExtendMode aExtendMode); /** * Draw an image. * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering * @param aRenderingContext Where to draw the image, set up with an * appropriate scale and transform for drawing in * app units. * @param aImage The image.