Fix #601: Show over-/underexposed
authorColin Clark <colin.clark@cclark.uk>
Tue, 9 Jul 2019 18:03:31 +0000 (19:03 +0100)
committerColin Clark <colin.clark@cclark.uk>
Tue, 9 Jul 2019 18:03:31 +0000 (19:03 +0100)
https://github.com/BestImageViewer/geeqie/issues/601

Menu View/Over-Under Exposed, or keystroke Shift-E

14 files changed:
doc/docbook/GuideMainWindowMenus.xml
doc/docbook/GuideReferenceKeyboardShortcuts.xml
src/icons/Makefile.am
src/icons/icon_exposure.png [new file with mode: 0644]
src/image.c
src/image.h
src/layout_image.c
src/layout_image.h
src/layout_util.c
src/options.h
src/pixbuf_util.c
src/pixbuf_util.h
src/toolbar.c
src/typedefs.h

index ff883ba..0c58c5c 100644 (file)
           <para>Toggles the full screen window display.</para>\r
         </listitem>\r
       </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <menuchoice>\r
+            <shortcut>\r
+              <keycombo>\r
+                <keycap>Shift</keycap>\r
+                <keycap>E</keycap>\r
+              </keycombo>\r
+            </shortcut>\r
+            <guimenu>Over/Under Exposure</guimenu>\r
+          </menuchoice>\r
+        </term>\r
+        <listitem>\r
+          <para>\r
+            Toggles highlighting of pixels that are either over or under exposed. This function operates on the displayed pixels. If a raw file is being displayed, the embedded jpeg data is being used. If the image is zoomed, the zoom type affects the results. Two-pass rendering also has some influence.\r
+            <para />\r
+            If a pixel has any channel at zero or full-scale, the pixel is displayed in red.\r
+          </para>\r
+        </listitem>\r
+      </varlistentry>\r
       <varlistentry>\r
         <term>\r
           <guimenu>Keywords</guimenu>\r
index ee8b6bb..7b5993d 100644 (file)
             <entry />\r
             <entry>Toggle Keywords sidebar.</entry>\r
           </row>\r
+          <row>\r
+            <entry>\r
+              <code>\r
+                Shift +\r
+                <keycap>E</keycap>\r
+              </code>\r
+            </entry>\r
+            <entry />\r
+            <entry>Toggle highlighting of pixels that are over or under exposed.</entry>\r
+          </row>\r
           <row>\r
             <entry>\r
               <keycap>V</keycap>\r
index e7a3f36..2ecc437 100644 (file)
@@ -49,7 +49,8 @@ ICONS_INLINE = \
        icon_original.png \
        icon_trash.png \
        icon_heic.png \
-       icon_grayscale.png
+       icon_grayscale.png \
+       icon_exposure.png
 
 ICONS_INLINE_PAIRS = \
        folder_closed           $(srcdir)/folder_closed.png     \
@@ -97,7 +98,8 @@ ICONS_INLINE_PAIRS = \
        icon_original   $(srcdir)/icon_original.png \
        icon_trash      $(srcdir)/icon_trash.png \
        icon_heic       $(srcdir)/icon_heic.png \
-       icon_grayscale  $(srcdir)/icon_grayscale.png
+       icon_grayscale  $(srcdir)/icon_grayscale.png \
+       icon_exposure   $(srcdir)/icon_exposure.png
 
 icons_inline.h: $(ICONS_INLINE) Makefile.in
        @sh -ec "echo '/* Auto generated file, do not edit */'; echo; \
diff --git a/src/icons/icon_exposure.png b/src/icons/icon_exposure.png
new file mode 100644 (file)
index 0000000..05d55a1
Binary files /dev/null and b/src/icons/icon_exposure.png differ
index 57670ab..31fb26a 100644 (file)
@@ -564,7 +564,7 @@ static void image_post_process_tile_color_cb(PixbufRenderer *pr, GdkPixbuf **pix
        ImageWindow *imd = (ImageWindow *)data;
        if (imd->cm) color_man_correct_region(imd->cm, *pixbuf, x, y, w, h);
        if (imd->desaturate) pixbuf_desaturate_rect(*pixbuf, x, y, w, h);
-
+       if (imd->overunderexposed) pixbuf_highlight_overunderexposed(*pixbuf, x, y, w, h);
 }
 
 void image_alter_orientation(ImageWindow *imd, FileData *fd_n, AlterType type)
@@ -658,7 +658,7 @@ void image_alter_orientation(ImageWindow *imd, FileData *fd_n, AlterType type)
 void image_set_desaturate(ImageWindow *imd, gboolean desaturate)
 {
        imd->desaturate = desaturate;
-       if (imd->cm || imd->desaturate)
+       if (imd->cm || imd->desaturate || imd->overunderexposed)
                pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, image_post_process_tile_color_cb, (gpointer) imd, (imd->cm != NULL) );
        else
                pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, NULL, NULL, TRUE);
@@ -670,6 +670,21 @@ gboolean image_get_desaturate(ImageWindow *imd)
        return imd->desaturate;
 }
 
+void image_set_overunderexposed(ImageWindow *imd, gboolean overunderexposed)
+{
+       imd->overunderexposed = overunderexposed;
+       if (imd->cm || imd->desaturate || imd->overunderexposed)
+               pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, image_post_process_tile_color_cb, (gpointer) imd, (imd->cm != NULL) );
+       else
+               pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, NULL, NULL, TRUE);
+       pixbuf_renderer_set_orientation((PixbufRenderer *)imd->pr, imd->orientation);
+}
+
+gboolean image_get_overunderexposed(ImageWindow *imd)
+{
+       return imd->overunderexposed;
+}
+
 /*
  *-------------------------------------------------------------------
  * read ahead (prebuffer)
@@ -1349,7 +1364,7 @@ void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom, gboo
                image_post_process_color(imd, 0, FALSE); /* TODO: error handling */
                }
 
-       if (imd->cm || imd->desaturate)
+       if (imd->cm || imd->desaturate || imd->overunderexposed)
                pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, image_post_process_tile_color_cb, (gpointer) imd, (imd->cm != NULL) );
 
        image_state_set(imd, IMAGE_STATE_IMAGE);
@@ -1467,7 +1482,7 @@ void image_move_from_image(ImageWindow *imd, ImageWindow *source)
 
        pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
 
-       if (imd->cm || imd->desaturate)
+       if (imd->cm || imd->desaturate || imd->overunderexposed)
                pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, image_post_process_tile_color_cb, (gpointer) imd, (imd->cm != NULL) );
        else
                pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, NULL, NULL, TRUE);
@@ -1529,7 +1544,7 @@ void image_copy_from_image(ImageWindow *imd, ImageWindow *source)
 
        pixbuf_renderer_copy(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
 
-       if (imd->cm || imd->desaturate)
+       if (imd->cm || imd->desaturate || imd->overunderexposed)
                pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, image_post_process_tile_color_cb, (gpointer) imd, (imd->cm != NULL) );
        else
                pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, NULL, NULL, TRUE);
index f3c48cb..9933aa2 100644 (file)
@@ -88,6 +88,8 @@ void image_set_scroll_center(ImageWindow *imd, gdouble x, gdouble y);
 void image_alter_orientation(ImageWindow *imd, FileData *fd, AlterType type);
 void image_set_desaturate(ImageWindow *imd, gboolean desaturate);
 gboolean image_get_desaturate(ImageWindow *imd);
+void image_set_overunderexposed(ImageWindow *imd, gboolean overunderexposed);
+gboolean image_get_overunderexposed(ImageWindow *imd);
 
 /* zoom */
 void image_zoom_adjust(ImageWindow *imd, gdouble increment);
index 570f77b..45544c6 100644 (file)
@@ -1231,6 +1231,20 @@ gboolean layout_image_get_desaturate(LayoutWindow *lw)
        return image_get_desaturate(lw->image);
 }
 
