From b987b66d70bda8b822b19864f18c4d66180b1f02 Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Sat, 7 Jan 2023 23:27:16 -0500 Subject: [PATCH 1/2] Remove unused function --- cairo.c | 16 ---------------- include/cairo_util.h | 4 ---- 2 files changed, 20 deletions(-) diff --git a/cairo.c b/cairo.c index ee7d3ad..7d620d4 100644 --- a/cairo.c +++ b/cairo.c @@ -13,22 +13,6 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { (color >> (0*8) & 0xFF) / 255.0); } -cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel) { - switch (subpixel) { - case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: - return CAIRO_SUBPIXEL_ORDER_RGB; - case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: - return CAIRO_SUBPIXEL_ORDER_BGR; - case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: - return CAIRO_SUBPIXEL_ORDER_VRGB; - case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: - return CAIRO_SUBPIXEL_ORDER_VBGR; - default: - return CAIRO_SUBPIXEL_ORDER_DEFAULT; - } - return CAIRO_SUBPIXEL_ORDER_DEFAULT; -} - #if HAVE_GDK_PIXBUF cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) { int chan = gdk_pixbuf_get_n_channels(gdkbuf); diff --git a/include/cairo_util.h b/include/cairo_util.h index fd611b6..651adab 100644 --- a/include/cairo_util.h +++ b/include/cairo_util.h @@ -9,10 +9,6 @@ #endif void cairo_set_source_u32(cairo_t *cairo, uint32_t color); -cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel); - -cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, - int width, int height); #if HAVE_GDK_PIXBUF From 0d448659520d043d981a8a972d87e4f452d1b6f4 Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Sat, 7 Jan 2023 11:58:51 -0500 Subject: [PATCH 2/2] Optionally submit 10 bit deep buffers This requires that the compositor support either XRGB2101010 or XBGR2101010, and that the background image is a 16-bit PNG. --- background-image.c | 45 ++++++++++++++++++++++++++------------------ cairo.c | 22 ++++++++++++++++++++++ include/cairo_util.h | 2 ++ main.c | 40 ++++++++++++++++++++++++++++++++++++++- pool-buffer.c | 15 ++++++++++++++- 5 files changed, 104 insertions(+), 20 deletions(-) diff --git a/background-image.c b/background-image.c index 3eae832..3b75cee 100644 --- a/background-image.c +++ b/background-image.c @@ -22,28 +22,37 @@ enum background_mode parse_background_mode(const char *mode) { } cairo_surface_t *load_background_image(const char *path) { - cairo_surface_t *image; + cairo_surface_t *image = NULL; + #if HAVE_GDK_PIXBUF - GError *err = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); - if (!pixbuf) { - swaybg_log(LOG_ERROR, "Failed to load background image (%s).", - err->message); - return NULL; + // Prefer to load PNG images with Cairo, since it can load images with + // higher bit depths at full precision + const char *suffix = strrchr(path, '.'); + if (suffix && (!strcmp(suffix, ".png") || !strcmp(suffix, ".PNG"))) { + image = cairo_image_surface_create_from_png(path); } - // Correct for embedded image orientation; typical images are not - // rotated and will be handled efficiently - GdkPixbuf *oriented = gdk_pixbuf_apply_embedded_orientation(pixbuf); - g_object_unref(pixbuf); - image = gdk_cairo_image_surface_create_from_pixbuf(oriented); - g_object_unref(oriented); -#else - image = cairo_image_surface_create_from_png(path); -#endif // HAVE_GDK_PIXBUF + + // if not a PNG image, try to load with gdk-pixbuf if (!image) { - swaybg_log(LOG_ERROR, "Failed to read background image."); - return NULL; + GError *err = NULL; + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); + if (!pixbuf) { + swaybg_log(LOG_ERROR, "Failed to load background image (%s).", + err->message); + return NULL; + } + + // Correct for embedded image orientation; typical images are not + // rotated and will be handled efficiently + GdkPixbuf *oriented = gdk_pixbuf_apply_embedded_orientation(pixbuf); + g_object_unref(pixbuf); + image = gdk_cairo_image_surface_create_from_pixbuf(oriented); + g_object_unref(oriented); } +#else // !HAVE_GDK_PIXBUF + image = cairo_image_surface_create_from_png(path); +#endif + if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { swaybg_log(LOG_ERROR, "Failed to read background image: %s." #if !HAVE_GDK_PIXBUF diff --git a/cairo.c b/cairo.c index 7d620d4..0271aec 100644 --- a/cairo.c +++ b/cairo.c @@ -1,3 +1,4 @@ +#include #include #include #include "cairo_util.h" @@ -13,6 +14,27 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { (color >> (0*8) & 0xFF) / 255.0); } +void cairo_rgb30_swap_rb(cairo_surface_t *surface) { + assert(cairo_image_surface_get_format(surface) == CAIRO_FORMAT_RGB30); + + unsigned char *data = cairo_image_surface_get_data(surface); + int w = cairo_image_surface_get_width(surface); + int h = cairo_image_surface_get_height(surface); + int stride = cairo_image_surface_get_stride(surface); + for (int y = 0; y < h; y++) { + uint32_t *row = (uint32_t *)(data + stride * y); + for (int x = 0; x < w; x++) { + uint32_t pix = row[x]; + // swap blue (0:10) and red (20:30) + pix = (pix & 0xc00ffc00) | ((pix & 0x3ff00000) >> 20) | + ((pix & 0x3ff) << 20); + row[x] = pix; + } + } + + cairo_surface_mark_dirty(surface); +} + #if HAVE_GDK_PIXBUF cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) { int chan = gdk_pixbuf_get_n_channels(gdkbuf); diff --git a/include/cairo_util.h b/include/cairo_util.h index 651adab..eb3998d 100644 --- a/include/cairo_util.h +++ b/include/cairo_util.h @@ -10,6 +10,8 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color); +void cairo_rgb30_swap_rb(cairo_surface_t *surface); + #if HAVE_GDK_PIXBUF cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( diff --git a/main.c b/main.c index 181adb4..aaf27b2 100644 --- a/main.c +++ b/main.c @@ -53,6 +53,8 @@ struct swaybg_state { struct wl_list outputs; // struct swaybg_output::link struct wl_list images; // struct swaybg_image::link bool run_display; + bool has_xrgb2101010; + bool has_xbgr2101010; }; struct swaybg_image { @@ -117,10 +119,26 @@ static struct wl_buffer *draw_buffer(const struct swaybg_output *output, r32, g32, b32, 0xFFFFFFFF); } + bool deep_image = false; + if (surface) { + cairo_format_t fmt = cairo_image_surface_get_format(surface); + deep_image = deep_image || fmt == CAIRO_FORMAT_RGB30; +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 17, 2) + deep_image = deep_image || fmt == CAIRO_FORMAT_RGB96F; + deep_image = deep_image || fmt == CAIRO_FORMAT_RGBA128F; +#endif + } + + uint32_t format = WL_SHM_FORMAT_XRGB8888; + if (deep_image && output->state->has_xrgb2101010) { + format = WL_SHM_FORMAT_XRGB2101010; + } else if (deep_image && output->state->has_xbgr2101010) { + format = WL_SHM_FORMAT_XBGR2101010; + } struct pool_buffer buffer; if (!create_buffer(&buffer, output->state->shm, - buffer_width, buffer_height, WL_SHM_FORMAT_XRGB8888)) { + buffer_width, buffer_height, format)) { return NULL; } @@ -133,6 +151,10 @@ static struct wl_buffer *draw_buffer(const struct swaybg_output *output, output->config->mode, buffer_width, buffer_height); } + if (format == WL_SHM_FORMAT_XBGR2101010) { + cairo_rgb30_swap_rb(buffer.surface); + } + // return wl_buffer for caller to use and destroy struct wl_buffer *wl_buf = buffer.buffer; buffer.buffer = NULL; @@ -398,6 +420,21 @@ static const struct wl_output_listener output_listener = { .description = output_description, }; + +static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) { + struct swaybg_state *state = data; + if (format == WL_SHM_FORMAT_XBGR2101010) { + state->has_xbgr2101010 = true; + } + if (format == WL_SHM_FORMAT_XRGB2101010) { + state->has_xrgb2101010 = true; + } +} + +static const struct wl_shm_listener shm_listener = { + .format = shm_format, +}; + static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { struct swaybg_state *state = data; @@ -406,6 +443,7 @@ static void handle_global(void *data, struct wl_registry *registry, wl_registry_bind(registry, name, &wl_compositor_interface, 4); } else if (strcmp(interface, wl_shm_interface.name) == 0) { state->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + wl_shm_add_listener(state->shm, &shm_listener, state); } else if (strcmp(interface, wl_output_interface.name) == 0) { struct swaybg_output *output = calloc(1, sizeof(struct swaybg_output)); output->state = state; diff --git a/pool-buffer.c b/pool-buffer.c index 4b7b63b..6aab4c2 100644 --- a/pool-buffer.c +++ b/pool-buffer.c @@ -36,6 +36,18 @@ static int anonymous_shm_open(void) { return -1; } +static uint32_t cairo_format_from_wayland_shm(uint32_t shm) { + switch (shm) { + case WL_SHM_FORMAT_XRGB8888: + return CAIRO_FORMAT_RGB24; + case WL_SHM_FORMAT_XBGR2101010: + case WL_SHM_FORMAT_XRGB2101010: + return CAIRO_FORMAT_RGB30; + default: + assert(0); + } +} + bool create_buffer(struct pool_buffer *buf, struct wl_shm *shm, int32_t width, int32_t height, uint32_t format) { uint32_t stride = width * 4; @@ -56,10 +68,11 @@ bool create_buffer(struct pool_buffer *buf, struct wl_shm *shm, wl_shm_pool_destroy(pool); close(fd); + cairo_format_t cairo_fmt = cairo_format_from_wayland_shm(format); buf->size = size; buf->data = data; buf->surface = cairo_image_surface_create_for_data(data, - CAIRO_FORMAT_RGB24, width, height, stride); + cairo_fmt, width, height, stride); buf->cairo = cairo_create(buf->surface); return true; }