nsxiv

Below I discuss my monkeypatch for nsxiv.

I use media-gfx/nsxiv because...

  1. It's cheap on ram while supporting stuff like webp and jpg without hassle.
  2. It's "minimal" but doesn't sacrifice usability.

My specific use case: I like having the entire background transparent, i.e. the image simply floats. Therefore I use this patch ⇗, with a small edit added, from nsxiv-extra.

Note: The patch normally wants you to set "Nsxiv.window.alpha: 0.0" in ~/.Xresources then run something like xrdb -merge ~/.Xresources" ⇗ whenever X starts.

Why would I want to add extra machinery when I can just poke the patch slightly? See the "edited by swomf" marker below.

/etc/portage/package.use/nsxiv
media-gfx/nsxiv exif savedconfig
/etc/portage/patches/media-gfx/nsxiv/alpha-275ec0d59.diff
diff --git a/image.c b/image.c
index 4e0cb21..c3a3c43 100644
--- a/image.c
+++ b/image.c
@@ -417,7 +417,6 @@ void img_render(img_t *img)
    win_t *win;
    int sx, sy, sw, sh;
    int dx, dy, dw, dh;
-	Imlib_Image bg;
 
    win = img->win;
    img_fit(img);
@@ -463,21 +462,45 @@ void img_render(img_t *img)
    imlib_context_set_anti_alias(img->anti_alias);
    imlib_context_set_drawable(win->buf.pm);
 
-	/* manual blending, for performance reasons.
-	 * see https://phab.enlightenment.org/T8969#156167 for more details.
-	 */
+	render_core(win, sx, sy, sw, sh, dx, dy, dw, dh, img->alpha_layer);
+	img->dirty = false;
+}
+
+bool img_fit_win(img_t *img, scalemode_t sm)
+{
+	float oz;
+
+	oz = img->zoom;
+	img->scalemode = sm;
+
+	if (img_fit(img)) {
+		img->x = img->win->w / 2 - (img->win->w / 2 - img->x) * img->zoom / oz;
+		img->y = img->win->h / 2 - (img->win->h / 2 - img->y) * img->zoom / oz;
+		img->checkpan = true;
+		return true;
+	} else {
+		return false;
+	}
+}
+void render_core(win_t* win, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh,
+                 bool alpha)
+{
+	Imlib_Image im, bg, bbg;
+	Imlib_Color_Modifier cmod;
+	XColor c;
+
    if (imlib_image_has_alpha()) {
-		if ((bg = imlib_create_image(dw, dh)) == NULL) {
-			error(0, ENOMEM, "Failed to create image");
-			goto fallback;
-		}
+		im = imlib_context_get_image();
+		cmod = imlib_context_get_color_modifier();
+		if ((bg = imlib_create_image(dw, dh)) == NULL)
+			error(EXIT_FAILURE, ENOMEM, NULL);
        imlib_context_set_image(bg);
-		imlib_image_set_has_alpha(0);
+		imlib_image_set_has_alpha(1);
 
-		if (img->alpha_layer) {
+		if (alpha) {
            int i, c, r;
-			uint32_t col[2] = { 0xFF666666, 0xFF999999 };
-			uint32_t *data = imlib_image_get_data();
+			DATA32 col[2] = { 0xFF666666, 0xFF999999 };
+			DATA32 * data = imlib_image_get_data();
 
            for (r = 0; r < dh; r++) {
                i = r * dw;
@@ -490,41 +513,43 @@ void img_render(img_t *img)
            }
            imlib_image_put_back_data(data);
        } else {
-			XColor c = win->win_bg;
-			imlib_context_set_color(c.red >> 8, c.green >> 8, c.blue >> 8, 0xFF);
+			imlib_image_clear();
+			c = win->win_bg_postmul;
+			imlib_context_set_color(c.red >> 8, c.green >> 8, c.blue >> 8,
+			                        win->win_alpha);
            imlib_image_fill_rectangle(0, 0, dw, dh);
-		}
-		imlib_context_set_blend(1);
-		imlib_context_set_operation(IMLIB_OP_COPY);
-		imlib_blend_image_onto_image(img->im, 0, sx, sy, sw, sh, 0, 0, dw, dh);
+
+
+		imlib_blend_image_onto_image(im, 1, sx, sy, sw, sh, 0, 0, dw, dh);
        imlib_context_set_color_modifier(NULL);
+
+		if (!alpha && win->win_alpha < 0xFF) {
+			/* blend onto black to get premultiplied alpha */
+			if ((bbg = imlib_create_image(dw, dh)) == NULL)
+				error(EXIT_FAILURE, ENOMEM, NULL);
+			imlib_context_set_image(bbg);
+			imlib_image_set_has_alpha(1);
+			imlib_context_set_color(0, 0, 0, 0xFF);
+			imlib_image_fill_rectangle(0, 0, dw, dh);
+			imlib_blend_image_onto_image(bg, 1, 0, 0, dw, dh, 0, 0, dw, dh);
+			imlib_image_copy_alpha_to_image(bg, 0, 0);
+			imlib_context_set_image(bg);
+			imlib_free_image();
+			imlib_context_set_image(bbg);
+		}
+
+		imlib_context_set_blend(0);
        imlib_render_image_on_drawable(dx, dy);
+		imlib_context_set_blend(1);
        imlib_free_image();
-		imlib_context_set_color_modifier(img->cmod);
-	} else {
-fallback:
+		imlib_context_set_color_modifier(cmod);
+	}} else {
+		imlib_image_set_has_alpha(1);
+		imlib_context_set_blend(0);
        imlib_render_image_part_on_drawable_at_size(sx, sy, sw, sh, dx, dy, dw, dh);
-	}
-	img->dirty = false;
-}
-
-bool img_fit_win(img_t *img, scalemode_t sm)
-{
-	float oz;
-
-	oz = img->zoom;
-	img->scalemode = sm;
-
-	if (img_fit(img)) {
-		img->x = img->win->w / 2 - (img->win->w / 2 - img->x) * img->zoom / oz;
-		img->y = img->win->h / 2 - (img->win->h / 2 - img->y) * img->zoom / oz;
-		img->checkpan = true;
-		return true;
-	} else {
-		return false;
+		imlib_context_set_blend(1);
    }
 }
