ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/background.C
(Generate patch)

Comparing rxvt-unicode/src/background.C (file contents):
Revision 1.102 by sf-exg, Sun Oct 31 09:34:23 2010 UTC vs.
Revision 1.230 by sf-exg, Sun Jun 3 15:50:22 2012 UTC

1/*----------------------------------------------------------------------* 1/*----------------------------------------------------------------------*
2 * File: background.C - former xpm.C 2 * File: background.C - former xpm.C
3 *----------------------------------------------------------------------* 3 *----------------------------------------------------------------------*
4 * 4 *
5 * All portions of code are copyright by their respective author/s. 5 * All portions of code are copyright by their respective author/s.
6 * Copyright (c) 2005-2008 Marc Lehmann <pcg@goof.com> 6 * Copyright (c) 2005-2008 Marc Lehmann <schmorp@schmorp.de>
7 * Copyright (c) 2007 Sasha Vasko <sasha@aftercode.net> 7 * Copyright (c) 2007 Sasha Vasko <sasha@aftercode.net>
8 * Copyright (c) 2010 Emanuele Giaquinta <e.giaquinta@glauco.it> 8 * Copyright (c) 2010-2012 Emanuele Giaquinta <e.giaquinta@glauco.it>
9 * 9 *
10 * This program is free software; you can redistribute it and/or modify 10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by 11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or 12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version. 13 * (at your option) any later version.
20 * You should have received a copy of the GNU General Public License 20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software 21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *---------------------------------------------------------------------*/ 23 *---------------------------------------------------------------------*/
24 24
25#include <cmath> 25#include <math.h>
26#include "../config.h" /* NECESSARY */ 26#include "../config.h" /* NECESSARY */
27#include "rxvt.h" /* NECESSARY */ 27#include "rxvt.h" /* NECESSARY */
28 28
29#define DO_TIMING_TEST 0 29#if XRENDER
30 30# include <X11/extensions/Xrender.h>
31#if DO_TIMING_TEST
32# include <sys/time.h>
33#define TIMING_TEST_START(id) \
34 struct timeval timing_test_##id##_stv; \
35 gettimeofday (&timing_test_##id##_stv, NULL);
36
37#define TIMING_TEST_PRINT_RESULT(id) \
38 do { \
39 struct timeval tv; \
40 gettimeofday (&tv, NULL); \
41 tv.tv_sec -= (timing_test_##id##_stv).tv_sec; \
42 fprintf (stderr, "%s: %s: %d: elapsed %ld usec\n", #id, __FILE__, __LINE__, \
43 tv.tv_sec * 1000000 + tv.tv_usec - (timing_test_##id##_stv).tv_usec); \
44 } while (0)
45
46#else
47#define TIMING_TEST_START(id) do {} while (0)
48#define TIMING_TEST_PRINT_RESULT(id) do {} while (0)
49#endif 31#endif
50 32
51/* 33#ifndef FilterConvolution
52 * Pixmap geometry string interpretation : 34#define FilterConvolution "convolution"
53 * Each geometry string contains zero or one scale/position 35#endif
54 * adjustment and may optionally be followed by a colon and one or more 36
55 * colon-delimited pixmap operations. 37#ifndef RepeatPad
56 * The following table shows the valid geometry strings and their 38#define RepeatPad True
57 * effects on the background image : 39#endif
58 *
59 * WxH+X+Y Set scaling to W% by H%, and position to X% by Y%.
60 * W and H are percentages of the terminal window size.
61 * X and Y are also percentages; e.g., +50+50 centers
62 * the image in the window.
63 * WxH+X Assumes Y == X
64 * WxH Assumes Y == X == 50 (centers the image)
65 * W+X+Y Assumes H == W
66 * W+X Assumes H == W and Y == X
67 * W Assumes H == W and Y == X == 50
68 *
69 * Adjusting position only :
70 * =+X+Y Set position to X% by Y% (absolute).
71 * =+X Set position to X% by X%.
72 * +X+Y Adjust position horizontally X% and vertically Y%
73 * from current position (relative).
74 * +X Adjust position horizontally X% and vertically X%
75 * from current position.
76 *
77 * Adjusting scale only :
78 * Wx0 Multiply horizontal scaling factor by W%
79 * 0xH Multiply vertical scaling factor by H%
80 * 0x0 No scaling (show image at normal size).
81 *
82 * Pixmap Operations : (should be prepended by a colon)
83 * tile Tile image. Scaling/position modifiers above will affect
84 * the tile size and origin.
85 * propscale When scaling, scale proportionally. That is, maintain the
86 * proper aspect ratio for the image. Any portion of the
87 * background not covered by the image is filled with the
88 * current background color.
89 * hscale Scale horizontally, tile vertically ?
90 * vscale Tile horizontally, scale vertically ?
91 * scale Scale both up and down
92 * auto Same as 100x100+50+50
93 */
94 40
95#ifdef HAVE_BG_PIXMAP 41#ifdef HAVE_BG_PIXMAP
96bgPixmap_t::bgPixmap_t () 42# if XRENDER
43static Picture
44create_xrender_mask (Display *dpy, Drawable drawable, Bool argb, Bool component_alpha)
97{ 45{
98 // this is basically redundant as bgPixmap_t is only used in 46 Pixmap pixmap = XCreatePixmap (dpy, drawable, 1, 1, argb ? 32 : 8);
99 // zero_initialised-derived structs 47
100#ifdef HAVE_AFTERIMAGE 48 XRenderPictFormat *format = XRenderFindStandardFormat (dpy, argb ? PictStandardARGB32 : PictStandardA8);
101 original_asim = NULL; 49 XRenderPictureAttributes pa;
50 pa.repeat = True;
51 pa.component_alpha = component_alpha;
52 Picture mask = XRenderCreatePicture (dpy, pixmap, format, CPRepeat | CPComponentAlpha, &pa);
53
54 XFreePixmap (dpy, pixmap);
55
56 return mask;
57}
102#endif 58# endif
103#ifdef HAVE_PIXBUF
104 pixbuf = NULL;
105#endif
106#ifdef BG_IMAGE_FROM_FILE
107 have_image = false;
108 h_scale = v_scale = 0;
109 h_align = v_align = 0;
110#endif
111#ifdef ENABLE_TRANSPARENCY
112 shade = 100;
113 recoded_root_pmap = None;
114#endif
115 flags = 0;
116 pixmap = None;
117 valid_since = invalid_since = 0;
118 target = 0;
119}
120 59
121void 60void
122bgPixmap_t::destroy () 61rxvt_term::bg_destroy ()
123{ 62{
124#ifdef HAVE_AFTERIMAGE 63# ifdef BG_IMAGE_FROM_FILE
125 if (original_asim) 64 for (vector<rxvt_image>::iterator bg_image = image_vec.begin (); bg_image < image_vec.end (); bg_image++)
126 safe_asimage_destroy (original_asim); 65 bg_image->destroy ();
127#endif 66# endif
128 67
129#ifdef HAVE_PIXBUF 68 if (bg_pixmap)
130 if (pixbuf)
131 g_object_unref (pixbuf);
132#endif
133
134#ifdef ENABLE_TRANSPARENCY
135 if (recoded_root_pmap && target)
136 XFreePixmap (target->dpy, recoded_root_pmap);
137#endif
138
139 if (pixmap && target)
140 XFreePixmap (target->dpy, pixmap); 69 XFreePixmap (dpy, bg_pixmap);
141} 70}
142 71
143bool 72bool
144bgPixmap_t::window_size_sensitive () 73rxvt_term::bg_set_position (int x, int y)
74{
75
76 if (target_x != x
77 || target_y != y)
78 {
79 target_x = x;
80 target_y = y;
81 return true;
82 }
83 return false;
84}
85
86bool
87rxvt_term::bg_window_size_sensitive ()
145{ 88{
146# ifdef ENABLE_TRANSPARENCY 89# ifdef ENABLE_TRANSPARENCY
147 if (flags & isTransparent) 90 if (bg_flags & BG_IS_TRANSPARENT)
148 return true; 91 return true;
149# endif 92# endif
150 93
151# ifdef BG_IMAGE_FROM_FILE 94# ifdef BG_IMAGE_FROM_FILE
152 if (have_image) 95 for (vector<rxvt_image>::iterator bg_image = image_vec.begin (); bg_image < image_vec.end (); bg_image++)
153 { 96 {
154 if (flags & sizeSensitive) 97 if ((bg_image->flags & IM_IS_SIZE_SENSITIVE)
98 || bg_image->width () > szHint.width
99 || bg_image->height () > szHint.height)
155 return true; 100 return true;
156 } 101 }
157# endif 102# endif
158 103
159 return false; 104 return false;
160} 105}
161 106
162bool 107bool
163bgPixmap_t::window_position_sensitive () 108rxvt_term::bg_window_position_sensitive ()
164{ 109{
165# ifdef ENABLE_TRANSPARENCY 110# ifdef ENABLE_TRANSPARENCY
166 if (flags & isTransparent) 111 if (bg_flags & BG_IS_TRANSPARENT)
167 return true; 112 return true;
168# endif 113# endif
169 114
170# ifdef BG_IMAGE_FROM_FILE 115# ifdef BG_IMAGE_FROM_FILE
171 if (have_image) 116 for (vector<rxvt_image>::iterator bg_image = image_vec.begin (); bg_image < image_vec.end (); bg_image++)
172 { 117 {
173 if (flags & rootAlign) 118 if (bg_image->flags & IM_ROOT_ALIGN)
174 return true; 119 return true;
175 } 120 }
176# endif 121# endif
177 122
178 return false; 123 return false;
179};
180
181bool bgPixmap_t::need_client_side_rendering ()
182{
183# ifdef HAVE_AFTERIMAGE
184 if (original_asim)
185 return true;
186# endif
187 return false;
188} 124}
189 125
190# ifdef BG_IMAGE_FROM_FILE 126# ifdef BG_IMAGE_FROM_FILE
191static inline bool
192check_set_scale_value (int geom_flags, int flag, unsigned int &scale, unsigned int new_value)
193{
194 if (geom_flags & flag)
195 {
196 if (new_value > 1000)
197 new_value = 1000;
198 if (new_value != scale)
199 {
200 scale = new_value;
201 return true;
202 }
203 }
204 return false;
205}
206
207static inline bool
208check_set_align_value (int geom_flags, int flag, int &align, int new_value)
209{
210 if (geom_flags & flag)
211 {
212 if (new_value < -100)
213 new_value = -100;
214 else if (new_value > 200)
215 new_value = 200;
216 if (new_value != align)
217 {
218 align = new_value;
219 return true;
220 }
221 }
222 return false;
223}
224
225static inline int 127static inline int
226make_align_position (int align, int window_size, int image_size) 128make_align_position (int align, int window_size, int image_size)
227{ 129{
228 int diff = window_size - image_size;
229 int smaller = min (image_size, window_size);
230
231 if (align >= 0 && align <= 100) 130 if (align >= 0 && align <= 100)
232 return diff * align / 100; 131 return lerp (0, window_size - image_size, align);
233 else if (align > 100 && align <= 200) 132 else if (align > 100)
234 return ((align - 100) * smaller / 100) + window_size - smaller; 133 return lerp (window_size - image_size, window_size, align - 100);
235 else if (align >= -100 && align < 0) 134 else
236 return ((align + 100) * smaller / 100) - image_size; 135 return lerp (-image_size, 0, align + 100);
237 return 0;
238} 136}
239 137
240static inline int 138static inline int
241make_clip_rectangle (int pos, int size, int target_size, int &dst_pos, int &dst_size) 139make_clip_rectangle (int pos, int size, int target_size, int &dst_pos, int &dst_size)
242{ 140{
248 src_pos = -pos; 146 src_pos = -pos;
249 dst_pos = 0; 147 dst_pos = 0;
250 dst_size += pos; 148 dst_size += pos;
251 } 149 }
252 150
253 if (dst_pos + dst_size > target_size)
254 dst_size = target_size - dst_pos; 151 min_it (dst_size, target_size - dst_pos);
255 return src_pos; 152 return src_pos;
256} 153}
257 154
258bool 155static void
259bgPixmap_t::set_geometry (const char *geom) 156parse_style (const char *style, int &x, int &y, unsigned int &w, unsigned int &h, uint8_t &flags)
260{ 157{
158 if (!strcasecmp (style, "tiled"))
159 {
160 flags = IM_TILE;
161 w = h = noScale;
162 x = y = 0;
163 }
164 else if (!strcasecmp (style, "aspect-stretched"))
165 {
166 flags = IM_KEEP_ASPECT;
167 w = h = windowScale;
168 x = y = centerAlign;
169 }
170 else if (!strcasecmp (style, "stretched"))
171 {
172 flags = 0;
173 w = h = windowScale;
174 x = y = centerAlign;
175 }
176 else if (!strcasecmp (style, "centered"))
177 {
178 flags = 0;
179 w = h = noScale;
180 x = y = centerAlign;
181 }
182 else if (!strcasecmp (style, "root-tiled"))
183 {
184 flags = IM_TILE|IM_ROOT_ALIGN;
185 w = h = noScale;
186 x = y = 0;
187 }
188}
189
190bool
191rxvt_image::set_geometry (const char *geom, bool update)
192{
193 bool changed = false;
261 int geom_flags = 0, changed = 0; 194 int geom_flags = 0;
262 int x = 0, y = 0; 195 int x = h_align;
196 int y = v_align;
263 unsigned int w = 0, h = 0; 197 unsigned int w = h_scale;
264 unsigned int n; 198 unsigned int h = v_scale;
265 unsigned long new_flags = (flags & (~geometryFlags)); 199 uint8_t new_flags = 0;
266 const char *p;
267# define MAXLEN_GEOM 256 /* could be longer than regular geometry string */
268 200
269 if (geom == NULL) 201 if (geom == NULL)
270 return false; 202 return false;
271 203
272 char str[MAXLEN_GEOM]; 204 if (geom[0])
273
274 while (isspace(*geom)) ++geom;
275 if ((p = strchr (geom, ';')) == NULL)
276 p = strchr (geom, '\0');
277
278 n = (p - geom);
279 if (n < MAXLEN_GEOM)
280 { 205 {
281 char *ops; 206 char **arr = rxvt_strsplit (':', geom);
282 new_flags |= geometrySet;
283 207
284 memcpy (str, geom, n); 208 for (int i = 0; arr[i]; i++)
285 str[n] = '\0';
286 if (str[0] == ':')
287 ops = &str[0];
288 else if (str[0] != 'x' && str[0] != 'X' && isalpha(str[0]))
289 ops = &str[0];
290 else
291 { 209 {
292 char *tmp; 210 if (!strncasecmp (arr[i], "style=", 6))
293 ops = strchr (str, ':');
294 if (ops != NULL)
295 { 211 {
296 for (tmp = ops-1; tmp >= str && isspace(*tmp); --tmp); 212 parse_style (arr[i] + 6, x, y, w, h, new_flags);
297 *(++tmp) = '\0'; 213 geom_flags = WidthValue|HeightValue|XValue|YValue;
298 if (ops == tmp) ++ops;
299 } 214 }
300 } 215 else if (!strcasecmp (arr[i], "op=tile"))
216 new_flags |= IM_TILE;
217 else if (!strcasecmp (arr[i], "op=keep-aspect"))
218 new_flags |= IM_KEEP_ASPECT;
219 else if (!strcasecmp (arr[i], "op=root-align"))
220 new_flags |= IM_ROOT_ALIGN;
301 221
302 if (ops > str || ops == NULL) 222 // deprecated
303 { 223 else if (!strcasecmp (arr[i], "tile"))
304 /* we have geometry string - let's handle it prior to applying ops */
305 geom_flags = XParseGeometry (str, &x, &y, &w, &h);
306
307 if ((geom_flags & XValue) && !(geom_flags & YValue))
308 { 224 {
309 y = x; 225 new_flags |= IM_TILE;
226 w = h = noScale;
310 geom_flags |= YValue; 227 geom_flags |= WidthValue|HeightValue;
311 } 228 }
312 229 else if (!strcasecmp (arr[i], "propscale"))
313 if (flags & geometrySet)
314 { 230 {
315 /* new geometry is an adjustment to the old one ! */ 231 new_flags |= IM_KEEP_ASPECT;
232 w = h = windowScale;
316 if ((geom_flags & WidthValue) && (geom_flags & HeightValue)) 233 geom_flags |= WidthValue|HeightValue;
317 {
318 if (w == 0 && h != 0)
319 {
320 w = h_scale;
321 h = (v_scale * h) / 100;
322 }
323 else if (h == 0 && w != 0)
324 {
325 w = (h_scale * w) / 100;
326 h = v_scale;
327 }
328 }
329 if (geom_flags & XValue)
330 {
331 if (str[0] != '=')
332 {
333 y += v_align;
334 x += h_align;
335 }
336 }
337 } 234 }
338 else /* setting up geometry from scratch */ 235 else if (!strcasecmp (arr[i], "hscale"))
339 { 236 {
340 if (!(geom_flags & XValue)) 237 new_flags |= IM_TILE;
341 { 238 w = windowScale;
342 /* use default geometry - centered */
343 x = y = defaultAlign;
344 }
345 else if (!(geom_flags & YValue))
346 y = x; 239 h = noScale;
347
348 if ((geom_flags & (WidthValue|HeightValue)) == 0) 240 geom_flags |= WidthValue|HeightValue;
349 {
350 /* use default geometry - scaled */
351 w = h = defaultScale;
352 }
353 else if (geom_flags & WidthValue)
354 {
355 if (!(geom_flags & HeightValue))
356 h = w;
357 }
358 else
359 w = h;
360 } 241 }
361 } /* done parsing geometry string */ 242 else if (!strcasecmp (arr[i], "vscale"))
362 else if (!(flags & geometrySet))
363 {
364 /* default geometry - scaled and centered */
365 x = y = defaultAlign;
366 w = h = defaultScale;
367 }
368
369 if (!(flags & geometrySet))
370 geom_flags |= WidthValue|HeightValue|XValue|YValue;
371
372 if (ops)
373 {
374 while (*ops)
375 { 243 {
376 while (*ops == ':' || isspace(*ops)) ++ops; 244 new_flags |= IM_TILE;
377 245 h = windowScale;
378# define CHECK_GEOM_OPS(op_str) (strncasecmp (ops, (op_str), sizeof (op_str) - 1) == 0)
379 if (CHECK_GEOM_OPS ("tile"))
380 {
381 w = h = noScale; 246 w = noScale;
382 geom_flags |= WidthValue|HeightValue; 247 geom_flags |= WidthValue|HeightValue;
383 } 248 }
384 else if (CHECK_GEOM_OPS ("propscale")) 249 else if (!strcasecmp (arr[i], "scale"))
385 { 250 {
386 new_flags |= propScale;
387 }
388 else if (CHECK_GEOM_OPS ("hscale"))
389 {
390 if (w == 0) w = windowScale;
391
392 h = noScale; 251 w = h = windowScale;
393 geom_flags |= WidthValue|HeightValue; 252 geom_flags |= WidthValue|HeightValue;
394 } 253 }
395 else if (CHECK_GEOM_OPS ("vscale")) 254 else if (!strcasecmp (arr[i], "auto"))
396 { 255 {
397 if (h == 0) h = windowScale;
398
399 w = noScale;
400 geom_flags |= WidthValue|HeightValue;
401 }
402 else if (CHECK_GEOM_OPS ("scale"))
403 {
404 if (h == 0) h = windowScale;
405 if (w == 0) w = windowScale;
406
407 geom_flags |= WidthValue|HeightValue;
408 }
409 else if (CHECK_GEOM_OPS ("auto"))
410 {
411 w = h = windowScale; 256 w = h = windowScale;
412 x = y = centerAlign; 257 x = y = centerAlign;
413 geom_flags |= WidthValue|HeightValue|XValue|YValue; 258 geom_flags |= WidthValue|HeightValue|XValue|YValue;
414 } 259 }
415 else if (CHECK_GEOM_OPS ("root")) 260 else if (!strcasecmp (arr[i], "root"))
416 { 261 {
417 new_flags |= rootAlign; 262 new_flags |= IM_TILE|IM_ROOT_ALIGN;
418 w = h = noScale; 263 w = h = noScale;
419 geom_flags |= WidthValue|HeightValue; 264 geom_flags |= WidthValue|HeightValue;
420 } 265 }
421# undef CHECK_GEOM_OPS
422 266
423 while (*ops != ':' && *ops != '\0') ++ops; 267 else
268 geom_flags |= XParseGeometry (arr[i], &x, &y, &w, &h);
424 } /* done parsing ops */ 269 } /* done parsing ops */
425 }
426 270
427 if (check_set_scale_value (geom_flags, WidthValue, h_scale, w)) ++changed; 271 rxvt_free_strsplit (arr);
428 if (check_set_scale_value (geom_flags, HeightValue, v_scale, h)) ++changed; 272 }
429 if (check_set_align_value (geom_flags, XValue, h_align, x)) ++changed; 273
430 if (check_set_align_value (geom_flags, YValue, v_align, y)) ++changed; 274 new_flags |= flags & ~IM_GEOMETRY_FLAGS;
275
276 if (!update)
431 } 277 {
278 if (!(geom_flags & XValue))
279 x = y = defaultAlign;
280 else if (!(geom_flags & YValue))
281 y = x;
432 282
433 if (new_flags != flags) 283 if (!(geom_flags & (WidthValue|HeightValue)))
284 w = h = defaultScale;
285 else if (!(geom_flags & HeightValue))
286 h = w;
287 else if (!(geom_flags & WidthValue))
288 w = h;
289 }
290
291 clamp_it (x, -100, 200);
292 clamp_it (y, -100, 200);
293
294 if (flags != new_flags
295 || h_scale != w
296 || v_scale != h
297 || h_align != x
298 || v_align != y)
434 { 299 {
435 flags = new_flags; 300 flags = new_flags;
301 h_scale = w;
302 v_scale = h;
303 h_align = x;
304 v_align = y;
436 changed++; 305 changed = true;
437 } 306 }
438 307
308 if (is_size_sensitive ())
309 flags |= IM_IS_SIZE_SENSITIVE;
310 else
311 flags &= ~IM_IS_SIZE_SENSITIVE;
312
439 return (changed > 0); 313 return changed;
440} 314}
441 315
442void 316void
443bgPixmap_t::get_image_geometry (int image_width, int image_height, int &w, int &h, int &x, int &y) 317rxvt_term::get_image_geometry (rxvt_image &image, int &w, int &h, int &x, int &y)
444{ 318{
319 int image_width = image.width ();
320 int image_height = image.height ();
445 int target_width = target->szHint.width; 321 int target_width = szHint.width;
446 int target_height = target->szHint.height; 322 int target_height = szHint.height;
323 int h_scale = min (image.h_scale, 32767 * 100 / target_width);
324 int v_scale = min (image.v_scale, 32767 * 100 / target_height);
447 325
448 if (flags & propScale) 326 w = h_scale * target_width / 100;
327 h = v_scale * target_height / 100;
328
329 if (image.flags & IM_KEEP_ASPECT)
449 { 330 {
450 float scale = (float)target_width / image_width; 331 float scale = (float)w / image_width;
451 min_it (scale, (float)target_height / image_height); 332 min_it (scale, (float)h / image_height);
452 w = image_width * scale + 0.5; 333 w = image_width * scale + 0.5;
453 h = image_height * scale + 0.5; 334 h = image_height * scale + 0.5;
454 } 335 }
455 else
456 {
457 w = h_scale * target_width / 100;
458 h = v_scale * target_height / 100;
459 }
460 336
461 if (!w) w = image_width; 337 if (!w) w = image_width;
462 if (!h) h = image_height; 338 if (!h) h = image_height;
463 339
464 if (flags & rootAlign) 340 if (image.flags & IM_ROOT_ALIGN)
465 { 341 {
466 target->get_window_origin (x, y);
467 x = -x; 342 x = -target_x;
468 y = -y; 343 y = -target_y;
469 } 344 }
470 else 345 else
471 { 346 {
472 x = make_align_position (h_align, target_width, w); 347 x = make_align_position (image.h_align, target_width, w);
473 y = make_align_position (v_align, target_height, h); 348 y = make_align_position (image.v_align, target_height, h);
474 } 349 }
475
476 flags &= ~sizeSensitive;
477 if ((flags & propScale) || h_scale || v_scale
478 || (!(flags & rootAlign) && (h_align || v_align))
479 || w > target_width || h > target_height)
480 flags |= sizeSensitive;
481} 350}
482 351
483# ifdef HAVE_AFTERIMAGE 352# ifdef HAVE_PIXBUF
484bool 353bool
485bgPixmap_t::render_image (unsigned long background_flags) 354rxvt_term::pixbuf_to_pixmap (GdkPixbuf *pixbuf, Pixmap pixmap, GC gc,
355 int src_x, int src_y, int dst_x, int dst_y,
356 unsigned int width, unsigned int height, bool argb)
486{ 357{
487 if (target == NULL) 358 XImage *ximage;
359 char *line;
360 int width_r, width_g, width_b, width_a;
361 int sh_r, sh_g, sh_b, sh_a;
362 uint32_t red_mask, green_mask, blue_mask, alpha_mask;
363 int rowstride;
364 int channels;
365 unsigned char *row;
366
367 if (visual->c_class != TrueColor)
488 return false; 368 return false;
489 369
490 target->init_asv (); 370 if (argb)
491
492 ASImage *background = NULL;
493 ARGB32 background_tint = TINT_LEAVE_SAME;
494
495# ifdef ENABLE_TRANSPARENCY
496 if (background_flags)
497 background = pixmap2ximage (target->asv, pixmap, 0, 0, pmap_width, pmap_height, AllPlanes, 100);
498
499 if (!(background_flags & transpPmapTinted) && (flags & tintNeeded))
500 {
501 ShadingInfo as_shade;
502 as_shade.shading = shade;
503
504 rgba c (rgba::MAX_CC,rgba::MAX_CC,rgba::MAX_CC);
505 if (flags & tintSet)
506 tint.get (c);
507 as_shade.tintColor.red = c.r;
508 as_shade.tintColor.green = c.g;
509 as_shade.tintColor.blue = c.b;
510
511 background_tint = shading2tint32 (&as_shade);
512 } 371 {
513 372 red_mask = 0xff << 16;
514 if (!(background_flags & transpPmapBlurred) && (flags & blurNeeded) && background != NULL) 373 green_mask = 0xff << 8;
374 blue_mask = 0xff;
375 alpha_mask = 0xff << 24;
515 { 376 }
516 ASImage *tmp = blur_asimage_gauss (target->asv, background, h_blurRadius, v_blurRadius, 0xFFFFFFFF, 377 else
517 (original_asim == NULL || tint == TINT_LEAVE_SAME) ? ASA_XImage : ASA_ASImage, 378 {
518 100, ASIMAGE_QUALITY_DEFAULT); 379 red_mask = visual->red_mask;
380 green_mask = visual->green_mask;
381 blue_mask = visual->blue_mask;
382#if XRENDER
383 XRenderPictFormat *format = XRenderFindVisualFormat (dpy, visual);
519 if (tmp) 384 if (format)
520 { 385 alpha_mask = (uint32_t)format->direct.alphaMask << format->direct.alpha;
521 destroy_asimage (&background); 386 else
522 background = tmp; 387#endif
388 alpha_mask = 0;
389 }
390
391 width_r = ecb_popcount32 (red_mask);
392 width_g = ecb_popcount32 (green_mask);
393 width_b = ecb_popcount32 (blue_mask);
394 width_a = ecb_popcount32 (alpha_mask);
395
396 if (width_r > 8 || width_g > 8 || width_b > 8 || width_a > 8)
397 return false;
398
399 sh_r = ecb_ctz32 (red_mask);
400 sh_g = ecb_ctz32 (green_mask);
401 sh_b = ecb_ctz32 (blue_mask);
402 sh_a = ecb_ctz32 (alpha_mask);
403
404 if (width > 32767 || height > 32767)
405 return false;
406
407 ximage = XCreateImage (dpy, visual, argb ? 32 : depth, ZPixmap, 0, 0,
408 width, height, 32, 0);
409 if (!ximage)
410 return false;
411
412 if (height > INT_MAX / ximage->bytes_per_line
413 || !(ximage->data = (char *)malloc (height * ximage->bytes_per_line)))
414 {
415 XDestroyImage (ximage);
416 return false;
417 }
418
419 ximage->byte_order = ecb_big_endian () ? MSBFirst : LSBFirst;
420
421 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
422 channels = gdk_pixbuf_get_n_channels (pixbuf);
423 row = gdk_pixbuf_get_pixels (pixbuf) + src_y * rowstride + src_x * channels;
424 line = ximage->data;
425
426 rgba c (0, 0, 0);
427
428 if (channels == 4 && alpha_mask == 0)
429 {
430 pix_colors[Color_bg].get (c);
431 c.r >>= 8;
432 c.g >>= 8;
433 c.b >>= 8;
434 }
435
436 for (int y = 0; y < height; y++)
437 {
438 for (int x = 0; x < width; x++)
523 } 439 {
524 } 440 unsigned char *pixel = row + x * channels;
525# endif 441 uint32_t value;
442 unsigned char r, g, b, a;
526 443
527 ASImage *result = 0; 444 if (channels == 4)
445 {
446 a = pixel[3];
447 r = (pixel[0] * a + c.r * (0xff - a)) / 0xff;
448 g = (pixel[1] * a + c.g * (0xff - a)) / 0xff;
449 b = (pixel[2] * a + c.b * (0xff - a)) / 0xff;
450 }
451 else
452 {
453 a = 0xff;
454 r = pixel[0];
455 g = pixel[1];
456 b = pixel[2];
457 }
528 458
459 value = ((r >> (8 - width_r)) << sh_r)
460 | ((g >> (8 - width_g)) << sh_g)
461 | ((b >> (8 - width_b)) << sh_b)
462 | ((a >> (8 - width_a)) << sh_a);
463
464 if (ximage->bits_per_pixel == 32)
465 ((uint32_t *)line)[x] = value;
466 else
467 XPutPixel (ximage, x, y, value);
468 }
469
470 row += rowstride;
471 line += ximage->bytes_per_line;
472 }
473
474 XPutImage (dpy, pixmap, gc, ximage, 0, 0, dst_x, dst_y, width, height);
475 XDestroyImage (ximage);
476 return true;
477}
478
479bool
480rxvt_term::render_image (rxvt_image &image)
481{
482 GdkPixbuf *pixbuf = image.pixbuf;
483 if (!pixbuf)
484 return false;
485
486 bool need_blend = bg_flags & BG_IS_VALID;
487
488 if (need_blend
489 && !(display->flags & DISPLAY_HAS_RENDER))
490 return false;
491
492 GdkPixbuf *result;
493
494 int image_width = gdk_pixbuf_get_width (pixbuf);
495 int image_height = gdk_pixbuf_get_height (pixbuf);
496
529 int target_width = target->szHint.width; 497 int target_width = szHint.width;
530 int target_height = target->szHint.height; 498 int target_height = szHint.height;
531 int new_pmap_width = target_width; 499 int new_pmap_width = target_width;
532 int new_pmap_height = target_height; 500 int new_pmap_height = target_height;
533 501
534 int x = 0; 502 int x = 0;
535 int y = 0; 503 int y = 0;
536 int w = 0; 504 int w = 0;
537 int h = 0; 505 int h = 0;
538 506
539 if (original_asim) 507 get_image_geometry (image, w, h, x, y);
540 get_image_geometry (original_asim->width, original_asim->height, w, h, x, y);
541 508
542 if (!original_asim 509 if (!(image.flags & IM_ROOT_ALIGN)
543 || (!(flags & rootAlign)
544 && (x >= target_width
545 || y >= target_height
546 || (x + w <= 0)
547 || (y + h <= 0))))
548 {
549 if (background)
550 {
551 new_pmap_width = background->width;
552 new_pmap_height = background->height;
553 result = background;
554
555 if (background_tint != TINT_LEAVE_SAME)
556 {
557 ASImage *tmp = tile_asimage (target->asv, background, 0, 0,
558 target_width, target_height, background_tint,
559 ASA_XImage, 100, ASIMAGE_QUALITY_DEFAULT);
560 if (tmp)
561 result = tmp;
562 }
563 }
564 else
565 new_pmap_width = new_pmap_height = 0;
566 }
567 else
568 {
569 result = original_asim;
570
571 if ((w != original_asim->width)
572 || (h != original_asim->height))
573 {
574 result = scale_asimage (target->asv, original_asim,
575 w, h,
576 background ? ASA_ASImage : ASA_XImage,
577 100, ASIMAGE_QUALITY_DEFAULT);
578 }
579
580 if (background == NULL)
581 {
582 if (h_scale == 0 || v_scale == 0)
583 {
584 /* if tiling - pixmap has to be sized exactly as the image,
585 but there is no need to make it bigger than the window! */
586 new_pmap_width = min (result->width, target_width);
587 new_pmap_height = min (result->height, target_height);
588
589 /* we also need to tile our image in both directions */
590 ASImage *tmp = tile_asimage (target->asv, result,
591 (int)result->width - x,
592 (int)result->height - y,
593 new_pmap_width,
594 new_pmap_height,
595 TINT_LEAVE_SAME, ASA_XImage,
596 100, ASIMAGE_QUALITY_DEFAULT);
597 if (tmp)
598 {
599 if (result != original_asim)
600 destroy_asimage (&result);
601
602 result = tmp;
603 }
604 }
605 }
606 else
607 {
608 /* if blending background and image - pixmap has to be sized same as target window */
609 ASImageLayer *layers = create_image_layers (2);
610
611 layers[0].im = background;
612 layers[0].clip_width = target_width;
613 layers[0].clip_height = target_height;
614 layers[0].tint = background_tint;
615 layers[1].im = result;
616
617 if (h_scale == 0 || v_scale == 0)
618 {
619 /* tile horizontally */
620 while (x > 0) x -= (int)result->width;
621 layers[1].dst_x = x;
622 layers[1].clip_width = result->width+target_width;
623 }
624 else
625 {
626 /* clip horizontally */
627 layers[1].dst_x = x;
628 layers[1].clip_width = result->width;
629 }
630
631 if (h_scale == 0 || v_scale == 0)
632 {
633 while (y > 0) y -= (int)result->height;
634 layers[1].dst_y = y;
635 layers[1].clip_height = result->height + target_height;
636 }
637 else
638 {
639 layers[1].dst_y = y;
640 layers[1].clip_height = result->height;
641 }
642
643 if (target->rs[Rs_blendtype])
644 {
645 layers[1].merge_scanlines = blend_scanlines_name2func (target->rs[Rs_blendtype]);
646 if (layers[1].merge_scanlines == NULL)
647 layers[1].merge_scanlines = alphablend_scanlines;
648 }
649
650 ASImage *tmp = merge_layers (target->asv, layers, 2, target_width, target_height,
651 ASA_XImage, 0, ASIMAGE_QUALITY_DEFAULT);
652
653 if (tmp)
654 {
655 if (result != original_asim)
656 destroy_asimage (&result);
657
658 result = tmp;
659 }
660
661 free (layers);
662 }
663 }
664
665 bool ret = false;
666
667 if (result)
668 {
669 XGCValues gcv;
670 GC gc;
671
672 if (pixmap)
673 {
674 if (pmap_width != new_pmap_width
675 || pmap_height != new_pmap_height
676 || pmap_depth != target->depth)
677 {
678 XFreePixmap (target->dpy, pixmap);
679 pixmap = None;
680 }
681 }
682
683 /* create Pixmap */
684 if (pixmap == None)
685 {
686 pixmap = XCreatePixmap (target->dpy, target->vt, new_pmap_width, new_pmap_height, target->depth);
687 pmap_width = new_pmap_width;
688 pmap_height = new_pmap_height;
689 pmap_depth = target->depth;
690 }
691 /* fill with background color (if result's not completely overlapping it) */
692 gcv.foreground = target->pix_colors[Color_bg];
693 gc = XCreateGC (target->dpy, target->vt, GCForeground, &gcv);
694
695 int src_x = 0, src_y = 0, dst_x = 0, dst_y = 0;
696 int dst_width = result->width, dst_height = result->height;
697 if (background == NULL)
698 {
699 if (!(h_scale == 0 || v_scale == 0))
700 {
701 src_x = make_clip_rectangle (x, result->width , new_pmap_width , dst_x, dst_width );
702 src_y = make_clip_rectangle (y, result->height, new_pmap_height, dst_y, dst_height);
703 }
704
705 if (dst_x > 0 || dst_y > 0
706 || dst_x + dst_width < new_pmap_width
707 || dst_y + dst_height < new_pmap_height)
708 XFillRectangle (target->dpy, pixmap, gc, 0, 0, new_pmap_width, new_pmap_height);
709 }
710
711 /* put result on pixmap */
712 if (dst_x < new_pmap_width && dst_y < new_pmap_height)
713 asimage2drawable (target->asv, pixmap, result, gc, src_x, src_y, dst_x, dst_y, dst_width, dst_height, True);
714
715 if (result != background && result != original_asim)
716 destroy_asimage (&result);
717
718 XFreeGC (target->dpy, gc);
719
720 ret = true;
721 }
722
723 if (background)
724 destroy_asimage (&background);
725
726 return ret;
727}
728# endif /* HAVE_AFTERIMAGE */
729
730# ifdef HAVE_PIXBUF
731bool
732bgPixmap_t::render_image (unsigned long background_flags)
733{
734 if (target == NULL)
735 return false;
736
737 if (!pixbuf)
738 return false;
739
740#if !XRENDER
741 if (background_flags)
742 return false;
743#endif
744
745 GdkPixbuf *result;
746
747 int image_width = gdk_pixbuf_get_width (pixbuf);
748 int image_height = gdk_pixbuf_get_height (pixbuf);
749
750 int target_width = target->szHint.width;
751 int target_height = target->szHint.height;
752 int new_pmap_width = target_width;
753 int new_pmap_height = target_height;
754
755 int x = 0;
756 int y = 0;
757 int w = 0;
758 int h = 0;
759
760 get_image_geometry (image_width, image_height, w, h, x, y);
761
762 if (!(flags & rootAlign)
763 && (x >= target_width 510 && (x >= target_width
764 || y >= target_height 511 || y >= target_height
765 || (x + w <= 0) 512 || x + w <= 0
766 || (y + h <= 0))) 513 || y + h <= 0))
767 return false; 514 return false;
768 515
769 result = pixbuf; 516 result = pixbuf;
770 517
771 if ((w != image_width) 518 if (w != image_width
772 || (h != image_height)) 519 || h != image_height)
773 { 520 {
774 result = gdk_pixbuf_scale_simple (pixbuf, 521 result = gdk_pixbuf_scale_simple (pixbuf,
775 w, h, 522 w, h,
776 GDK_INTERP_BILINEAR); 523 GDK_INTERP_BILINEAR);
777 } 524 }
778 525
526 if (!result)
527 return false;
528
779 bool ret = false; 529 bool ret = false;
780 530
781 if (result)
782 {
783 XGCValues gcv; 531 XGCValues gcv;
784 GC gc; 532 GC gc;
785 Pixmap root_pmap; 533 Pixmap tmp_pixmap;
786 534
787 image_width = gdk_pixbuf_get_width (result); 535 image_width = gdk_pixbuf_get_width (result);
788 image_height = gdk_pixbuf_get_height (result); 536 image_height = gdk_pixbuf_get_height (result);
789 537
790 if (background_flags) 538 if (need_blend)
791 { 539 tmp_pixmap = XCreatePixmap (dpy, vt, new_pmap_width, new_pmap_height, 32);
792 root_pmap = pixmap; 540 else
793 pixmap = None; 541 {
542 // optimise bg pixmap size when tiling, but only if there are no
543 // other pixbufs to render. Otherwise, the bg pixmap size must
544 // be equal to the window size.
545 if ((image.flags & IM_TILE)
546 && image_vec.size () == 1)
794 } 547 {
795 else
796 {
797 if (h_scale == 0 || v_scale == 0)
798 {
799 new_pmap_width = min (image_width, target_width); 548 new_pmap_width = min (image_width, target_width);
800 new_pmap_height = min (image_height, target_height); 549 new_pmap_height = min (image_height, target_height);
801 }
802 }
803
804 if (pixmap)
805 { 550 }
806 if (pmap_width != new_pmap_width
807 || pmap_height != new_pmap_height
808 || pmap_depth != target->depth)
809 {
810 XFreePixmap (target->dpy, pixmap);
811 pixmap = None;
812 }
813 }
814 551
815 if (pixmap == None) 552 if (bg_pixmap == None
553 || bg_pmap_width != new_pmap_width
554 || bg_pmap_height != new_pmap_height)
816 { 555 {
556 if (bg_pixmap)
557 XFreePixmap (dpy, bg_pixmap);
817 pixmap = XCreatePixmap (target->dpy, target->vt, new_pmap_width, new_pmap_height, target->depth); 558 bg_pixmap = XCreatePixmap (dpy, vt, new_pmap_width, new_pmap_height, depth);
818 pmap_width = new_pmap_width; 559 bg_pmap_width = new_pmap_width;
819 pmap_height = new_pmap_height; 560 bg_pmap_height = new_pmap_height;
820 pmap_depth = target->depth;
821 }
822
823 gcv.foreground = target->pix_colors[Color_bg];
824 gc = XCreateGC (target->dpy, target->vt, GCForeground, &gcv);
825
826 if (h_scale == 0 || v_scale == 0)
827 { 561 }
562
563 tmp_pixmap = bg_pixmap;
564 }
565
566 gcv.foreground = pix_colors[Color_bg];
567 gc = XCreateGC (dpy, tmp_pixmap, GCForeground, &gcv);
568
569 if (gc)
570 {
571 if (image.flags & IM_TILE)
572 {
828 Pixmap tile = XCreatePixmap (target->dpy, target->vt, image_width, image_height, target->depth); 573 Pixmap tile = XCreatePixmap (dpy, vt, image_width, image_height, need_blend ? 32 : depth);
829 gdk_pixbuf_xlib_render_to_drawable (result, tile, gc, 574 pixbuf_to_pixmap (result, tile, gc,
830 0, 0, 575 0, 0,
831 0, 0, 576 0, 0,
832 image_width, image_height, 577 image_width, image_height, need_blend);
833 XLIB_RGB_DITHER_NONE,
834 0, 0);
835 578
836 gcv.tile = tile; 579 gcv.tile = tile;
837 gcv.fill_style = FillTiled; 580 gcv.fill_style = FillTiled;
838 gcv.ts_x_origin = x; 581 gcv.ts_x_origin = x;
839 gcv.ts_y_origin = y; 582 gcv.ts_y_origin = y;
840 XChangeGC (target->dpy, gc, GCFillStyle | GCTile | GCTileStipXOrigin | GCTileStipYOrigin, &gcv); 583 XChangeGC (dpy, gc, GCFillStyle | GCTile | GCTileStipXOrigin | GCTileStipYOrigin, &gcv);
841 584
842 XFillRectangle (target->dpy, pixmap, gc, 0, 0, new_pmap_width, new_pmap_height); 585 XFillRectangle (dpy, tmp_pixmap, gc, 0, 0, new_pmap_width, new_pmap_height);
843 XFreePixmap (target->dpy, tile); 586 XFreePixmap (dpy, tile);
844 } 587 }
845 else 588 else
846 { 589 {
847 int src_x, src_y, dst_x, dst_y; 590 int src_x, src_y, dst_x, dst_y;
848 int dst_width, dst_height; 591 int dst_width, dst_height;
851 src_y = make_clip_rectangle (y, image_height, new_pmap_height, dst_y, dst_height); 594 src_y = make_clip_rectangle (y, image_height, new_pmap_height, dst_y, dst_height);
852 595
853 if (dst_x > 0 || dst_y > 0 596 if (dst_x > 0 || dst_y > 0
854 || dst_x + dst_width < new_pmap_width 597 || dst_x + dst_width < new_pmap_width
855 || dst_y + dst_height < new_pmap_height) 598 || dst_y + dst_height < new_pmap_height)
856 XFillRectangle (target->dpy, pixmap, gc, 0, 0, new_pmap_width, new_pmap_height); 599 XFillRectangle (dpy, tmp_pixmap, gc, 0, 0, new_pmap_width, new_pmap_height);
857 600
858 if (dst_x < new_pmap_width && dst_y < new_pmap_height) 601 if (dst_x < new_pmap_width && dst_y < new_pmap_height)
859 gdk_pixbuf_xlib_render_to_drawable (result, pixmap, gc, 602 pixbuf_to_pixmap (result, tmp_pixmap, gc,
860 src_x, src_y, 603 src_x, src_y,
861 dst_x, dst_y, 604 dst_x, dst_y,
862 dst_width, dst_height, 605 dst_width, dst_height, need_blend);
863 XLIB_RGB_DITHER_NONE,
864 0, 0);
865 } 606 }
607
608 if (image.need_blur ())
609 blur_pixmap (tmp_pixmap, new_pmap_width, new_pmap_height, need_blend, image.h_blurRadius, image.v_blurRadius);
610 if (image.need_tint ())
611 tint_pixmap (tmp_pixmap, new_pmap_width, new_pmap_height, need_blend, image.tint, image.tint_set, image.shade);
866 612
867#if XRENDER 613#if XRENDER
868 if (background_flags) 614 if (need_blend)
869 { 615 {
870 Display *dpy = target->dpy;
871 XRenderPictureAttributes pa;
872
873 XRenderPictFormat *src_format = XRenderFindVisualFormat (dpy, target->visual);
874 Picture src = XRenderCreatePicture (dpy, root_pmap, src_format, 0, &pa);
875
876 XRenderPictFormat *dst_format = XRenderFindVisualFormat (dpy, target->visual);
877 Picture dst = XRenderCreatePicture (dpy, pixmap, dst_format, 0, &pa);
878
879 pa.repeat = True;
880 Pixmap mask_pmap = XCreatePixmap (dpy, target->vt, 1, 1, 8);
881 XRenderPictFormat *mask_format = XRenderFindStandardFormat (dpy, PictStandardA8); 616 XRenderPictFormat *argb_format = XRenderFindStandardFormat (dpy, PictStandardARGB32);
617 XRenderPictFormat *format = XRenderFindVisualFormat (dpy, visual);
618
882 Picture mask = XRenderCreatePicture (dpy, mask_pmap, mask_format, CPRepeat, &pa); 619 Picture src = XRenderCreatePicture (dpy, tmp_pixmap, argb_format, 0, 0);
883 XFreePixmap (dpy, mask_pmap);
884 620
885 if (src && dst && mask) 621 Picture dst = XRenderCreatePicture (dpy, bg_pixmap, format, 0, 0);
886 { 622
623 Picture mask = create_xrender_mask (dpy, vt, False, False);
624
887 XRenderColor mask_c; 625 XRenderColor mask_c;
888 626
889 mask_c.alpha = 0x8000; 627 mask_c.alpha = image.alpha;
890 mask_c.red = 0; 628 mask_c.red =
891 mask_c.green = 0; 629 mask_c.green =
892 mask_c.blue = 0; 630 mask_c.blue = 0;
893 XRenderFillRectangle (dpy, PictOpSrc, mask, &mask_c, 0, 0, 1, 1); 631 XRenderFillRectangle (dpy, PictOpSrc, mask, &mask_c, 0, 0, 1, 1);
632
894 XRenderComposite (dpy, PictOpOver, src, mask, dst, 0, 0, 0, 0, 0, 0, target_width, target_height); 633 XRenderComposite (dpy, PictOpOver, src, mask, dst, 0, 0, 0, 0, 0, 0, target_width, target_height);
895 }
896 634
897 XRenderFreePicture (dpy, src); 635 XRenderFreePicture (dpy, src);
898 XRenderFreePicture (dpy, dst); 636 XRenderFreePicture (dpy, dst);
899 XRenderFreePicture (dpy, mask); 637 XRenderFreePicture (dpy, mask);
900
901 XFreePixmap (dpy, root_pmap);
902 } 638 }
903#endif 639#endif
904 640
905 if (result != pixbuf)
906 g_object_unref (result);
907
908 XFreeGC (target->dpy, gc); 641 XFreeGC (dpy, gc);
909 642
910 ret = true; 643 ret = true;
911 } 644 }
912 645
646 if (result != pixbuf)
647 g_object_unref (result);
648
649 if (need_blend)
650 XFreePixmap (dpy, tmp_pixmap);
651
913 return ret; 652 return ret;
914} 653}
915# endif /* HAVE_PIXBUF */ 654# endif /* HAVE_PIXBUF */
916 655
917bool 656# ifndef NO_RESOURCES
918bgPixmap_t::set_file (const char *file) 657static int
658rxvt_define_image (XrmDatabase *database ecb_unused,
659 XrmBindingList bindings ecb_unused,
660 XrmQuarkList quarks,
661 XrmRepresentation *type ecb_unused,
662 XrmValue *value,
663 XPointer closure ecb_unused)
919{ 664{
920 assert (file); 665 int size;
921 666
922 if (*file) 667 for (size = 0; quarks[size] != NULLQUARK; size++)
923 { 668 ;
924 if (const char *p = strchr (file, ';'))
925 {
926 size_t len = p - file;
927 char *f = rxvt_temp_buf<char> (len + 1);
928 memcpy (f, file, len);
929 f[len] = '\0';
930 file = f;
931 }
932 669
933# ifdef HAVE_AFTERIMAGE 670 if (size >= 2)
934 if (!target->asimman) 671 {
935 target->asimman = create_generic_imageman (target->rs[Rs_path]); 672 int id = strtol (XrmQuarkToString (quarks[size-2]), 0, 0);
936 ASImage *image = get_asimage (target->asimman, file, 0xFFFFFFFF, 100); 673 if (id >= 1)
937 if (image) 674 GET_R->parse_image (id, XrmQuarkToString (quarks[size-1]), (char *)value->addr);
938 { 675 }
939 if (original_asim) 676 return False;
940 safe_asimage_destroy (original_asim); 677}
941 original_asim = image;
942 have_image = true;
943 return true;
944 }
945# endif 678# endif
946 679
680void
681rxvt_term::parse_image (int id, const char *type, const char *arg)
682{
683 if (image_vec.size () < id + 1)
684 image_vec.resize (id + 1);
685
686 rxvt_image *image = &image_vec[id];
687}
688
689rxvt_image::rxvt_image ()
690{
691 alpha = 0xffff;
692 flags = 0;
693 h_scale =
694 v_scale = defaultScale;
695 h_align =
696 v_align = defaultAlign;
697
947# ifdef HAVE_PIXBUF 698# ifdef HAVE_PIXBUF
948 GdkPixbuf *image = gdk_pixbuf_new_from_file (file, NULL); 699 pixbuf = 0;
949 if (image)
950 {
951 if (pixbuf)
952 g_object_unref (pixbuf);
953 pixbuf = image;
954 have_image = true;
955 return true;
956 }
957# endif 700# endif
958 } 701}
959 702
703bool
704rxvt_image::set_file_geometry (const char *file)
705{
706 if (!file || !*file)
960 return false; 707 return false;
708
709 const char *p = strchr (file, ';');
710
711 if (p)
712 {
713 size_t len = p - file;
714 char *f = rxvt_temp_buf<char> (len + 1);
715 memcpy (f, file, len);
716 f[len] = '\0';
717 file = f;
718 }
719
720 bool ret = set_file (file);
721 alpha = 0x8000;
722 if (ret)
723 set_geometry (p ? p + 1 : "");
724 return ret;
725}
726
727bool
728rxvt_image::set_file (const char *file)
729{
730 bool ret = false;
731
732# ifdef HAVE_PIXBUF
733 GdkPixbuf *image = gdk_pixbuf_new_from_file (file, NULL);
734 if (image)
735 {
736 if (pixbuf)
737 g_object_unref (pixbuf);
738 pixbuf = image;
739 ret = true;
740 }
741# endif
742
743 if (ret)
744 flags |= IM_IS_SET;
745
746 return ret;
961} 747}
962 748
963# endif /* BG_IMAGE_FROM_FILE */ 749# endif /* BG_IMAGE_FROM_FILE */
964 750
965# ifdef ENABLE_TRANSPARENCY
966bool 751bool
967bgPixmap_t::set_transparent ()
968{
969 if (!(flags & isTransparent))
970 {
971 flags |= isTransparent;
972 return true;
973 }
974
975 return false;
976}
977
978bool
979bgPixmap_t::set_blur_radius (const char *geom) 752image_effects::set_blur (const char *geom)
980{ 753{
981 int changed = 0; 754 bool changed = false;
982 unsigned int hr, vr; 755 unsigned int hr, vr;
983 int junk; 756 int junk;
984 int geom_flags = XParseGeometry (geom, &junk, &junk, &hr, &vr); 757 int geom_flags = XParseGeometry (geom, &junk, &junk, &hr, &vr);
985 758
986 if (!(geom_flags & WidthValue)) 759 if (!(geom_flags & WidthValue))
991 min_it (hr, 128); 764 min_it (hr, 128);
992 min_it (vr, 128); 765 min_it (vr, 128);
993 766
994 if (h_blurRadius != hr) 767 if (h_blurRadius != hr)
995 { 768 {
996 ++changed; 769 changed = true;
997 h_blurRadius = hr; 770 h_blurRadius = hr;
998 } 771 }
999 772
1000 if (v_blurRadius != vr) 773 if (v_blurRadius != vr)
1001 { 774 {
1002 ++changed; 775 changed = true;
1003 v_blurRadius = vr; 776 v_blurRadius = vr;
1004 } 777 }
1005 778
1006 if (v_blurRadius == 0 && h_blurRadius == 0)
1007 flags &= ~blurNeeded;
1008 else
1009 flags |= blurNeeded;
1010
1011#if XRENDER
1012 XFilters *filters = XRenderQueryFilters (target->dpy, target->vt);
1013 if (filters)
1014 {
1015 for (int i = 0; i < filters->nfilter; i++)
1016 if (!strcmp (filters->filter[i], FilterConvolution))
1017 flags |= bgPixmap_t::blurServerSide;
1018
1019 XFree (filters);
1020 }
1021#endif
1022
1023 return (changed > 0); 779 return changed;
1024} 780}
1025 781
1026static inline unsigned long
1027compute_tint_shade_flags (rxvt_color *tint, int shade)
1028{
1029 unsigned long flags = 0;
1030 rgba c (rgba::MAX_CC,rgba::MAX_CC,rgba::MAX_CC);
1031 bool has_shade = shade != 100;
1032
1033 if (tint)
1034 {
1035 tint->get (c);
1036# define IS_COMPONENT_WHOLESOME(cmp) ((cmp) <= 0x000700 || (cmp) >= 0x00f700)
1037 if (!has_shade && IS_COMPONENT_WHOLESOME (c.r)
1038 && IS_COMPONENT_WHOLESOME (c.g)
1039 && IS_COMPONENT_WHOLESOME (c.b))
1040 flags |= bgPixmap_t::tintWholesome;
1041# undef IS_COMPONENT_WHOLESOME
1042 }
1043
1044 if (has_shade)
1045 flags |= bgPixmap_t::tintNeeded;
1046 else if (tint)
1047 {
1048 if ((c.r > 0x000700 || c.g > 0x000700 || c.b > 0x000700)
1049 && (c.r < 0x00f700 || c.g < 0x00f700 || c.b < 0x00f700))
1050 {
1051 flags |= bgPixmap_t::tintNeeded;
1052 }
1053 }
1054
1055 if (flags & bgPixmap_t::tintNeeded)
1056 {
1057 if (flags & bgPixmap_t::tintWholesome)
1058 flags |= bgPixmap_t::tintServerSide;
1059 else
1060 {
1061#if XRENDER
1062 flags |= bgPixmap_t::tintServerSide;
1063#endif
1064 }
1065 }
1066
1067 return flags;
1068}
1069
1070bool 782bool
1071bgPixmap_t::set_tint (rxvt_color &new_tint) 783image_effects::set_tint (const rxvt_color &new_tint)
1072{ 784{
1073 if (!(flags & tintSet) || tint != new_tint) 785 if (!tint_set || tint != new_tint)
1074 { 786 {
1075 unsigned long new_flags = compute_tint_shade_flags (&new_tint, shade);
1076 tint = new_tint; 787 tint = new_tint;
1077 flags = (flags & ~tintFlags) | new_flags | tintSet; 788 tint_set = true;
789
1078 return true; 790 return true;
1079 } 791 }
1080 792
1081 return false; 793 return false;
1082} 794}
1083 795
1084bool 796bool
1085bgPixmap_t::unset_tint ()
1086{
1087 unsigned long new_flags = compute_tint_shade_flags (NULL, shade);
1088
1089 if (new_flags != (flags & tintFlags))
1090 {
1091 flags = (flags & ~tintFlags) | new_flags;
1092 return true;
1093 }
1094
1095 return false;
1096}
1097
1098bool
1099bgPixmap_t::set_shade (const char *shade_str) 797image_effects::set_shade (const char *shade_str)
1100{ 798{
1101 int new_shade = (shade_str) ? atoi (shade_str) : 100; 799 int new_shade = atoi (shade_str);
1102 800
1103 clamp_it (new_shade, -100, 200); 801 clamp_it (new_shade, -100, 200);
1104 if (new_shade < 0) 802 if (new_shade < 0)
1105 new_shade = 200 - (100 + new_shade); 803 new_shade = 200 - (100 + new_shade);
1106 804
1107 if (new_shade != shade) 805 if (new_shade != shade)
1108 { 806 {
1109 unsigned long new_flags = compute_tint_shade_flags ((flags & tintSet) ? &tint : NULL, new_shade);
1110 shade = new_shade; 807 shade = new_shade;
1111 flags = (flags & (~tintFlags | tintSet)) | new_flags;
1112 return true; 808 return true;
1113 } 809 }
1114 810
1115 return false; 811 return false;
1116} 812}
1137 params[i+2] = XDoubleToFixed (kernel[i] / sum); 833 params[i+2] = XDoubleToFixed (kernel[i] / sum);
1138} 834}
1139#endif 835#endif
1140 836
1141bool 837bool
1142bgPixmap_t::blur_pixmap (Pixmap pixmap, Visual *visual, int width, int height) 838rxvt_term::blur_pixmap (Pixmap pixmap, int width, int height, bool argb, int h_blurRadius, int v_blurRadius)
1143{ 839{
1144 bool ret = false; 840 bool ret = false;
1145#if XRENDER 841#if XRENDER
842 if (!(display->flags & DISPLAY_HAS_RENDER_CONV))
843 return false;
844
1146 int size = max (h_blurRadius, v_blurRadius) * 2 + 1; 845 int size = max (h_blurRadius, v_blurRadius) * 2 + 1;
1147 double *kernel = (double *)malloc (size * sizeof (double)); 846 double *kernel = (double *)malloc (size * sizeof (double));
1148 XFixed *params = (XFixed *)malloc ((size + 2) * sizeof (XFixed)); 847 XFixed *params = (XFixed *)malloc ((size + 2) * sizeof (XFixed));
1149 848
1150 Display *dpy = target->dpy;
1151 XRenderPictureAttributes pa; 849 XRenderPictureAttributes pa;
1152 XRenderPictFormat *format = XRenderFindVisualFormat (dpy, visual); 850 XRenderPictFormat *format = argb ? XRenderFindStandardFormat (dpy, PictStandardARGB32)
851 : XRenderFindVisualFormat (dpy, visual);
1153 852
853 pa.repeat = RepeatPad;
1154 Picture src = XRenderCreatePicture (dpy, pixmap, format, 0, &pa); 854 Picture src = XRenderCreatePicture (dpy, pixmap, format, CPRepeat, &pa);
855 Pixmap tmp = XCreatePixmap (dpy, pixmap, width, height, depth);
1155 Picture dst = XRenderCreatePicture (dpy, pixmap, format, 0, &pa); 856 Picture dst = XRenderCreatePicture (dpy, tmp, format, CPRepeat, &pa);
857 XFreePixmap (dpy, tmp);
1156 858
1157 if (kernel && params && src && dst) 859 if (kernel && params)
1158 { 860 {
1159 if (h_blurRadius)
1160 {
1161 size = h_blurRadius * 2 + 1; 861 size = h_blurRadius * 2 + 1;
1162 get_gaussian_kernel (h_blurRadius, size, kernel, params); 862 get_gaussian_kernel (h_blurRadius, size, kernel, params);
1163 863
1164 XRenderSetPictureFilter (dpy, src, FilterConvolution, params, size+2); 864 XRenderSetPictureFilter (dpy, src, FilterConvolution, params, size+2);
1165 XRenderComposite (dpy, 865 XRenderComposite (dpy,
1166 PictOpSrc, 866 PictOpSrc,
1167 src, 867 src,
1168 None, 868 None,
1169 dst, 869 dst,
1170 0, 0, 870 0, 0,
1171 0, 0, 871 0, 0,
1172 0, 0, 872 0, 0,
1173 width, height); 873 width, height);
1174 }
1175 874
1176 if (v_blurRadius) 875 ::swap (src, dst);
1177 { 876
1178 size = v_blurRadius * 2 + 1; 877 size = v_blurRadius * 2 + 1;
1179 get_gaussian_kernel (v_blurRadius, size, kernel, params); 878 get_gaussian_kernel (v_blurRadius, size, kernel, params);
1180 swap (params[0], params[1]); 879 ::swap (params[0], params[1]);
1181 880
1182 XRenderSetPictureFilter (dpy, src, FilterConvolution, params, size+2); 881 XRenderSetPictureFilter (dpy, src, FilterConvolution, params, size+2);
1183 XRenderComposite (dpy, 882 XRenderComposite (dpy,
1184 PictOpSrc, 883 PictOpSrc,
1185 src, 884 src,
1186 None, 885 None,
1187 dst, 886 dst,
1188 0, 0, 887 0, 0,
1189 0, 0, 888 0, 0,
1190 0, 0, 889 0, 0,
1191 width, height); 890 width, height);
1192 }
1193 891
1194 ret = true; 892 ret = true;
1195 } 893 }
1196 894
1197 free (kernel); 895 free (kernel);
1201#endif 899#endif
1202 return ret; 900 return ret;
1203} 901}
1204 902
1205bool 903bool
1206bgPixmap_t::tint_pixmap (Pixmap pixmap, Visual *visual, int width, int height) 904rxvt_term::tint_pixmap (Pixmap pixmap, int width, int height, bool argb, rxvt_color &tint, bool tint_set, int shade)
1207{ 905{
1208 Display *dpy = target->dpy;
1209 bool ret = false; 906 bool ret = false;
1210 907
1211 if (flags & tintWholesome) 908 rgba c (rgba::MAX_CC, rgba::MAX_CC, rgba::MAX_CC);
909
910 if (tint_set)
911 tint.get (c);
912
913 if (shade == 100
914 && (c.r <= 0x00ff || c.r >= 0xff00)
915 && (c.g <= 0x00ff || c.g >= 0xff00)
916 && (c.b <= 0x00ff || c.b >= 0xff00))
1212 { 917 {
1213 XGCValues gcv; 918 XGCValues gcv;
1214 GC gc; 919 GC gc;
1215 920
1216 /* In this case we can tint image server-side getting significant 921 /* In this case we can tint image server-side getting significant
1225 XFillRectangle (dpy, pixmap, gc, 0, 0, width, height); 930 XFillRectangle (dpy, pixmap, gc, 0, 0, width, height);
1226 ret = true; 931 ret = true;
1227 XFreeGC (dpy, gc); 932 XFreeGC (dpy, gc);
1228 } 933 }
1229 } 934 }
1230 else
1231 {
1232# if XRENDER 935# if XRENDER
1233 rgba c (rgba::MAX_CC,rgba::MAX_CC,rgba::MAX_CC); 936 else if (display->flags & DISPLAY_HAS_RENDER)
1234 937 {
1235 if (flags & tintSet)
1236 tint.get (c);
1237
1238 if (shade <= 100) 938 if (shade <= 100)
1239 { 939 {
1240 c.r = (c.r * shade) / 100; 940 c.r = c.r * shade / 100;
1241 c.g = (c.g * shade) / 100; 941 c.g = c.g * shade / 100;
1242 c.b = (c.b * shade) / 100; 942 c.b = c.b * shade / 100;
1243 } 943 }
1244 else 944 else
1245 { 945 {
1246 c.r = ((0xffff - c.r) * (200 - shade)) / 100; 946 c.r = c.r * (200 - shade) / 100;
1247 c.g = ((0xffff - c.g) * (200 - shade)) / 100; 947 c.g = c.g * (200 - shade) / 100;
1248 c.b = ((0xffff - c.b) * (200 - shade)) / 100; 948 c.b = c.b * (200 - shade) / 100;
1249 } 949 }
1250 950
1251 XRenderPictFormat *solid_format = XRenderFindStandardFormat (dpy, PictStandardARGB32); 951 XRenderPictFormat *format = argb ? XRenderFindStandardFormat (dpy, PictStandardARGB32)
1252 XRenderPictFormat *format = XRenderFindVisualFormat (dpy, visual); 952 : XRenderFindVisualFormat (dpy, visual);
1253 XRenderPictureAttributes pa;
1254 953
1255 Picture back_pic = XRenderCreatePicture (dpy, pixmap, format, 0, &pa); 954 Picture back_pic = XRenderCreatePicture (dpy, pixmap, format, 0, 0);
1256 955
1257 pa.repeat = True; 956 Picture overlay_pic = create_xrender_mask (dpy, pixmap, True, False);
1258 957
1259 Pixmap overlay_pmap = XCreatePixmap (dpy, pixmap, 1, 1, 32); 958 Picture mask_pic = create_xrender_mask (dpy, pixmap, True, True);
1260 Picture overlay_pic = XRenderCreatePicture (dpy, overlay_pmap, solid_format, CPRepeat, &pa);
1261 XFreePixmap (dpy, overlay_pmap);
1262 959
1263 pa.component_alpha = True;
1264 Pixmap mask_pmap = XCreatePixmap (dpy, pixmap, 1, 1, 32);
1265 Picture mask_pic = XRenderCreatePicture (dpy, mask_pmap, solid_format, CPRepeat|CPComponentAlpha, &pa);
1266 XFreePixmap (dpy, mask_pmap);
1267
1268 if (mask_pic && overlay_pic && back_pic)
1269 {
1270 XRenderColor mask_c; 960 XRenderColor mask_c;
1271 961
1272 memset (&mask_c, (shade > 100) ? 0xFF : 0x0, sizeof (mask_c));
1273 mask_c.alpha = 0xffff; 962 mask_c.alpha = 0xffff;
963 mask_c.red =
964 mask_c.green =
965 mask_c.blue = 0;
966 XRenderFillRectangle (dpy, PictOpSrc, overlay_pic, &mask_c, 0, 0, 1, 1);
967
968 mask_c.alpha = 0;
969 mask_c.red = 0xffff - c.r;
970 mask_c.green = 0xffff - c.g;
971 mask_c.blue = 0xffff - c.b;
972 XRenderFillRectangle (dpy, PictOpSrc, mask_pic, &mask_c, 0, 0, 1, 1);
973
974 XRenderComposite (dpy, PictOpOver, overlay_pic, mask_pic, back_pic, 0, 0, 0, 0, 0, 0, width, height);
975
976 if (shade > 100)
977 {
978 mask_c.alpha = 0;
979 mask_c.red =
980 mask_c.green =
981 mask_c.blue = 0xffff * (shade - 100) / 100;
1274 XRenderFillRectangle (dpy, PictOpSrc, overlay_pic, &mask_c, 0, 0, 1, 1); 982 XRenderFillRectangle (dpy, PictOpSrc, overlay_pic, &mask_c, 0, 0, 1, 1);
1275 983
1276 mask_c.alpha = 0;
1277 mask_c.red = 0xffff - c.r;
1278 mask_c.green = 0xffff - c.g;
1279 mask_c.blue = 0xffff - c.b;
1280 XRenderFillRectangle (dpy, PictOpSrc, mask_pic, &mask_c, 0, 0, 1, 1);
1281 XRenderComposite (dpy, PictOpOver, overlay_pic, mask_pic, back_pic, 0, 0, 0, 0, 0, 0, width, height); 984 XRenderComposite (dpy, PictOpOver, overlay_pic, None, back_pic, 0, 0, 0, 0, 0, 0, width, height);
985 }
986
1282 ret = true; 987 ret = true;
1283 }
1284 988
1285 XRenderFreePicture (dpy, mask_pic); 989 XRenderFreePicture (dpy, mask_pic);
1286 XRenderFreePicture (dpy, overlay_pic); 990 XRenderFreePicture (dpy, overlay_pic);
1287 XRenderFreePicture (dpy, back_pic); 991 XRenderFreePicture (dpy, back_pic);
992 }
1288# endif 993# endif
1289 }
1290 994
1291 return ret; 995 return ret;
1292} 996}
1293 997
1294/* make_transparency_pixmap() 998# ifdef ENABLE_TRANSPARENCY
999/*
1295 * Builds a pixmap of the same size as the terminal window that contains 1000 * Builds a pixmap of the same size as the terminal window that contains
1296 * the tiled portion of the root pixmap that is supposed to be covered by 1001 * the tiled portion of the root pixmap that is supposed to be covered by
1297 * our window. 1002 * our window.
1298 */ 1003 */
1299unsigned long 1004bool
1300bgPixmap_t::make_transparency_pixmap () 1005rxvt_term::render_root_image ()
1301{ 1006{
1302 unsigned long result = 0; 1007 bool ret = false;
1303
1304 if (target == NULL)
1305 return 0;
1306 1008
1307 /* root dimensions may change from call to call - but Display structure should 1009 /* root dimensions may change from call to call - but Display structure should
1308 * be always up-to-date, so let's use it : 1010 * be always up-to-date, so let's use it :
1309 */ 1011 */
1310 int screen = target->display->screen; 1012 int screen = display->screen;
1311 Display *dpy = target->dpy; 1013 int root_depth = DefaultDepth (dpy, screen);
1312 int root_width = DisplayWidth (dpy, screen); 1014 int root_width = DisplayWidth (dpy, screen);
1313 int root_height = DisplayHeight (dpy, screen); 1015 int root_height = DisplayHeight (dpy, screen);
1016 unsigned int root_pmap_width, root_pmap_height;
1314 int window_width = target->szHint.width; 1017 int window_width = szHint.width;
1315 int window_height = target->szHint.height; 1018 int window_height = szHint.height;
1316 int sx, sy; 1019 int sx, sy;
1317 XGCValues gcv; 1020 XGCValues gcv;
1318 GC gc; 1021 GC gc;
1319 1022
1320 target->get_window_origin (sx, sy); 1023 sx = target_x;
1024 sy = target_y;
1321 1025
1322 /* check if we are outside of the visible part of the virtual screen : */ 1026 /* check if we are outside of the visible part of the virtual screen : */
1323 if (sx + window_width <= 0 || sy + window_height <= 0 1027 if (sx + window_width <= 0 || sy + window_height <= 0
1324 || sx >= root_width || sy >= root_height) 1028 || sx >= root_width || sy >= root_height)
1325 return 0; 1029 return 0;
1326 1030
1327 if (root_pixmap == None) 1031 // validate root pixmap and get its size
1328 return 0;
1329
1330 Pixmap tiled_root_pmap = XCreatePixmap (dpy, target->vt, window_width, window_height, target->depth);
1331
1332 if (tiled_root_pmap == None) /* something really bad happened - abort */
1333 return 0;
1334
1335 /* straightforward pixmap copy */
1336 gcv.tile = root_pixmap;
1337 gcv.fill_style = FillTiled;
1338
1339 while (sx < 0) sx += (int)root_width;
1340 while (sy < 0) sy += (int)root_height;
1341
1342 gcv.ts_x_origin = -sx;
1343 gcv.ts_y_origin = -sy;
1344 gc = XCreateGC (dpy, target->vt, GCFillStyle | GCTile | GCTileStipXOrigin | GCTileStipYOrigin, &gcv);
1345
1346 if (gc)
1347 {
1348 XFillRectangle (dpy, tiled_root_pmap, gc, 0, 0, window_width, window_height);
1349 result |= transpPmapTiled;
1350 XFreeGC (dpy, gc);
1351 }
1352
1353 if (tiled_root_pmap != None)
1354 {
1355 if (!need_client_side_rendering ())
1356 {
1357 if (flags & (blurNeeded | blurServerSide))
1358 {
1359 if (blur_pixmap (tiled_root_pmap, target->visual, window_width, window_height))
1360 result |= transpPmapBlurred;
1361 }
1362 if (flags & (tintNeeded | tintServerSide))
1363 {
1364 if (tint_pixmap (tiled_root_pmap, target->visual, window_width, window_height))
1365 result |= transpPmapTinted;
1366 }
1367 } /* server side rendering completed */
1368
1369 if (pixmap)
1370 XFreePixmap (dpy, pixmap);
1371
1372 pixmap = tiled_root_pmap;
1373 pmap_width = window_width;
1374 pmap_height = window_height;
1375 pmap_depth = target->depth;
1376 }
1377
1378 return result;
1379}
1380
1381void
1382bgPixmap_t::set_root_pixmap ()
1383{
1384 Pixmap new_root_pixmap = target->get_pixmap_property (XA_XROOTPMAP_ID);
1385 if (new_root_pixmap == None)
1386 new_root_pixmap = target->get_pixmap_property (XA_ESETROOT_PMAP_ID);
1387
1388 root_pixmap = new_root_pixmap;
1389
1390 unsigned int width, height;
1391 int depth = DefaultDepth (target->dpy, target->display->screen);
1392
1393 // validate root pixmap
1394 if (root_pixmap != None) 1032 if (root_pixmap != None)
1395 { 1033 {
1396 Window wdummy; 1034 Window wdummy;
1397 int idummy; 1035 int idummy;
1398 unsigned int udummy; 1036 unsigned int udummy;
1399 1037
1400 target->allowedxerror = -1; 1038 allowedxerror = -1;
1401 1039
1402 if (!XGetGeometry (target->dpy, root_pixmap, &wdummy, &idummy, &idummy, &width, &height, &udummy, &udummy)) 1040 if (!XGetGeometry (dpy, root_pixmap, &wdummy, &idummy, &idummy, &root_pmap_width, &root_pmap_height, &udummy, &udummy))
1403 root_pixmap = None; 1041 root_pixmap = None;
1404 1042
1405 target->allowedxerror = 0; 1043 allowedxerror = 0;
1406 } 1044 }
1407 1045
1046 Pixmap recoded_root_pmap = root_pixmap;
1047
1408 if (root_pixmap != None && depth != target->depth) 1048 if (root_pixmap != None && root_depth != depth)
1409 { 1049 {
1410#if XRENDER 1050#if XRENDER
1411 Display *dpy = target->dpy; 1051 if (display->flags & DISPLAY_HAS_RENDER)
1412 XRenderPictureAttributes pa; 1052 {
1053 recoded_root_pmap = XCreatePixmap (dpy, vt, root_pmap_width, root_pmap_height, depth);
1413 1054
1414 XRenderPictFormat *src_format = XRenderFindVisualFormat (dpy, DefaultVisual (dpy, target->display->screen)); 1055 XRenderPictFormat *src_format = XRenderFindVisualFormat (dpy, DefaultVisual (dpy, screen));
1415 Picture src = XRenderCreatePicture (dpy, root_pixmap, src_format, 0, &pa); 1056 Picture src = XRenderCreatePicture (dpy, root_pixmap, src_format, 0, 0);
1416 1057
1417 if (recoded_root_pmap)
1418 XFreePixmap (dpy, recoded_root_pmap);
1419 recoded_root_pmap = XCreatePixmap (dpy, target->vt, width, height, target->depth);
1420 XRenderPictFormat *dst_format = XRenderFindVisualFormat (dpy, target->visual); 1058 XRenderPictFormat *dst_format = XRenderFindVisualFormat (dpy, visual);
1421 Picture dst = XRenderCreatePicture (dpy, recoded_root_pmap, dst_format, 0, &pa); 1059 Picture dst = XRenderCreatePicture (dpy, recoded_root_pmap, dst_format, 0, 0);
1422 1060
1423 if (src && dst)
1424 {
1425 XRenderComposite (dpy, PictOpSrc, src, None, dst, 0, 0, 0, 0, 0, 0, width, height); 1061 XRenderComposite (dpy, PictOpSrc, src, None, dst, 0, 0, 0, 0, 0, 0, root_pmap_width, root_pmap_height);
1426 root_pixmap = recoded_root_pmap; 1062
1063 XRenderFreePicture (dpy, src);
1064 XRenderFreePicture (dpy, dst);
1427 } 1065 }
1428 else 1066 else
1429 root_pixmap = None;
1430
1431 XRenderFreePicture (dpy, src);
1432 XRenderFreePicture (dpy, dst);
1433#else
1434 root_pixmap = None;
1435#endif 1067#endif
1068 recoded_root_pmap = None;
1069 }
1070
1071 if (recoded_root_pmap == None)
1072 return 0;
1073
1074 if (bg_pixmap == None
1075 || bg_pmap_width != window_width
1076 || bg_pmap_height != window_height)
1436 } 1077 {
1078 if (bg_pixmap)
1079 XFreePixmap (dpy, bg_pixmap);
1080 bg_pixmap = XCreatePixmap (dpy, vt, window_width, window_height, depth);
1081 bg_pmap_width = window_width;
1082 bg_pmap_height = window_height;
1083 }
1084
1085 /* straightforward pixmap copy */
1086 while (sx < 0) sx += root_pmap_width;
1087 while (sy < 0) sy += root_pmap_height;
1088
1089 gcv.tile = recoded_root_pmap;
1090 gcv.fill_style = FillTiled;
1091 gcv.ts_x_origin = -sx;
1092 gcv.ts_y_origin = -sy;
1093 gc = XCreateGC (dpy, vt, GCFillStyle | GCTile | GCTileStipXOrigin | GCTileStipYOrigin, &gcv);
1094
1095 if (gc)
1096 {
1097 XFillRectangle (dpy, bg_pixmap, gc, 0, 0, window_width, window_height);
1098 ret = true;
1099 bool need_blur = root_effects.need_blur ();
1100 bool need_tint = root_effects.need_tint ();
1101
1102 if (need_blur)
1103 {
1104 if (blur_pixmap (bg_pixmap, window_width, window_height, false,
1105 root_effects.h_blurRadius, root_effects.v_blurRadius))
1106 need_blur = false;
1107 }
1108 if (need_tint)
1109 {
1110 if (tint_pixmap (bg_pixmap, window_width, window_height, false,
1111 root_effects.tint, root_effects.tint_set, root_effects.shade))
1112 need_tint = false;
1113 }
1114 if (need_tint)
1115 {
1116 XImage *ximage = XGetImage (dpy, bg_pixmap, 0, 0, bg_pmap_width, bg_pmap_height, AllPlanes, ZPixmap);
1117 if (ximage)
1118 {
1119 /* our own client-side tinting */
1120 tint_ximage (ximage, root_effects.tint, root_effects.tint_set, root_effects.shade);
1121
1122 XPutImage (dpy, bg_pixmap, gc, ximage, 0, 0, 0, 0, ximage->width, ximage->height);
1123 XDestroyImage (ximage);
1124 }
1125 }
1126
1127 XFreeGC (dpy, gc);
1128 }
1129
1130 if (recoded_root_pmap != root_pixmap)
1131 XFreePixmap (dpy, recoded_root_pmap);
1132
1133 return ret;
1134}
1135
1136void
1137rxvt_term::bg_set_root_pixmap ()
1138{
1139 Pixmap new_root_pixmap = get_pixmap_property (xa[XA_XROOTPMAP_ID]);
1140 if (new_root_pixmap == None)
1141 new_root_pixmap = get_pixmap_property (xa[XA_ESETROOT_PMAP_ID]);
1142
1143 root_pixmap = new_root_pixmap;
1437} 1144}
1438# endif /* ENABLE_TRANSPARENCY */ 1145# endif /* ENABLE_TRANSPARENCY */
1439 1146
1440# ifndef HAVE_AFTERIMAGE 1147bool
1441static void ShadeXImage(Visual *visual, XImage *srcImage, int shade, int rm, int gm, int bm); 1148rxvt_term::bg_render ()
1149{
1150 bg_invalidate ();
1151# ifdef ENABLE_TRANSPARENCY
1152 if (bg_flags & BG_IS_TRANSPARENT)
1153 {
1154 /* we need to re-generate transparency pixmap in that case ! */
1155 if (render_root_image ())
1156 bg_flags |= BG_IS_VALID;
1157 }
1442# endif 1158# endif
1443 1159
1444bool 1160# ifdef BG_IMAGE_FROM_FILE
1445bgPixmap_t::render () 1161 for (vector<rxvt_image>::iterator bg_image = image_vec.begin (); bg_image < image_vec.end (); bg_image++)
1446{
1447 unsigned long background_flags = 0;
1448
1449 if (target == NULL)
1450 return false;
1451
1452 invalidate ();
1453# ifdef ENABLE_TRANSPARENCY
1454 if (flags & isTransparent)
1455 { 1162 {
1456 /* we need to re-generate transparency pixmap in that case ! */ 1163 if (render_image (*bg_image))
1457 background_flags = make_transparency_pixmap (); 1164 bg_flags |= BG_IS_VALID;
1458 if (background_flags == 0)
1459 return false;
1460 else if ((background_flags & transpTransformations) == (flags & transpTransformations))
1461 flags = flags & ~isInvalid;
1462 } 1165 }
1463# endif 1166# endif
1464 1167
1168 if (!(bg_flags & BG_IS_VALID))
1169 {
1170 if (bg_pixmap != None)
1171 {
1172 XFreePixmap (dpy, bg_pixmap);
1173 bg_pixmap = None;
1174 }
1175 }
1176
1177 scr_recolour (false);
1178 bg_flags |= BG_NEEDS_REFRESH;
1179
1180 bg_valid_since = ev::now ();
1181
1182 return true;
1183}
1184
1185void
1186rxvt_term::bg_init ()
1187{
1465# ifdef BG_IMAGE_FROM_FILE 1188#ifdef BG_IMAGE_FROM_FILE
1466 if (have_image 1189 if (rs[Rs_backgroundPixmap])
1467 || (background_flags & transpTransformations) != (flags & transpTransformations))
1468 {
1469 if (render_image (background_flags))
1470 flags = flags & ~isInvalid;
1471 } 1190 {
1191 rxvt_image *image = new_image ();
1192 if (!image->set_file_geometry (rs[Rs_backgroundPixmap]))
1193 image_vec.pop_back ();
1194 }
1195
1196# ifndef NO_RESOURCES
1197 find_resources ("image", "Image", XrmEnumAllLevels, rxvt_define_image);
1472# endif 1198# endif
1473 1199
1474 XImage *result = NULL; 1200 vector<rxvt_image>::iterator bg_image = image_vec.begin ();
1475 1201 while (bg_image != image_vec.end ())
1476 if (background_flags && (flags & isInvalid))
1477 {
1478 result = XGetImage (target->dpy, pixmap, 0, 0, pmap_width, pmap_height, AllPlanes, ZPixmap);
1479 } 1202 {
1480 1203 if (!(bg_image->flags & IM_IS_SET))
1481 if (result) 1204 bg_image = image_vec.erase (bg_image);
1482 { 1205 else
1483# if !defined(HAVE_AFTERIMAGE) && !XRENDER
1484 /* our own client-side tinting */
1485 if (!(background_flags & transpPmapTinted) && (flags & tintNeeded))
1486 {
1487 rgba c (rgba::MAX_CC,rgba::MAX_CC,rgba::MAX_CC);
1488 if (flags & tintSet)
1489 tint.get (c);
1490 ShadeXImage (DefaultVisual (target->dpy, target->display->screen), result, shade, c.r, c.g, c.b);
1491 } 1206 {
1492# endif 1207 if (bg_image->is_size_sensitive ())
1208 bg_image->flags |= IM_IS_SIZE_SENSITIVE;
1493 1209
1494 GC gc = XCreateGC (target->dpy, target->vt, 0UL, NULL); 1210 bg_image++;
1495
1496 if (gc)
1497 { 1211 }
1498 XPutImage (target->dpy, pixmap, gc, result, 0, 0, 0, 0, result->width, result->height);
1499
1500 XFreeGC (target->dpy, gc);
1501 flags = flags & ~isInvalid;
1502 }
1503
1504 XDestroyImage (result);
1505 }
1506
1507 if (flags & isInvalid)
1508 {
1509 if (pixmap != None)
1510 {
1511 XFreePixmap (target->dpy, pixmap);
1512 pixmap = None;
1513 }
1514 }
1515
1516 apply ();
1517
1518 valid_since = ev::now ();
1519
1520 return true;
1521}
1522
1523bool
1524bgPixmap_t::set_target (rxvt_term *new_target)
1525{
1526 if (new_target)
1527 if (target != new_target)
1528 {
1529 target = new_target;
1530 return true;
1531 } 1212 }
1532 1213
1533 return false; 1214 if (image_vec.size () > 0
1215 && !bg_window_position_sensitive ())
1216 update_background ();
1217#endif
1218}
1219
1220/* based on code from aterm-0.4.2 */
1221
1222static inline void
1223fill_lut (uint32_t *lookup, uint32_t mask, int sh, unsigned short low, unsigned short high)
1224{
1225 for (int i = 0; i <= mask >> sh; i++)
1226 {
1227 uint32_t tmp;
1228 tmp = i * high;
1229 tmp += (mask >> sh) * low;
1230 lookup[i] = (tmp / 0xffff) << sh;
1231 }
1534} 1232}
1535 1233
1536void 1234void
1537bgPixmap_t::apply () 1235rxvt_term::tint_ximage (XImage *ximage, rxvt_color &tint, bool tint_set, int shade)
1538{ 1236{
1539 if (target) 1237 unsigned int size_r, size_g, size_b;
1540 {
1541 if (pixmap != None)
1542 {
1543 /* set target's background to pixmap */
1544# ifdef ENABLE_TRANSPARENCY
1545 if (flags & isTransparent)
1546 {
1547 XSetWindowBackgroundPixmap (target->dpy, target->parent[0], pixmap);
1548 XSetWindowBackgroundPixmap (target->dpy, target->vt, ParentRelative);
1549
1550 if (target->scrollBar.win)
1551 XSetWindowBackgroundPixmap (target->dpy, target->scrollBar.win, ParentRelative);
1552 }
1553 else
1554# endif
1555 {
1556 /* force old pixmap dereference in case it was transparent before :*/
1557 XSetWindowBackground (target->dpy, target->parent[0], target->pix_colors[Color_border]);
1558 XSetWindowBackgroundPixmap (target->dpy, target->vt, pixmap);
1559 /* do we also need to set scrollbar's background here ? */
1560
1561 if (target->scrollBar.win)
1562 XSetWindowBackground (target->dpy, target->scrollBar.win, target->pix_colors[Color_border]);
1563 }
1564 }
1565 else
1566 {
1567 /* set target background to a pixel */
1568 XSetWindowBackground (target->dpy, target->parent[0], target->pix_colors[Color_border]);
1569 XSetWindowBackground (target->dpy, target->vt, target->pix_colors[Color_bg]);
1570 /* do we also need to set scrollbar's background here ? */
1571 if (target->scrollBar.win)
1572 XSetWindowBackground (target->dpy, target->scrollBar.win, target->pix_colors[Color_border]);
1573 }
1574
1575 /* don't want Expose on the parent or vt. It is better to use
1576 scr_touch or we get a great deal of flicker otherwise: */
1577 XClearWindow (target->dpy, target->parent[0]);
1578
1579 if (target->scrollBar.state && target->scrollBar.win)
1580 {
1581 target->scrollBar.state = STATE_IDLE;
1582 target->scrollBar.show (0);
1583 }
1584
1585 target->want_refresh = 1;
1586 flags |= hasChanged;
1587 }
1588}
1589
1590#endif /* HAVE_BG_PIXMAP */
1591
1592#if defined(ENABLE_TRANSPARENCY) && !defined(HAVE_AFTERIMAGE) && !XRENDER
1593/* taken from aterm-0.4.2 */
1594
1595static void
1596ShadeXImage(Visual *visual, XImage *srcImage, int shade, int rm, int gm, int bm)
1597{
1598 int sh_r, sh_g, sh_b; 1238 int sh_r, sh_g, sh_b;
1599 uint32_t mask_r, mask_g, mask_b; 1239 uint32_t mask_r, mask_g, mask_b;
1600 uint32_t *lookup, *lookup_r, *lookup_g, *lookup_b; 1240 uint32_t *lookup, *lookup_r, *lookup_g, *lookup_b;
1601 unsigned int lower_lim_r, lower_lim_g, lower_lim_b; 1241 unsigned short low;
1602 unsigned int upper_lim_r, upper_lim_g, upper_lim_b;
1603 int i;
1604 int host_byte_order = byteorder.big_endian () ? MSBFirst : LSBFirst; 1242 int host_byte_order = ecb_big_endian () ? MSBFirst : LSBFirst;
1605 1243
1606 if (visual->c_class != TrueColor || srcImage->format != ZPixmap) return; 1244 if (visual->c_class != TrueColor || ximage->format != ZPixmap) return;
1607 1245
1608 /* for convenience */ 1246 /* for convenience */
1609 mask_r = visual->red_mask; 1247 mask_r = visual->red_mask;
1610 mask_g = visual->green_mask; 1248 mask_g = visual->green_mask;
1611 mask_b = visual->blue_mask; 1249 mask_b = visual->blue_mask;
1612 1250
1613 /* boring lookup table pre-initialization */ 1251 /* boring lookup table pre-initialization */
1614 switch (srcImage->depth) 1252 sh_r = ecb_ctz32 (mask_r);
1615 { 1253 sh_g = ecb_ctz32 (mask_g);
1616 case 15: 1254 sh_b = ecb_ctz32 (mask_b);
1617 if ((mask_r != 0x7c00) || 1255
1618 (mask_g != 0x03e0) || 1256 size_r = mask_r >> sh_r;
1619 (mask_b != 0x001f)) 1257 size_g = mask_g >> sh_g;
1258 size_b = mask_b >> sh_b;
1259
1260 if (size_r++ > 255 || size_g++ > 255 || size_b++ > 255)
1620 return; 1261 return;
1621 lookup = (uint32_t *) malloc (sizeof (uint32_t)*(32+32+32)); 1262
1263 lookup = (uint32_t *)malloc (sizeof (uint32_t) * (size_r + size_g + size_b));
1622 lookup_r = lookup; 1264 lookup_r = lookup;
1623 lookup_g = lookup+32; 1265 lookup_g = lookup + size_r;
1624 lookup_b = lookup+32+32; 1266 lookup_b = lookup + size_r + size_g;
1625 sh_r = 10; 1267
1626 sh_g = 5; 1268 rgba c (rgba::MAX_CC, rgba::MAX_CC, rgba::MAX_CC);
1627 sh_b = 0; 1269
1628 break; 1270 if (tint_set)
1629 case 16: 1271 tint.get (c);
1630 if ((mask_r != 0xf800) ||
1631 (mask_g != 0x07e0) ||
1632 (mask_b != 0x001f))
1633 return;
1634 lookup = (uint32_t *) malloc (sizeof (uint32_t)*(32+64+32));
1635 lookup_r = lookup;
1636 lookup_g = lookup+32;
1637 lookup_b = lookup+32+64;
1638 sh_r = 11;
1639 sh_g = 5;
1640 sh_b = 0;
1641 break;
1642 case 24:
1643 if ((mask_r != 0xff0000) ||
1644 (mask_g != 0x00ff00) ||
1645 (mask_b != 0x0000ff))
1646 return;
1647 lookup = (uint32_t *) malloc (sizeof (uint32_t)*(256+256+256));
1648 lookup_r = lookup;
1649 lookup_g = lookup+256;
1650 lookup_b = lookup+256+256;
1651 sh_r = 16;
1652 sh_g = 8;
1653 sh_b = 0;
1654 break;
1655 case 32:
1656 if ((mask_r != 0xff0000) ||
1657 (mask_g != 0x00ff00) ||
1658 (mask_b != 0x0000ff))
1659 return;
1660 lookup = (uint32_t *) malloc (sizeof (uint32_t)*(256+256+256));
1661 lookup_r = lookup;
1662 lookup_g = lookup+256;
1663 lookup_b = lookup+256+256;
1664 sh_r = 16;
1665 sh_g = 8;
1666 sh_b = 0;
1667 break;
1668 default:
1669 return; /* we do not support this color depth */
1670 }
1671 1272
1672 /* prepare limits for color transformation (each channel is handled separately) */ 1273 /* prepare limits for color transformation (each channel is handled separately) */
1673 if (shade > 100) 1274 if (shade > 100)
1674 { 1275 {
1675 shade = 200 - shade; 1276 c.r = c.r * (200 - shade) / 100;
1277 c.g = c.g * (200 - shade) / 100;
1278 c.b = c.b * (200 - shade) / 100;
1676 1279
1677 lower_lim_r = 65535-rm; 1280 low = 0xffff * (shade - 100) / 100;
1678 lower_lim_g = 65535-gm;
1679 lower_lim_b = 65535-bm;
1680
1681 lower_lim_r = 65535-(unsigned int)(((uint32_t)lower_lim_r)*((uint32_t)shade)/100);
1682 lower_lim_g = 65535-(unsigned int)(((uint32_t)lower_lim_g)*((uint32_t)shade)/100);
1683 lower_lim_b = 65535-(unsigned int)(((uint32_t)lower_lim_b)*((uint32_t)shade)/100);
1684
1685 upper_lim_r = upper_lim_g = upper_lim_b = 65535;
1686 } 1281 }
1687 else 1282 else
1688 { 1283 {
1284 c.r = c.r * shade / 100;
1285 c.g = c.g * shade / 100;
1286 c.b = c.b * shade / 100;
1689 1287
1690 lower_lim_r = lower_lim_g = lower_lim_b = 0; 1288 low = 0;
1691
1692 upper_lim_r = (unsigned int)((((uint32_t)rm)*((uint32_t)shade))/100);
1693 upper_lim_g = (unsigned int)((((uint32_t)gm)*((uint32_t)shade))/100);
1694 upper_lim_b = (unsigned int)((((uint32_t)bm)*((uint32_t)shade))/100);
1695 } 1289 }
1696 1290
1697 /* fill our lookup tables */ 1291 /* fill our lookup tables */
1698 for (i = 0; i <= mask_r>>sh_r; i++) 1292 fill_lut (lookup_r, mask_r, sh_r, low, c.r);
1699 { 1293 fill_lut (lookup_g, mask_g, sh_g, low, c.g);
1700 uint32_t tmp; 1294 fill_lut (lookup_b, mask_b, sh_b, low, c.b);
1701 tmp = ((uint32_t)i)*((uint32_t)(upper_lim_r-lower_lim_r));
1702 tmp += ((uint32_t)(mask_r>>sh_r))*((uint32_t)lower_lim_r);
1703 lookup_r[i] = (tmp/65535)<<sh_r;
1704 }
1705 for (i = 0; i <= mask_g>>sh_g; i++)
1706 {
1707 uint32_t tmp;
1708 tmp = ((uint32_t)i)*((uint32_t)(upper_lim_g-lower_lim_g));
1709 tmp += ((uint32_t)(mask_g>>sh_g))*((uint32_t)lower_lim_g);
1710 lookup_g[i] = (tmp/65535)<<sh_g;
1711 }
1712 for (i = 0; i <= mask_b>>sh_b; i++)
1713 {
1714 uint32_t tmp;
1715 tmp = ((uint32_t)i)*((uint32_t)(upper_lim_b-lower_lim_b));
1716 tmp += ((uint32_t)(mask_b>>sh_b))*((uint32_t)lower_lim_b);
1717 lookup_b[i] = (tmp/65535)<<sh_b;
1718 }
1719 1295
1720 /* apply table to input image (replacing colors by newly calculated ones) */ 1296 /* apply table to input image (replacing colors by newly calculated ones) */
1721 if (srcImage->bits_per_pixel == 32 1297 if (ximage->bits_per_pixel == 32
1722 && (srcImage->depth == 24 || srcImage->depth == 32)
1723 && srcImage->byte_order == host_byte_order) 1298 && ximage->byte_order == host_byte_order)
1724 { 1299 {
1725 uint32_t *p1, *pf, *p, *pl; 1300 char *line = ximage->data;
1726 p1 = (uint32_t *) srcImage->data;
1727 pf = (uint32_t *) (srcImage->data + srcImage->height * srcImage->bytes_per_line);
1728 1301
1729 while (p1 < pf) 1302 for (int y = 0; y < ximage->height; y++)
1730 { 1303 {
1731 p = p1; 1304 uint32_t *p = (uint32_t *)line;
1732 pl = p1 + srcImage->width; 1305 for (int x = 0; x < ximage->width; x++)
1733 for (; p < pl; p++)
1734 { 1306 {
1735 *p = lookup_r[(*p & 0xff0000) >> 16] | 1307 *p = lookup_r[(*p & mask_r) >> sh_r] |
1736 lookup_g[(*p & 0x00ff00) >> 8] | 1308 lookup_g[(*p & mask_g) >> sh_g] |
1737 lookup_b[(*p & 0x0000ff)] | 1309 lookup_b[(*p & mask_b) >> sh_b];
1738 (*p & 0xff000000); 1310 p++;
1739 } 1311 }
1740 p1 = (uint32_t *) ((char *) p1 + srcImage->bytes_per_line); 1312 line += ximage->bytes_per_line;
1741 } 1313 }
1742 } 1314 }
1743 else 1315 else
1744 { 1316 {
1745 for (int y = 0; y < srcImage->height; y++) 1317 for (int y = 0; y < ximage->height; y++)
1746 for (int x = 0; x < srcImage->width; x++) 1318 for (int x = 0; x < ximage->width; x++)
1747 { 1319 {
1748 unsigned long pixel = XGetPixel (srcImage, x, y); 1320 unsigned long pixel = XGetPixel (ximage, x, y);
1749 pixel = lookup_r[(pixel & mask_r) >> sh_r] | 1321 pixel = lookup_r[(pixel & mask_r) >> sh_r] |
1750 lookup_g[(pixel & mask_g) >> sh_g] | 1322 lookup_g[(pixel & mask_g) >> sh_g] |
1751 lookup_b[(pixel & mask_b) >> sh_b]; 1323 lookup_b[(pixel & mask_b) >> sh_b];
1752 XPutPixel (srcImage, x, y, pixel); 1324 XPutPixel (ximage, x, y, pixel);
1753 } 1325 }
1754 } 1326 }
1755 1327
1756 free (lookup); 1328 free (lookup);
1757} 1329}
1758#endif /* defined(ENABLE_TRANSPARENCY) && !defined(HAVE_AFTERIMAGE) */ 1330
1331#endif /* HAVE_BG_PIXMAP */

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines