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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines