From 36c8d419147f9c55cc580b223734a86cb0878cd9 Mon Sep 17 00:00:00 2001 From: Chris Lee <@klee93> Date: Sun, 24 Feb 2019 01:49:01 +0100 Subject: [PATCH] use xrender to capture screenshots --- src/util/window.c | 225 ++++++++++++++++++++++------------------------ 1 file changed, 109 insertions(+), 116 deletions(-) diff --git a/src/util/window.c b/src/util/window.c index b12df4c..12af9ab 100644 --- a/src/util/window.c +++ b/src/util/window.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -383,28 +384,42 @@ void smooth_thumbnail(cairo_surface_t *image_surface) } } -// This is measured to be slightly faster. -#define GetPixel(ximg, x, y) ((u_int32_t *)&(ximg->data[y * ximg->bytes_per_line]))[x] -//#define GetPixel XGetPixel +// This is measured to be slightly faster than XGetPixel. +static u_int32_t GetPixel(XImage *ximg, unsigned x, unsigned y) +{ + if (x >= ximg->width || y >= ximg->height) { + fprintf(stderr, RED "GetPixel read overflow: %u %u %d %d\n", + x, y, ximg->width, ximg->height); + return 0; + } + return ((u_int32_t *)&(ximg->data[y * ximg->bytes_per_line]))[x]; +} +// TODO: the xrender method only works well if there is a compositor running cairo_surface_t *get_window_thumbnail_ximage(Window win, size_t size, gboolean use_shm) { + fprintf(stderr, GREEN "tint2: win=%u: getting thumbnail of size %u" RESET "\n", win, size); cairo_surface_t *result = NULL; XWindowAttributes wa; if (!XGetWindowAttributes(server.display, win, &wa) || wa.width <= 0 || wa.height <= 0 || - wa.map_state != IsViewable) + wa.map_state != IsViewable) { + fprintf(stderr, YELLOW "tint2: win=%u: no win attributes" RESET "\n", win); goto err0; + } - if (window_is_iconified(win)) + if (window_is_iconified(win)) { + fprintf(stderr, YELLOW "tint2: win=%u: no thumbnail for minimized window" RESET "\n", win); goto err0; + } size_t w, h; - w = (size_t)wa.width; - h = (size_t)wa.height; + w = (size_t)wa.width + wa.x; + h = (size_t)wa.height + wa.y; size_t tw, th, fw; size_t ox, oy; tw = size; th = h * tw / w; + double scale = th / (double)h; if (th > tw * 0.618) { th = (size_t)(tw * 0.618); fw = w * th / h; @@ -414,22 +429,69 @@ cairo_surface_t *get_window_thumbnail_ximage(Window win, size_t size, gboolean u fw = tw; ox = oy = 0; } - if (!w || !h || !tw || !th || !fw) + if (!w || !h || !tw || !th || !fw) { + fprintf(stderr, YELLOW "tint2: win=%u: no thumbnail for size 0x0" RESET "\n", win); goto err0; + } - XShmSegmentInfo shminfo; - XImage *ximg; - if (use_shm) - ximg = XShmCreateImage(server.display, - wa.visual, - (unsigned)wa.depth, - ZPixmap, - NULL, - &shminfo, - (unsigned)w, - (unsigned)h); - else - ximg = XGetImage(server.display, win, 0, 0, (unsigned)w, (unsigned)h, AllPlanes, ZPixmap); + XImage *ximg = NULL; + + XGetWindowAttributes(server.display, win, &wa); + if (wa.map_state != IsViewable) { + fprintf(stderr, YELLOW "tint2: win=%u: no thumbnail for non-viewable window" RESET "\n", win); + goto err4; + } + + Pixmap pix = XCreatePixmap(server.display, win, tw, th, wa.depth); + if (!pix) { + fprintf(stderr, YELLOW "tint2: win=%u: no pixmap" RESET "\n", win); + goto err4; + } + XRenderPictureAttributes attrs = { + .repeat = RepeatNone + }; + XRenderPictFormat *fmt = XRenderFindVisualFormat(server.display, wa.visual); + if (!fmt) { + fprintf(stderr, YELLOW "tint2: win=%u: no XRender format" RESET "\n", win); + goto err4; + } + Picture src_pic = XRenderCreatePicture(server.display, win, fmt, CPRepeat, &attrs); + if (!src_pic) { + fprintf(stderr, YELLOW "tint2: win=%u: no src picture" RESET "\n", win); + goto err4; + } + Picture dst_pic = XRenderCreatePicture(server.display, pix, fmt, CPRepeat, &attrs); + if (!dst_pic) { + fprintf(stderr, YELLOW "tint2: win=%u: no src picture" RESET "\n", win); + goto err4; + } + fprintf(stderr, GREEN "tint2: win=%u: transformation %u %u %f xy %d %d" RESET "\n", win, ox, oy, scale, + wa.x, wa.y); + XTransform transform = { + { + { + XDoubleToFixed(1./scale), + XDoubleToFixed(0), + XDoubleToFixed(ox-wa.x/scale) + }, + { + XDoubleToFixed(0), + XDoubleToFixed(1./scale), + XDoubleToFixed(oy-wa.y/scale) + }, + { + XDoubleToFixed(0), + XDoubleToFixed(0), + XDoubleToFixed(1) + } + } + }; + XRenderSetPictureTransform(server.display, src_pic, &transform); + XRenderSetPictureFilter(server.display, src_pic, FilterBilinear, NULL, 0); + XRenderComposite(server.display, PictOpSrc, src_pic, None, dst_pic, 0, 0, 0, 0, 0, 0, w, h); + XSync(server.display, False); + + ximg = XGetImage(server.display, pix, 0, 0, (unsigned)tw, (unsigned)th, AllPlanes, ZPixmap); if (!ximg) { fprintf(stderr, RED "tint2: !ximg" RESET "\n"); goto err0; @@ -438,87 +500,32 @@ cairo_surface_t *get_window_thumbnail_ximage(Window win, size_t size, gboolean u fprintf(stderr, RED "tint2: unusual bits_per_pixel" RESET "\n"); goto err1; } - if (use_shm) { - shminfo.shmid = shmget(IPC_PRIVATE, (size_t)(ximg->bytes_per_line * ximg->height), IPC_CREAT | 0777); - if (shminfo.shmid < 0) { - fprintf(stderr, RED "tint2: !shmget" RESET "\n"); - goto err1; - } - shminfo.shmaddr = ximg->data = (char *)shmat(shminfo.shmid, 0, 0); - if (!shminfo.shmaddr) { - fprintf(stderr, RED "tint2: !shmat" RESET "\n"); - goto err2; - } - shminfo.readOnly = False; - if (!XShmAttach(server.display, &shminfo)) { - fprintf(stderr, RED "tint2: !xshmattach" RESET "\n"); - goto err3; - } - if (!XShmGetImage(server.display, win, ximg, 0, 0, AllPlanes)) { - fprintf(stderr, RED "tint2: !xshmgetimage" RESET "\n"); - goto err4; - } - } - - XGetWindowAttributes(server.display, win, &wa); - if (wa.map_state != IsViewable) - goto err4; + fprintf(stderr, GREEN "tint2: creating image surface %ux%u" RESET "\n", tw, th); result = cairo_image_surface_create(CAIRO_FORMAT_RGB24, (int)tw, (int)th); u_int32_t *data = (u_int32_t *)cairo_image_surface_get_data(result); memset(data, 0, tw * th); - // Fixed-point precision - const size_t prec = 1 << 16; - const size_t xstep = w * prec / fw; - const size_t ystep = h * prec / th; - - const size_t offset_y1 = 0 * ystep / 8; - const size_t offset_x1 = 3 * xstep / 8; - - const size_t offset_y2 = 1 * ystep / 8; - const size_t offset_x2 = 6 * xstep / 8; - - const size_t offset_y3 = 4 * ystep / 8; - const size_t offset_x3 = 2 * xstep / 8; - - const size_t offset_y4 = 4 * ystep / 8; - const size_t offset_x4 = 4 * xstep / 8; - - const size_t offset_y5 = 4 * ystep / 8; - const size_t offset_x5 = 7 * xstep / 8; - - const size_t offset_y6 = 6 * ystep / 8; - const size_t offset_x6 = 1 * xstep / 8; - - const size_t offset_y7 = 7 * ystep / 8; - const size_t offset_x7 = 6 * xstep / 8; - const u_int32_t rmask = (u_int32_t)ximg->red_mask; const u_int32_t gmask = (u_int32_t)ximg->green_mask; const u_int32_t bmask = (u_int32_t)ximg->blue_mask; - for (size_t yt = 0, y = 0; yt < th; yt++, y += ystep) { - for (size_t xt = 0, x = 0; xt < fw; xt++, x += xstep) { - size_t j = yt * tw + ox + xt; - u_int32_t c1 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x1) / prec), (int)((y + offset_y1) / prec)); - u_int32_t c2 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x2) / prec), (int)((y + offset_y2) / prec)); - u_int32_t c3 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x3) / prec), (int)((y + offset_y3) / prec)); - u_int32_t c4 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x4) / prec), (int)((y + offset_y4) / prec)); - u_int32_t c5 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x5) / prec), (int)((y + offset_y5) / prec)); - u_int32_t c6 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x6) / prec), (int)((y + offset_y6) / prec)); - u_int32_t c7 = (u_int32_t)GetPixel(ximg, (int)((x + offset_x7) / prec), (int)((y + offset_y7) / prec)); - u_int32_t b = ((c1 & bmask) + (c2 & bmask) + (c3 & bmask) + (c4 & bmask) + (c5 & bmask) * 2 + (c6 & bmask) + - (c7 & bmask)) / - 8; - u_int32_t g = ((c1 & gmask) + (c2 & gmask) + (c3 & gmask) + (c4 & gmask) + (c5 & gmask) * 2 + (c6 & gmask) + - (c7 & gmask)) / - 8; - u_int32_t r = ((c1 & rmask) + (c2 & rmask) + (c3 & rmask) + (c4 & rmask) + (c5 & rmask) * 2 + (c6 & rmask) + - (c7 & rmask)) / - 8; - data[j] = (r & rmask) | (g & gmask) | (b & bmask); + for (size_t y = 0; y < th; y++) { + for (size_t x = 0; x < tw; x++) { + u_int32_t c = (u_int32_t)XGetPixel(ximg, x, y); + data[y * tw + x] = c; } } + + imlib_context_set_drawable(pix); + Imlib_Image img = imlib_create_image_from_drawable(0, 0, 0, tw, th, 1); + //Imlib_Image img = imlib_create_image_using_data(tw, th, data); + imlib_context_set_image(img); + char path[256]; + static unsigned count = 0; + sprintf(path, "thumb-%u-%u.png", win, count++); + imlib_save_image(path); + imlib_free_image(); + // Convert to argb32 if (rmask & 0xff0000) { // argb32 or rgb24 => Nothing to do @@ -541,24 +548,23 @@ cairo_surface_t *get_window_thumbnail_ximage(Window win, size_t size, gboolean u } // 2nd pass - smooth_thumbnail(result); + //smooth_thumbnail(result); + cairo_surface_flush(result); if (ximg) { XDestroyImage(ximg); ximg = NULL; } -err4: - if (use_shm) - XShmDetach(server.display, &shminfo); -err3: - if (use_shm) - shmdt(shminfo.shmaddr); -err2: - if (use_shm) - shmctl(shminfo.shmid, IPC_RMID, NULL); err1: +err4: if (ximg) XDestroyImage(ximg); + if (src_pic) + XRenderFreePicture(server.display, src_pic); + if (dst_pic) + XRenderFreePicture(server.display, dst_pic); + if (pix) + XFreePixmap(server.display, pix); err0: return result; } @@ -578,24 +584,11 @@ gboolean cairo_surface_is_blank(cairo_surface_t *image_surface) cairo_surface_t *get_window_thumbnail(Window win, int size) { cairo_surface_t *image_surface = NULL; - const gboolean shm_allowed = FALSE; - if (shm_allowed && server.has_shm && server.composite_manager) { - image_surface = get_window_thumbnail_ximage(win, (size_t)size, TRUE); - if (image_surface && cairo_surface_is_blank(image_surface)) { - cairo_surface_destroy(image_surface); - image_surface = NULL; - } - if (debug_thumbnails) { - if (!image_surface) - fprintf(stderr, YELLOW "tint2: XShmGetImage failed, trying slower method" RESET "\n"); - else - fprintf(stderr, "tint2: captured window using XShmGetImage\n"); - } - } if (!image_surface) { image_surface = get_window_thumbnail_ximage(win, (size_t)size, FALSE); if (image_surface && cairo_surface_is_blank(image_surface)) { + fprintf(stderr, RED "tint2: captured blank thumbnail" RESET "\n"); cairo_surface_destroy(image_surface); image_surface = NULL; }