-
 bool img_zoom_to(img_t *img, float z)
 {
    int x, y;
diff --git a/nsxiv.h b/nsxiv.h
index 1711c26..46aab2d 100644
--- a/nsxiv.h
+++ b/nsxiv.h
@@ -230,6 +230,7 @@ void img_update_color_modifiers(img_t*);
 bool img_change_color_modifier(img_t*, int, int*);
 bool img_frame_navigate(img_t*, int);
 bool img_frame_animate(img_t*);
+void render_core(win_t*, int, int, int, int, int, int, int, int, bool);
 Imlib_Image img_open(const fileinfo_t*);
 #if HAVE_LIBEXIF
 void exif_auto_orientate(const fileinfo_t*);
@@ -407,9 +408,11 @@ struct win {
    Window xwin;
    win_env_t env;
 
-	XColor win_bg;
+	XColor win_bg; /* pre-multiplied alpha */
+	XColor win_bg_postmul; /* post-multiplied alpha */
    XColor win_fg;
    XColor mrk_fg;
+	unsigned int win_alpha;
 #if HAVE_LIBFONTS
    XftColor bar_bg;
    XftColor bar_fg;
diff --git a/thumbs.c b/thumbs.c
index be887e4..dd0dbaa 100644
--- a/thumbs.c
+++ b/thumbs.c
@@ -489,7 +489,7 @@ void tns_render(tns_t *tns)
            t->x = x + (thumb_sizes[tns->zl] - t->w) / 2;
            t->y = y + (thumb_sizes[tns->zl] - t->h) / 2;
            imlib_context_set_image(t->im);
-			imlib_render_image_on_drawable_at_size(t->x, t->y, t->w, t->h);
+			render_core(win, 0, 0, t->w, t->h, t->x, t->y, t->w, t->h, false);
            if (tns->files[i].flags & FF_MARK)
                tns_mark(tns, i, true);
        } else {
diff --git a/window.c b/window.c
index 42c3f2b..0fc7783 100644
--- a/window.c
+++ b/window.c
@@ -117,7 +117,7 @@ static const char *win_res(XrmDatabase db, const char *name, const char *def)
 void win_init(win_t *win)
 {
    win_env_t *e;
-	const char *win_bg, *win_fg, *mrk_fg;
+	const char *win_bg, *win_fg, *mrk_fg, *win_alpha;
    char *res_man;
    XrmDatabase db;
 #if HAVE_LIBFONTS
@@ -125,7 +125,9 @@ void win_init(win_t *win)
 
    static char lbuf[512 + UTF8_PADDING], rbuf[64 + UTF8_PADDING];
 #endif
-
+	XVisualInfo vis;
+	float alpha;
+	char *endptr;
    memset(win, 0, sizeof(*win));
 
    e = &win->env;
@@ -135,9 +137,16 @@ void win_init(win_t *win)
    e->scr = DefaultScreen(e->dpy);
    e->scrw = DisplayWidth(e->dpy, e->scr);
    e->scrh = DisplayHeight(e->dpy, e->scr);
-	e->depth = DefaultDepth(e->dpy, e->scr);
-	e->vis = DefaultVisual(e->dpy, e->scr);
-	e->cmap = DefaultColormap(e->dpy, e->scr);
+
+	if (XMatchVisualInfo(e->dpy, e->scr, 32, TrueColor, &vis)) {
+		e->depth = 32;
+		e->vis = vis.visual;
+		e->cmap = XCreateColormap(e->dpy, DefaultRootWindow(e->dpy), e->vis, None);
+	} else {
+		e->depth = DefaultDepth(e->dpy, e->scr);
+		e->vis = DefaultVisual(e->dpy, e->scr);
+		e->cmap = DefaultColormap(e->dpy, e->scr);
+	}
 
    if (setlocale(LC_CTYPE, "") == NULL || XSupportsLocale() == 0)
        error(0, 0, "No locale support");
@@ -153,6 +162,25 @@ void win_init(win_t *win)
    win_alloc_color(e, win_fg, &win->win_fg);
    win_alloc_color(e, mrk_fg, &win->mrk_fg);
 
+	/* apply alpha */
+	win->win_bg_postmul = win->win_bg;
+	win_alpha = "0.0"; // win_res(db, RES_CLASS ".window.alpha", "1.0"); // NOTE: edited by swomf :)
+	alpha = strtof(win_alpha, &endptr);
+	if (!(*endptr == '\0' && alpha >= 0.0 && alpha <= 1.0))
+		error(EXIT_FAILURE, 0, "Error parsing alpha");
+	win->win_alpha = 0xFF;
+	if (e->depth == 32 && alpha < 1.0) {
+		win->win_alpha *= alpha;
+		win->win_bg.red *= alpha;
+		win->win_bg.green *= alpha;
+		win->win_bg.blue *= alpha;
+		win->win_bg.pixel =
+			(((unsigned long) win->win_bg.blue  >> 8) <<  0) |
+			(((unsigned long) win->win_bg.green >> 8) <<  8) |
+			(((unsigned long) win->win_bg.red   >> 8) << 16) |
+			(((unsigned long) win->win_alpha        ) << 24);
+	}
+
 #if HAVE_LIBFONTS
    bar_bg = win_res(db, BAR_BG[0], BAR_BG[1] ? BAR_BG[1] : win_bg);
    bar_fg = win_res(db, BAR_FG[0], BAR_FG[1] ? BAR_FG[1] : win_fg);