1 #include <assert.h>
2 #include "background-image.h"
3 #include "cairo.h"
4 #include "log.h"
5 #include "swaylock.h"
6
parse_background_mode(const char * mode)7 enum background_mode parse_background_mode(const char *mode) {
8 if (strcmp(mode, "stretch") == 0) {
9 return BACKGROUND_MODE_STRETCH;
10 } else if (strcmp(mode, "fill") == 0) {
11 return BACKGROUND_MODE_FILL;
12 } else if (strcmp(mode, "fit") == 0) {
13 return BACKGROUND_MODE_FIT;
14 } else if (strcmp(mode, "center") == 0) {
15 return BACKGROUND_MODE_CENTER;
16 } else if (strcmp(mode, "tile") == 0) {
17 return BACKGROUND_MODE_TILE;
18 } else if (strcmp(mode, "solid_color") == 0) {
19 return BACKGROUND_MODE_SOLID_COLOR;
20 }
21 swaylock_log(LOG_ERROR, "Unsupported background mode: %s", mode);
22 return BACKGROUND_MODE_INVALID;
23 }
24
load_background_from_buffer(void * buf,uint32_t format,uint32_t width,uint32_t height,uint32_t stride,enum wl_output_transform transform)25 cairo_surface_t *load_background_from_buffer(void *buf, uint32_t format,
26 uint32_t width, uint32_t height, uint32_t stride, enum wl_output_transform transform) {
27 bool rotated =
28 transform == WL_OUTPUT_TRANSFORM_90 ||
29 transform == WL_OUTPUT_TRANSFORM_270 ||
30 transform == WL_OUTPUT_TRANSFORM_FLIPPED_90 ||
31 transform == WL_OUTPUT_TRANSFORM_FLIPPED_270;
32
33 cairo_surface_t *image;
34 if (rotated) {
35 image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, height, width);
36 } else {
37 image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
38 }
39 if (image == NULL) {
40 swaylock_log(LOG_ERROR, "Failed to create image..");
41 return NULL;
42 }
43
44 unsigned char *destbuf = cairo_image_surface_get_data(image);
45 size_t destwidth = cairo_image_surface_get_width(image);
46 size_t destheight = cairo_image_surface_get_height(image);
47 size_t deststride = cairo_image_surface_get_stride(image);
48 unsigned char *srcbuf = buf;
49 size_t srcstride = stride;
50 size_t minstride = srcstride < deststride ? srcstride : deststride;
51
52 // Lots of these are mostly-copy-and-pasted, with a lot of boilerplate
53 // for each case.
54 // The only interesting differencess between a lot of these cases are
55 // the definitions of srcx and srcy.
56 // I don't think it's worth adding a macro to make this "cleaner" though,
57 // as that would obfuscate what's actually going on.
58 switch (transform) {
59 case WL_OUTPUT_TRANSFORM_NORMAL:
60 // In most cases, the transform is probably normal. Luckily, it can be
61 // done with just one big memcpy.
62 if (srcstride == deststride) {
63 memcpy(destbuf, srcbuf, destheight * deststride);
64 } else {
65 for (size_t y = 0; y < destheight; ++y) {
66 memcpy(destbuf + y * deststride, srcbuf + y * srcstride, minstride);
67 }
68 }
69 break;
70 case WL_OUTPUT_TRANSFORM_90:
71 for (size_t desty = 0; desty < destheight; ++desty) {
72 size_t srcx = desty;
73 for (size_t destx = 0; destx < destwidth; ++destx) {
74 size_t srcy = destwidth - destx - 1;
75 *((uint32_t *)(destbuf + desty * deststride) + destx) =
76 *((uint32_t *)(srcbuf + srcy * srcstride) + srcx);
77 }
78 }
79 break;
80 case WL_OUTPUT_TRANSFORM_180:
81 for (size_t desty = 0; desty < destheight; ++desty) {
82 size_t srcy = destheight - desty - 1;
83 for (size_t destx = 0; destx < destwidth; ++destx) {
84 size_t srcx = destwidth - destx - 1;
85 *((uint32_t *)(destbuf + desty * deststride) + destx) =
86 *((uint32_t *)(srcbuf + srcy * srcstride) + srcx);
87 }
88 }
89 break;
90 case WL_OUTPUT_TRANSFORM_270:
91 for (size_t desty = 0; desty < destheight; ++desty) {
92 size_t srcx = destheight - desty - 1;
93 for (size_t destx = 0; destx < destwidth; ++destx) {
94 size_t srcy = destx;
95 *((uint32_t *)(destbuf + desty * deststride) + destx) =
96 *((uint32_t *)(srcbuf + srcy * srcstride) + srcx);
97 }
98 }
99 break;
100 case WL_OUTPUT_TRANSFORM_FLIPPED:
101 for (size_t desty = 0; desty < destheight; ++desty) {
102 size_t srcy = desty;
103 for (size_t destx = 0; destx < destwidth; ++destx) {
104 size_t srcx = destwidth - destx - 1;
105 *((uint32_t *)(destbuf + desty * deststride) + destx) =
106 *((uint32_t *)(srcbuf + srcy * srcstride) + srcx);
107 }
108 }
109 break;
110 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
111 for (size_t desty = 0; desty < destheight; ++desty) {
112 size_t srcx = desty;
113 for (size_t destx = 0; destx < destwidth; ++destx) {
114 size_t srcy = destx;
115 *((uint32_t *)(destbuf + desty * deststride) + destx) =
116 *((uint32_t *)(srcbuf + srcy * srcstride) + srcx);
117 }
118 }
119 break;
120 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
121 for (size_t desty = 0; desty < destheight; ++desty) {
122 size_t srcy = destheight - desty - 1;
123 memcpy(destbuf + desty * deststride, srcbuf + srcy * srcstride, minstride);
124 }
125 break;
126 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
127 for (size_t desty = 0; desty < destheight; ++desty) {
128 size_t srcx = destheight - desty - 1;
129 for (size_t destx = 0; destx < destwidth; ++destx) {
130 size_t srcy = destwidth - destx - 1;
131 *((uint32_t *)(destbuf + desty * deststride) + destx) =
132 *((uint32_t *)(srcbuf + srcy * srcstride) + srcx);
133 }
134 }
135 break;
136 }
137
138 return image;
139 }
140
load_background_image(const char * path)141 cairo_surface_t *load_background_image(const char *path) {
142 cairo_surface_t *image;
143 #if HAVE_GDK_PIXBUF
144 GError *err = NULL;
145 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);
146 if (!pixbuf) {
147 swaylock_log(LOG_ERROR, "Failed to load background image (%s).",
148 err->message);
149 return NULL;
150 }
151 image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
152 g_object_unref(pixbuf);
153 #else
154 image = cairo_image_surface_create_from_png(path);
155 #endif // HAVE_GDK_PIXBUF
156 if (!image) {
157 swaylock_log(LOG_ERROR, "Failed to read background image.");
158 return NULL;
159 }
160 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
161 swaylock_log(LOG_ERROR, "Failed to read background image: %s."
162 #if !HAVE_GDK_PIXBUF
163 "\nSway was compiled without gdk_pixbuf support, so only"
164 "\nPNG images can be loaded. This is the likely cause."
165 #endif // !HAVE_GDK_PIXBUF
166 , cairo_status_to_string(cairo_surface_status(image)));
167 return NULL;
168 }
169 return image;
170 }
171
render_background_image(cairo_t * cairo,cairo_surface_t * image,enum background_mode mode,int buffer_width,int buffer_height)172 void render_background_image(cairo_t *cairo, cairo_surface_t *image,
173 enum background_mode mode, int buffer_width, int buffer_height) {
174 double width = cairo_image_surface_get_width(image);
175 double height = cairo_image_surface_get_height(image);
176
177 cairo_save(cairo);
178 switch (mode) {
179 case BACKGROUND_MODE_STRETCH:
180 cairo_scale(cairo,
181 (double)buffer_width / width,
182 (double)buffer_height / height);
183 cairo_set_source_surface(cairo, image, 0, 0);
184 break;
185 case BACKGROUND_MODE_FILL: {
186 double window_ratio = (double)buffer_width / buffer_height;
187 double bg_ratio = width / height;
188
189 if (window_ratio > bg_ratio) {
190 double scale = (double)buffer_width / width;
191 cairo_scale(cairo, scale, scale);
192 cairo_set_source_surface(cairo, image,
193 0, (double)buffer_height / 2 / scale - height / 2);
194 } else {
195 double scale = (double)buffer_height / height;
196 cairo_scale(cairo, scale, scale);
197 cairo_set_source_surface(cairo, image,
198 (double)buffer_width / 2 / scale - width / 2, 0);
199 }
200 break;
201 }
202 case BACKGROUND_MODE_FIT: {
203 double window_ratio = (double)buffer_width / buffer_height;
204 double bg_ratio = width / height;
205
206 if (window_ratio > bg_ratio) {
207 double scale = (double)buffer_height / height;
208 cairo_scale(cairo, scale, scale);
209 cairo_set_source_surface(cairo, image,
210 (double)buffer_width / 2 / scale - width / 2, 0);
211 } else {
212 double scale = (double)buffer_width / width;
213 cairo_scale(cairo, scale, scale);
214 cairo_set_source_surface(cairo, image,
215 0, (double)buffer_height / 2 / scale - height / 2);
216 }
217 break;
218 }
219 case BACKGROUND_MODE_CENTER:
220 cairo_set_source_surface(cairo, image,
221 (double)buffer_width / 2 - width / 2,
222 (double)buffer_height / 2 - height / 2);
223 break;
224 case BACKGROUND_MODE_TILE: {
225 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
226 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
227 cairo_set_source(cairo, pattern);
228 break;
229 }
230 case BACKGROUND_MODE_SOLID_COLOR:
231 case BACKGROUND_MODE_INVALID:
232 assert(0);
233 break;
234 }
235 cairo_paint(cairo);
236 cairo_restore(cairo);
237 }
238