commit b880be4

sewn  ·  2026-05-07 23:12:58 +0000 UTC
parent 470591f
implement color, fit, fill, tile, stretch
1 files changed,  +119, -19
M wawa.c
M wawa.c
+119, -19
  1@@ -1,4 +1,5 @@
  2 /* See LICENSE file for copyright and license details. */
  3+#include <byteswap.h>
  4 #include <fcntl.h>
  5 #include <stdbool.h>
  6 #include <sys/mman.h>
  7@@ -31,23 +32,27 @@ struct output {
  8 	struct wl_list link;
  9 };
 10 
 11+static struct wl_display *display;
 12+static struct wl_registry *registry;
 13+static struct wl_compositor *compositor;
 14+static struct wl_shm *shm;
 15+static struct zwlr_layer_shell_v1 *layer_shell;
 16+static struct wl_list outputs;
 17+
 18 /*
 19  * Must be valid when a single monitor is misconfigured
 20  * or during startup, after which the image is freed.
 21  */
 22 struct {
 23+	FILE *fp;
 24 	int width, height;
 25 	unsigned char *data;
 26 } image;
 27 
 28-static struct wl_display *display;
 29-static struct wl_registry *registry;
 30-static struct wl_compositor *compositor;
 31-static struct wl_shm *shm;
 32-static struct zwlr_layer_shell_v1 *layer_shell;
 33-static struct wl_list outputs;
 34+static uint32_t color;
 35 
 36-static const char *filename;
 37+static void image_fill(unsigned char *dst, struct output *output);
 38+static void (*image_modify)(unsigned char *, struct output *) = image_fill;
 39 
 40 static void
 41 die(const char *fmt, ...)
 42@@ -69,7 +74,61 @@ die(const char *fmt, ...)
 43 }
 44 
 45 static void
 46-image_crop(unsigned char *dst, struct output *output)
 47+parse_color(const char *src)
 48+{
 49+	int len;
 50+
 51+	if (src[0] == '#')
 52+		src++;
 53+	len = strlen(src);
 54+	if (len != 6 && len != 8)
 55+		die("bad color: %s", src);
 56+
 57+	color = strtoul(src, NULL, 16);
 58+	if (len == 6)
 59+		color = (color << 8) | 0xFF;
 60+
 61+	color = bswap_32(color);
 62+}
 63+
 64+static void
 65+image_color(unsigned char *dst, struct output *output)
 66+{
 67+	for (size_t i = 0; i < output->size; i += 4)
 68+		memcpy(dst + i, &color, 4);
 69+}
 70+
 71+static void
 72+image_stretch(unsigned char *dst, struct output *output)
 73+{
 74+	stbir_resize_uint8_linear(
 75+	  image.data, image.width, image.height, image.width * 4,
 76+		dst, output->width, output->height, output->stride, 4);
 77+}
 78+
 79+static void
 80+image_fit(unsigned char *dst, struct output *output)
 81+{
 82+	struct {
 83+		int width, height;
 84+		int x, y;
 85+	} crop;
 86+	double factor;
 87+
 88+	factor = fmin((double)output->width/image.width, (double)output->height/image.height);
 89+	crop.width = image.width * factor;
 90+	crop.height = image.height * factor;
 91+	crop.x = (output->width - crop.width) / 2;
 92+	crop.y = (output->height - crop.height) / 2;
 93+	stbir_resize_uint8_linear(
 94+	  image.data, image.width, image.height, image.width * 4,
 95+		dst + (crop.y * output->stride) + (crop.x * 4),
 96+		crop.width, crop.height, output->stride,
 97+		4);
 98+}
 99+
