Skip to content

Commit 7d2a4ff

Browse files
committed
Don't scale and repeat a pattern as it may create weird results.
Fixes #1324.
1 parent 44cb95d commit 7d2a4ff

1 file changed

Lines changed: 43 additions & 18 deletions

File tree

Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -223,23 +223,43 @@ void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSiz
223223
image = clippedImageSurface.get();
224224
}
225225

226-
cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image);
227-
switch (imageInterpolationQuality) {
228-
case InterpolationQuality::DoNotInterpolate:
229-
case InterpolationQuality::Low:
230-
cairo_pattern_set_filter(pattern, CAIRO_FILTER_FAST);
231-
break;
232-
case InterpolationQuality::Default:
233-
cairo_pattern_set_filter(pattern, CAIRO_FILTER_BILINEAR);
234-
break;
235-
case InterpolationQuality::Medium:
236-
cairo_pattern_set_filter(pattern, CAIRO_FILTER_GOOD);
237-
break;
238-
case InterpolationQuality::High:
239-
cairo_pattern_set_filter(pattern, CAIRO_FILTER_BEST);
240-
break;
226+
// We can't sample a pattern with repeat and scale it at the same time with a filter better than fast,
227+
// because the filter will check the pixels around the target pixel, and if we're at the edge of the
228+
// pattern, the pixels checked are gotten with repeat from the other end of the pattern, which leads
229+
// to invalid results.
230+
// To avoid this we need to create a new pattern with the scaled size, so when sampling there's no
231+
// scaling involved.
232+
RefPtr<cairo_surface_t> scaledImageSurface;
233+
if (patternTransform.a() != 1.0 || patternTransform.d() != 1.0) {
234+
IntSize scaledImageSurfaceSize = enclosingIntRect(FloatRect(0, 0, tileRect.width() * patternTransform.a(), tileRect.height() * patternTransform.d())).size();
235+
scaledImageSurface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, scaledImageSurfaceSize.width(), scaledImageSurfaceSize.height()));
236+
RefPtr<cairo_t> scaledImageContext = adoptRef(cairo_create(scaledImageSurface.get()));
237+
238+
RefPtr<cairo_pattern_t> pattern = cairo_pattern_create_for_surface(image);
239+
switch (imageInterpolationQuality) {
240+
case InterpolationQuality::DoNotInterpolate:
241+
case InterpolationQuality::Low:
242+
cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_FAST);
243+
break;
244+
case InterpolationQuality::Default:
245+
cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_BILINEAR);
246+
break;
247+
case InterpolationQuality::Medium:
248+
cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_GOOD);
249+
break;
250+
case InterpolationQuality::High:
251+
cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_BEST);
252+
break;
253+
}
254+
cairo_pattern_set_extend(pattern.get(), CAIRO_EXTEND_PAD);
255+
cairo_matrix_t patternMatrix = { patternTransform.a(), 0, 0, patternTransform.d(), 0, 0 };
256+
cairo_matrix_invert(&patternMatrix);
257+
cairo_pattern_set_matrix(pattern.get(), &patternMatrix);
258+
cairo_set_source(scaledImageContext.get(), pattern.get());
259+
cairo_rectangle(scaledImageContext.get(), 0, 0, scaledImageSurfaceSize.width(), scaledImageSurfaceSize.height());
260+
cairo_fill(scaledImageContext.get());
261+
image = scaledImageSurface.get();
241262
}
242-
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
243263

244264
// Due to a limitation in pixman, cairo cannot handle transformation matrices with values bigger than 32768. If the value is
245265
// bigger, cairo is not able to paint anything, and this is the reason for the missing backgrounds reported in
@@ -271,14 +291,19 @@ void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSiz
271291
// Regarding the pattern matrix, what we do is reduce the translation component of the matrix taking advantage of the fact that we
272292
// are drawing a repeated pattern. This means that, assuming that (w, h) is the size of the pattern, samplig it at (x, y) is the same
273293
// than sampling it at (x mod w, y mod h), so we transform the translation component of the pattern matrix in that way.
274-
275-
cairo_matrix_t patternMatrix = toCairoMatrix(patternTransform);
276294
// dx and dy are added here as well to compensate the previous translation of the destination rectangle.
277295
double phaseOffsetX = phase.x() + tileRect.x() * patternTransform.a() + dx;
278296
double phaseOffsetY = phase.y() + tileRect.y() * patternTransform.d() + dy;
279297
// this is where we perform the (x mod w, y mod h) metioned above, but with floats instead of integers.
280298
phaseOffsetX -= std::trunc(phaseOffsetX / (tileRect.width() * patternTransform.a())) * tileRect.width() * patternTransform.a();
281299
phaseOffsetY -= std::trunc(phaseOffsetY / (tileRect.height() * patternTransform.d())) * tileRect.height() * patternTransform.d();
300+
301+
cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image);
302+
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
303+
cairo_matrix_t patternMatrix = toCairoMatrix(patternTransform);
304+
// If scale was needed, it was already done before.
305+
patternMatrix.xx = 1.0;
306+
patternMatrix.yy = 1.0;
282307
cairo_matrix_t phaseMatrix = {1, 0, 0, 1, phaseOffsetX, phaseOffsetY};
283308
cairo_matrix_t combined;
284309
cairo_matrix_multiply(&combined, &patternMatrix, &phaseMatrix);

0 commit comments

Comments
 (0)