croptool

Image cropping tool
git clone git://lumidify.org/croptool.git (fast, but not encrypted)
git clone https://lumidify.org/git/croptool.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit b4e5a51cc9a504d4386fe372378826d561babad4
parent 59805483b26574b37bb1784eaeb323dd71489f4b
Author: lumidify <nobody@lumidify.org>
Date:   Fri,  5 Mar 2021 23:02:35 +0100

Add double buffering

Diffstat:
MMakefile | 9+++++++++
MREADME | 5++++-
Mcroptool.1 | 7++-----
Mcroptool.c | 97++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
4 files changed, 99 insertions(+), 19 deletions(-)

diff --git a/Makefile b/Makefile @@ -13,6 +13,15 @@ MAN1 = ${BIN:=.1} CFLAGS += -D_POSIX_C_SOURCE=200809L `pkg-config --cflags x11` `imlib2-config --cflags` LDFLAGS += `pkg-config --libs x11` `imlib2-config --libs` -lm +# Configuration options: + +# comment to disable double buffering +CFLAGS += `pkg-config --cflags xext` +LDFLAGS += `pkg-config --libs xext` + +# uncomment to disable double buffering +#CFLAGS += -DNODB + all: ${BIN} .c: diff --git a/README b/README @@ -1,8 +1,11 @@ croptool - mass image cropping tool REQUIREMENTS: xlib, imlib2 +OPTIONAL: xext (for double-buffering extension) croptool is a simple tool to select cropping rectangles on images and print out a command to crop each image. -See croptool.1 for more information. +See Makefile for compile-time options. + +See croptool.1 for usage information. diff --git a/croptool.1 b/croptool.1 @@ -1,4 +1,4 @@ -.Dd January 10, 2021 +.Dd March 5, 2021 .Dt CROPTOOL 1 .Os .Sh NAME @@ -95,7 +95,7 @@ Redraw the window. This is useful when automatic redrawing is disabled with .Bl -tag -width Ds .It LEFT-CLICK When inside an existing cropping rectangle, drag it around. When on one of the -edges, resize the rectangle, locking it to the that dimension. When on one of +edges, resize the rectangle, locking it to that dimension. When on one of the corners, resize the rectangle regardless of dimension. When outside an existing cropping rectangle, start a new cropping rectangle. .El @@ -110,9 +110,6 @@ existing cropping rectangle, start a new cropping rectangle. The filenames are printed out without any escaping, so filenames with quotes may cause issues depending on the output format. .Pp -Nothing in particular has been done to prevent screen flicker, so there is -flickering when resizing the window or cropping rectangle. -.Pp Transparent portions of images should probably be shown differently, but I'm too lazy to fix that and don't really care at the moment. .Pp diff --git a/croptool.c b/croptool.c @@ -26,6 +26,9 @@ #include <X11/keysym.h> #include <X11/XF86keysym.h> #include <X11/cursorfont.h> +#ifndef NODB +#include <X11/extensions/Xdbe.h> +#endif #include <Imlib2.h> /* The number of pixels to check on each side when checking @@ -84,6 +87,11 @@ static struct { GC gc; Window win; Visual *vis; + Drawable drawable; + #ifndef NODB + XdbeBackBuffer back_buf; + int db_enabled; + #endif Colormap cm; int screen; int depth; @@ -284,12 +292,63 @@ setup(int argc, char *argv[]) { state.depth = DefaultDepth(state.dpy, state.screen); state.cm = DefaultColormap(state.dpy, state.screen); + #ifndef NODB + state.db_enabled = 0; + /* based on http://wili.cc/blog/xdbe.html */ + int major, minor; + if (XdbeQueryExtension(state.dpy, &major, &minor)) { + int num_screens = 1; + Drawable screens[] = { DefaultRootWindow(state.dpy) }; + XdbeScreenVisualInfo *info = XdbeGetVisualInfo(state.dpy, screens, &num_screens); + if (!info || num_screens < 1 || info->count < 1) { + fprintf(stderr, "Warning: No visuals support Xdbe, " + "double buffering disabled.\n"); + } else { + XVisualInfo xvisinfo_templ; + xvisinfo_templ.visualid = info->visinfo[0].visual; + xvisinfo_templ.screen = 0; + xvisinfo_templ.depth = info->visinfo[0].depth; + int matches; + XVisualInfo *xvisinfo_match = XGetVisualInfo( + state.dpy, + VisualIDMask | VisualScreenMask | VisualDepthMask, + &xvisinfo_templ, &matches + ); + if (!xvisinfo_match || matches < 1) { + fprintf(stderr, "Warning: Couldn't match a Visual " + "with double buffering, double " + "buffering disabled.\n"); + } else { + state.vis = xvisinfo_match->visual; + state.depth = xvisinfo_match->depth; + state.db_enabled = 1; + } + XFree(xvisinfo_match); + } + XdbeFreeVisualInfo(info); + } else { + fprintf(stderr, "Warning: No Xdbe support, double buffering disabled.\n"); + } + #endif + memset(&attrs, 0, sizeof(attrs)); - attrs.background_pixmap = None; + attrs.background_pixel = BlackPixel(state.dpy, state.screen); attrs.colormap = state.cm; + attrs.bit_gravity = NorthWestGravity; state.win = XCreateWindow(state.dpy, DefaultRootWindow(state.dpy), 0, 0, state.window_w, state.window_h, 0, state.depth, - InputOutput, state.vis, CWBackPixmap | CWColormap, &attrs); + InputOutput, state.vis, CWBackPixel | CWColormap | CWBitGravity, &attrs); + + #ifndef NODB + if (state.db_enabled) { + state.back_buf = XdbeAllocateBackBufferName(state.dpy, state.win, XdbeCopied); + state.drawable = state.back_buf; + } else { + state.drawable = state.win; + } + #else + state.drawable = state.win; + #endif memset(&gcv, 0, sizeof(gcv)); gcv.line_width = LINE_WIDTH; @@ -327,7 +386,7 @@ setup(int argc, char *argv[]) { imlib_context_set_display(state.dpy); imlib_context_set_visual(state.vis); imlib_context_set_colormap(state.cm); - imlib_context_set_drawable(state.win); + imlib_context_set_drawable(state.drawable); state.updates = imlib_updates_init(); next_picture(0); @@ -442,8 +501,8 @@ redraw(void) { Imlib_Updates current_update; if (!state.cur_image || state.cur_selection < 0) { XSetForeground(state.dpy, state.gc, BlackPixel(state.dpy, state.screen)); - XFillRectangle(state.dpy, state.win, state.gc, 0, 0, state.window_w, state.window_h); - return; + XFillRectangle(state.dpy, state.drawable, state.gc, 0, 0, state.window_w, state.window_h); + goto swap_buffers; } struct Selection *sel = &state.selections[state.cur_selection]; state.updates = imlib_updates_merge_for_rendering(state.updates, sel->scaled_w, sel->scaled_h); @@ -456,11 +515,9 @@ redraw(void) { imlib_context_set_image(buffer); imlib_blend_image_onto_image( state.cur_image, 0, 0, 0, - state.selections[state.cur_selection].orig_w, - state.selections[state.cur_selection].orig_h, + sel->orig_w, sel->orig_h, -up_x, -up_y, - state.selections[state.cur_selection].scaled_w, - state.selections[state.cur_selection].scaled_h); + sel->scaled_w, sel->scaled_h); imlib_render_image_on_drawable(up_x, up_y); imlib_free_image(); } @@ -470,8 +527,8 @@ redraw(void) { /* wipe the black area around the image */ XSetForeground(state.dpy, state.gc, BlackPixel(state.dpy, state.screen)); - XFillRectangle(state.dpy, state.win, state.gc, 0, sel->scaled_h, sel->scaled_w, state.window_h - sel->scaled_h); - XFillRectangle(state.dpy, state.win, state.gc, sel->scaled_w, 0, state.window_w - sel->scaled_w, state.window_h); + XFillRectangle(state.dpy, state.drawable, state.gc, 0, sel->scaled_h, sel->scaled_w, state.window_h - sel->scaled_h); + XFillRectangle(state.dpy, state.drawable, state.gc, sel->scaled_w, 0, state.window_w - sel->scaled_w, state.window_h); /* draw the rectangle */ struct Rect rect = sel->rect; @@ -479,8 +536,22 @@ redraw(void) { XColor col = state.cur_col == 1 ? state.col1 : state.col2; XSetForeground(state.dpy, state.gc, col.pixel); sort_coordinates(&rect.x0, &rect.y0, &rect.x1, &rect.y1); - XDrawRectangle(state.dpy, state.win, state.gc, rect.x0, rect.y0, rect.x1 - rect.x0, rect.y1 - rect.y0); + XDrawRectangle(state.dpy, state.drawable, state.gc, rect.x0, rect.y0, rect.x1 - rect.x0, rect.y1 - rect.y0); + } + +swap_buffers: + #ifndef NODB + if (state.db_enabled) { + XdbeSwapInfo swap_info; + swap_info.swap_window = state.win; + swap_info.swap_action = XdbeCopied; + + if (!XdbeSwapBuffers(state.dpy, &swap_info, 1)) + fprintf(stderr, "Warning: Unable to swap buffers.\n"); } + #else + ; + #endif } static void @@ -864,7 +935,7 @@ switch_color(void) { static void key_press(XEvent event) { XWindowAttributes attrs; - char buf[64]; + char buf[32]; KeySym sym; XLookupString(&event.xkey, buf, sizeof(buf), &sym, NULL); switch (sym) {