100+static void
101+image_fill(unsigned char *dst, struct output *output)
102 {
103 	struct {
104 		int width, height;
105@@ -78,16 +137,38 @@ image_crop(unsigned char *dst, struct output *output)
106 	double factor;
107 
108 	factor = fmin((double)image.width/output->width, (double)image.height/output->height);
109-	crop.x = (image.width - (output->width * factor)) / 2;
110-	crop.y = (image.height - (output->height * factor)) / 2;
111 	crop.width = output->width * factor;
112 	crop.height = output->height * factor;
113+	crop.x = (image.width - (crop.width)) / 2;
114+	crop.y = (image.height - (crop.height)) / 2;
115 	stbir_resize_uint8_linear(
116-	  image.data + (crop.y * image.width + crop.x) * 4, crop.width, crop.height, image.width * 4,
117+	  image.data + (crop.y * image.width + crop.x) * 4,
118+	  crop.width, crop.height, image.width * 4,
119 		dst, output->width, output->height, output->stride,
120 		4);
121 }
122 
123+static void
124+image_tile(unsigned char *dst, struct output *o)
125+{
126+	unsigned char *to, *src;
127+	uint16_t off_x, off_y, w, h;
128+
129+	/* implementation shamelessly stolen from xwallpaper, MIT:
130+	 * 2025 Tobias Stoeckmann <tobias@stoeckmann.org> */
131+	for (off_y = 0; off_y < o->height; off_y += image.height) {
132+		h = (off_y + image.height > o->height) ? o->height - off_y : image.height;
133+		for (off_x = 0; off_x < o->width; off_x += image.width) {
134+			w = (off_x + image.width > o->width) ? o->width - off_x : image.width;
135+			for (int y = 0; y < h; y++) {
136+				to = dst + ((off_y + y) * o->stride);
137+				src = image.data + (y * image.width * 4);
138+				memcpy(to + (off_x * 4), src, w * 4);
139+			}
140+		}
141+	}
142+}
143+
144 static struct wl_buffer *
145 output_load_image(struct output *output)
146 {
147@@ -113,7 +194,7 @@ output_load_image(struct output *output)
148 	wl_shm_pool_destroy(shm_pool);
149 	close(fd);
150 
151-	image_crop(data, output);
152+	image_modify(data, output);
153 
154 	/* RGBA->BGRA */
155 	for (int i = 0; i < output->size; i += 4) {
156@@ -138,9 +219,9 @@ layer_surface_handle_configure(void *data, struct zwlr_layer_surface_v1 *layer_s
157 	output->configured = width == output->width && height == output->height;
158 	if (output->configured) return;
159 
160-	if (!image.data) {
161+	if (!image.data && image.fp) {
162 		fputs("loaded image", stderr);
163-		if (!(image.data = stbi_load(filename,
164+		if (!(image.data = stbi_load_from_file(image.fp,
165 		                       &image.width, &image.height, NULL, 4)))
166 			die("failed to load image: %s", stbi_failure_reason());
167 	}
168@@ -247,16 +328,29 @@ static const struct wl_registry_listener registry_listener = {
169 	.global_remove = registry_handle_remove,
170 };
171 
172+
173+
174 int
175 main(int argc, char *argv[])
176 {
177-	if (argc != 2) {
178-		fprintf(stderr, "usage: %s filename\n", argv[0]);
179-		return EXIT_FAILURE;
180+	switch (argc) {
181+	case 2:
182+		parse_color(argv[1]);
183+		image_modify = image_color;
184+		break;
185+	case 3:
186+		if (!strcmp(argv[1], "stretch")) image_modify = image_stretch;
187+		else if (!strcmp(argv[1], "fit")) image_modify = image_fit;
188+		else if (!strcmp(argv[1], "fill")) image_modify = image_fill;
189+		else if (!strcmp(argv[1], "tile")) image_modify = image_tile;
190+		else goto usage;
191+
192+		if (!(image.fp = fopen(argv[2], "rb"))) die("fopen %s:", argv[2]);
193+		break;
194+	default:
195+		goto usage;
196 	}
197 
198-	filename = argv[1];
199-
200 	if (!(display = wl_display_connect(NULL)))
201 		die("failed to connect to wayland");
202 
203@@ -273,4 +367,10 @@ main(int argc, char *argv[])
204 		;
205 
206 	return EXIT_SUCCESS;
207+
208+usage:
209+	fprintf(stderr, "usage: %s stretch|fit|fill|tile filename\n"
210+	                "       %s RRGGBBAA\n",
211+	        argv[0], argv[0]);
212+	return EXIT_FAILURE;
213 }