+void layout_image_set_overunderexposed(LayoutWindow *lw, gboolean overunderexposed)
+{
+       if (!layout_valid(&lw)) return;
+
+       image_set_overunderexposed(lw->image, overunderexposed);
+}
+
+gboolean layout_image_get_overunderexposed(LayoutWindow *lw)
+{
+       if (!layout_valid(&lw)) return FALSE;
+
+       return image_get_overunderexposed(lw->image);
+}
+
 /* stereo */
 /*
 gint layout_image_stereo_get(LayoutWindow *lw)
index 347c547..a284c7c 100644 (file)
@@ -63,6 +63,8 @@ void layout_image_zoom_set_fill_geometry(LayoutWindow *lw, gboolean vertical, gb
 void layout_image_alter_orientation(LayoutWindow *lw, AlterType type);
 void layout_image_set_desaturate(LayoutWindow *lw, gboolean desaturate);
 gboolean layout_image_get_desaturate(LayoutWindow *lw);
+void layout_image_set_overunderexposed(LayoutWindow *lw, gboolean overunderexposed);
+gboolean layout_image_get_overunderexposed(LayoutWindow *lw);
 
 void layout_image_rating(LayoutWindow *lw, const gchar *rating);
 
index 70e27e5..5eb32fb 100644 (file)
@@ -545,6 +545,13 @@ static void layout_menu_select_rectangle_cb(GtkToggleAction *action, gpointer da
        options->draw_rectangle = gtk_toggle_action_get_active(action);
 }
 
+static void layout_menu_select_overunderexposed_cb(GtkToggleAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+
+       layout_image_set_overunderexposed(lw, gtk_toggle_action_get_active(action));
+}
+
 static void layout_menu_write_rotate(GtkToggleAction *action, gpointer data, gboolean keep_date)
 {
        LayoutWindow *lw = data;
@@ -2020,6 +2027,7 @@ static GtkToggleActionEntry menu_toggle_entries[] = {
   { "Animate", NULL,   N_("GIF _animation"),           "A",                    N_("Toggle GIF animation"),                     CB(layout_menu_animate_cb),      FALSE  },
   { "ExifRotate",      GTK_STOCK_ORIENTATION_PORTRAIT,                 N_("_Exif rotate"),             "<alt>X",               N_("Exif rotate"),                      CB(layout_menu_exif_rotate_cb), FALSE },
   { "DrawRectangle",   PIXBUF_INLINE_ICON_DRAW_RECTANGLE,                      N_("Draw Rectangle"),           NULL,           N_("Draw Rectangle"),                   CB(layout_menu_select_rectangle_cb), FALSE },
+  { "OverUnderExposed",        PIXBUF_INLINE_ICON_EXPOSURE,    N_("Over/Under Exposed"),       "<shift>E",             N_("Over/Under Exposed"),               CB(layout_menu_select_overunderexposed_cb), FALSE },
 };
 
 static GtkRadioActionEntry menu_radio_entries[] = {
@@ -2274,6 +2282,7 @@ static const gchar *menu_ui_description =
 "        <menuitem action='HistogramModeLog'/>"
 "        <menuitem action='HistogramModeCycle'/>"
 "      </menu>"
+"      <menuitem action='OverUnderExposed'/>"
 "      <menuitem action='FullScreen'/>"
 "      <placeholder name='ViewSection'/>"
 "      <separator/>"
@@ -3152,6 +3161,9 @@ static void layout_util_sync_views(LayoutWindow *lw)
        action = gtk_action_group_get_action(lw->action_group, "ExifRotate");
        gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), options->image.exif_rotate_enable);
 
+       action = gtk_action_group_get_action(lw->action_group, "OverUnderExposed");
+       gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), options->overunderexposed);
+
        action = gtk_action_group_get_action(lw->action_group, "DrawRectangle");
        gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), options->draw_rectangle);
 
index 65e8b74..722ff22 100644 (file)
@@ -38,6 +38,7 @@ struct _ConfOptions
        gboolean show_guidelines;
        gboolean draw_rectangle;
        gboolean show_predefined_keyword_tree;
+       gboolean overunderexposed;
 
        /* various */
        gboolean tree_descend_subdirs;
index d758c3e..1a16759 100644 (file)
@@ -148,6 +148,7 @@ static PixbufInline inline_pixbuf_data[] = {
        { PIXBUF_INLINE_ICON_TRASH,     icon_trash },
        { PIXBUF_INLINE_ICON_HEIF,      icon_heic },
        { PIXBUF_INLINE_ICON_GRAYSCALE, icon_grayscale },
+       { PIXBUF_INLINE_ICON_EXPOSURE,  icon_exposure },
        { NULL, NULL }
 };
 
