CAIRO_STATUS_INVALID_MATRIX: Pattern matrix cache invalidation in cairo_pattern
0 reputation · 30 May 2021, 13:32 UTC
0 reputation · 30 May 2021, 13:32 UTC
The Cairo graphics library utilizes an internal cache for transformed coordinates in linear and radial gradients to optimize rendering performance. When a pattern's matrix is modified using cairo_pattern_set_matrix, the library must determine when to invalidate this cache to ensure rendering correctness.
In libcairo 1.18.x, there is an unresolved behavior regarding whether the cache is automatically refreshed upon matrix updates. This creates uncertainty when patterns are reused across multiple draw calls after their transformation matrices have been altered, as some backends may continue to render using stale cached coordinates.
cairo_pattern component guarantee cache invalidation immediately following a cairo_pattern_set_matrix call?33275 reputation · 30 May 2021, 20:52 UTC
cairo_pattern_set_matrixIn Cairo 1.18.x the pattern object keeps an internal dirty flag that is set whenever cairo_pattern_set_matrix() is called. The flag tells every backend that the cached transformed coordinates for linear and radial gradients are stale and must be recomputed on the next paint operation. Therefore, the library itself guarantees that the cache is marked for invalidation immediately after a matrix change.
dirty flag but keeps the previously computed coordinates in a small per‑pattern buffer. When the flag is set, the buffer is cleared on the next cairo_xcb_surface_flush() or when the pattern is re‑set as the source. If a pattern is reused across multiple draw calls without resetting it as the source, the XCB backend may continue to use the stale buffer until the surface is flushed.cairo_win32_surface_flush(). When the dirty flag is set, the backend discards any cached data immediately, so subsequent paint operations will always use the updated matrix. However, if a pattern is applied to a context, changed, and then used again without resetting it as the source, the Win32 backend may still reference the old coordinates until the surface is flushed.dirty flag on matrix change.CAIRO_STATUS_INVALID_MATRIX if the new matrix contains non‑finite values.cairo_pattern_t.
cairo_pattern_t *pat = cairo_pattern_create_radial(...);
cairo_matrix_t m;
// Initialize m to the desired transform
cairo_pattern_set_matrix(pat, &m);
cairo_set_source(cr, pat);
cairo_set_source(cr, pat); // re‑set to clear the dirty flag
cairo_surface_flush(surface);
To decide whether you need to re‑set the pattern or flush the surface, please confirm:
If the answer is yes, re‑setting the pattern as the source or flushing the surface immediately after the matrix change is the safest approach.
Use comments to ask for clarification. Post a solution as an answer.
33,275 reputation · 30 May 2021, 18:27 UTC
One clarification worth separating from the cache question: CAIRO_STATUS_INVALID_MATRIX is not a symptom of stale cached coordinates. It is returned by cairo_pattern_status() when the matrix you passed to cairo_pattern_set_matrix() is singular (non-invertible) — for example a scale of zero on an axis. In that case the draw fails outright rather than rendering with old data, so if you are seeing that status code, check the matrix itself before suspecting a backend cache bug.
For the actual invalidation question, the practical test is to render the same gradient twice on the image backend (the reference implementation) — once before and once after cairo_pattern_set_matrix() — then repeat on XCB or Win32 surfaces and diff the pixels. If the image backend updates but another backend does not, you have a genuine stale-cache issue to report upstream; if all backends agree, the behavior is consistent and the uncertainty in 1.18.x is likely about a specific fallback path rather than the general case. Calling cairo_pattern_get_matrix() after the set also confirms the new matrix actually round-tripped.