1 /*----------------------------------------------------------------------*
2  * File:	rxvtimg.C
3  *----------------------------------------------------------------------*
4  *
5  * All portions of code are copyright by their respective author/s.
6  * Copyright (c) 2012      Marc Lehmann <schmorp@schmorp.de>
7  * Copyright (c) 2012      Emanuele Giaquinta <e.giaquinta@glauco.it>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *---------------------------------------------------------------------*/
23 
24 #include <string.h>
25 #include <math.h>
26 #include "../config.h"
27 #include "rxvt.h"
28 
29 #if HAVE_IMG
30 
31 typedef rxvt_img::nv nv;
32 
33 namespace
34 {
35   struct mat3x3
36   {
37     nv v[3][3];
38 
mat3x3mat3x339     mat3x3 ()
40     {
41     }
42 
mat3x3mat3x343     mat3x3 (const nv *matrix)
44     {
45       memcpy (v, matrix, sizeof (v));
46     }
47 
mat3x3mat3x348     mat3x3 (nv v11, nv v12, nv v13, nv v21, nv v22, nv v23, nv v31, nv v32, nv v33)
49     {
50       v[0][0] = v11; v[0][1] = v12; v[0][2] = v13;
51       v[1][0] = v21; v[1][1] = v22; v[1][2] = v23;
52       v[2][0] = v31; v[2][1] = v32; v[2][2] = v33;
53     }
54 
55     mat3x3 inverse ();
56 
57           nv *operator [](int i)       { return &v[i][0]; }
58     const nv *operator [](int i) const { return &v[i][0]; }
59 
60     operator const nv * () const { return &v[0][0]; }
61     operator       nv * ()       { return &v[0][0]; }
62 
63     // quite inefficient, hopefully gcc pulls the w calc out of any loops
apply1mat3x364     nv apply1 (int i, nv x, nv y)
65     {
66       mat3x3 &m = *this;
67 
68       nv v = m[i][0] * x + m[i][1] * y + m[i][2];
69       nv w = m[2][0] * x + m[2][1] * y + m[2][2];
70 
71       return v * (1. / w);
72     }
73 
74     static mat3x3 translate (nv x, nv y);
75     static mat3x3 scale     (nv s, nv t);
76     static mat3x3 rotate    (nv phi);
77   };
78 
79   mat3x3
inverse()80   mat3x3::inverse ()
81   {
82     mat3x3 &m = *this;
83     mat3x3 inv;
84 
85     nv s0 = m[2][2] * m[1][1] - m[2][1] * m[1][2];
86     nv s1 = m[2][1] * m[0][2] - m[2][2] * m[0][1];
87     nv s2 = m[1][2] * m[0][1] - m[1][1] * m[0][2];
88 
89     nv invdet = 1. / (m[0][0] * s0 + m[1][0] * s1 + m[2][0] * s2);
90 
91     inv[0][0] = invdet * s0;
92     inv[0][1] = invdet * s1;
93     inv[0][2] = invdet * s2;
94 
95     inv[1][0] = invdet * (m[2][0] * m[1][2] - m[2][2] * m[1][0]);
96     inv[1][1] = invdet * (m[2][2] * m[0][0] - m[2][0] * m[0][2]);
97     inv[1][2] = invdet * (m[1][0] * m[0][2] - m[1][2] * m[0][0]);
98 
99     inv[2][0] = invdet * (m[2][1] * m[1][0] - m[2][0] * m[1][1]);
100     inv[2][1] = invdet * (m[2][0] * m[0][1] - m[2][1] * m[0][0]);
101     inv[2][2] = invdet * (m[1][1] * m[0][0] - m[1][0] * m[0][1]);
102 
103     return inv;
104   }
105 
106   static mat3x3
107   operator *(const mat3x3 &a, const mat3x3 &b)
108   {
109     mat3x3 r;
110 
111     for (int i = 0; i < 3; ++i)
112       for (int j = 0; j < 3; ++j)
113         r[i][j] = a[i][0] * b[0][j]
114                 + a[i][1] * b[1][j]
115                 + a[i][2] * b[2][j];
116 
117     return r;
118   }
119 
120   mat3x3
translate(nv x,nv y)121   mat3x3::translate (nv x, nv y)
122   {
123     return mat3x3 (
124       1, 0, x,
125       0, 1, y,
126       0, 0, 1
127     );
128   }
129 
130   mat3x3
scale(nv s,nv t)131   mat3x3::scale (nv s, nv t)
132   {
133     return mat3x3 (
134       s, 0, 0,
135       0, t, 0,
136       0, 0, 1
137     );
138   }
139 
140   // clockwise
141   mat3x3
rotate(nv phi)142   mat3x3::rotate (nv phi)
143   {
144     nv s = sin (phi);
145     nv c = cos (phi);
146 
147     return mat3x3 (
148       c, -s, 0,
149       s,  c, 0,
150       0,  0, 1
151     );
152   }
153 
154   struct composer
155   {
156     rxvt_img *srcimg, *dstimg;
157     Picture src, dst, msk;
158     Display *dpy;
159 
160     ecb_noinline
161     composer (rxvt_img *srcimg, rxvt_img *dstimg = 0)
srcimgcomposer162     : srcimg (srcimg), dstimg (dstimg), msk (0)
163     {
164       if (!this->dstimg)
165         this->dstimg = srcimg->new_empty ();
166       else if (!this->dstimg->pm) // somewhat unsatisfying
167         this->dstimg->alloc ();
168 
169       dpy =       srcimg->d->dpy;
170       src =       srcimg->picture ();
171       dst = this->dstimg->picture ();
172     }
173 
174     ecb_noinline
175     void mask (bool rgb = true, int w = 1, int h = 1)
176     {
177       Pixmap pixmap = XCreatePixmap (dpy, srcimg->pm, w, h, rgb ? 32 : 8);
178 
179       XRenderPictFormat *format = XRenderFindStandardFormat (dpy, rgb ? PictStandardARGB32 : PictStandardA8);
180       XRenderPictureAttributes pa;
181       pa.repeat = RepeatNormal;
182       pa.component_alpha = rgb;
183       msk = XRenderCreatePicture (dpy, pixmap, format, CPRepeat | CPComponentAlpha, &pa);
184 
185       XFreePixmap (dpy, pixmap);
186 
187       ecb_assume (msk);
188     }
189 
190     // CreateSolidFill creates a very very very weird picture
maskcomposer191     void mask (const rgba &c)
192     {
193       // the casts are needed in C++11 (see 8.5.1)
194       XRenderColor rc = {
195         (unsigned short)(c.r * c.a / 65535),
196         (unsigned short)(c.g * c.a / 65535),
197         (unsigned short)(c.b * c.a / 65535),
198         c.a
199       };
200       msk = XRenderCreateSolidFill (dpy, &rc);
201       ecb_assume (msk);
202     }
203 
fillcomposer204     void fill (const rgba &c)
205     {
206       XRenderColor rc = {
207         (unsigned short)(c.r * c.a / 65535),
208         (unsigned short)(c.g * c.a / 65535),
209         (unsigned short)(c.b * c.a / 65535),
210         c.a
211       };
212 
213       XRenderFillRectangle (dpy, PictOpSrc, msk, &rc, 0, 0, 1, 1);
214     }
215 
216     operator rxvt_img *()
217     {
218       return dstimg;
219     }
220 
221     ecb_noinline
~composercomposer222     ~composer ()
223     {
224                XRenderFreePicture (dpy, src);
225                XRenderFreePicture (dpy, dst);
226       if (msk) XRenderFreePicture (dpy, msk);
227     }
228   };
229 }
230 
231 static XRenderPictFormat *
find_alpha_format_for(Display * dpy,XRenderPictFormat * format)232 find_alpha_format_for (Display *dpy, XRenderPictFormat *format)
233 {
234   if (format->direct.alphaMask)
235     return format; // already has alpha
236 
237   // try to find a suitable alpha format, one bit alpha is enough for our purposes
238   if (format->type == PictTypeDirect)
239     for (int n = 0; XRenderPictFormat *f = XRenderFindFormat (dpy, 0, 0, n); ++n)
240       if (f->direct.alphaMask
241           && f->type == PictTypeDirect
242           && ecb_popcount32 (f->direct.redMask  ) >= ecb_popcount32 (format->direct.redMask  )
243           && ecb_popcount32 (f->direct.greenMask) >= ecb_popcount32 (format->direct.greenMask)
244           && ecb_popcount32 (f->direct.blueMask ) >= ecb_popcount32 (format->direct.blueMask ))
245         return f;
246 
247   // should be a very good fallback
248   return XRenderFindStandardFormat (dpy, PictStandardARGB32);
249 }
250 
rxvt_img(rxvt_screen * screen,XRenderPictFormat * format,int x,int y,int width,int height,int repeat)251 rxvt_img::rxvt_img (rxvt_screen *screen, XRenderPictFormat *format, int x, int y, int width, int height, int repeat)
252 : d(screen->display), x(x), y(y), w(width), h(height), format(format), repeat(repeat),
253   pm(0), ref(0)
254 {
255 }
256 
rxvt_img(rxvt_display * display,XRenderPictFormat * format,int x,int y,int width,int height,int repeat)257 rxvt_img::rxvt_img (rxvt_display *display, XRenderPictFormat *format, int x, int y, int width, int height, int repeat)
258 : d(display), x(x), y(y), w(width), h(height), format(format), repeat(repeat),
259   pm(0), ref(0)
260 {
261 }
262 
rxvt_img(const rxvt_img & img)263 rxvt_img::rxvt_img (const rxvt_img &img)
264 : d(img.d), x(img.x), y(img.y), w(img.w), h(img.h), format(img.format), repeat(img.repeat), pm(img.pm), ref(img.ref)
265 {
266   ++ref->cnt;
267 }
268 
269 rxvt_img *
new_from_root(rxvt_screen * s)270 rxvt_img::new_from_root (rxvt_screen *s)
271 {
272   Display *dpy = s->dpy;
273   unsigned int root_pm_w, root_pm_h;
274   Pixmap root_pixmap = s->display->get_pixmap_property (s->display->xa [XA_XROOTPMAP_ID]);
275   if (root_pixmap == None)
276     root_pixmap = s->display->get_pixmap_property (s->display->xa [XA_ESETROOT_PMAP_ID]);
277 
278   if (root_pixmap == None)
279     return 0;
280 
281   Window wdummy;
282   int idummy;
283   unsigned int udummy;
284 
285   if (!XGetGeometry (dpy, root_pixmap, &wdummy, &idummy, &idummy, &root_pm_w, &root_pm_h, &udummy, &udummy))
286     return 0;
287 
288   rxvt_img *img = new rxvt_img (
289      s,
290      XRenderFindVisualFormat (dpy, DefaultVisual (dpy, s->display->screen)),
291      0,
292      0,
293      root_pm_w,
294      root_pm_h
295   );
296 
297   img->pm = root_pixmap;
298   img->ref = new pixref (root_pm_w, root_pm_h);
299   img->ref->ours = false;
300 
301   return img;
302 }
303 
304 # if HAVE_PIXBUF
305 
306 rxvt_img *
new_from_pixbuf(rxvt_screen * s,GdkPixbuf * pb)307 rxvt_img::new_from_pixbuf (rxvt_screen *s, GdkPixbuf *pb)
308 {
309   Display *dpy = s->dpy;
310 
311   int width  = gdk_pixbuf_get_width  (pb);
312   int height = gdk_pixbuf_get_height (pb);
313 
314   if (width > 32767 || height > 32767) // well, we *could* upload in chunks
315     rxvt_fatal ("rxvt_img::new_from_pixbuf: image too big (maximum size 32768x32768).\n");
316 
317   // since we require rgb24/argb32 formats from xrender we assume
318   // that both 24 and 32 bpp MUST be supported by any screen that supports xrender
319 
320   int byte_order = ecb_big_endian () ? MSBFirst : LSBFirst;
321 
322   XImage xi;
323 
324   xi.width            = width;
325   xi.height           = height;
326   xi.xoffset          = 0;
327   xi.format           = ZPixmap;
328   xi.byte_order       = ImageByteOrder (dpy);
329   xi.bitmap_unit      = 0;         //XY only, unused
330   xi.bitmap_bit_order = 0;         //XY only, unused
331   xi.bitmap_pad       = BitmapPad (dpy);
332   xi.depth            = 32;
333   xi.bytes_per_line   = 0;
334   xi.bits_per_pixel   = 32;         //Z only
335   xi.red_mask         = 0x00000000; //Z only, unused
336   xi.green_mask       = 0x00000000; //Z only, unused
337   xi.blue_mask        = 0x00000000; //Z only, unused
338   xi.obdata           = 0;          // probably unused
339 
340   bool byte_order_mismatch = byte_order != xi.byte_order;
341 
342   if (!XInitImage (&xi))
343     rxvt_fatal ("unable to initialise ximage, please report.\n");
344 
345   if (height > INT_MAX / xi.bytes_per_line)
346     rxvt_fatal ("rxvt_img::new_from_pixbuf: image too big for Xlib.\n");
347 
348   xi.data = (char *)rxvt_malloc (height * xi.bytes_per_line);
349 
350   int rowstride = gdk_pixbuf_get_rowstride (pb);
351   bool pb_has_alpha = gdk_pixbuf_get_has_alpha (pb);
352   unsigned char *row = gdk_pixbuf_get_pixels (pb);
353 
354   char *line = xi.data;
355 
356   for (int y = 0; y < height; y++)
357     {
358       unsigned char *src = row;
359       uint32_t      *dst = (uint32_t *)line;
360 
361       for (int x = 0; x < width; x++)
362         {
363           uint8_t r = *src++;
364           uint8_t g = *src++;
365           uint8_t b = *src++;
366           uint8_t a = *src;
367 
368           // this is done so it can be jump-free, but newer gcc's clone inner the loop
369           a = pb_has_alpha ? a : 255;
370           src += pb_has_alpha;
371 
372           r = (r * a + 127) / 255;
373           g = (g * a + 127) / 255;
374           b = (b * a + 127) / 255;
375 
376           uint32_t v = (a << 24) | (r << 16) | (g << 8) | b;
377 
378           if (ecb_big_endian () ? !byte_order_mismatch : byte_order_mismatch)
379             v = ecb_bswap32 (v);
380 
381           *dst++ = v;
382         }
383 
384       row += rowstride;
385       line += xi.bytes_per_line;
386     }
387 
388   rxvt_img *img = new rxvt_img (s, XRenderFindStandardFormat (dpy, PictStandardARGB32), 0, 0, width, height);
389   img->alloc ();
390 
391   GC gc = XCreateGC (dpy, img->pm, 0, 0);
392   XPutImage (dpy, img->pm, gc, &xi, 0, 0, 0, 0, width, height);
393   XFreeGC (dpy, gc);
394 
395   free (xi.data);
396 
397   return img;
398 }
399 
400 rxvt_img *
new_from_file(rxvt_screen * s,const char * filename)401 rxvt_img::new_from_file (rxvt_screen *s, const char *filename)
402 {
403   GError *err = 0;
404   GdkPixbuf *pb = gdk_pixbuf_new_from_file (filename, &err);
405 
406   if (!pb)
407     try
408       {
409         rxvt_fatal ("rxvt_img::new_from_file: %s\n", err->message);
410       }
411     catch (...)
412       {
413         g_error_free (err);
414         throw;
415       }
416 
417   rxvt_img *img = new_from_pixbuf (s, pb);
418 
419   g_object_unref (pb);
420 
421   return img;
422 }
423 
424 # endif
425 
426 void
destroy()427 rxvt_img::destroy ()
428 {
429   if (--ref->cnt)
430     return;
431 
432   if (pm && ref->ours)
433     XFreePixmap (d->dpy, pm);
434 
435   delete ref;
436 }
437 
~rxvt_img()438 rxvt_img::~rxvt_img ()
439 {
440   destroy ();
441 }
442 
443 void
alloc()444 rxvt_img::alloc ()
445 {
446   pm = XCreatePixmap (d->dpy, d->root, w, h, format->depth);
447   ref = new pixref (w, h);
448 }
449 
450 rxvt_img *
new_empty()451 rxvt_img::new_empty ()
452 {
453   rxvt_img *img = new rxvt_img (d, format, x, y, w, h, repeat);
454   img->alloc ();
455 
456   return img;
457 }
458 
459 Picture
picture()460 rxvt_img::picture ()
461 {
462   Display *dpy = d->dpy;
463 
464   XRenderPictureAttributes pa;
465   pa.repeat = repeat;
466   Picture pic = XRenderCreatePicture (dpy, pm, format, CPRepeat, &pa);
467 
468   return pic;
469 }
470 
471 void
unshare()472 rxvt_img::unshare ()
473 {
474   if (ref->cnt == 1 && ref->ours)
475     return;
476 
477   Pixmap pm2 = XCreatePixmap (d->dpy, d->root, ref->w, ref->h, format->depth);
478   GC gc = XCreateGC (d->dpy, pm, 0, 0);
479   XCopyArea (d->dpy, pm, pm2, gc, 0, 0, ref->w, ref->h, 0, 0);
480   XFreeGC (d->dpy, gc);
481 
482   destroy ();
483 
484   pm = pm2;
485   ref = new pixref (ref->w, ref->h);
486 }
487 
488 void
fill(const rgba & c,int x,int y,int w,int h)489 rxvt_img::fill (const rgba &c, int x, int y, int w, int h)
490 {
491   XRenderColor rc = { c.r, c.g, c.b, c.a };
492 
493   Display *dpy = d->dpy;
494   Picture src = picture ();
495   XRenderFillRectangle (dpy, PictOpSrc, src, &rc, x, y, w, h);
496   XRenderFreePicture (dpy, src);
497 }
498 
499 void
fill(const rgba & c)500 rxvt_img::fill (const rgba &c)
501 {
502   fill (c, 0, 0, w, h);
503 }
504 
505 void
add_alpha()506 rxvt_img::add_alpha ()
507 {
508   if (format->direct.alphaMask)
509     return;
510 
511   composer cc (this, new rxvt_img (d, find_alpha_format_for (d->dpy, format), x, y, w, h, repeat));
512 
513   XRenderComposite (cc.dpy, PictOpSrc, cc.src, None, cc.dst, 0, 0, 0, 0, 0, 0, w, h);
514 
515   rxvt_img *img = cc;
516 
517   ::swap (img->ref, ref);
518   ::swap (img->pm , pm );
519 
520   delete img;
521 }
522 
523 static void
get_gaussian_kernel(int radius,int width,nv * kernel,XFixed * params)524 get_gaussian_kernel (int radius, int width, nv *kernel, XFixed *params)
525 {
526   nv sigma = radius / 2.0;
527   nv scale = sqrt (2.0 * M_PI) * sigma;
528   nv sum = 0.0;
529 
530   for (int i = 0; i < width; i++)
531     {
532       nv x = i - width / 2;
533       kernel[i] = exp (-(x * x) / (2.0 * sigma * sigma)) / scale;
534       sum += kernel[i];
535     }
536 
537   params[0] = XDoubleToFixed (width);
538   params[1] = XDoubleToFixed (1);
539 
540   for (int i = 0; i < width; i++)
541     params[i+2] = XDoubleToFixed (kernel[i] / sum);
542 }
543 
544 rxvt_img *
blur(int rh,int rv)545 rxvt_img::blur (int rh, int rv)
546 {
547   if (!(d->flags & DISPLAY_HAS_RENDER_CONV))
548     return clone ();
549 
550   Display *dpy = d->dpy;
551   int size = max (rh, rv) * 2 + 1;
552   nv *kernel = (nv *)malloc (size * sizeof (nv));
553   XFixed *params = rxvt_temp_buf<XFixed> (size + 2);
554   rxvt_img *img = new_empty ();
555 
556   XRenderPictureAttributes pa;
557   pa.repeat = RepeatPad;
558   Picture src = XRenderCreatePicture (dpy, pm, format, CPRepeat, &pa);
559   Picture dst = XRenderCreatePicture (dpy, img->pm, format, 0, 0);
560 
561   Pixmap tmp_pm = XCreatePixmap (dpy, pm, w, h, format->depth);
562   Picture tmp = XRenderCreatePicture (dpy, tmp_pm , format, CPRepeat, &pa);
563   XFreePixmap (dpy, tmp_pm);
564 
565   if (kernel && params)
566     {
567       size = rh * 2 + 1;
568       get_gaussian_kernel (rh, size, kernel, params);
569 
570       XRenderSetPictureFilter (dpy, src, FilterConvolution, params, size+2);
571       XRenderComposite (dpy,
572                         PictOpSrc,
573                         src,
574                         None,
575                         tmp,
576                         0, 0,
577                         0, 0,
578                         0, 0,
579                         w, h);
580 
581       size = rv * 2 + 1;
582       get_gaussian_kernel (rv, size, kernel, params);
583       ::swap (params[0], params[1]);
584 
585       XRenderSetPictureFilter (dpy, tmp, FilterConvolution, params, size+2);
586       XRenderComposite (dpy,
587                         PictOpSrc,
588                         tmp,
589                         None,
590                         dst,
591                         0, 0,
592                         0, 0,
593                         0, 0,
594                         w, h);
595     }
596 
597   free (kernel);
598 
599   XRenderFreePicture (dpy, src);
600   XRenderFreePicture (dpy, dst);
601   XRenderFreePicture (dpy, tmp);
602 
603   return img;
604 }
605 
606 rxvt_img *
muladd(nv mul,nv add)607 rxvt_img::muladd (nv mul, nv add)
608 {
609   // STEP 1: double the image width, fill all odd columns with white (==1)
610 
611   composer cc (this, new rxvt_img (d, format, 0, 0, w * 2, h, repeat));
612 
613   // why the hell does XRenderSetPictureTransform want a writable matrix :(
614   // that keeps us from just static const'ing this matrix.
615   XTransform h_double = {
616     0x08000, 0,     0,
617     0, 0x10000,     0,
618     0,     0, 0x10000
619   };
620 
621   XRenderSetPictureFilter (cc.dpy, cc.src, "nearest", 0, 0);
622   XRenderSetPictureTransform (cc.dpy, cc.src, &h_double);
623   XRenderComposite (cc.dpy, PictOpSrc, cc.src, None, cc.dst, 0, 0, 0, 0, 0, 0, w * 2, h);
624 
625   cc.mask (false, 2, 1);
626 
627   static const XRenderColor c0 = {     0,     0,     0,     0 };
628   XRenderFillRectangle (cc.dpy, PictOpSrc, cc.msk, &c0, 0, 0, 1, 1);
629   static const XRenderColor c1 = { 65535, 65535, 65535, 65535 };
630   XRenderFillRectangle (cc.dpy, PictOpSrc, cc.msk, &c1, 1, 0, 1, 1);
631 
632   Picture white = XRenderCreateSolidFill (cc.dpy, &c1);
633 
634   XRenderComposite (cc.dpy, PictOpOver, white, cc.msk, cc.dst, 0, 0, 0, 0, 0, 0, w * 2, h);
635 
636   XRenderFreePicture (cc.dpy, white);
637 
638   // STEP 2: convolve the image with a 3x1 filter
639   // a 2x1 filter would obviously suffice, but given the total lack of specification
640   // for xrender, I expect different xrender implementations to randomly diverge.
641   // we also halve the image, and hope for the best (again, for lack of specs).
642   composer cc2 (cc.dstimg);
643 
644   XFixed kernel [] = {
645     XDoubleToFixed (3), XDoubleToFixed (1),
646     XDoubleToFixed (0), XDoubleToFixed (mul), XDoubleToFixed (add)
647   };
648 
649   XTransform h_halve = {
650     0x20000, 0,      0,
651     0, 0x10000,      0,
652     0,      0, 0x10000
653   };
654 
655   XRenderSetPictureFilter (cc.dpy, cc2.src, "nearest", 0, 0);
656   XRenderSetPictureTransform (cc.dpy, cc2.src, &h_halve);
657   XRenderSetPictureFilter (cc.dpy, cc2.src, FilterConvolution, kernel, ecb_array_length (kernel));
658 
659   XRenderComposite (cc.dpy, PictOpSrc, cc2.src, None, cc2.dst, 0, 0, 0, 0, 0, 0, w * 2, h);
660 
661   return cc2;
662 }
663 
664 ecb_noinline static void
extract(int32_t cl0,int32_t cl1,int32_t & c,unsigned short & xc)665 extract (int32_t cl0, int32_t cl1, int32_t &c, unsigned short &xc)
666 {
667   int32_t x = clamp (c, cl0, cl1);
668   c -= x;
669   xc = x;
670 }
671 
672 ecb_noinline static bool
extract(int32_t cl0,int32_t cl1,int32_t & r,int32_t & g,int32_t & b,int32_t & a,unsigned short & xr,unsigned short & xg,unsigned short & xb,unsigned short & xa)673 extract (int32_t cl0, int32_t cl1, int32_t &r, int32_t &g, int32_t &b, int32_t &a, unsigned short &xr, unsigned short &xg, unsigned short &xb, unsigned short &xa)
674 {
675   extract (cl0, cl1, r, xr);
676   extract (cl0, cl1, g, xg);
677   extract (cl0, cl1, b, xb);
678   extract (cl0, cl1, a, xa);
679 
680   return xr | xg | xb | xa;
681 }
682 
683 void
brightness(int32_t r,int32_t g,int32_t b,int32_t a)684 rxvt_img::brightness (int32_t r, int32_t g, int32_t b, int32_t a)
685 {
686   unshare ();
687 
688   Display *dpy = d->dpy;
689   Picture dst = XRenderCreatePicture (dpy, pm, format, 0, 0);
690 
691   // loop should not be needed for brightness, as only -1..1 makes sense
692   //while (r | g | b | a)
693     {
694       XRenderColor mask_c;
695 
696       if (extract (0, 65535, r, g, b, a, mask_c.red, mask_c.green, mask_c.blue, mask_c.alpha))
697         XRenderFillRectangle (dpy, PictOpAdd, dst, &mask_c, 0, 0, w, h);
698 
699       if (extract (-65535, 0, r, g, b, a, mask_c.red, mask_c.green, mask_c.blue, mask_c.alpha))
700         {
701           XRenderColor mask_w = { 65535, 65535, 65535, 65535 };
702           XRenderFillRectangle (dpy, PictOpDifference, dst, &mask_w, 0, 0, w, h);
703           mask_c.red   = -mask_c.red; //TODO: verify that doing clamp, assign, and negation does the right thing
704           mask_c.green = -mask_c.green;
705           mask_c.blue  = -mask_c.blue;
706           mask_c.alpha = -mask_c.alpha;
707           XRenderFillRectangle (dpy, PictOpAdd, dst, &mask_c, 0, 0, w, h);
708           XRenderFillRectangle (dpy, PictOpDifference, dst, &mask_w, 0, 0, w, h);
709         }
710     }
711 
712   XRenderFreePicture (dpy, dst);
713 }
714 
715 void
contrast(int32_t r,int32_t g,int32_t b,int32_t a)716 rxvt_img::contrast (int32_t r, int32_t g, int32_t b, int32_t a)
717 {
718   if (r < 0 || g < 0 || b < 0 || a < 0)
719     rxvt_fatal ("rxvt_img::contrast does not support negative values.\n");
720 
721   // premultiply (yeah, these are not exact, sue me or fix it)
722   r = (r * (a >> 8)) >> 8;
723   g = (g * (a >> 8)) >> 8;
724   b = (b * (a >> 8)) >> 8;
725 
726   composer cc (this);
727   rxvt_img *img = cc;
728   img->fill (rgba (0, 0, 0, 0));
729 
730   cc.mask (true);
731 
732   //TODO: this operator does not yet implement some useful contrast
733   while (r | g | b | a)
734     {
735       XRenderColor mask_c;
736 
737       if (extract (0, 65535, r, g, b, a, mask_c.red, mask_c.green, mask_c.blue, mask_c.alpha))
738         {
739           XRenderFillRectangle (cc.dpy, PictOpSrc, cc.msk, &mask_c, 0, 0, 1, 1);
740           XRenderComposite (cc.dpy, PictOpAdd, cc.src, cc.msk, cc.dst, 0, 0, 0, 0, 0, 0, w, h);
741         }
742     }
743 
744   ::swap (img->ref, ref);
745   ::swap (img->pm , pm );
746 
747   delete img;
748 }
749 
750 void
draw(rxvt_img * img,int op,nv mask)751 rxvt_img::draw (rxvt_img *img, int op, nv mask)
752 {
753   unshare ();
754 
755   composer cc (img, this);
756 
757   if (mask != 1.)
758     cc.mask (rgba (0, 0, 0, float_to_component (mask)));
759 
760   XRenderComposite (cc.dpy, op, cc.src, cc.msk, cc.dst, x - img->x, y - img->y, 0, 0, 0, 0, w, h);
761 }
762 
763 rxvt_img *
clone()764 rxvt_img::clone ()
765 {
766   return new rxvt_img (*this);
767 }
768 
769 rxvt_img *
reify()770 rxvt_img::reify ()
771 {
772   if (x == 0 && y == 0 && w == ref->w && h == ref->h)
773     return clone ();
774 
775   // add an alpha channel if...
776   bool alpha = !format->direct.alphaMask // pixmap has none yet
777                && (x || y)               // we need one because of non-zero offset
778                && repeat == RepeatNone;  // and we have no good pixels to fill with
779 
780   composer cc (this, new rxvt_img (d, alpha ? find_alpha_format_for (d->dpy, format) : format,
781                                    0, 0, w, h, repeat));
782 
783   if (repeat == RepeatNone)
784     {
785       XRenderColor rc = { 0, 0, 0, 0 };
786       XRenderFillRectangle (cc.dpy, PictOpSrc, cc.dst, &rc, 0, 0, w, h);//TODO: split into four fillrectangles
787       XRenderComposite (cc.dpy, PictOpSrc, cc.src, None, cc.dst, 0, 0, 0, 0, x, y, ref->w, ref->h);
788     }
789   else
790     XRenderComposite (cc.dpy, PictOpSrc, cc.src, None, cc.dst, -x, -y, 0, 0, 0, 0, w, h);
791 
792   return cc;
793 }
794 
795 rxvt_img *
sub_rect(int x,int y,int width,int height)796 rxvt_img::sub_rect (int x, int y, int width, int height)
797 {
798   rxvt_img *img = clone ();
799 
800   img->x -= x;
801   img->y -= y;
802 
803   if (w != width || h != height)
804     {
805       img->w = width;
806       img->h = height;
807 
808       rxvt_img *img2 = img->reify ();
809       delete img;
810       img = img2;
811     }
812 
813   return img;
814 }
815 
816 rxvt_img *
transform(const nv matrix[3][3])817 rxvt_img::transform (const nv matrix[3][3])
818 {
819   return transform (mat3x3 (&matrix[0][0]));
820 }
821 
822 rxvt_img *
transform(const nv * matrix)823 rxvt_img::transform (const nv *matrix)
824 {
825   mat3x3 m (matrix);
826 
827   // calculate new pixel bounding box coordinates
828   nv rmin[2], rmax[2];
829 
830   for (int i = 0; i < 2; ++i)
831     {
832       nv v;
833 
834       v = m.apply1 (i, 0+x, 0+y);         rmin [i] =            rmax [i] = v;
835       v = m.apply1 (i, w+x, 0+y); min_it (rmin [i], v); max_it (rmax [i],  v);
836       v = m.apply1 (i, 0+x, h+y); min_it (rmin [i], v); max_it (rmax [i],  v);
837       v = m.apply1 (i, w+x, h+y); min_it (rmin [i], v); max_it (rmax [i],  v);
838     }
839 
840   float sx = rmin [0] - x;
841   float sy = rmin [1] - y;
842 
843   // TODO: adjust matrix for subpixel accuracy
844   int nx = floor (rmin [0]);
845   int ny = floor (rmin [1]);
846 
847   int new_width  = ceil (rmax [0] - rmin [0]);
848   int new_height = ceil (rmax [1] - rmin [1]);
849 
850   mat3x3 inv = (mat3x3::translate (-x, -y) * m * mat3x3::translate (x, y)).inverse ();
851 
852   composer cc (this, new rxvt_img (d, format, nx, ny, new_width, new_height, repeat));
853 
854   XTransform xfrm;
855 
856   for (int i = 0; i < 3; ++i)
857     for (int j = 0; j < 3; ++j)
858       xfrm.matrix [i][j] = XDoubleToFixed (inv [i][j]);
859 
860   XRenderSetPictureFilter (cc.dpy, cc.src, "good", 0, 0);
861   XRenderSetPictureTransform (cc.dpy, cc.src, &xfrm);
862   XRenderComposite (cc.dpy, PictOpSrc, cc.src, None, cc.dst, sx, sy, 0, 0, 0, 0, new_width, new_height);
863 
864   return cc;
865 }
866 
867 rxvt_img *
scale(int new_width,int new_height)868 rxvt_img::scale (int new_width, int new_height)
869 {
870   if (w == new_width && h == new_height)
871     return clone ();
872 
873   int old_repeat_mode = repeat;
874   repeat = RepeatPad; // not right, but xrender can't properly scale it seems
875 
876   rxvt_img *img = transform (mat3x3::scale (new_width / (nv)w, new_height / (nv)h));
877 
878   repeat = old_repeat_mode;
879   img->repeat = repeat;
880 
881   return img;
882 }
883 
884 rxvt_img *
rotate(int cx,int cy,nv phi)885 rxvt_img::rotate (int cx, int cy, nv phi)
886 {
887   move (-cx, -cy);
888   rxvt_img *img = transform (mat3x3::rotate (phi));
889   move ( cx,  cy);
890   img->move (cx, cy);
891 
892   return img;
893 }
894 
895 rxvt_img *
convert_format(XRenderPictFormat * new_format,const rgba & bg)896 rxvt_img::convert_format (XRenderPictFormat *new_format, const rgba &bg)
897 {
898   if (new_format == format)
899     return clone ();
900 
901   composer cc (this, new rxvt_img (d, new_format, x, y, w, h, repeat));
902 
903   int op = PictOpSrc;
904 
905   if (format->direct.alphaMask && !new_format->direct.alphaMask)
906     {
907       // does it have to be that complicated
908       XRenderColor rc = { bg.r, bg.g, bg.b, bg.a };
909       XRenderFillRectangle (cc.dpy, PictOpSrc, cc.dst, &rc, 0, 0, w, h);
910 
911       op = PictOpOver;
912     }
913 
914   XRenderComposite (cc.dpy, op, cc.src, None, cc.dst, 0, 0, 0, 0, 0, 0, w, h);
915 
916   return cc;
917 }
918 
919 rxvt_img *
tint(const rgba & c)920 rxvt_img::tint (const rgba &c)
921 {
922   composer cc (this);
923   cc.mask (true);
924   cc.fill (c);
925 
926   XRenderComposite (cc.dpy, PictOpSrc, cc.src, cc.msk, cc.dst, 0, 0, 0, 0, 0, 0, w, h);
927 
928   return cc;
929 }
930 
931 rxvt_img *
shade(nv factor,rgba c)932 rxvt_img::shade (nv factor, rgba c)
933 {
934   clamp_it (factor, -1., 1.);
935   factor++;
936 
937   if (factor > 1)
938     {
939       c.r = c.r * (2 - factor);
940       c.g = c.g * (2 - factor);
941       c.b = c.b * (2 - factor);
942     }
943   else
944     {
945       c.r = c.r * factor;
946       c.g = c.g * factor;
947       c.b = c.b * factor;
948     }
949 
950   rxvt_img *img = this->tint (c);
951 
952   if (factor > 1)
953     {
954       c.a = 0xffff;
955       c.r =
956       c.g =
957       c.b = 0xffff * (factor - 1);
958 
959       img->brightness (c.r, c.g, c.b, c.a);
960     }
961 
962   return img;
963 }
964 
965 rxvt_img *
filter(const char * name,int nparams,nv * params)966 rxvt_img::filter (const char *name, int nparams, nv *params)
967 {
968   composer cc (this);
969 
970   XFixed *xparams = rxvt_temp_buf<XFixed> (nparams);
971 
972   for (int i = 0; i < nparams; ++i)
973     xparams [i] = XDoubleToFixed (params [i]);
974 
975   XRenderSetPictureFilter (cc.dpy, cc.src, name, xparams, nparams);
976 
977   XRenderComposite (cc.dpy, PictOpSrc, cc.src, 0, cc.dst, 0, 0, 0, 0, 0, 0, w, h);
978 
979   return cc;
980 }
981 
982 #endif
983 
984