@@ -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