Skip to content

Commit 0d44865

Browse files
committed
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.
1 parent b987b66 commit 0d44865

File tree

5 files changed

+104
-20
lines changed

5 files changed

+104
-20
lines changed

background-image.c

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,37 @@ enum background_mode parse_background_mode(const char *mode) {
2222
}
2323

2424
cairo_surface_t *load_background_image(const char *path) {
25-
cairo_surface_t *image;
25+
cairo_surface_t *image = NULL;
26+
2627
#if HAVE_GDK_PIXBUF
27-
GError *err = NULL;
28-
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);
29-
if (!pixbuf) {
30-
swaybg_log(LOG_ERROR, "Failed to load background image (%s).",
31-
err->message);
32-
return NULL;
28+
// Prefer to load PNG images with Cairo, since it can load images with
29+
// higher bit depths at full precision
30+
const char *suffix = strrchr(path, '.');
31+
if (suffix && (!strcmp(suffix, ".png") || !strcmp(suffix, ".PNG"))) {
32+
image = cairo_image_surface_create_from_png(path);
3333
}
34-
// Correct for embedded image orientation; typical images are not
35-
// rotated and will be handled efficiently
36-
GdkPixbuf *oriented = gdk_pixbuf_apply_embedded_orientation(pixbuf);
37-
g_object_unref(pixbuf);
38-
image = gdk_cairo_image_surface_create_from_pixbuf(oriented);
39-
g_object_unref(oriented);
40-
#else
41-
image = cairo_image_surface_create_from_png(path);
42-
#endif // HAVE_GDK_PIXBUF
34+
35+
// if not a PNG image, try to load with gdk-pixbuf
4336
if (!image) {
44-
swaybg_log(LOG_ERROR, "Failed to read background image.");
45-
return NULL;
37+
GError *err = NULL;
38+
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);
39+
if (!pixbuf) {
40+
swaybg_log(LOG_ERROR, "Failed to load background image (%s).",
41+
err->message);
42+
return NULL;
43+
}
44+
45+
// Correct for embedded image orientation; typical images are not
46+
// rotated and will be handled efficiently
47+
GdkPixbuf *oriented = gdk_pixbuf_apply_embedded_orientation(pixbuf);
48+
g_object_unref(pixbuf);
49+
image = gdk_cairo_image_surface_create_from_pixbuf(oriented);
50+
g_object_unref(oriented);
4651
}
52+
#else // !HAVE_GDK_PIXBUF
53+
image = cairo_image_surface_create_from_png(path);
54+
#endif
55+
4756
if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
4857
swaybg_log(LOG_ERROR, "Failed to read background image: %s."
4958
#if !HAVE_GDK_PIXBUF

cairo.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <assert.h>
12
#include <stdint.h>
23
#include <cairo.h>
34
#include "cairo_util.h"
@@ -13,6 +14,27 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
1314
(color >> (0*8) & 0xFF) / 255.0);
1415
}
1516

17+
void cairo_rgb30_swap_rb(cairo_surface_t *surface) {
18+
assert(cairo_image_surface_get_format(surface) == CAIRO_FORMAT_RGB30);
19+
20+
unsigned char *data = cairo_image_surface_get_data(surface);
21+
int w = cairo_image_surface_get_width(surface);
22+
int h = cairo_image_surface_get_height(surface);
23+
int stride = cairo_image_surface_get_stride(surface);
24+
for (int y = 0; y < h; y++) {
25+
uint32_t *row = (uint32_t *)(data + stride * y);
26+
for (int x = 0; x < w; x++) {
27+
uint32_t pix = row[x];
28+
// swap blue (0:10) and red (20:30)
29+
pix = (pix & 0xc00ffc00) | ((pix & 0x3ff00000) >> 20) |
30+
((pix & 0x3ff) << 20);
31+
row[x] = pix;
32+
}
33+
}
34+
35+
cairo_surface_mark_dirty(surface);
36+
}
37+
1638
#if HAVE_GDK_PIXBUF
1739
cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) {
1840
int chan = gdk_pixbuf_get_n_channels(gdkbuf);

include/cairo_util.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
void cairo_set_source_u32(cairo_t *cairo, uint32_t color);
1212

13+
void cairo_rgb30_swap_rb(cairo_surface_t *surface);
14+
1315
#if HAVE_GDK_PIXBUF
1416

1517
cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(

main.c

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ struct swaybg_state {
5353
struct wl_list outputs; // struct swaybg_output::link
5454
struct wl_list images; // struct swaybg_image::link
5555
bool run_display;
56+
bool has_xrgb2101010;
57+
bool has_xbgr2101010;
5658
};
5759

5860
struct swaybg_image {
@@ -117,10 +119,26 @@ static struct wl_buffer *draw_buffer(const struct swaybg_output *output,
117119
r32, g32, b32, 0xFFFFFFFF);
118120
}
119121

122+
bool deep_image = false;
123+
if (surface) {
124+
cairo_format_t fmt = cairo_image_surface_get_format(surface);
125+
deep_image = deep_image || fmt == CAIRO_FORMAT_RGB30;
126+
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 17, 2)
127+
deep_image = deep_image || fmt == CAIRO_FORMAT_RGB96F;
128+
deep_image = deep_image || fmt == CAIRO_FORMAT_RGBA128F;
129+
#endif
130+
}
131+
132+
uint32_t format = WL_SHM_FORMAT_XRGB8888;
133+
if (deep_image && output->state->has_xrgb2101010) {
134+
format = WL_SHM_FORMAT_XRGB2101010;
135+
} else if (deep_image && output->state->has_xbgr2101010) {
136+
format = WL_SHM_FORMAT_XBGR2101010;
137+
}
120138

121139
struct pool_buffer buffer;
122140
if (!create_buffer(&buffer, output->state->shm,
123-
buffer_width, buffer_height, WL_SHM_FORMAT_XRGB8888)) {
141+
buffer_width, buffer_height, format)) {
124142
return NULL;
125143
}
126144

