зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1719886 - Support translated no-repeat patterns in canvas FillRect. r=jrmuizel
This is a bandaid designed to cope with a subset of the encountered transforms that may be applied to a pattern inside FillRect. The existing code did not consider the transform at all that might be applied to a pattern when it tried to manually clip the geometry to a no-repeat pattern. This manual clipping only seems to occur in FillRect, whereas no-repeat patterns are not properly handled anywhere else in canvas entry-points. To fix this more generally requires a clamp-to-transparent tile mode (like Skia's decal or Cairo's none) which we can't currently rely upon with our D2D support. However, that is much beyond the scope of this temporary workaround. Differential Revision: https://phabricator.services.mozilla.com/D122186
This commit is contained in:
Родитель
69c0a6cb2c
Коммит
608abb2330
|
@ -2548,49 +2548,39 @@ void CanvasRenderingContext2D::FillRect(double aX, double aY, double aW,
|
|||
|
||||
const ContextState* state = &CurrentState();
|
||||
if (state->patternStyles[Style::FILL]) {
|
||||
CanvasPattern::RepeatMode repeat =
|
||||
state->patternStyles[Style::FILL]->mRepeat;
|
||||
auto& style = state->patternStyles[Style::FILL];
|
||||
CanvasPattern::RepeatMode repeat = style->mRepeat;
|
||||
// In the FillRect case repeat modes are easy to deal with.
|
||||
bool limitx = repeat == CanvasPattern::RepeatMode::NOREPEAT ||
|
||||
repeat == CanvasPattern::RepeatMode::REPEATY;
|
||||
bool limity = repeat == CanvasPattern::RepeatMode::NOREPEAT ||
|
||||
repeat == CanvasPattern::RepeatMode::REPEATX;
|
||||
|
||||
IntSize patternSize =
|
||||
state->patternStyles[Style::FILL]->mSurface->GetSize();
|
||||
|
||||
// We always need to execute painting for non-over operators, even if
|
||||
// we end up with w/h = 0.
|
||||
if (limitx) {
|
||||
if (aX < 0) {
|
||||
aW += aX;
|
||||
if (aW < 0) {
|
||||
aW = 0;
|
||||
}
|
||||
|
||||
aX = 0;
|
||||
if ((limitx || limity) && style->mTransform.IsRectilinear()) {
|
||||
// For rectilinear transforms, we can just get the transformed pattern
|
||||
// bounds and intersect them with the fill rectangle bounds.
|
||||
// TODO: If the transform is not rectilinear, then we would need a fully
|
||||
// general clip path to represent the X and Y clip planes bounding the
|
||||
// pattern. For such cases, it would be more efficient to rely on Skia's
|
||||
// Decal tiling mode rather than trying to generate a path. Until then,
|
||||
// just punt to relying on the default Clamp mode.
|
||||
gfx::Rect patternBounds(style->mSurface->GetRect());
|
||||
patternBounds = style->mTransform.TransformBounds(patternBounds);
|
||||
gfx::Rect bounds(aX, aY, aW, aH);
|
||||
// We always need to execute painting for non-over operators, even if
|
||||
// we end up with w/h = 0.
|
||||
bounds = bounds.Intersect(patternBounds);
|
||||
if (style->mTransform.HasNonAxisAlignedTransform()) {
|
||||
// If there is an rotation (90 or 270 degrees), the X axis of the
|
||||
// pattern projects onto the Y axis of the geometry, and vice versa.
|
||||
std::swap(limitx, limity);
|
||||
}
|
||||
if (aX + aW > patternSize.width) {
|
||||
aW = patternSize.width - aX;
|
||||
if (aW < 0) {
|
||||
aW = 0;
|
||||
}
|
||||
if (limitx) {
|
||||
aX = bounds.x;
|
||||
aW = bounds.width;
|
||||
}
|
||||
}
|
||||
if (limity) {
|
||||
if (aY < 0) {
|
||||
aH += aY;
|
||||
if (aH < 0) {
|
||||
aH = 0;
|
||||
}
|
||||
|
||||
aY = 0;
|
||||
}
|
||||
if (aY + aH > patternSize.height) {
|
||||
aH = patternSize.height - aY;
|
||||
if (aH < 0) {
|
||||
aH = 0;
|
||||
}
|
||||
if (limity) {
|
||||
aY = bounds.y;
|
||||
aH = bounds.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
Pattern Canvas<br>
|
||||
<canvas id="patternCanvas" style="border: 1px solid black" width="10" height="10"></canvas><br>
|
||||
Main Canvas (red square should be in top-left corner)<br>
|
||||
<canvas id="canvas" style="border: 1px solid black" width="100" height="100"></canvas>
|
||||
<script>
|
||||
|
||||
// Draw a 10x10 red rectangle that will be used for the pattern.
|
||||
const patternCanvas = document.getElementById("patternCanvas");
|
||||
const patternCtx = patternCanvas.getContext("2d");
|
||||
patternCtx.fillStyle = "red";
|
||||
patternCtx.fillRect(0, 0, patternCanvas.width, patternCanvas.height);
|
||||
|
||||
const canvas = document.getElementById("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
const pattern = ctx.createPattern(patternCanvas, "no-repeat");
|
||||
|
||||
ctx.fillStyle = pattern;
|
||||
// Fill the entire canvas with the pattern.
|
||||
ctx.fillRect(0, 0, 100, 100);
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
Pattern Canvas<br>
|
||||
<canvas id="patternCanvas" style="border: 1px solid black" width="10" height="10"></canvas><br>
|
||||
Main Canvas (red square should be in top-left corner)<br>
|
||||
<canvas id="canvas" style="border: 1px solid black" width="100" height="100"></canvas>
|
||||
<script>
|
||||
|
||||
// Draw a 10x10 red rectangle that will be used for the pattern.
|
||||
const patternCanvas = document.getElementById("patternCanvas");
|
||||
const patternCtx = patternCanvas.getContext("2d");
|
||||
patternCtx.fillStyle = "red";
|
||||
patternCtx.fillRect(0, 0, patternCanvas.width, patternCanvas.height);
|
||||
|
||||
const canvas = document.getElementById("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const height = 100;
|
||||
ctx.translate(0, height);
|
||||
|
||||
const pattern = ctx.createPattern(patternCanvas, "no-repeat");
|
||||
// Reverse translation applied to the canvas.
|
||||
pattern.setTransform((new DOMMatrix()).translate(0, -height));
|
||||
|
||||
ctx.fillStyle = pattern;
|
||||
// Fill the entire canvas with the pattern.
|
||||
ctx.fillRect(0, -height, 100, 100);
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -177,4 +177,4 @@ fuzzy-if(azureSkia,0-16,0-2) fuzzy-if(Android,0-3,0-40) fuzzy-if(/^Windows\x20NT
|
|||
skip-if(Android) == visible-occluded.html visible-occluded-ref.html
|
||||
|
||||
== 1678909-1.html 1678909-1-ref.html
|
||||
|
||||
== 1719886-1.html 1719886-1-ref.html
|
||||
|
|
Загрузка…
Ссылка в новой задаче