1 /* grab-ximage.c --- grab the screen to an XImage for use with OpenGL.
2 * xscreensaver, Copyright (c) 2001-2008 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
28 # include <OpenGL/glu.h>
31 # include <X11/Xlib.h>
32 # include <X11/Xutil.h>
33 # include <GL/gl.h> /* only for GLfloat */
34 # include <GL/glu.h> /* for gluBuild2DMipmaps */
35 # include <GL/glx.h> /* for glXMakeCurrent() */
40 #endif /* HAVE_JWZGLES */
42 #include "grab-ximage.h"
43 #include "grabscreen.h"
46 /* If REFORMAT_IMAGE_DATA is defined, then we convert Pixmaps to textures
49 - get Pixmap as an XImage in whatever form the server hands us;
50 - convert that XImage to 32-bit RGBA in client-local endianness;
51 - make the texture using RGBA, UNSIGNED_BYTE.
53 If undefined, we do this:
55 - get Pixmap as an XImage in whatever form the server hands us;
56 - figure out what OpenGL texture packing parameters correspond to
57 the image data that the server sent us and use that, e.g.,
58 BGRA, INT_8_8_8_8_REV.
60 You might expect the second method to be faster, since we're not making
61 a second copy of the data and iterating each pixel before we hand it
62 to GL. But, you'd be wrong. The first method is almost 6x faster.
63 I guess GL is reformatting it *again*, and doing it very inefficiently!
65 #define REFORMAT_IMAGE_DATA
68 #ifdef HAVE_XSHM_EXTENSION
69 # include "resources.h"
71 #endif /* HAVE_XSHM_EXTENSION */
73 extern char *progname;
80 # include <X11/Xutil.h>
84 #define MAX(a,b) ((a)>(b)?(a):(b))
87 #define countof(x) (sizeof((x))/sizeof((*x)))
90 static int debug_p = 0;
95 union { int i; char c[sizeof(int)]; } u;
101 #ifdef REFORMAT_IMAGE_DATA
103 /* Given a bitmask, returns the position and width of the field.
106 decode_mask (unsigned long mask, unsigned long *pos_ret,
107 unsigned long *size_ret)
110 for (i = 0; i < 32; i++)
111 if (mask & (1L << i))
115 for (; i < 32; i++, j++)
116 if (! (mask & (1L << i)))
124 /* Given a value and a field-width, expands the field to fill out 8 bits.
127 spread_bits (unsigned char value, unsigned char width)
131 case 8: return value;
132 case 7: return (value << 1) | (value >> 6);
133 case 6: return (value << 2) | (value >> 4);
134 case 5: return (value << 3) | (value >> 2);
135 case 4: return (value << 4) | (value);
136 case 3: return (value << 5) | (value << 2) | (value >> 2);
137 case 2: return (value << 6) | (value << 4) | (value);
138 default: abort(); break;
144 convert_ximage_to_rgba32 (Screen *screen, XImage *image)
146 Display *dpy = DisplayOfScreen (screen);
147 Visual *visual = DefaultVisualOfScreen (screen);
150 unsigned long crpos=0, cgpos=0, cbpos=0, capos=0; /* bitfield positions */
151 unsigned long srpos=0, sgpos=0, sbpos=0;
152 unsigned long srmsk=0, sgmsk=0, sbmsk=0;
153 unsigned long srsiz=0, sgsiz=0, sbsiz=0;
155 unsigned char spread_map[3][256];
157 /* Note: height+2 in "to" to work around an array bounds overrun
158 in gluBuild2DMipmaps / gluScaleImage.
160 XImage *from = image;
161 XImage *to = XCreateImage (dpy, visual, 32, /* depth */
162 ZPixmap, 0, 0, from->width, from->height + 2,
165 to->data = (char *) calloc (to->height, to->bytes_per_line);
167 /* Set the bit order in the XImage structure to whatever the
168 local host's native bit order is.
170 to->bitmap_bit_order =
172 (bigendian() ? MSBFirst : LSBFirst);
174 if (visual_class (screen, visual) == PseudoColor ||
175 visual_class (screen, visual) == GrayScale)
177 Colormap cmap = DefaultColormapOfScreen (screen);
178 int ncolors = visual_cells (screen, visual);
180 colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
181 for (i = 0; i < ncolors; i++)
183 XQueryColors (dpy, cmap, colors, ncolors);
186 if (colors == 0) /* truecolor */
188 srmsk = to->red_mask;
189 sgmsk = to->green_mask;
190 sbmsk = to->blue_mask;
192 decode_mask (srmsk, &srpos, &srsiz);
193 decode_mask (sgmsk, &sgpos, &sgsiz);
194 decode_mask (sbmsk, &sbpos, &sbsiz);
197 /* Pack things in "RGBA" order in client endianness. */
199 crpos = 24, cgpos = 16, cbpos = 8, capos = 0;
201 crpos = 0, cgpos = 8, cbpos = 16, capos = 24;
203 if (colors == 0) /* truecolor */
206 for (i = 0; i < 256; i++)
208 spread_map[0][i] = spread_bits (i, srsiz);
209 spread_map[1][i] = spread_bits (i, sgsiz);
210 spread_map[2][i] = spread_bits (i, sbsiz);
214 /* trying to track down an intermittent crash in ximage_putpixel_32 */
215 if (to->width < from->width) abort();
216 if (to->height < from->height) abort();
218 for (y = 0; y < from->height; y++)
219 for (x = 0; x < from->width; x++)
221 unsigned long sp = XGetPixel (from, x, y);
222 unsigned char sr, sg, sb;
227 sr = colors[sp].red & 0xFF;
228 sg = colors[sp].green & 0xFF;
229 sb = colors[sp].blue & 0xFF;
233 sr = (sp & srmsk) >> srpos;
234 sg = (sp & sgmsk) >> sgpos;
235 sb = (sp & sbmsk) >> sbpos;
237 sr = spread_map[0][sr];
238 sg = spread_map[1][sg];
239 sb = spread_map[2][sb];
242 cp = ((sr << crpos) |
247 XPutPixel (to, x, y, cp);
250 if (colors) free (colors);
255 #endif /* REFORMAT_IMAGE_DATA */
257 /* Shrinks the XImage by a factor of two.
258 We use this when mipmapping fails on large textures.
261 halve_image (XImage *ximage, XRectangle *geom)
263 int w2 = ximage->width/2;
264 int h2 = ximage->height/2;
268 if (w2 <= 32 || h2 <= 32) /* let's not go crazy here, man. */
272 fprintf (stderr, "%s: shrinking image %dx%d -> %dx%d\n",
273 progname, ximage->width, ximage->height, w2, h2);
275 ximage2 = (XImage *) calloc (1, sizeof (*ximage2));
278 ximage2->height = h2;
279 ximage2->bytes_per_line = 0;
281 XInitImage (ximage2);
283 ximage2->data = (char *) calloc (h2, ximage2->bytes_per_line);
286 fprintf (stderr, "%s: out of memory (scaling %dx%d image to %dx%d)\n",
287 progname, ximage->width, ximage->height, w2, h2);
291 for (y = 0; y < h2; y++)
292 for (x = 0; x < w2; x++)
293 XPutPixel (ximage2, x, y, XGetPixel (ximage, x*2, y*2));
310 #ifdef REFORMAT_IMAGE_DATA
312 /* Pulls the Pixmap bits from the server and returns an XImage
313 in some format acceptable to OpenGL.
316 pixmap_to_gl_ximage (Screen *screen, Window window, Pixmap pixmap)
318 Display *dpy = DisplayOfScreen (screen);
319 unsigned int width, height, depth;
321 # ifdef HAVE_XSHM_EXTENSION
322 Bool use_shm = get_boolean_resource (dpy, "useSHM", "Boolean");
323 XShmSegmentInfo shm_info;
324 # endif /* HAVE_XSHM_EXTENSION */
326 XImage *server_ximage = 0;
327 XImage *client_ximage = 0;
333 XGetGeometry (dpy, pixmap, &root, &x, &y, &width, &height, &bw, &depth);
336 if (width < 5 || height < 5) /* something's gone wrong somewhere... */
339 /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
341 # ifdef HAVE_XSHM_EXTENSION
344 Visual *visual = DefaultVisualOfScreen (screen);
345 server_ximage = create_xshm_image (dpy, visual, depth,
346 ZPixmap, 0, &shm_info,
349 XShmGetImage (dpy, pixmap, server_ximage, 0, 0, ~0L);
353 # endif /* HAVE_XSHM_EXTENSION */
356 server_ximage = XGetImage (dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
358 client_ximage = convert_ximage_to_rgba32 (screen, server_ximage);
360 # ifdef HAVE_XSHM_EXTENSION
362 destroy_xshm_image (dpy, server_ximage, &shm_info);
364 # endif /* HAVE_XSHM_EXTENSION */
365 XDestroyImage (server_ximage);
367 return client_ximage;
371 # else /* ! REFORMAT_IMAGE_DATA */
374 unsigned int depth, red_mask, green_mask, blue_mask; /* when this... */
375 GLint type, format; /* ...use this. */
378 /* Abbreviate these so that the table entries all fit on one line...
380 #define BYTE GL_UNSIGNED_BYTE
381 #define BYTE_2_3_3_REV GL_UNSIGNED_BYTE_2_3_3_REV
382 #define BYTE_3_3_2 GL_UNSIGNED_BYTE_3_3_2
383 #define INT_10_10_10_2 GL_UNSIGNED_INT_10_10_10_2
384 #define INT_2_10_10_10_REV GL_UNSIGNED_INT_2_10_10_10_REV
385 #define INT_8_8_8_8 GL_UNSIGNED_INT_8_8_8_8
386 #define INT_8_8_8_8_REV GL_UNSIGNED_INT_8_8_8_8_REV
387 #define SHORT_1_5_5_5_REV GL_UNSIGNED_SHORT_1_5_5_5_REV
388 #define SHORT_4_4_4_4 GL_UNSIGNED_SHORT_4_4_4_4
389 #define SHORT_4_4_4_4_REV GL_UNSIGNED_SHORT_4_4_4_4_REV
390 #define SHORT_5_5_5_1 GL_UNSIGNED_SHORT_5_5_5_1
391 #define SHORT_5_6_5 GL_UNSIGNED_SHORT_5_6_5
392 #define SHORT_5_6_5_REV GL_UNSIGNED_SHORT_5_6_5_REV
394 static const conversion_table ctable[] = {
395 { 8, 0x000000E0, 0x0000001C, 0x00000003, BYTE_3_3_2, GL_RGB },
396 { 8, 0x00000007, 0x00000038, 0x000000C0, BYTE_2_3_3_REV, GL_RGB },
397 { 16, 0x0000F800, 0x000007E0, 0x0000001F, SHORT_5_6_5, GL_RGB },
398 { 16, 0x0000001F, 0x000007E0, 0x0000F800, SHORT_5_6_5_REV, GL_RGB },
399 { 16, 0x0000F000, 0x00000F00, 0x000000F0, SHORT_4_4_4_4, GL_RGBA },
400 { 16, 0x000000F0, 0x00000F00, 0x0000F000, SHORT_4_4_4_4, GL_BGRA },
401 { 16, 0x0000000F, 0x000000F0, 0x00000F00, SHORT_4_4_4_4, GL_ABGR_EXT },
402 { 16, 0x0000000F, 0x000000F0, 0x00000F00, SHORT_4_4_4_4_REV, GL_RGBA },
403 { 16, 0x00000F00, 0x000000F0, 0x0000000F, SHORT_4_4_4_4_REV, GL_BGRA },
404 { 16, 0x0000F800, 0x000007C0, 0x0000003E, SHORT_5_5_5_1, GL_RGBA },
405 { 16, 0x0000003E, 0x000007C0, 0x0000F800, SHORT_5_5_5_1, GL_BGRA },
406 { 16, 0x00000001, 0x0000003E, 0x000007C0, SHORT_5_5_5_1, GL_ABGR_EXT },
407 { 16, 0x0000001F, 0x000003E0, 0x00007C00, SHORT_1_5_5_5_REV, GL_RGBA },
408 { 16, 0x00007C00, 0x000003E0, 0x0000001F, SHORT_1_5_5_5_REV, GL_BGRA },
409 { 32, 0xFF000000, 0x00FF0000, 0x0000FF00, INT_8_8_8_8, GL_RGBA },
410 { 32, 0x0000FF00, 0x00FF0000, 0xFF000000, INT_8_8_8_8, GL_BGRA },
411 { 32, 0x000000FF, 0x0000FF00, 0x00FF0000, INT_8_8_8_8, GL_ABGR_EXT },
412 { 32, 0x000000FF, 0x0000FF00, 0x00FF0000, INT_8_8_8_8_REV, GL_RGBA },
413 { 32, 0x00FF0000, 0x0000FF00, 0x000000FF, INT_8_8_8_8_REV, GL_BGRA },
414 { 32, 0xFFC00000, 0x003FF000, 0x00000FFC, INT_10_10_10_2, GL_RGBA },
415 { 32, 0x00000FFC, 0x003FF000, 0xFFC00000, INT_10_10_10_2, GL_BGRA },
416 { 32, 0x00000003, 0x00000FFC, 0x003FF000, INT_10_10_10_2, GL_ABGR_EXT },
417 { 32, 0x000003FF, 0x000FFC00, 0x3FF00000, INT_2_10_10_10_REV, GL_RGBA },
418 { 32, 0x3FF00000, 0x000FFC00, 0x000003FF, INT_2_10_10_10_REV, GL_BGRA },
419 { 24, 0x000000FF, 0x0000FF00, 0x00FF0000, BYTE, GL_RGB },
420 { 24, 0x00FF0000, 0x0000FF00, 0x000000FF, BYTE, GL_BGR },
424 /* Given an XImage, returns the GL settings to use its data as a texture.
427 gl_settings_for_ximage (XImage *image,
428 GLint *type_ret, GLint *format_ret, GLint *swap_ret)
431 for (i = 0; i < countof(ctable); ++i)
433 if (image->bits_per_pixel == ctable[i].depth &&
434 image->red_mask == ctable[i].red_mask &&
435 image->green_mask == ctable[i].green_mask &&
436 image->blue_mask == ctable[i].blue_mask)
438 *type_ret = ctable[i].type;
439 *format_ret = ctable[i].format;
441 if (image->bits_per_pixel == 24)
443 /* don't know how to test this... */
444 *type_ret = (ctable[i].type == GL_RGB) ? GL_BGR : GL_RGB;
449 *swap_ret = !!(image->byte_order == MSBFirst) ^ !!bigendian();
454 fprintf (stderr, "%s: using %s %s %d for %d %08lX %08lX %08lX\n",
456 (*format_ret == GL_RGB ? "RGB" :
457 *format_ret == GL_BGR ? "BGR" :
458 *format_ret == GL_RGBA ? "RGBA" :
459 *format_ret == GL_BGRA ? "BGRA" :
460 *format_ret == GL_ABGR_EXT ? "ABGR_EXT" :
462 (*type_ret == BYTE ? "BYTE" :
463 *type_ret == BYTE_3_3_2 ? "BYTE_3_3_2" :
464 *type_ret == BYTE_2_3_3_REV ? "BYTE_2_3_3_REV" :
465 *type_ret == INT_8_8_8_8 ? "INT_8_8_8_8" :
466 *type_ret == INT_8_8_8_8_REV ? "INT_8_8_8_8_REV" :
467 *type_ret == INT_10_10_10_2 ? "INT_10_10_10_2" :
468 *type_ret == INT_2_10_10_10_REV ? "INT_2_10_10_10_REV":
469 *type_ret == SHORT_4_4_4_4 ? "SHORT_4_4_4_4" :
470 *type_ret == SHORT_4_4_4_4_REV ? "SHORT_4_4_4_4_REV" :
471 *type_ret == SHORT_5_5_5_1 ? "SHORT_5_5_5_1" :
472 *type_ret == SHORT_1_5_5_5_REV ? "SHORT_1_5_5_5_REV" :
473 *type_ret == SHORT_5_6_5 ? "SHORT_5_6_5" :
474 *type_ret == SHORT_5_6_5_REV ? "SHORT_5_6_5_REV" :
477 image->bits_per_pixel,
478 image->red_mask, image->green_mask, image->blue_mask);
485 /* Unknown RGB fields? */
489 #endif /* ! REFORMAT_IMAGE_DATA */
492 GLXContext glx_context;
494 int pix_width, pix_height, pix_depth;
499 /* Used in async mode
501 void (*callback) (const char *filename, XRectangle *geometry,
502 int iw, int ih, int tw, int th,
508 char **filename_return;
509 XRectangle *geometry_return;
510 int *image_width_return;
511 int *image_height_return;
512 int *texture_width_return;
513 int *texture_height_return;
518 /* Returns the current time in seconds as a double.
524 # ifdef GETTIMEOFDAY_TWO_ARGS
526 gettimeofday(&now, &tzp);
531 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
535 /* return the next larger power of 2. */
540 while (i < value) i <<= 1;
545 /* Loads the given XImage into GL's texture memory.
546 The image may be of any size.
547 If mipmap_p is true, then make mipmaps instead of just a single texture.
548 Writes to stderr and returns False on error.
551 ximage_to_texture (XImage *ximage,
552 GLint type, GLint format,
555 XRectangle *geometry,
558 int max_reduction = 7;
561 int orig_width = ximage->width;
562 int orig_height = ximage->height;
570 /* gluBuild2DMipmaps doesn't require textures to be a power of 2. */
571 tex_width = ximage->width;
572 tex_height = ximage->height;
575 fprintf (stderr, "%s: mipmap %d x %d\n",
576 progname, ximage->width, ximage->height);
578 gluBuild2DMipmaps (GL_TEXTURE_2D, 3, ximage->width, ximage->height,
579 format, type, ximage->data);
584 /* glTexImage2D() requires the texture sizes to be powers of 2.
585 So first, create a texture of that size (but don't write any
588 tex_width = to_pow2 (ximage->width);
589 tex_height = to_pow2 (ximage->height);
592 fprintf (stderr, "%s: texture %d x %d (%d x %d)\n",
593 progname, ximage->width, ximage->height,
594 tex_width, tex_height);
596 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0,
600 /* Now load our non-power-of-2 image data into the existing texture. */
603 glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0,
604 ximage->width, ximage->height,
605 GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
613 const char *s = (char *) gluErrorString (err);
617 sprintf (buf, "unknown error %d", (int) err);
621 while (glGetError() != GL_NO_ERROR)
622 ; /* clear any lingering errors */
624 if (++err_count > max_reduction)
628 "%s: %dx%d texture failed, even after reducing to %dx%d:\n"
629 "%s: The error was: \"%s\".\n"
630 "%s: probably this means "
631 "\"your video card is worthless and weak\"?\n\n",
632 progname, orig_width, orig_height,
633 ximage->width, ximage->height,
641 fprintf (stderr, "%s: mipmap error (%dx%d): %s\n",
642 progname, ximage->width, ximage->height, s);
643 halve_image (ximage, geometry);
648 if (width_return) *width_return = tex_width;
649 if (height_return) *height_return = tex_height;
654 static void load_texture_async_cb (Screen *screen,
655 Window window, Drawable drawable,
656 const char *name, XRectangle *geometry,
660 /* Grabs an image of the desktop (or another random image file) and
661 loads the image into GL's texture memory.
662 When the callback is called, the image data will have been loaded
663 into texture number `texid' (via glBindTexture.)
665 If an error occurred, width/height will be 0.
668 load_texture_async (Screen *screen, Window window,
669 GLXContext glx_context,
670 int desired_width, int desired_height,
673 void (*callback) (const char *filename,
674 XRectangle *geometry,
682 Display *dpy = DisplayOfScreen (screen);
683 XWindowAttributes xgwa;
684 img_closure *data = (img_closure *) calloc (1, sizeof(*data));
687 data->load_time = double_time();
690 data->mipmap_p = mipmap_p;
691 data->glx_context = glx_context;
692 data->callback = callback;
693 data->closure = closure;
695 XGetWindowAttributes (dpy, window, &xgwa);
696 data->pix_width = xgwa.width;
697 data->pix_height = xgwa.height;
698 data->pix_depth = xgwa.depth;
700 if (desired_width && desired_width < xgwa.width)
701 data->pix_width = desired_width;
702 if (desired_height && desired_height < xgwa.height)
703 data->pix_height = desired_height;
705 data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
707 load_image_async (screen, window, data->pixmap,
708 load_texture_async_cb, data);
712 /* Once we have an XImage, this loads it into GL.
713 This is used in both synchronous and asynchronous mode.
716 load_texture_async_cb (Screen *screen, Window window, Drawable drawable,
717 const char *name, XRectangle *geometry, void *closure)
719 Display *dpy = DisplayOfScreen (screen);
723 int iw=0, ih=0, tw=0, th=0;
724 double cvt_time=0, tex_time=0, done_time=0;
725 img_closure *data = (img_closure *) closure;
726 /* copy closure data to stack and free the original before running cb */
727 img_closure dd = *data;
728 memset (data, 0, sizeof (*data));
733 glXMakeCurrent (dpy, window, dd.glx_context);
735 if (geometry->width <= 0 || geometry->height <= 0)
737 /* This can happen if an old version of xscreensaver-getimage
741 geometry->width = dd.pix_width;
742 geometry->height = dd.pix_height;
745 if (geometry->width <= 0 || geometry->height <= 0)
749 cvt_time = double_time();
751 # ifdef REFORMAT_IMAGE_DATA
752 ximage = pixmap_to_gl_ximage (screen, window, dd.pixmap);
754 type = GL_UNSIGNED_BYTE;
756 #else /* ! REFORMAT_IMAGE_DATA */
758 Visual *visual = DefaultVisualOfScreen (screen);
761 ximage = XCreateImage (dpy, visual, dd.pix_depth, ZPixmap, 0, 0,
762 dd.pix_width, dd.pix_height, 32, 0);
764 /* Note: height+2 in "to" to be to work around an array bounds overrun
765 in gluBuild2DMipmaps / gluScaleImage. */
766 ximage->data = (char *) calloc (ximage->height+2, ximage->bytes_per_line);
769 !XGetSubImage (dpy, dd.pixmap, 0, 0, ximage->width, ximage->height,
770 ~0L, ximage->format, ximage, 0, 0))
772 XDestroyImage (ximage);
776 gl_settings_for_ximage (ximage, &type, &format, &swap);
777 glPixelStorei (GL_UNPACK_SWAP_BYTES, !swap);
779 #endif /* REFORMAT_IMAGE_DATA */
781 XFreePixmap (dpy, dd.pixmap);
785 tex_time = double_time();
794 glBindTexture (GL_TEXTURE_2D, dd.texid);
796 glPixelStorei (GL_UNPACK_ALIGNMENT, ximage->bitmap_pad / 8);
797 ok = ximage_to_texture (ximage, type, format, &tw, &th, geometry,
801 iw = ximage->width; /* in case the image was shrunk */
806 if (ximage) XDestroyImage (ximage);
809 iw = ih = tw = th = 0;
812 done_time = double_time();
816 /* prints: A + B + C = D
817 A = file I/O time (happens in background)
818 B = time to pull bits from server (this process)
819 C = time to convert bits to GL textures (this process)
820 D = total elapsed time from "want image" to "see image"
822 B+C is responsible for any frame-rate glitches.
824 "%s: loading elapsed: %.2f + %.2f + %.2f = %.2f sec\n",
826 cvt_time - dd.load_time,
828 done_time - tex_time,
829 done_time - dd.load_time);
832 /* asynchronous mode */
833 dd.callback (name, geometry, iw, ih, tw, th, dd.closure);
836 /* synchronous mode */
837 if (dd.filename_return) *dd.filename_return = (char *) name;
838 if (dd.geometry_return) *dd.geometry_return = *geometry;
839 if (dd.image_width_return) *dd.image_width_return = iw;
840 if (dd.image_height_return) *dd.image_height_return = ih;
841 if (dd.texture_width_return) *dd.texture_width_return = tw;
842 if (dd.texture_height_return) *dd.texture_height_return = th;