Bug 723498 - Gimp changes contrast and color of images

Add color management options to the screenshot plug-in:

By default, it tries to tag the image with the monitor profile;
alternatively, there is an option to convert the image to sRGB.

This works mostly fine on *one* monitor given its profile is
configured correctly. With more than one monitor, funny things happen
depending on the platform and on what we are shooting (window, screen,
area). There are some FIXMEs left in the code.
This commit is contained in:
Michael Natterer
2017-01-31 21:26:44 +01:00
parent 10b67c5b3f
commit e518b9753d
6 changed files with 183 additions and 72 deletions

View File

@ -84,6 +84,7 @@ screenshot_gnome_shell_shoot (ScreenshotValues *shootvals,
const gchar *method = NULL; const gchar *method = NULL;
GVariant *args = NULL; GVariant *args = NULL;
GVariant *retval; GVariant *retval;
gint monitor = shootvals->monitor;
gboolean success; gboolean success;
if (shootvals->select_delay > 0) if (shootvals->select_delay > 0)
@ -99,6 +100,8 @@ screenshot_gnome_shell_shoot (ScreenshotValues *shootvals,
shootvals->show_cursor, shootvals->show_cursor,
TRUE, /* flash */ TRUE, /* flash */
filename); filename);
/* FIXME: figure profile */
break; break;
case SHOOT_REGION: case SHOOT_REGION:
@ -126,6 +129,11 @@ screenshot_gnome_shell_shoot (ScreenshotValues *shootvals,
shootvals->y2 - shootvals->y1, shootvals->y2 - shootvals->y1,
TRUE, /* flash */ TRUE, /* flash */
filename); filename);
monitor =
gdk_screen_get_monitor_at_point (screen,
(shootvals->x1 + shootvals->x2) / 2,
(shootvals->y1 + shootvals->y2) / 2);
break; break;
case SHOOT_WINDOW: case SHOOT_WINDOW:
@ -135,6 +143,8 @@ screenshot_gnome_shell_shoot (ScreenshotValues *shootvals,
shootvals->show_cursor, shootvals->show_cursor,
TRUE, /* flash */ TRUE, /* flash */
filename); filename);
/* FIXME: figure monitor */
break; break;
} }
@ -154,10 +164,20 @@ screenshot_gnome_shell_shoot (ScreenshotValues *shootvals,
if (success && filename) if (success && filename)
{ {
GimpColorProfile *profile;
*image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE, *image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
filename, filename); filename, filename);
gimp_image_set_filename (*image_ID, "screenshot.png"); gimp_image_set_filename (*image_ID, "screenshot.png");
profile = gimp_screen_get_color_profile (screen, monitor);
if (profile)
{
gimp_image_set_color_profile (*image_ID, profile);
g_object_unref (profile);
}
g_unlink (filename); g_unlink (filename);
g_free (filename); g_free (filename);

View File

@ -125,6 +125,9 @@ screenshot_osx_shoot (ScreenshotValues *shootvals,
if (system ((const char *) command) == EXIT_SUCCESS) if (system ((const char *) command) == EXIT_SUCCESS)
{ {
/* don't attach a profile, screencapture attached one
*/
*image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE, *image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
filename, filename); filename, filename);
gimp_image_set_filename (*image_ID, "screenshot.png"); gimp_image_set_filename (*image_ID, "screenshot.png");

View File

@ -133,24 +133,47 @@ screenshot_win32_shoot (ScreenshotValues *shootvals,
gint32 *image_ID, gint32 *image_ID,
GError **error) GError **error)
{ {
GimpPDBStatusType status = GIMP_PDB_EXECUTION_ERROR;
/* leave "shootvals->monitor" alone until somebody patches the code
* to be able to get a monitor's color profile
*/
image_id = image_ID; image_id = image_ID;
winsnapvals.delay = shootvals->select_delay; winsnapvals.delay = shootvals->select_delay;
if (shootvals->shoot_type == SHOOT_ROOT) if (shootvals->shoot_type == SHOOT_ROOT)
{ {
doCapture(0); doCapture (0);
return GIMP_PDB_SUCCESS;
status = GIMP_PDB_SUCCESS;
} }
else if (shootvals->shoot_type == SHOOT_WINDOW) else if (shootvals->shoot_type == SHOOT_WINDOW)
{ {
doWindowCapture(); doWindowCapture ();
return GIMP_PDB_SUCCESS;
status = GIMP_PDB_SUCCESS;
} }
else if (shootvals->shoot_type == SHOOT_REGION) else if (shootvals->shoot_type == SHOOT_REGION)
return GIMP_PDB_EXECUTION_ERROR; {
/* FIXME */
}
return GIMP_PDB_EXECUTION_ERROR; if (status == GIMP_PDB_SUCCESS)
{
GimpColorProfile *profile;
profile = gimp_screen_get_color_profile (screen, monitor);
if (profile)
{
gimp_image_set_color_profile (*image_ID, profile);
g_object_unref (profile);
}
}
return status;
} }
@ -213,25 +236,25 @@ sendBMPToGimp(HBITMAP hBMP, HDC hDC, RECT rect)
/* Check that we got the memory */ /* Check that we got the memory */
if (!capBytes) if (!capBytes)
{ {
g_message (_("No data captured")); g_message (_("No data captured"));
return; return;
} }
/* Flip the red and blue bytes */ /* Flip the red and blue bytes */
flipRedAndBlueBytes(width, height); flipRedAndBlueBytes (width, height);
/* Set up the image and layer types */ /* Set up the image and layer types */
imageType = GIMP_RGB; imageType = GIMP_RGB;
layerType = GIMP_RGB_IMAGE; layerType = GIMP_RGB_IMAGE;
/* Create the GIMP image and layers */ /* Create the GIMP image and layers */
new_image_id = gimp_image_new(width, height, imageType); new_image_id = gimp_image_new (width, height, imageType);
layer_id = gimp_layer_new(new_image_id, _("Background"), layer_id = gimp_layer_new (new_image_id, _("Background"),
ROUND4(width), height, ROUND4 (width), height,
layerType, layerType,
100, GIMP_LAYER_MODE_NORMAL); 100, GIMP_LAYER_MODE_NORMAL);
gimp_image_insert_layer(new_image_id, layer_id, -1, 0); gimp_image_insert_layer (new_image_id, layer_id, -1, 0);
/* make rectangle */ /* make rectangle */
rectangle = g_new (GeglRectangle, 1); rectangle = g_new (GeglRectangle, 1);
@ -244,17 +267,18 @@ sendBMPToGimp(HBITMAP hBMP, HDC hDC, RECT rect)
buffer = gimp_drawable_get_buffer (layer_id); buffer = gimp_drawable_get_buffer (layer_id);
/* fill the buffer */ /* fill the buffer */
gegl_buffer_set (buffer, rectangle, 0, NULL, (guchar *) capBytes, GEGL_AUTO_ROWSTRIDE); gegl_buffer_set (buffer, rectangle, 0, NULL, (guchar *) capBytes,
GEGL_AUTO_ROWSTRIDE);
/* flushing data */ /* flushing data */
gegl_buffer_flush (buffer); gegl_buffer_flush (buffer);
/* Now resize the layer down to the correct size if necessary. */ /* Now resize the layer down to the correct size if necessary. */
if (width != ROUND4(width)) { if (width != ROUND4 (width))
gimp_layer_resize (layer_id, width, height, 0, 0); {
gimp_image_resize (new_image_id, width, height, 0, 0); gimp_layer_resize (layer_id, width, height, 0, 0);
} gimp_image_resize (new_image_id, width, height, 0, 0);
/* Finish up */ }
*image_id = new_image_id; *image_id = new_image_id;

View File

@ -553,17 +553,19 @@ screenshot_x11_shoot (ScreenshotValues *shootvals,
gint32 *image_ID, gint32 *image_ID,
GError **error) GError **error)
{ {
GdkDisplay *display; GdkDisplay *display;
GdkWindow *window; GdkWindow *window;
cairo_surface_t *screenshot; cairo_surface_t *screenshot;
cairo_region_t *shape = NULL; cairo_region_t *shape = NULL;
cairo_t *cr; cairo_t *cr;
GdkRectangle rect; GimpColorProfile *profile;
GdkRectangle screen_rect; GdkRectangle rect;
gchar *name = NULL; GdkRectangle screen_rect;
gint screen_x; gchar *name = NULL;
gint screen_y; gint screen_x;
gint x, y; gint screen_y;
gint monitor = shootvals->monitor;
gint x, y;
/* use default screen if we are running non-interactively */ /* use default screen if we are running non-interactively */
if (screen == NULL) if (screen == NULL)
@ -589,21 +591,29 @@ screenshot_x11_shoot (ScreenshotValues *shootvals,
if (shootvals->shoot_type == SHOOT_REGION) if (shootvals->shoot_type == SHOOT_REGION)
{ {
rect.x = MIN (shootvals->x1, shootvals->x2); rect.x = MIN (shootvals->x1, shootvals->x2);
rect.y = MIN (shootvals->y1, shootvals->y2); rect.y = MIN (shootvals->y1, shootvals->y2);
rect.width = ABS (shootvals->x2 - shootvals->x1); rect.width = ABS (shootvals->x2 - shootvals->x1);
rect.height = ABS (shootvals->y2 - shootvals->y1); rect.height = ABS (shootvals->y2 - shootvals->y1);
monitor = gdk_screen_get_monitor_at_point (screen,
rect.x + rect.width / 2,
rect.y + rect.height / 2);
} }
else else
{ {
if (shootvals->shoot_type == SHOOT_ROOT) if (shootvals->shoot_type == SHOOT_ROOT)
{ {
window = gdk_screen_get_root_window (screen); window = gdk_screen_get_root_window (screen);
/* FIXME: figure monitor */
} }
else else
{ {
window = gdk_x11_window_foreign_new_for_display (display, window = gdk_x11_window_foreign_new_for_display (display,
shootvals->window_id); shootvals->window_id);
monitor = gdk_screen_get_monitor_at_window (screen, window);
} }
if (! window) if (! window)
@ -665,6 +675,14 @@ screenshot_x11_shoot (ScreenshotValues *shootvals,
if (shootvals->shoot_type == SHOOT_ROOT && shootvals->show_cursor) if (shootvals->shoot_type == SHOOT_ROOT && shootvals->show_cursor)
add_cursor_image (*image_ID, display); add_cursor_image (*image_ID, display);
profile = gimp_screen_get_color_profile (screen, monitor);
if (profile)
{
gimp_image_set_color_profile (*image_ID, profile);
g_object_unref (profile);
}
return GIMP_PDB_SUCCESS; return GIMP_PDB_SUCCESS;
} }

View File

@ -77,12 +77,14 @@ static ScreenshotValues shootvals =
SHOOT_WINDOW, /* root window */ SHOOT_WINDOW, /* root window */
TRUE, /* include WM decorations */ TRUE, /* include WM decorations */
0, /* window ID */ 0, /* window ID */
0, /* monitor */
0, /* select delay */ 0, /* select delay */
0, /* coords of region dragged out by pointer */ 0, /* coords of region dragged out by pointer */
0, 0,
0, 0,
0, 0,
FALSE /* show cursor */ FALSE, /* show cursor */
SCREENSHOT_PROFILE_POLICY_MONITOR
}; };
const GimpPlugInInfo PLUG_IN_INFO = const GimpPlugInInfo PLUG_IN_INFO =
@ -217,7 +219,7 @@ run (const gchar *name,
gimp_get_data (PLUG_IN_PROC, &shootvals); gimp_get_data (PLUG_IN_PROC, &shootvals);
shootvals.window_id = 0; shootvals.window_id = 0;
/* Get information from the dialog */ /* Get information from the dialog */
if (! shoot_dialog (&screen)) if (! shoot_dialog (&screen))
status = GIMP_PDB_CANCEL; status = GIMP_PDB_CANCEL;
break; break;
@ -253,7 +255,9 @@ run (const gchar *name,
{ {
if (shootvals.shoot_type == SHOOT_WINDOW || if (shootvals.shoot_type == SHOOT_WINDOW ||
shootvals.shoot_type == SHOOT_REGION) shootvals.shoot_type == SHOOT_REGION)
status = GIMP_PDB_CALLING_ERROR; {
status = GIMP_PDB_CALLING_ERROR;
}
} }
break; break;
@ -275,6 +279,17 @@ run (const gchar *name,
{ {
gchar *comment = gimp_get_default_comment (); gchar *comment = gimp_get_default_comment ();
if (shootvals.profile_policy == SCREENSHOT_PROFILE_POLICY_SRGB)
{
GimpColorProfile *srgb_profile = gimp_color_profile_new_rgb_srgb ();
gimp_image_convert_color_profile (image_ID,
srgb_profile,
GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
TRUE);
g_object_unref (srgb_profile);
}
if (comment) if (comment)
{ {
GimpParasite *parasite; GimpParasite *parasite;
@ -435,28 +450,13 @@ shoot_dialog (GdkScreen **screen)
main_vbox, FALSE, FALSE, 0); main_vbox, FALSE, FALSE, 0);
gtk_widget_show (main_vbox); gtk_widget_show (main_vbox);
/* Hints */
/* Create delay hints notebook early */
notebook = g_object_new (GTK_TYPE_NOTEBOOK, notebook = g_object_new (GTK_TYPE_NOTEBOOK,
"show-border", FALSE, "show-border", FALSE,
"show-tabs", FALSE, "show-tabs", FALSE,
NULL); NULL);
gtk_box_pack_end (GTK_BOX (main_vbox), notebook, FALSE, FALSE, 0);
gtk_widget_show (notebook);
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook), SHOOT_ROOT,
_("After the delay, the screenshot is taken."));
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook), SHOOT_REGION,
_("After the delay, drag your mouse to select "
"the region for the screenshot."));
#ifdef G_OS_WIN32
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook), SHOOT_WINDOW,
_("Click in a window to snap it after delay."));
#else
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook), SHOOT_WINDOW,
_("At the end of the delay, click in a window "
"to snap it."));
#endif
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), shootvals.shoot_type);
/* Area */ /* Area */
frame = gimp_frame_new (_("Area")); frame = gimp_frame_new (_("Area"));
@ -467,8 +467,7 @@ shoot_dialog (GdkScreen **screen)
gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_container_add (GTK_CONTAINER (frame), vbox);
gtk_widget_show (vbox); gtk_widget_show (vbox);
/* Aingle window */
/* single window */
button = gtk_radio_button_new_with_mnemonic (radio_group, button = gtk_radio_button_new_with_mnemonic (radio_group,
_("Take a screenshot of " _("Take a screenshot of "
"a single _window")); "a single _window"));
@ -483,7 +482,7 @@ shoot_dialog (GdkScreen **screen)
G_CALLBACK (shoot_radio_button_toggled), G_CALLBACK (shoot_radio_button_toggled),
notebook); notebook);
/* window decorations */ /* Window decorations */
if (capabilities & SCREENSHOT_CAN_SHOOT_DECORATIONS) if (capabilities & SCREENSHOT_CAN_SHOOT_DECORATIONS)
{ {
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
@ -508,7 +507,7 @@ shoot_dialog (GdkScreen **screen)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
shootvals.shoot_type == SHOOT_WINDOW); shootvals.shoot_type == SHOOT_WINDOW);
/* whole screen */ /* Whole screen */
button = gtk_radio_button_new_with_mnemonic (radio_group, button = gtk_radio_button_new_with_mnemonic (radio_group,
_("Take a screenshot of " _("Take a screenshot of "
"the entire _screen")); "the entire _screen"));
@ -523,7 +522,7 @@ shoot_dialog (GdkScreen **screen)
G_CALLBACK (shoot_radio_button_toggled), G_CALLBACK (shoot_radio_button_toggled),
notebook); notebook);
/* mouse pointer */ /* Mouse pointer */
if (capabilities & SCREENSHOT_CAN_SHOOT_POINTER) if (capabilities & SCREENSHOT_CAN_SHOOT_POINTER)
{ {
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
@ -548,7 +547,7 @@ shoot_dialog (GdkScreen **screen)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
shootvals.shoot_type == SHOOT_ROOT); shootvals.shoot_type == SHOOT_ROOT);
/* dragged region */ /* Dragged region */
if (capabilities & SCREENSHOT_CAN_SHOOT_REGION) if (capabilities & SCREENSHOT_CAN_SHOOT_REGION)
{ {
button = gtk_radio_button_new_with_mnemonic (radio_group, button = gtk_radio_button_new_with_mnemonic (radio_group,
@ -592,11 +591,50 @@ shoot_dialog (GdkScreen **screen)
G_CALLBACK (gimp_int_adjustment_update), G_CALLBACK (gimp_int_adjustment_update),
&shootvals.select_delay); &shootvals.select_delay);
/* this is the unit label of a spinbutton */ /* translators: this is the unit label of a spinbutton */
label = gtk_label_new (_("seconds")); label = gtk_label_new (_("seconds"));
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label); gtk_widget_show (label);
/* Delay hints */
gtk_box_pack_start (GTK_BOX (vbox), notebook, FALSE, FALSE, 0);
gtk_widget_show (notebook);
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook), SHOOT_ROOT,
_("After the delay, the screenshot is taken."));
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook), SHOOT_REGION,
_("After the delay, drag your mouse to select "
"the region for the screenshot."));
#ifdef G_OS_WIN32
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook), SHOOT_WINDOW,
_("Click in a window to snap it after delay."));
#else
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook), SHOOT_WINDOW,
_("At the end of the delay, click in a window "
"to snap it."));
#endif
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), shootvals.shoot_type);
/* Color profile */
frame = gimp_int_radio_group_new (TRUE,
_("Color Profile"),
G_CALLBACK (gimp_radio_button_update),
&shootvals.profile_policy,
SCREENSHOT_PROFILE_POLICY_MONITOR,
_("Tag image with _monitor profile"),
SCREENSHOT_PROFILE_POLICY_MONITOR,
NULL,
_("Convert image to sR_GB"),
SCREENSHOT_PROFILE_POLICY_SRGB,
NULL,
NULL);
gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
gtk_widget_show (dialog); gtk_widget_show (dialog);
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK); run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);

View File

@ -36,6 +36,12 @@ typedef enum
SCREENSHOT_CAN_SHOOT_REGION = 0x1 << 3 SCREENSHOT_CAN_SHOOT_REGION = 0x1 << 3
} ScreenshotCapabilities; } ScreenshotCapabilities;
typedef enum
{
SCREENSHOT_PROFILE_POLICY_MONITOR,
SCREENSHOT_PROFILE_POLICY_SRGB
} ScreenshotProfilePolicy;
typedef enum typedef enum
{ {
SHOOT_ROOT, SHOOT_ROOT,
@ -45,15 +51,17 @@ typedef enum
typedef struct typedef struct
{ {
ShootType shoot_type; ShootType shoot_type;
gboolean decorate; gboolean decorate;
guint window_id; guint window_id;
guint select_delay; gint monitor;
gint x1; guint select_delay;
gint y1; gint x1;
gint x2; gint y1;
gint y2; gint x2;
gboolean show_cursor; gint y2;
gboolean show_cursor;
ScreenshotProfilePolicy profile_policy;
} ScreenshotValues; } ScreenshotValues;