Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Optionally submit 10 bit buffers #54

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 26 additions & 19 deletions background-image.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,35 @@ enum background_mode parse_background_mode(const char *mode) {
}

cairo_surface_t *load_background_image(const char *path) {
cairo_surface_t *image;
#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;
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);
}
// 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 HAVE_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);
}
#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
Expand Down
32 changes: 19 additions & 13 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,20 +14,25 @@ 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;
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;
}
}
return CAIRO_SUBPIXEL_ORDER_DEFAULT;

cairo_surface_mark_dirty(surface);
}

#if HAVE_GDK_PIXBUF
Expand Down
4 changes: 1 addition & 3 deletions include/cairo_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@
#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);
void cairo_rgb30_swap_rb(cairo_surface_t *surface);

#if HAVE_GDK_PIXBUF

Expand Down
72 changes: 50 additions & 22 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -99,48 +101,58 @@ struct swaybg_output {
// Create a wl_buffer with the specified dimensions and content
static struct wl_buffer *draw_buffer(const struct swaybg_output *output,
cairo_surface_t *surface, uint32_t buffer_width, uint32_t buffer_height) {
uint32_t bg_color = output->config->color ? output->config->color : 0x3f3f3fff;

if (buffer_width == 1 && buffer_height == 1 &&
output->config->mode == BACKGROUND_MODE_SOLID_COLOR &&
output->state->single_pixel_buffer_manager) {
// create and return single pixel buffer
uint8_t r8 = (output->config->color >> 24) & 0xFF;
uint8_t g8 = (output->config->color >> 16) & 0xFF;
uint8_t b8 = (output->config->color >> 8) & 0xFF;
uint8_t a8 = (output->config->color >> 0) & 0xFF;
uint8_t r8 = (bg_color >> 24) & 0xFF;
uint8_t g8 = (bg_color >> 16) & 0xFF;
uint8_t b8 = (bg_color >> 8) & 0xFF;
uint32_t f = 0xFFFFFFFF / 0xFF; // division result is an integer
uint32_t r32 = r8 * f;
uint32_t g32 = g8 * f;
uint32_t b32 = b8 * f;
uint32_t a32 = a8 * f;
return wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(
output->state->single_pixel_buffer_manager, r32, g32, b32, a32);
output->state->single_pixel_buffer_manager,
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_ARGB8888)) {
buffer_width, buffer_height, format)) {
return NULL;
}

cairo_t *cairo = buffer.cairo;
cairo_save(cairo);
cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
cairo_set_source_u32(cairo, bg_color);
cairo_paint(cairo);
cairo_restore(cairo);
if (output->config->mode == BACKGROUND_MODE_SOLID_COLOR) {
cairo_set_source_u32(cairo, output->config->color);
cairo_paint(cairo);
} else {
if (output->config->color) {
cairo_set_source_u32(cairo, output->config->color);
cairo_paint(cairo);
}

if (surface) {
render_background_image(cairo, surface,
output->config->mode, buffer_width, buffer_height);
}
if (surface) {
render_background_image(cairo, surface,
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
Expand Down Expand Up @@ -408,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;
Expand All @@ -416,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;
Expand Down
15 changes: 14 additions & 1 deletion pool-buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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_ARGB32, width, height, stride);
cairo_fmt, width, height, stride);
buf->cairo = cairo_create(buf->surface);
return true;
}
Expand Down