app: fix a crash when converting to higher precision.

gimp_display_shell_render() writes to a GeglBuffer backed by allocated memory
(shell->profile_data). Unfortunately while converting prevision in
gimp_image_convert_precision(), we change the "precision" property (hence the
source format) first, hence end up trying to write data in a too small buffer.
This crash was hard to find as it was not showing up on my machine (though it
did produce rendering artifacts!), unless I built both GIMP and babl with
`b_sanitize=address`.

Note that an alternate fix was to make sure that the profile_data buffer is big
enough (by calling gimp_display_shell_profile_update() before rendering), but
anyway the image is in an inconsistent state while conversion is in progress:
whereas the `src_format` is the new one, the `src_profile` is still the old one
(and cannot be changed before we finish converting).

Moreover the render happen regularly on progress signals, once after each
converted drawable. So each of these rendering step happens in an inconsistent
state, with the wrong profile set, some of the drawables converted and others
not yet.
We could still render properly if each drawable's buffer used space-aware format
(thus allowing different drawables to use different profiles/spaces), but it
feels over-engineering the problem. It might be much better to ignore rendering
steps while converting the image precision. Moreover it would obviously make a
faster conversion.

See discussions in #9136 for this crash, which didn't have dedicated report
AFAIK.
This commit is contained in:
Jehan
2023-02-19 13:54:03 +01:00
parent 1022dcbcd2
commit de25be9210
6 changed files with 70 additions and 3 deletions

View File

@ -274,13 +274,17 @@ gimp_display_shell_canvas_expose (GtkWidget *widget,
/* ignore events on overlays */
if (eevent->window == gtk_widget_get_window (widget))
{
cairo_t *cr;
GimpImage *image = gimp_display_get_image (shell->display);
cairo_t *cr;
cr = gdk_cairo_create (gtk_widget_get_window (shell->canvas));
gdk_cairo_region (cr, eevent->region);
cairo_clip (cr);
if (gimp_display_get_image (shell->display))
/* If we are currently converting the image, it might be in inconsistent
* state and should not be redrawn.
*/
if (image != NULL && ! gimp_image_get_converting (image))
{
gimp_display_shell_canvas_draw_image (shell, cr);
}

View File

@ -75,6 +75,15 @@ gimp_display_shell_render (GimpDisplayShell *shell,
g_return_if_fail (scale > 0.0);
image = gimp_display_get_image (shell->display);
/* While converting, the render can be wrong; but worse, we rely on allocated
* data which might be the wrong size and this was a crash we had which was
* hard to diagnose as it doesn't always crash immediately (see discussions in
* #9136). This is why this assert is important. We want to make sure we never
* call this when the shell's image is in the inconsistent "converting" state.
*/
g_return_if_fail (! gimp_image_get_converting (image));
buffer = gimp_pickable_get_buffer (
gimp_display_shell_get_pickable (shell));
#ifdef USE_NODE_BLIT