@@ -1459,4 +1460,52 @@ void pixbuf_desaturate_rect(GdkPixbuf *pb,
                        }
                }
 }
+
+/*
+ *-----------------------------------------------------------------------------
+ * pixbuf highlight under/over exposure *-----------------------------------------------------------------------------
+ */
+void pixbuf_highlight_overunderexposed(GdkPixbuf *pb, gint x, gint y, gint w, gint h)
+{
+       gboolean has_alpha;
+       gint pw, ph, prs;
+       guchar *p_pix;
+       guchar *pp;
+       gint i, j;
+
+       if (!pb) return;
+
+       pw = gdk_pixbuf_get_width(pb);
+       ph = gdk_pixbuf_get_height(pb);
+
+       if (x < 0 || x + w > pw) return;
+       if (y < 0 || y + h > ph) return;
+
+       has_alpha = gdk_pixbuf_get_has_alpha(pb);
+       prs = gdk_pixbuf_get_rowstride(pb);
+       p_pix = gdk_pixbuf_get_pixels(pb);
+
+       for (i = 0; i < h; i++)
+               {
+               pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
+               for (j = 0; j < w; j++)
+                       {
+                       if (pp[0] == 255 || pp[1] == 255 || pp[2] == 255 || pp[0] == 0 || pp[1] == 0 || pp[2] == 0)
+                               {
+                               *pp = 255;
+                               pp++;
+                               *pp = 0;
+                               pp++;
+                               *pp = 0;
+                               pp++;
+                               if (has_alpha) pp++;
+                               }
+                       else
+                               {
+                               pp = pp + 3;
+                               if (has_alpha) pp++;
+                               }
+                       }
+               }
+}
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index e3142ed..bcec3ae 100644 (file)
@@ -76,6 +76,7 @@ gboolean pixbuf_scale_aspect(gint req_w, gint req_h, gint old_w, gint old_h, gin
 #define PIXBUF_INLINE_ICON_TRASH       "icon_trash"
 #define PIXBUF_INLINE_ICON_HEIF        "icon_heic"
 #define PIXBUF_INLINE_ICON_GRAYSCALE   "icon_grayscale"
+#define PIXBUF_INLINE_ICON_EXPOSURE            "icon_exposure"
 
 #define PIXBUF_INLINE_ICON_CW  "icon_rotate_clockwise"
 #define PIXBUF_INLINE_ICON_CCW "icon_rotate_counter_clockwise"
@@ -131,6 +132,8 @@ void pixbuf_draw_shadow(GdkPixbuf *pb,
 
 void pixbuf_desaturate_rect(GdkPixbuf *pb,
                            gint x, gint y, gint w, gint h);
+void pixbuf_highlight_overunderexposed(GdkPixbuf *pb,
+                           gint x, gint y, gint w, gint h);
 
 
 /* clipping utils */
index 93cc581..1c49a2a 100644 (file)
@@ -120,6 +120,7 @@ static const UseableToolbarItems useable_toolbar_items[] = {
        {"Zoom25",      N_("Zoom 1:4"), GTK_STOCK_FILE},
        {"ConnectZoomIn",       N_("Connected Zoom in"), GTK_STOCK_ZOOM_IN},
        {"Grayscale",   N_("Grayscale"), PIXBUF_INLINE_ICON_GRAYSCALE},
+       {"OverUnderExposed",    N_("Over Under Exposed"), PIXBUF_INLINE_ICON_EXPOSURE},
        {"HideTools",   N_("Hide file list"), PIXBUF_INLINE_ICON_HIDETOOLS},
        {"SlideShowPause",      N_("Pause slideshow"), GTK_STOCK_MEDIA_PAUSE},
        {"SlideShowFaster",     N_("Slideshow Faster"), GTK_STOCK_FILE},
index b31ca33..6f6e2ce 100644 (file)
@@ -538,6 +538,7 @@ struct _ImageWindow
        gboolean delay_flip;
        gint orientation;
        gboolean desaturate;
+       gboolean overunderexposed;
        gint user_stereo;
 
        gboolean mouse_wheel_mode;