Skip to content

Commit

Permalink
Optionally submit 10 bit deep buffers
Browse files Browse the repository at this point in the history
This requires that the compositor support either XRGB2101010 or XBGR2101010,
and that the background image is a 16-bit PNG.
  • Loading branch information
mstoeckl committed Jan 8, 2023
1 parent 2c76e5e commit a33e9dc
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 10 deletions.
41 changes: 33 additions & 8 deletions background-image.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,42 @@ 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;

// 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);
}

// if not a PNG image, try to load with gdk-pixbuf
#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);
if (!image) {
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;
}
image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
g_object_unref(pixbuf);
}
#endif // HAVE_GDK_PIXBUF

if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
swaybg_log(LOG_ERROR, "Failed to read background image: %s."
#if !HAVE_GDK_PIXBUF
"\nSway was compiled without gdk_pixbuf support, so only"
"\nPNG images can be loaded. This is the likely cause."
#endif // !HAVE_GDK_PIXBUF
, cairo_status_to_string(cairo_surface_status(image)));
return NULL;
}
image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
g_object_unref(pixbuf);

#if HAVE_GDK_PIXBUF

#else
image = cairo_image_surface_create_from_png(path);
#endif // HAVE_GDK_PIXBUF
Expand Down
22 changes: 22 additions & 0 deletions cairo.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <assert.h>
#include <stdint.h>
#include <cairo.h>
#include "cairo_util.h"
Expand All @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions include/cairo_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
41 changes: 40 additions & 1 deletion main.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,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 {
Expand Down Expand Up @@ -147,9 +149,26 @@ static void render_frame(struct swaybg_output *output, cairo_surface_t *surface)
return;
}

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;
}

Expand All @@ -163,6 +182,10 @@ static void render_frame(struct swaybg_output *output, cairo_surface_t *surface)
output->config->mode, buffer_width, buffer_height);
}

if (format == WL_SHM_FORMAT_XBGR2101010) {
cairo_rgb30_swap_rb(buffer.surface);
}

wl_surface_set_buffer_scale(output->surface, output->scale);
wl_surface_attach(output->surface, buffer.buffer, 0, 0);
wl_surface_damage_buffer(output->surface, 0, 0, INT32_MAX, INT32_MAX);
Expand Down Expand Up @@ -349,6 +372,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;
Expand All @@ -357,6 +395,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;
Expand Down
16 changes: 15 additions & 1 deletion pool-buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
#include "log.h"
#include "pool-buffer.h"

static int anonymous_shm_open(void) {
Expand Down Expand Up @@ -37,6 +38,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;
Expand All @@ -57,10 +70,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;
}
Expand Down

0 comments on commit a33e9dc

Please sign in to comment.