X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Finterference.c;h=329e21d54ad9df1b574bbfdc495e389a4335f194;hp=db1513189dcaf45cb195d73935f3ed391cdcd394;hb=8afc01a67be4fbf3f1cc0fce9adf01b5289a21c6;hpb=3f1091236d800c43a3124c44c7da54e53f205b13 diff --git a/hacks/interference.c b/hacks/interference.c index db151318..329e21d5 100644 --- a/hacks/interference.c +++ b/hacks/interference.c @@ -33,6 +33,9 @@ * Made animation speed independent of FPS. * Added cleanup code, fixed a few glitches. * Added gratuitous #ifdefs. + * Last modified: Fri Feb 21 02:14:29 2014, + * Added support for SMP rendering. + * Tweaked math a bit re: performance. */ #include @@ -40,6 +43,8 @@ #include "screenhack.h" +#include "thread_util.h" + #ifdef HAVE_STDINT_H # include #else @@ -59,7 +64,7 @@ Does double-buffering make sense? (gridsize = 2) USE_XIMAGE is off: Yes (-db: 4.1 FPS, -no-db: 2.9 FPS) XPutImage in strips: No (-db: 35.9 FPS, -no-db: 38.7 FPS) XPutImage, whole image: No (-db: 32.3 FPS, -no-db: 33.7 FPS) -MIT-SHM, whole image: Doesn't work anyway: (37.3 FPS) +MIT-SHM, whole image: Doesn't work anyway: (-no-db: 37.3 FPS) If gridsize = 1, XPutImage is slow when the XImage is one line at a time. XPutImage in strips: -db: 21.2 FPS, -no-db: 19.7 FPS @@ -78,7 +83,7 @@ quite a bit worse when gridsize = 1. * gridsize = 1. (And SHM is turned off.) */ #define USE_BIG_XIMAGE -/* Numbers are wave_table size, measured in unsigned integers. +/* Numbers are wave_table size, measured in # of unsigned integers. * FPS/radius = 50/radius = 800/radius = 1500/Big-O memory usage * * Use at most one of the following: @@ -125,6 +130,7 @@ static const char *interference_defaults [] = { #ifdef USE_IPHONE "*ignoreRotation: True", #endif + THREAD_DEFAULTS 0 }; @@ -145,6 +151,7 @@ static XrmOptionDescRec interference_options [] = { { "-shm", ".useSHM", XrmoptionNoArg, "True" }, { "-no-shm", ".useSHM", XrmoptionNoArg, "False" }, #endif /* HAVE_XSHM_EXTENSION */ + THREAD_OPTIONS { 0, 0, 0, 0 } }; @@ -195,15 +202,15 @@ struct inter_context { int h; Colormap cmap; Screen *screen; + unsigned bits_per_pixel; XColor* pal; #ifndef USE_XIMAGE GC* gcs; #endif int radius; /* Not always the same as the X resource. */ double last_frame; -#ifdef USE_XIMAGE - uint32_t* row; -#endif + + struct threadpool threadpool; /* * lookup tables @@ -216,6 +223,18 @@ struct inter_context { struct inter_source* source; }; +struct inter_thread +{ + const struct inter_context *context; + unsigned thread_id; + +#ifdef USE_XIMAGE + uint32_t* row; +#endif + + unsigned* result_row; +}; + #ifdef HAVE_DOUBLE_BUFFER_EXTENSION # define TARGET(c) ((c)->back_buf ? (c)->back_buf : \ (c)->pix_buf ? (c)->pix_buf : (c)->win) @@ -319,7 +338,6 @@ static double fast_inv_table(unsigned x) #endif -/* Also destroys c->row. */ static void destroy_image(Display* dpy, struct inter_context* c) { #ifdef USE_XIMAGE @@ -330,13 +348,19 @@ static void destroy_image(Display* dpy, struct inter_context* c) } else # endif { - /* Also frees c->ximage->data, which isn't allocated by XCreateImage. */ + /* Don't let XDestroyImage free c->ximage->data. */ + thread_free(c->ximage->data); + c->ximage->data = NULL; XDestroyImage(c->ximage); } } - - free(c->row); #endif + + if(c->threadpool.count) + { + threadpool_destroy(&c->threadpool); + c->threadpool.count = 0; + } } static void inter_free(Display* dpy, struct inter_context* c) @@ -368,12 +392,17 @@ static void inter_free(Display* dpy, struct inter_context* c) free(c->source); } -static void abort_no_mem(void) +static void abort_on_error(int error) { - fprintf(stderr, "interference: %s\n", strerror(ENOMEM)); + fprintf(stderr, "interference: %s\n", strerror(error)); exit(1); } +static void abort_no_mem(void) +{ + abort_on_error(ENOMEM); +} + static void check_no_mem(Display* dpy, struct inter_context* c, void* ptr) { if(!ptr) { @@ -382,19 +411,234 @@ static void check_no_mem(Display* dpy, struct inter_context* c, void* ptr) } } -/* On allocation error, c->row == NULL. */ +static int inter_thread_create( + void* self_raw, + struct threadpool* pool, + unsigned id) +{ + struct inter_thread* self = (struct inter_thread*)self_raw; + const struct inter_context* c = GET_PARENT_OBJ(struct inter_context, threadpool, pool); + + self->context = c; + self->thread_id = id; + + self->result_row = malloc((c->w / c->grid_size) * sizeof(unsigned)); + if(!self->result_row) + return ENOMEM; + +#ifdef USE_XIMAGE + self->row = malloc((c->w / c->grid_size) * sizeof(uint32_t)); + if(!self->row) { + free(self->result_row); + return ENOMEM; + } +#endif + + return 0; +} + +static void inter_thread_destroy(void* self_raw) +{ + struct inter_thread* self = (struct inter_thread*)self_raw; +#ifdef USE_XIMAGE + free(self->row); +#endif + free(self->result_row); +} + +/* +A higher performance design would have input and output queues, so that when +worker threads finish with one frame, they can pull the next work order from +the queue and get started on it immediately, rather than going straight to +sleep. The current "single-buffered" design still provides reasonable +performance at low frame rates; high frame rates are noticeably less efficient. +*/ + +static void inter_thread_run(void* self_raw) +{ + struct inter_thread* self = (struct inter_thread*)self_raw; + const struct inter_context* c = self->context; + + int i, j, k; + unsigned result; + int dist1; + int g = c->grid_size; + unsigned w_div_g = c->w/g; + + int dx, dy, g2 = 2 * g * g; + int px, py, px2g; + + int dist0, ddist; + +#ifdef USE_XIMAGE + unsigned img_y = g * self->thread_id; + void *scanline = c->ximage->data + c->ximage->bytes_per_line * g * self->thread_id; +#endif + + for(j = self->thread_id; j < c->h/g; j += c->threadpool.count) { + px = g/2; + py = j*g + px; + + memset(self->result_row, 0, w_div_g * sizeof(unsigned)); + + for(k = 0; k < c->count; k++) { + + dx = px - c->source[k].x; + dy = py - c->source[k].y; + + dist0 = dx*dx + dy*dy; + ddist = -2 * g * c->source[k].x; + + /* px2g = g*(px*2 + g); */ + px2g = g2; + + for(i = 0; i < w_div_g; i++) { + /* + * Discarded possibilities for improving performance here: + * 1. Using octagon-based distance estimation + * (Which causes giant octagons to appear.) + * 2. Square root approximation by reinterpret-casting IEEE floats to + * integers. + * (Which causes angles to appear when two waves interfere.) + */ + +/* int_float u; + u.f = dx*dx + dy*dy; + u.i = (1 << 29) + (u.i >> 1) - (1 << 22); + dist = u.f; */ + +#if defined USE_FAST_SQRT_BIGTABLE2 + dist1 = FAST_TABLE(dist0); +#elif defined USE_FAST_SQRT_HACKISH + dist1 = fast_log2(dist0); +#else + dist1 = sqrt(dist0); +#endif + + if(dist1 < c->radius) + self->result_row[i] += c->wave_height[dist1]; + + dist0 += px2g + ddist; + px2g += g2; + } + } + + for(i = 0; i < w_div_g; i++) { + + result = self->result_row[i]; + + /* It's slightly faster to do a subtraction or two before calculating the + * modulus. - D.O. */ + if(result >= c->colors) + { + result -= c->colors; + if(result >= c->colors) + result %= (unsigned)c->colors; + } + +#ifdef USE_XIMAGE + self->row[i] = c->pal[result].pixel; +#else + XFillRectangle(c->dpy, TARGET(c), c->gcs[result], g*i, g*j, g, g); +#endif /* USE_XIMAGE */ + } + +#ifdef USE_XIMAGE + /* Fill in these `gridsize' horizontal bits in the scanline */ + if(c->ximage->bits_per_pixel == 32) + { + uint32_t *ptr = (uint32_t *)scanline; + for(i = 0; i < w_div_g; i++) { + for(k = 0; k < g; k++) + ptr[g*i+k] = self->row[i]; + } + } + else if(c->ximage->bits_per_pixel == 24) + { + uint8_t *ptr = (uint8_t *)scanline; + for(i = 0; i < w_div_g; i++) { + for(k = 0; k < g; k++) { + uint32_t pixel = self->row[i]; + /* Might not work on big-endian. */ + ptr[0] = pixel; + ptr[1] = (pixel & 0x0000ff00) >> 8; + ptr[2] = (pixel & 0x00ff0000) >> 16; + ptr += 3; + } + } + } + else if(c->ximage->bits_per_pixel == 16) + { + uint16_t *ptr = (uint16_t *)scanline; + for(i = 0; i < w_div_g; i++) { + for(k = 0; k < g; k++) + ptr[g*i+k] = self->row[i]; + } + } + else if(c->ximage->bits_per_pixel == 8) + { + uint8_t *ptr = (uint8_t *)scanline; + for(i = 0; i < w_div_g; i++) { + for(k = 0; k < g; k++) + ptr[g*i+k] = self->row[i]; + } + } + else + { + for(i = 0; i < w_div_g; i++) { + for(k = 0; k < g; k++) + /* XPutPixel is thread safe as long as the XImage didn't have its + * bits_per_pixel changed. */ + XPutPixel(c->ximage, (g*i)+k, img_y, self->row[i]); + } + } + + /* Only the first scanline of the image has been filled in; clone that + scanline to the rest of the `gridsize' lines in the ximage */ + for(k = 0; k < (g-1); k++) + memcpy(c->ximage->data + (c->ximage->bytes_per_line * (img_y + k + 1)), + c->ximage->data + (c->ximage->bytes_per_line * img_y), + c->ximage->bytes_per_line); + +# ifndef USE_BIG_XIMAGE + /* Move the bits for this horizontal stripe to the server. */ +# ifdef HAVE_XSHM_EXTENSION + if (!c->use_shm) +# endif /* HAVE_XSHM_EXTENSION */ + XPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage, + 0, 0, 0, g*j, c->ximage->width, c->ximage->height); +# endif + +# if defined HAVE_XSHM_EXTENSION && !defined USE_BIG_XIMAGE + if (c->use_shm) +# endif + { +# if defined HAVE_XSHM_EXTENSION || defined USE_BIG_XIMAGE + scanline = (char *)scanline + c->ximage->bytes_per_line * g * c->threadpool.count; + img_y += g * c->threadpool.count; +# endif + } + +#endif /* USE_XIMAGE */ + } +} + +/* On allocation error, c->ximage == NULL. */ static void create_image( Display* dpy, struct inter_context* c, const XWindowAttributes* xgwa) { #ifdef USE_XIMAGE - c->row = malloc((c->w / c->grid_size) * sizeof(uint32_t)); - check_no_mem(dpy, c, c->row); + + /* Set the width so that each thread can work on a different line. */ + unsigned align = thread_memory_alignment(dpy) * 8 - 1; + /* The width of a scan line, in *bits*. */ + unsigned width = (xgwa->width * c->bits_per_pixel + align) & ~align; # ifdef HAVE_XSHM_EXTENSION /* - * interference used to put one row at a time to the X server. This changes + * Interference used to put one row at a time to the X server. This changes * today. * * XShmPutImage is asynchronous; the contents of the XImage must not be @@ -417,7 +661,7 @@ static void create_image( { c->ximage = create_xshm_image(dpy, xgwa->visual, xgwa->depth, ZPixmap, 0, &c->shm_info, - xgwa->width, xgwa->height); + width / c->bits_per_pixel, xgwa->height); if (!c->ximage) c->use_shm = False; /* If create_xshm_image fails, it will not be attempted again. */ @@ -437,29 +681,52 @@ static void create_image( # else c->grid_size, /* height */ # endif - 8, 0); /* pad, bpl */ + 8, width / 8); /* pad, bpl */ if(c->ximage) { - c->ximage->data = (char *) - calloc(c->ximage->height, c->ximage->bytes_per_line); - - if(!c->ximage->data) + if(thread_malloc((void **)&c->ximage->data, dpy, + c->ximage->height * c->ximage->bytes_per_line)) { - free(c->ximage); + XFree(c->ximage); c->ximage = NULL; } } } - if(!c->ximage) + check_no_mem(dpy, c, c->ximage); +#endif /* USE_XIMAGE */ + + { + static const struct threadpool_class cls = { - free(c->row); - c->row = 0; - } + sizeof(struct inter_thread), + inter_thread_create, + inter_thread_destroy + }; + + int error = threadpool_create( + &c->threadpool, + &cls, + dpy, +#if defined USE_XIMAGE && defined USE_BIG_XIMAGE + hardware_concurrency(dpy) +#else + 1 + /* At least three issues with threads without USE_BIG_XIMAGE: + * 1. Most of Xlib isn't thread safe without XInitThreads. + * 2. X(Un)LockDisplay would need to be called for each line, which is terrible. + * 3. There's only one XImage buffer at the moment. + */ +#endif + ); - check_no_mem(dpy, c, c->row); -#endif /* USE_XIMAGE */ + if(error) { + c->threadpool.count = 0; /* See the note in thread_util.h. */ + inter_free(dpy, c); + abort_on_error(error); + } + } } static void create_pix_buf(Display* dpy, Window win, struct inter_context *c, @@ -509,6 +776,7 @@ static void inter_init(Display* dpy, Window win, struct inter_context* c) c->dpy = dpy; c->win = win; + c->delay = get_integer_resource(dpy, "delay", "Integer"); XGetWindowAttributes(c->dpy, c->win, &xgwa); @@ -516,6 +784,8 @@ static void inter_init(Display* dpy, Window win, struct inter_context* c) c->h = xgwa.height; c->cmap = xgwa.colormap; c->screen = xgwa.screen; + c->bits_per_pixel = get_bits_per_pixel(c->dpy, xgwa.depth); + check_no_mem(dpy, c, (void *)(ptrdiff_t)c->bits_per_pixel); #ifdef HAVE_XSHM_EXTENSION c->use_shm = get_boolean_resource(dpy, "useSHM", "Boolean"); @@ -662,65 +932,52 @@ static void inter_init(Display* dpy, Window win, struct inter_context* c) (c->h/2 + ((int)(cos(c->source[i].y_theta)*((float)c->h/2.0)))) /* - * This is rather suboptimal. Calculating the distance per-pixel is going to + * This is somewhat suboptimal. Calculating the distance per-pixel is going to * be a lot slower than using now-ubiquitous SIMD CPU instructions to do four - * or eight pixels at a time. Plus, this could be almost trivially - * parallelized, what with all the multi-core hardware nowadays. + * or eight pixels at a time. */ #ifdef TEST_PATTERN static uint32_t _alloc_color(struct inter_context *c, uint16_t r, uint16_t g, uint16_t b) { - XColor color; - color.red = r; - color.green = g; - color.blue = b; - XAllocColor(c->dpy, c->cmap, &color); - return color.pixel; + XColor color; + color.red = r; + color.green = g; + color.blue = b; + XAllocColor(c->dpy, c->cmap, &color); + return color.pixel; } static void _copy_test(Display *dpy, Drawable src, Drawable dst, GC gc, int x, int y, uint32_t cells) { - XCopyArea(dpy, src, dst, gc, 0, 0, 3, 2, x, y); - - { - XImage *image = XGetImage(dpy, src, 0, 0, 3, 2, cells, ZPixmap); - XPutImage(dpy, dst, gc, image, 0, 0, x, y + 2, 3, 2); - XDestroyImage(image); - } + XCopyArea(dpy, src, dst, gc, 0, 0, 3, 2, x, y); + + { + XImage *image = XGetImage(dpy, src, 0, 0, 3, 2, cells, ZPixmap); + XPutImage(dpy, dst, gc, image, 0, 0, x, y + 2, 3, 2); + XDestroyImage(image); + } } static void _test_pattern(Display *dpy, Drawable d, GC gc, const uint32_t *rgb) { - unsigned x; - for(x = 0; x != 3; ++x) - { - XSetForeground(dpy, gc, rgb[x]); - XDrawPoint(dpy, d, gc, x, 0); - XSetForeground(dpy, gc, rgb[2 - x]); - XFillRectangle(dpy, d, gc, x, 1, 1, 1); - } - - _copy_test(dpy, d, d, gc, 0, 2, rgb[0] | rgb[1] | rgb[2]); + unsigned x; + for(x = 0; x != 3; ++x) + { + XSetForeground(dpy, gc, rgb[x]); + XDrawPoint(dpy, d, gc, x, 0); + XSetForeground(dpy, gc, rgb[2 - x]); + XFillRectangle(dpy, d, gc, x, 1, 1, 1); + } + + _copy_test(dpy, d, d, gc, 0, 2, rgb[0] | rgb[1] | rgb[2]); } #endif /* TEST_PATTERN */ static unsigned long do_inter(struct inter_context* c) { - int i, j, k; - unsigned result; - int dist; - int g = c->grid_size; - unsigned w_div_g = c->w/g; - - int dx, dy; - int px, py; - -#ifdef USE_XIMAGE - unsigned img_y = 0; - void *scanline = c->ximage->data; -#endif + int i; double now; float elapsed; @@ -747,134 +1004,8 @@ static unsigned long do_inter(struct inter_context* c) c->source[i].y = source_y(c, i); } - for(j = 0; j < c->h/g; j++) { - for(i = 0; i < w_div_g; i++) { - result = 0; - px = i*g + g/2; - py = j*g + g/2; - for(k = 0; k < c->count; k++) { - - dx = px - c->source[k].x; - dy = py - c->source[k].y; - - /* - * Other possibilities for improving performance here: - * 1. Using octagon-based distance estimation - * (Which causes giant octagons to appear.) - * 2. Square root approximation by reinterpret-casting IEEE floats to - * integers. - * (Which causes angles to appear when two waves interfere.) - */ - -/* int_float u; - u.f = dx*dx + dy*dy; - u.i = (1 << 29) + (u.i >> 1) - (1 << 22); - dist = u.f; */ - -#if defined USE_FAST_SQRT_BIGTABLE2 - dist = dx*dx + dy*dy; - dist = FAST_TABLE(dist); -#elif defined USE_FAST_SQRT_HACKISH - dist = fast_log2(dx*dx + dy*dy); -#else - dist = sqrt(dx*dx + dy*dy); -#endif - - result += (dist >= c->radius ? 0 : c->wave_height[dist]); - } - - /* It's slightly faster to do a subtraction or two before calculating the - * modulus. - D.O. */ - if(result >= c->colors) - { - result -= c->colors; - if(result >= c->colors) - result %= (unsigned)c->colors; - } - -#ifdef USE_XIMAGE - c->row[i] = c->pal[result].pixel; -#else - XFillRectangle(c->dpy, TARGET(c), c->gcs[result], g*i, g*j, g, g); -#endif /* USE_XIMAGE */ - } - -#ifdef USE_XIMAGE - /* Fill in these `gridsize' horizontal bits in the scanline */ - if(c->ximage->bits_per_pixel == 32) - { - uint32_t *ptr = (uint32_t *)scanline; - for(i = 0; i < w_div_g; i++) { - for(k = 0; k < g; k++) - ptr[g*i+k] = c->row[i]; - } - } - else if(c->ximage->bits_per_pixel == 24) - { - uint8_t *ptr = (uint8_t *)scanline; - for(i = 0; i < w_div_g; i++) { - for(k = 0; k < g; k++) { - uint32_t pixel = c->row[i]; - /* Might not work on big-endian. */ - ptr[0] = pixel; - ptr[1] = (pixel & 0x0000ff00) >> 8; - ptr[2] = (pixel & 0x00ff0000) >> 16; - ptr += 3; - } - } - } - else if(c->ximage->bits_per_pixel == 16) - { - uint16_t *ptr = (uint16_t *)scanline; - for(i = 0; i < w_div_g; i++) { - for(k = 0; k < g; k++) - ptr[g*i+k] = c->row[i]; - } - } - else if(c->ximage->bits_per_pixel == 8) - { - uint8_t *ptr = (uint8_t *)scanline; - for(i = 0; i < w_div_g; i++) { - for(k = 0; k < g; k++) - ptr[g*i+k] = c->row[i]; - } - } - else - { - for(i = 0; i < w_div_g; i++) { - for(k = 0; k < g; k++) - XPutPixel(c->ximage, (g*i)+k, img_y, c->row[i]); - } - } - - /* Only the first scanline of the image has been filled in; clone that - scanline to the rest of the `gridsize' lines in the ximage */ - for(k = 0; k < (g-1); k++) - memcpy(c->ximage->data + (c->ximage->bytes_per_line * (img_y + k + 1)), - c->ximage->data + (c->ximage->bytes_per_line * img_y), - c->ximage->bytes_per_line); - -# ifndef USE_BIG_XIMAGE - /* Move the bits for this horizontal stripe to the server. */ -# ifdef HAVE_XSHM_EXTENSION - if (!c->use_shm) -# endif /* HAVE_XSHM_EXTENSION */ - XPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage, - 0, 0, 0, g*j, c->ximage->width, c->ximage->height); -# endif - -# if defined HAVE_XSHM_EXTENSION && !defined USE_BIG_XIMAGE - if (c->use_shm) -# endif - { -# if defined HAVE_XSHM_EXTENSION || defined USE_BIG_XIMAGE - scanline = (char *)scanline + c->ximage->bytes_per_line * g; - img_y += g; -# endif - } - -#endif /* USE_XIMAGE */ - } + threadpool_run(&c->threadpool, inter_thread_run); + threadpool_wait(&c->threadpool); #ifdef HAVE_XSHM_EXTENSION if (c->use_shm) @@ -896,43 +1027,44 @@ static unsigned long do_inter(struct inter_context* c) #endif #ifdef TEST_PATTERN - { -/* XWindowAttributes xgwa; - XGetWindowAttributes(c->dpy, c->win, &xgwa); */ - - // if(xgwa.width >= 9 && xgwa.height >= 10) - { - Screen *screen = ScreenOfDisplay(c->dpy, DefaultScreen(c->dpy)); - Visual *visual = DefaultVisualOfScreen(screen); - Pixmap pixmap = XCreatePixmap(c->dpy, TARGET(c), 3, 10, visual_depth(screen, visual)); - - { - XSetForeground(c->dpy, c->copy_gc, _alloc_color(c, 0xffff, 0x7fff, 0x7fff)); - XDrawPoint(c->dpy, TARGET(c), c->copy_gc, 0, c->h - 1); - } - - uint32_t rgb[3], cells; - rgb[0] = _alloc_color(c, 0xffff, 0, 0); - rgb[1] = _alloc_color(c, 0, 0xffff, 0); - rgb[2] = _alloc_color(c, 0, 0, 0xffff); - cells = rgb[0] | rgb[1] | rgb[2]; - - _test_pattern(c->dpy, TARGET(c), c->copy_gc, rgb); - _test_pattern(c->dpy, pixmap, c->copy_gc, rgb); - // Here's a good spot to verify that the pixmap contains the right colors at the top. - _copy_test(c->dpy, TARGET(c), pixmap, c->copy_gc, 0, 6, cells); - - XCopyArea(c->dpy, pixmap, TARGET(c), c->copy_gc, 0, 0, 3, 10, 3, 0); - { - XImage *image = XGetImage(c->dpy, pixmap, 0, 0, 3, 10, cells, ZPixmap); - XPutImage(c->dpy, TARGET(c), c->copy_gc, image, 0, 0, 6, 0, 3, 10); - XDestroyImage(image); - } - - XFreePixmap(c->dpy, pixmap); - XSync(c->dpy, False); - } - } + { +/* XWindowAttributes xgwa; + XGetWindowAttributes(c->dpy, c->win, &xgwa); */ + + /* if(xgwa.width >= 9 && xgwa.height >= 10) */ + { + Screen *screen = ScreenOfDisplay(c->dpy, DefaultScreen(c->dpy)); + Visual *visual = DefaultVisualOfScreen(screen); + Pixmap pixmap = XCreatePixmap(c->dpy, TARGET(c), 3, 10, visual_depth(screen, visual)); + + { + XSetForeground(c->dpy, c->copy_gc, _alloc_color(c, 0xffff, 0x7fff, 0x7fff)); + XDrawPoint(c->dpy, TARGET(c), c->copy_gc, 0, c->h - 1); + } + + uint32_t rgb[3], cells; + rgb[0] = _alloc_color(c, 0xffff, 0, 0); + rgb[1] = _alloc_color(c, 0, 0xffff, 0); + rgb[2] = _alloc_color(c, 0, 0, 0xffff); + cells = rgb[0] | rgb[1] | rgb[2]; + + _test_pattern(c->dpy, TARGET(c), c->copy_gc, rgb); + _test_pattern(c->dpy, pixmap, c->copy_gc, rgb); + /* Here's a good spot to verify that the pixmap contains the right colors + * at the top. */ + _copy_test(c->dpy, TARGET(c), pixmap, c->copy_gc, 0, 6, cells); + + XCopyArea(c->dpy, pixmap, TARGET(c), c->copy_gc, 0, 0, 3, 10, 3, 0); + { + XImage *image = XGetImage(c->dpy, pixmap, 0, 0, 3, 10, cells, ZPixmap); + XPutImage(c->dpy, TARGET(c), c->copy_gc, image, 0, 0, 6, 0, 3, 10); + XDestroyImage(image); + } + + XFreePixmap(c->dpy, pixmap); + XSync(c->dpy, False); + } + } #endif /* TEST_PATTERN */ #ifdef HAVE_DOUBLE_BUFFER_EXTENSION