Bug 1221840. Support repeating images in 1 axis. r=seth

This commit is contained in:
Mason Chang 2015-11-23 08:17:35 -08:00
parent c89963ec79
commit e74c79f0e3
17 changed files with 207 additions and 79 deletions

View File

@ -714,7 +714,7 @@ DrawGradient(CGColorSpaceRef aColorSpace,
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) {
@ -734,7 +734,7 @@ DrawGradient(CGColorSpaceRef aColorSpace,
//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 {
@ -775,8 +775,14 @@ isGradient(const Pattern &aPattern)
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
@ -815,6 +821,15 @@ CreateCGPattern(const Pattern &aPattern, CGAffineTransform aUserSpace)
// 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

View File

@ -1318,7 +1318,7 @@ DrawTargetD2D::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, E
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;
@ -2445,9 +2445,11 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
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));

View File

@ -805,7 +805,7 @@ DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops,
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;
@ -1468,10 +1468,15 @@ DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
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));
@ -1495,10 +1500,14 @@ DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
// 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));

View File

@ -186,7 +186,7 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap
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];
@ -215,7 +215,7 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap
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];
@ -257,8 +257,10 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap
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));

View File

@ -133,6 +133,10 @@ GfxExtendToCairoExtend(ExtendMode 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:

View File

@ -45,13 +45,27 @@ 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;

View File

@ -291,7 +291,7 @@ SkRectToRect(const SkRect &aRect)
}
static inline SkShader::TileMode
ExtendModeToTileMode(ExtendMode aMode)
ExtendModeToTileMode(ExtendMode aMode, Axis aAxis)
{
switch (aMode)
{
@ -301,6 +301,18 @@ ExtendModeToTileMode(ExtendMode aMode)
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;
}

View File

@ -202,10 +202,18 @@ enum class CompositionOp : int8_t {
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 {

View File

@ -33,7 +33,7 @@ bool
gfxSurfaceDrawable::DrawWithSamplingRect(gfxContext* aContext,
const gfxRect& aFillRect,
const gfxRect& aSamplingRect,
bool aRepeat,
ExtendMode aExtendMode,
const Filter& aFilter,
gfxFloat aOpacity)
{
@ -52,23 +52,24 @@ gfxSurfaceDrawable::DrawWithSamplingRect(gfxContext* aContext,
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;
}
@ -76,21 +77,15 @@ 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);
@ -127,7 +122,7 @@ gfxCallbackDrawable::MakeSurfaceDrawable(const Filter aFilter)
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) {
@ -137,20 +132,33 @@ gfxCallbackDrawable::MakeSurfaceDrawable(const Filter aFilter)
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)
@ -184,7 +192,7 @@ public:
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:
@ -204,7 +212,7 @@ gfxPatternDrawable::MakeCallbackDrawable()
bool
gfxPatternDrawable::Draw(gfxContext* aContext,
const gfxRect& aFillRect,
bool aRepeat,
ExtendMode aExtendMode,
const Filter& aFilter,
gfxFloat aOpacity,
const gfxMatrix& aTransform)
@ -214,7 +222,7 @@ gfxPatternDrawable::Draw(gfxContext* aContext,
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
@ -223,7 +231,7 @@ gfxPatternDrawable::Draw(gfxContext* aContext,
// 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);
}

View File

@ -10,6 +10,7 @@
#include "gfxRect.h"
#include "gfxMatrix.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Types.h"
class gfxContext;
class gfxPattern;
@ -34,14 +35,15 @@ public:
*/
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)
{
@ -69,22 +71,23 @@ public:
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());
@ -129,7 +132,7 @@ public:
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());
@ -153,11 +156,12 @@ public:
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();

View File

@ -459,7 +459,7 @@ CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
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();
@ -683,7 +683,7 @@ PrescaleAndTileDrawable(gfxDrawable* aDrawable,
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();
@ -722,9 +722,7 @@ gfxUtils::DrawPixelSnapped(gfxContext* aContext,
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);
@ -745,10 +743,11 @@ gfxUtils::DrawPixelSnapped(gfxContext* aContext,
// 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;
}
@ -773,13 +772,13 @@ gfxUtils::DrawPixelSnapped(gfxContext* aContext,
// 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

View File

@ -7,6 +7,7 @@
#define mozilla_image_ImageRegion_h
#include "gfxRect.h"
#include "mozilla/gfx/Types.h"
namespace mozilla {
namespace image {
@ -23,31 +24,37 @@ namespace image {
*/
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; }
@ -133,22 +140,30 @@ public:
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;
};

View File

@ -522,7 +522,7 @@ imgFrame::SurfaceForDrawing(bool aDoPadding,
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);
}
@ -586,6 +586,7 @@ bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
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

View File

@ -3335,14 +3335,30 @@ nsCSSRendering::PrepareBackgroundLayer(nsPresContext* aPresContext,
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);
@ -4648,6 +4664,7 @@ nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame,
, mPrepareResult(DrawResult::NOT_READY)
, mSize(0, 0)
, mFlags(aFlags)
, mExtendMode(ExtendMode::CLAMP)
{
}
@ -5036,7 +5053,8 @@ nsImageRenderer::Draw(nsPresContext* aPresContext,
aPresContext,
mImageContainer, imageSize, filter,
aDest, aFill, aAnchor, aDirtyRect,
ConvertImageRendererToDrawFlags(mFlags));
ConvertImageRendererToDrawFlags(mFlags),
mExtendMode);
}
case eStyleImageType_Gradient:
{

View File

@ -261,6 +261,7 @@ public:
bool IsReady() const { return mPrepareResult == DrawResult::SUCCESS; }
DrawResult PrepareResult() const { return mPrepareResult; }
void SetExtendMode(mozilla::gfx::ExtendMode aMode) { mExtendMode = aMode; }
private:
/**
@ -297,6 +298,7 @@ private:
DrawResult mPrepareResult;
nsSize mSize; // unscaled size of the image, in app units
uint32_t mFlags;
mozilla::gfx::ExtendMode mExtendMode;
};
/**

View File

@ -6219,7 +6219,8 @@ ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
const nsRect aDirty,
imgIContainer* aImage,
Filter aGraphicsFilter,
uint32_t aImageFlags)
uint32_t aImageFlags,
ExtendMode aExtendMode)
{
if (aDest.IsEmpty() || aFill.IsEmpty())
return SnappedImageDrawingParameters();
@ -6388,8 +6389,17 @@ ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
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);
@ -6406,7 +6416,8 @@ DrawImageInternal(gfxContext& aContext,
const nsPoint& aAnchor,
const nsRect& aDirty,
const SVGImageContext* aSVGContext,
uint32_t aImageFlags)
uint32_t aImageFlags,
ExtendMode aExtendMode = ExtendMode::CLAMP)
{
DrawResult result = DrawResult::SUCCESS;
@ -6424,7 +6435,7 @@ DrawImageInternal(gfxContext& aContext,
SnappedImageDrawingParameters params =
ComputeSnappedImageDrawingParameters(&aContext, appUnitsPerDevPixel, aDest,
aFill, aAnchor, aDirty, aImage,
aGraphicsFilter, aImageFlags);
aGraphicsFilter, aImageFlags, aExtendMode);
if (!params.shouldDraw) {
return result;
@ -6634,7 +6645,8 @@ nsLayoutUtils::DrawBackgroundImage(gfxContext& aContext,
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);
@ -6647,7 +6659,7 @@ nsLayoutUtils::DrawBackgroundImage(gfxContext& aContext,
return DrawImageInternal(aContext, aPresContext, aImage,
aGraphicsFilter, aDest, aFill, aAnchor,
aDirty, &svgContext, aImageFlags);
aDirty, &svgContext, aImageFlags, aExtendMode);
}
/* static */ DrawResult

View File

@ -119,6 +119,7 @@ class nsLayoutUtils
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;
@ -1769,7 +1770,8 @@ public:
* @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,
@ -1780,7 +1782,8 @@ public:
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
uint32_t aImageFlags);
uint32_t aImageFlags,
ExtendMode aExtendMode);
/**
* Draw an image.