1 #include "skippy.h"
2
3 typedef struct {
4 XRectangle rect;
5 int num_x, num_y;
6 double ratio_x, ratio_y;
7 } img_composite_params_t;
8
9 #define XRECTANGLE_INIT { .x = 0, .y = 0, .width = 0, .height = 0 }
10
11 #define IMG_COMPOSITE_PARAMS_INIT { \
12 .rect = XRECTANGLE_INIT, \
13 .num_x = 0, \
14 .num_y = 0, \
15 .ratio_x = 0.0f, \
16 .ratio_y = 0.0f, \
17 }
18
19 /**
20 * @brief Crop a rectangle by another rectangle.
21 */
22 static inline void
rect_crop(XRectangle * pdst,const XRectangle * psrc,const XRectangle * pbound)23 rect_crop(XRectangle *pdst, const XRectangle *psrc, const XRectangle *pbound) {
24 XRectangle *pdst_original = NULL;
25 XRectangle rtmp;
26 if (psrc == pdst) {
27 pdst_original = pdst;
28 pdst = &rtmp;
29 }
30 assert(psrc != pdst);
31 pdst->x = MAX(psrc->x, pbound->x);
32 pdst->y = MAX(psrc->y, pbound->y);
33 pdst->width = MAX(0,
34 MIN(psrc->x + psrc->width, pbound->x + pbound->width) - pdst->x);
35 pdst->height = MAX(0,
36 MIN(psrc->y + psrc->height, pbound->y + pbound->height) - pdst->y);
37 if (pdst_original)
38 *pdst_original = *pdst;
39 }
40
41 /**
42 * @brief Crop a rectangle by another rectangle, and adjust texture
43 * start coordinate.
44 *
45 * ptex_x and ptex_y must point to initialized values.
46 */
47 static inline void
rect_crop2(XRectangle * pdst,const XRectangle * psrc,const XRectangle * pbound,int * ptex_x,int * ptex_y,double ratio_x,double ratio_y)48 rect_crop2(XRectangle *pdst, const XRectangle *psrc, const XRectangle *pbound,
49 int *ptex_x, int *ptex_y, double ratio_x, double ratio_y) {
50 int original_x = psrc->x, original_y = psrc->y;
51 rect_crop(pdst, psrc, pbound);
52 *ptex_x += (pdst->x - original_x) / ratio_x;
53 *ptex_y += (pdst->y - original_y) / ratio_y;
54 }
55
56 /**
57 * @brief Get length of 1 pixel of data for the specified depth.
58 */
59 static inline int
depth_to_len(int depth)60 depth_to_len(int depth) {
61 int l = 8;
62 while (depth > l)
63 l *= 2;
64 return l / 8;
65 }
66
67 /**
68 * @brief Get X Render format for a specified depth.
69 */
70 static inline const XRenderPictFormat *
depth_to_rfmt(session_t * ps,int depth)71 depth_to_rfmt(session_t *ps, int depth) {
72 switch (depth) {
73 case 32: return XRenderFindStandardFormat(ps->dpy, PictStandardARGB32);
74 case 24: return XRenderFindStandardFormat(ps->dpy, PictStandardRGB24);
75 case 8: return XRenderFindStandardFormat(ps->dpy, PictStandardA8);
76 case 4: return XRenderFindStandardFormat(ps->dpy, PictStandardA4);
77 case 1: return XRenderFindStandardFormat(ps->dpy, PictStandardA1);
78 };
79 assert(0);
80 return NULL;
81 }
82
83 static inline void
free_pictw_keeppixmap(session_t * ps,pictw_t ** ppictw)84 free_pictw_keeppixmap(session_t *ps, pictw_t **ppictw) {
85 if (*ppictw) {
86 free_picture(ps, &(*ppictw)->pict);
87 free(*ppictw);
88 }
89 *ppictw = NULL;
90 }
91
92 static inline void
free_pictw(session_t * ps,pictw_t ** ppictw)93 free_pictw(session_t *ps, pictw_t **ppictw) {
94 if (*ppictw) {
95 free_pixmap(ps, &(*ppictw)->pxmap);
96 free_pictw_keeppixmap(ps, ppictw);
97 }
98 }
99
100 /**
101 * @brief Destroy a <code>pictspec_t</code>.
102 */
103 static inline void
free_pictspec(session_t * ps,pictspec_t * p)104 free_pictspec(session_t *ps, pictspec_t *p) {
105 free_pictw(ps, &p->img);
106 free(p->path);
107 }
108
109 /**
110 * @brief Build a pictw_t of specified size and depth.
111 */
112 static inline pictw_t *
create_pictw_frompixmap(session_t * ps,int width,int height,int depth,Pixmap pxmap)113 create_pictw_frompixmap(session_t *ps, int width, int height, int depth,
114 Pixmap pxmap) {
115 pictw_t *pictw = NULL;
116
117 if (!pxmap) {
118 printfef("(%d, %d, %d, %#010lx): Missing pixmap.", width, height, depth, pxmap);
119 return NULL;
120 }
121
122 // Acquire Pixmap info
123 if (!(width && height && depth)) {
124 Window rroot = None;
125 int rx = 0, ry = 0;
126 unsigned rwidth = 0, rheight = 0, rborder_width = 0, rdepth = 0;
127 if (!XGetGeometry(ps->dpy, pxmap,
128 &rroot, &rx, &ry, &rwidth, &rheight, &rborder_width, &rdepth)) {
129 printfef("(%d, %d, %d, %#010lx): Failed to determine pixmap size.", width, height, depth, pxmap);
130 return NULL;
131 }
132 width = rwidth;
133 height = rheight;
134 depth = rdepth;
135 }
136
137 // Sanity check
138 if (!(width && height && depth)) {
139 printfef("(%d, %d, %d, %#010lx): Failed to get pixmap info.", width, height, depth, pxmap);
140 return NULL;
141 }
142
143 // Find X Render format
144 const XRenderPictFormat *rfmt = depth_to_rfmt(ps, depth);
145 if (!rfmt) {
146 printfef("(%d, %d, %d, %#010lx): Failed to find X Render format for depth %d.",
147 width, height, depth, pxmap, depth);
148 return NULL;
149 }
150
151 // Create Picture
152 pictw = scalloc(1, pictw_t);
153 pictw->pxmap = pxmap;
154 pictw->width = width;
155 pictw->height = height;
156 pictw->depth = depth;
157
158 if (!(pictw->pict = XRenderCreatePicture(ps->dpy, pictw->pxmap,
159 rfmt, 0, NULL))) {
160 printfef("(%d, %d, %d, %#010lx): Failed to create Picture.",
161 width, height, depth, pxmap);
162 free_pictw(ps, &pictw);
163 }
164
165 return pictw;
166 }
167
168 /**
169 * @brief Build a pictw_t of specified size and depth.
170 */
171 static inline pictw_t *
create_pictw(session_t * ps,int width,int height,int depth)172 create_pictw(session_t *ps, int width, int height, int depth) {
173 Pixmap pxmap = XCreatePixmap(ps->dpy, ps->root, width, height, depth);
174 if (!pxmap) {
175 printfef("(%d, %d, %d): Failed to create Pixmap.", width, height, depth);
176 return NULL;
177 }
178
179 return create_pictw_frompixmap(ps, width, height, depth, pxmap);
180 }
181
182 static inline pictw_t *
clone_pictw(session_t * ps,pictw_t * pictw)183 clone_pictw(session_t *ps, pictw_t *pictw) {
184 if (!pictw) return NULL;
185 pictw_t *new_pictw = create_pictw(ps, pictw->width, pictw->height, pictw->depth);
186 if (!new_pictw) return NULL;
187 XRenderComposite(ps->dpy, PictOpSrc, pictw->pict, None, new_pictw->pict,
188 0, 0, 0, 0, 0, 0, new_pictw->width, new_pictw->height);
189
190 return new_pictw;
191 }
192
193 pictw_t *
194 simg_load(session_t *ps, const char *path, enum pict_posp_mode mode,
195 int twidth, int theight, enum align alg, enum align valg,
196 const XRenderColor *pc);
197
198 static inline pictw_t *
simg_load_s(session_t * ps,const pictspec_t * spec)199 simg_load_s(session_t *ps, const pictspec_t *spec) {
200 return simg_load(ps, spec->path, spec->mode, spec->twidth, spec->theight,
201 spec->alg, spec->valg, &spec->c);
202 }
203
204 static inline bool
simg_cachespec(session_t * ps,pictspec_t * spec)205 simg_cachespec(session_t *ps, pictspec_t *spec) {
206 free_pictw(ps, &spec->img);
207 if (spec->path
208 && !(spec->img = simg_load(ps, spec->path, PICTPOSP_ORIG, 0, 0,
209 ALIGN_MID, ALIGN_MID, NULL)))
210 return false;
211 return true;
212 }
213
214 pictw_t *
215 simg_postprocess(session_t *ps, pictw_t *src, enum pict_posp_mode mode,
216 int twidth, int theight, enum align alg, enum align valg,
217 const XRenderColor *pc);
218
219 void
220 simg_get_composite_params(pictw_t *src, int twidth, int theight,
221 enum pict_posp_mode mode, enum align alg, enum align valg,
222 img_composite_params_t *pparams);
223
224 void
225 simg_composite(session_t *ps, pictw_t *src, Picture dest,
226 int twidth, int theight, const img_composite_params_t *pparams,
227 const XRenderColor *pc, Picture mask, const XRectangle *pbound);
228
229 /**
230 * @brief XRenderFillRectangle() with area cropped by a rectangle.
231 */
232 static inline void
XRenderFillRectangle_cropped(Display * dpy,int op,Picture dst,const XRenderColor * color,int x,int y,unsigned int width,unsigned int height,const XRectangle * pbound)233 XRenderFillRectangle_cropped(Display *dpy, int op, Picture dst,
234 const XRenderColor *color,
235 int x, int y, unsigned int width, unsigned int height,
236 const XRectangle *pbound) {
237 XRectangle r = { .x = x, .y = y, .width = width, .height = height };
238 if (pbound)
239 rect_crop(&r, &r, pbound);
240 XRenderFillRectangle(dpy, op, dst, color,
241 r.x, r.y, r.width, r.height);
242 }
243
244 /**
245 * @brief XRenderComposite() with area cropped by a rectangle.
246 */
247 static inline void
XRenderComposite_cropped(Display * dpy,int op,Picture src,Picture mask,Picture dst,int src_x,int src_y,int mask_x,int mask_y,int dst_x,int dst_y,unsigned int width,unsigned int height,const XRectangle * pbound,double ratio_x,double ratio_y)248 XRenderComposite_cropped(Display *dpy, int op, Picture src, Picture mask,
249 Picture dst, int src_x, int src_y, int mask_x, int mask_y,
250 int dst_x, int dst_y, unsigned int width, unsigned int height,
251 const XRectangle *pbound, double ratio_x, double ratio_y) {
252 XRectangle r = { .x = dst_x, .y = dst_y, .width = width, .height = height };
253 if (pbound)
254 rect_crop2(&r, &r, pbound, &src_x, &src_y, ratio_x, ratio_y);
255 XRenderComposite(dpy, op, src, mask, dst, src_x, src_y,
256 mask_x, mask_y, r.x, r.y, r.width, r.height);
257 }
258
259 static inline pictw_t *
simg_pixmap_to_pictw(session_t * ps,int width,int height,int depth,Pixmap pxmap,Pixmap mask)260 simg_pixmap_to_pictw(session_t *ps, int width, int height, int depth,
261 Pixmap pxmap, Pixmap mask) {
262 GC gc = None;
263 pictw_t *porig = create_pictw_frompixmap(ps, width, height, depth, pxmap);
264 pictw_t *pmask = NULL;
265 pictw_t *pictw = NULL;
266
267 if (!porig) {
268 printfef("(%d, %d, %d, %#010lx, %#010lx): Failed to create picture for pixmap.",
269 width, height, depth, pxmap, mask);
270 goto simg_pixmap_to_pict_end;
271 }
272
273 if (mask) {
274 if (!(pmask = create_pictw_frompixmap(ps, width, height, depth, mask))) {
275 printfef("(%d, %d, %d, %#010lx, %#010lx): Failed to create picture for mask.",
276 width, height, depth, pxmap, mask);
277 goto simg_pixmap_to_pict_end;
278 }
279 // Probably we should check for width/height consistency between pixmap
280 // and mask...
281 }
282
283 if (!(pictw = create_pictw(ps, porig->width, porig->height, (mask ? 32: porig->depth)))) {
284 printfef("(%d, %d, %d, %#010lx, %#010lx): Failed to create target picture.",
285 width, height, depth, pxmap, mask);
286 goto simg_pixmap_to_pict_end;
287 }
288
289 // Copy content
290 static const XRenderColor XRC_TRANS = {
291 .red = 0, .green = 0, .blue = 0, .alpha = 0
292 };
293 XRenderFillRectangle(ps->dpy, PictOpSrc, pictw->pict, &XRC_TRANS,
294 0, 0, pictw->width, pictw->height);
295 XRenderComposite(ps->dpy, PictOpSrc, porig->pict, (pmask ? pmask->pict: None),
296 pictw->pict, 0, 0, 0, 0, 0, 0, pictw->width, pictw->height);
297
298 // Does core Xlib handle transparency correctly?
299 /*
300 gc = XCreateGC(ps->dpy, pictw->pxmap, 0, 0);
301 if (!gc) {
302 printfef("(%#010lx, %#010lx, %d, %d, %d): Failed to create GC.",
303 pxmap, mask, width, height, depth);
304 free_pictw(ps, &pictw);
305 goto simg_data_to_pict_end;
306 }
307 if (XCopyArea(ps->dpy, pxmap, pictw->pxmap, gc, 0, 0, width, height, 0, 0)) {
308 }
309 */
310
311 simg_pixmap_to_pict_end:
312 free_pictw_keeppixmap(ps, &porig);
313 free_pictw_keeppixmap(ps, &pmask);
314 if (gc)
315 XFreeGC(ps->dpy, gc);
316
317 return pictw;
318 }
319
320 static inline pictw_t *
simg_data_to_pictw(session_t * ps,int width,int height,int depth,const unsigned char * data,int bytes_per_line)321 simg_data_to_pictw(session_t *ps, int width, int height, int depth,
322 const unsigned char *data, int bytes_per_line) {
323 assert(data);
324 data = mmemcpy(data, height
325 * (bytes_per_line ? bytes_per_line: depth_to_len(depth) * width));
326 pictw_t *pictw = NULL;
327 GC gc = None;
328
329 // Use ARGB visual if needed
330 Visual *visual = DefaultVisual(ps->dpy, ps->screen);
331 if (32 == depth && ps->argb_visual)
332 visual = ps->argb_visual;
333 XImage *img = XCreateImage(ps->dpy, visual,
334 depth, ZPixmap, 0, (char *) data, width, height,
335 8, bytes_per_line);
336 if (!img) {
337 printfef("(%d, %d, %d): Failed to create XImage.",
338 width, height, depth);
339 goto simg_data_to_pict_end;
340 }
341 if (!(pictw = create_pictw(ps, width, height, depth))) {
342 printfef("(%d, %d, %d): Failed to create Picture.",
343 width, height, depth);
344 goto simg_data_to_pict_end;
345 }
346 gc = XCreateGC(ps->dpy, pictw->pxmap, 0, 0);
347 if (!gc) {
348 printfef("(%d, %d, %d): Failed to create GC.",
349 width, height, depth);
350 free_pictw(ps, &pictw);
351 goto simg_data_to_pict_end;
352 }
353 XPutImage(ps->dpy, pictw->pxmap, gc, img, 0, 0, 0, 0, width, height);
354
355 simg_data_to_pict_end:
356 if (img)
357 XDestroyImage(img);
358 if (gc)
359 XFreeGC(ps->dpy, gc);
360
361 return pictw;
362 }
363
364 static inline void
simg_data24_premultiply(unsigned char * data,int len)365 simg_data24_premultiply(unsigned char *data, int len) {
366 for (; len > 0; len--, data += 4) {
367 const unsigned char alpha = data[3];
368 if (0xff == alpha || !alpha) continue;
369 // This is slightly inaccurate because 1.0 == 255 instead of 256 (2 ** 8).
370 // Of course there are "smarter" ways to perform 3 multiplications all at
371 // once, but I don't feel like writing endian-specific code.
372 data[0] = ((uint_fast16_t) data[0] * alpha) >> 8;
373 data[1] = ((uint_fast16_t) data[1] * alpha) >> 8;
374 data[2] = ((uint_fast16_t) data[2] * alpha) >> 8;
375 }
376 }
377
378 static inline void
simg_data24_fillalpha(unsigned char * data,int len)379 simg_data24_fillalpha(unsigned char *data, int len) {
380 for (--len; len >= 0; --len) {
381 if (len) {
382 data[len * 4 + 0] = data[len * 3 + 0];
383 data[len * 4 + 1] = data[len * 3 + 1];
384 data[len * 4 + 2] = data[len * 3 + 2];
385 }
386 data[len * 4 + 3] = 0xff;
387 }
388 }
389
390 static inline void
simg_data24_tobgr(unsigned char * data,int len)391 simg_data24_tobgr(unsigned char *data, int len) {
392 for (--len; len >= 0; --len) {
393 unsigned char r = data[len * 4];
394 data[len * 4 + 0] = data[len * 4 + 2];
395 data[len * 4 + 2] = r;
396 }
397 }
398
399 static inline unsigned char *
simg_data32_from_long(const long * src,int len)400 simg_data32_from_long(const long *src, int len) {
401 if (4 == sizeof(long))
402 return (unsigned char *) src;
403
404 uint32_t *data = smalloc(len, uint32_t);
405 for (int i = 0; i < len; ++i)
406 data[i] = src[i];
407 return (unsigned char *) data;
408 }
409
410 static inline void
simg_data32_premultiply(unsigned char * data,int len)411 simg_data32_premultiply(unsigned char *data, int len) {
412 for (--len; len >= 0; --len) {
413 float a = (float) data[len * 4 + 3] / 0xff;
414 data[len * 4 + 0] *= a;
415 data[len * 4 + 1] *= a;
416 data[len * 4 + 2] *= a;
417 }
418 }
419