@@ -133,6 +151,10 @@ static struct wl_buffer *draw_buffer(const struct swaybg_output *output,
133151
output->config->mode, buffer_width, buffer_height);
134152
}
135153

154+
if (format == WL_SHM_FORMAT_XBGR2101010) {
155+
cairo_rgb30_swap_rb(buffer.surface);
156+
}
157+
136158
// return wl_buffer for caller to use and destroy
137159
struct wl_buffer *wl_buf = buffer.buffer;
138160
buffer.buffer = NULL;
@@ -398,6 +420,21 @@ static const struct wl_output_listener output_listener = {
398420
.description = output_description,
399421
};
400422

423+
424+
static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) {
425+
struct swaybg_state *state = data;
426+
if (format == WL_SHM_FORMAT_XBGR2101010) {
427+
state->has_xbgr2101010 = true;
428+
}
429+
if (format == WL_SHM_FORMAT_XRGB2101010) {
430+
state->has_xrgb2101010 = true;
431+
}
432+
}
433+
434+
static const struct wl_shm_listener shm_listener = {
435+
.format = shm_format,
436+
};
437+
401438
static void handle_global(void *data, struct wl_registry *registry,
402439
uint32_t name, const char *interface, uint32_t version) {
403440
struct swaybg_state *state = data;
@@ -406,6 +443,7 @@ static void handle_global(void *data, struct wl_registry *registry,
406443
wl_registry_bind(registry, name, &wl_compositor_interface, 4);
407444
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
408445
state->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
446+
wl_shm_add_listener(state->shm, &shm_listener, state);
409447
} else if (strcmp(interface, wl_output_interface.name) == 0) {
410448
struct swaybg_output *output = calloc(1, sizeof(struct swaybg_output));
411449
output->state = state;

pool-buffer.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ static int anonymous_shm_open(void) {
3636
return -1;
3737
}
3838

39+
static uint32_t cairo_format_from_wayland_shm(uint32_t shm) {
40+
switch (shm) {
41+
case WL_SHM_FORMAT_XRGB8888:
42+
return CAIRO_FORMAT_RGB24;
43+
case WL_SHM_FORMAT_XBGR2101010:
44+
case WL_SHM_FORMAT_XRGB2101010:
45+
return CAIRO_FORMAT_RGB30;
46+
default:
47+
assert(0);
48+
}
49+
}
50+
3951
bool create_buffer(struct pool_buffer *buf, struct wl_shm *shm,
4052
int32_t width, int32_t height, uint32_t format) {
4153
uint32_t stride = width * 4;
@@ -56,10 +68,11 @@ bool create_buffer(struct pool_buffer *buf, struct wl_shm *shm,
5668
wl_shm_pool_destroy(pool);
5769
close(fd);
5870

71+
cairo_format_t cairo_fmt = cairo_format_from_wayland_shm(format);
5972
buf->size = size;
6073
buf->data = data;
6174
buf->surface = cairo_image_surface_create_for_data(data,
62-
CAIRO_FORMAT_RGB24, width, height, stride);
75+
cairo_fmt, width, height, stride);
6376
buf->cairo = cairo_create(buf->surface);
6477
return true;
6578
}

0 commit comments

Comments
 (0)