aboutsummaryrefslogtreecommitdiffstats
path: root/common/background-image.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/background-image.c')
-rw-r--r--common/background-image.c220
1 files changed, 0 insertions, 220 deletions
diff --git a/common/background-image.c b/common/background-image.c
deleted file mode 100644
index 994a0805..00000000
--- a/common/background-image.c
+++ /dev/null
@@ -1,220 +0,0 @@
1#include <assert.h>
2#include "background-image.h"
3#include "cairo_util.h"
4#include "log.h"
5#if HAVE_GDK_PIXBUF
6#include <gdk-pixbuf/gdk-pixbuf.h>
7#endif
8
9enum background_mode parse_background_mode(const char *mode) {
10 if (strcmp(mode, "stretch") == 0) {
11 return BACKGROUND_MODE_STRETCH;
12 } else if (strcmp(mode, "fill") == 0) {
13 return BACKGROUND_MODE_FILL;
14 } else if (strcmp(mode, "fit") == 0) {
15 return BACKGROUND_MODE_FIT;
16 } else if (strcmp(mode, "center") == 0) {
17 return BACKGROUND_MODE_CENTER;
18 } else if (strcmp(mode, "tile") == 0) {
19 return BACKGROUND_MODE_TILE;
20 } else if (strcmp(mode, "solid_color") == 0) {
21 return BACKGROUND_MODE_SOLID_COLOR;
22 }
23 sway_log(SWAY_ERROR, "Unsupported background mode: %s", mode);
24 return BACKGROUND_MODE_INVALID;
25}
26
27#if HAVE_GDK_PIXBUF
28static cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(
29 const GdkPixbuf *gdkbuf) {
30 int chan = gdk_pixbuf_get_n_channels(gdkbuf);
31 if (chan < 3) {
32 return NULL;
33 }
34
35 const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf);
36 if (!gdkpix) {
37 return NULL;
38 }
39 gint w = gdk_pixbuf_get_width(gdkbuf);
40 gint h = gdk_pixbuf_get_height(gdkbuf);
41 int stride = gdk_pixbuf_get_rowstride(gdkbuf);
42
43 cairo_format_t fmt = (chan == 3) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32;
44 cairo_surface_t * cs = cairo_image_surface_create (fmt, w, h);
45 cairo_surface_flush (cs);
46 if ( !cs || cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) {
47 return NULL;
48 }
49
50 int cstride = cairo_image_surface_get_stride(cs);
51 unsigned char * cpix = cairo_image_surface_get_data(cs);
52
53 if (chan == 3) {
54 int i;
55 for (i = h; i; --i) {
56 const guint8 *gp = gdkpix;
57 unsigned char *cp = cpix;
58 const guint8* end = gp + 3*w;
59 while (gp < end) {
60#if G_BYTE_ORDER == G_LITTLE_ENDIAN
61 cp[0] = gp[2];
62 cp[1] = gp[1];
63 cp[2] = gp[0];
64#else
65 cp[1] = gp[0];
66 cp[2] = gp[1];
67 cp[3] = gp[2];
68#endif
69 gp += 3;
70 cp += 4;
71 }
72 gdkpix += stride;
73 cpix += cstride;
74 }
75 } else {
76 /* premul-color = alpha/255 * color/255 * 255 = (alpha*color)/255
77 * (z/255) = z/256 * 256/255 = z/256 (1 + 1/255)
78 * = z/256 + (z/256)/255 = (z + z/255)/256
79 * # recurse once
80 * = (z + (z + z/255)/256)/256
81 * = (z + z/256 + z/256/255) / 256
82 * # only use 16bit uint operations, loose some precision,
83 * # result is floored.
84 * -> (z + z>>8)>>8
85 * # add 0x80/255 = 0.5 to convert floor to round
86 * => (z+0x80 + (z+0x80)>>8 ) >> 8
87 * ------
88 * tested as equal to lround(z/255.0) for uint z in [0..0xfe02]
89 */
90#define PREMUL_ALPHA(x,a,b,z) \
91 G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \
92 G_STMT_END
93 int i;
94 for (i = h; i; --i) {
95 const guint8 *gp = gdkpix;
96 unsigned char *cp = cpix;
97 const guint8* end = gp + 4*w;
98 guint z1, z2, z3;
99 while (gp < end) {
100#if G_BYTE_ORDER == G_LITTLE_ENDIAN
101 PREMUL_ALPHA(cp[0], gp[2], gp[3], z1);
102 PREMUL_ALPHA(cp[1], gp[1], gp[3], z2);
103 PREMUL_ALPHA(cp[2], gp[0], gp[3], z3);
104 cp[3] = gp[3];
105#else
106 PREMUL_ALPHA(cp[1], gp[0], gp[3], z1);
107 PREMUL_ALPHA(cp[2], gp[1], gp[3], z2);
108 PREMUL_ALPHA(cp[3], gp[2], gp[3], z3);
109 cp[0] = gp[3];
110#endif
111 gp += 4;
112 cp += 4;
113 }
114 gdkpix += stride;
115 cpix += cstride;
116 }
117#undef PREMUL_ALPHA
118 }
119 cairo_surface_mark_dirty(cs);
120 return cs;
121}
122#endif // HAVE_GDK_PIXBUF
123
124cairo_surface_t *load_background_image(const char *path) {
125 cairo_surface_t *image;
126#if HAVE_GDK_PIXBUF
127 GError *err = NULL;
128 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);
129 if (!pixbuf) {
130 sway_log(SWAY_ERROR, "Failed to load background image (%s).",
131 err->message);
132 return NULL;
133 }
134 image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
135 g_object_unref(pixbuf);
136#else
137 image = cairo_image_surface_create_from_png(path);
138#endif // HAVE_GDK_PIXBUF
139 if (!image) {
140 sway_log(SWAY_ERROR, "Failed to read background image.");
141 return NULL;
142 }
143 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
144 sway_log(SWAY_ERROR, "Failed to read background image: %s."
145#if !HAVE_GDK_PIXBUF
146 "\nSway was compiled without gdk_pixbuf support, so only"
147 "\nPNG images can be loaded. This is the likely cause."
148#endif // !HAVE_GDK_PIXBUF
149 , cairo_status_to_string(cairo_surface_status(image)));
150 return NULL;
151 }
152 return image;
153}
154
155void render_background_image(cairo_t *cairo, cairo_surface_t *image,
156 enum background_mode mode, int buffer_width, int buffer_height) {
157 double width = cairo_image_surface_get_width(image);
158 double height = cairo_image_surface_get_height(image);
159
160 cairo_save(cairo);
161 switch (mode) {
162 case BACKGROUND_MODE_STRETCH:
163 cairo_scale(cairo,
164 (double)buffer_width / width,
165 (double)buffer_height / height);
166 cairo_set_source_surface(cairo, image, 0, 0);
167 break;
168 case BACKGROUND_MODE_FILL: {
169 double window_ratio = (double)buffer_width / buffer_height;
170 double bg_ratio = width / height;
171
172 if (window_ratio > bg_ratio) {
173 double scale = (double)buffer_width / width;
174 cairo_scale(cairo, scale, scale);
175 cairo_set_source_surface(cairo, image,
176 0, (double)buffer_height / 2 / scale - height / 2);
177 } else {
178 double scale = (double)buffer_height / height;
179 cairo_scale(cairo, scale, scale);
180 cairo_set_source_surface(cairo, image,
181 (double)buffer_width / 2 / scale - width / 2, 0);
182 }
183 break;
184 }
185 case BACKGROUND_MODE_FIT: {
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_height / height;
191 cairo_scale(cairo, scale, scale);
192 cairo_set_source_surface(cairo, image,
193 (double)buffer_width / 2 / scale - width / 2, 0);
194 } else {
195 double scale = (double)buffer_width / width;
196 cairo_scale(cairo, scale, scale);
197 cairo_set_source_surface(cairo, image,
198 0, (double)buffer_height / 2 / scale - height / 2);
199 }
200 break;
201 }
202 case BACKGROUND_MODE_CENTER:
203 cairo_set_source_surface(cairo, image,
204 (double)buffer_width / 2 - width / 2,
205 (double)buffer_height / 2 - height / 2);
206 break;
207 case BACKGROUND_MODE_TILE: {
208 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
209 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
210 cairo_set_source(cairo, pattern);
211 break;
212 }
213 case BACKGROUND_MODE_SOLID_COLOR:
214 case BACKGROUND_MODE_INVALID:
215 assert(0);
216 break;
217 }
218 cairo_paint(cairo);
219 cairo_restore(cairo);
220}