1 /* xscreensaver, Copyright (c) 1991-2018 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
14 But it's a bunch of function definitions that bear some resemblance to
15 Xlib and that do things to an XImage that bear some resemblance to the
16 things that Xlib might have done.
18 This handles things when jwxyz-gl.c can't.
25 #ifdef JWXYZ_IMAGE /* entire file */
29 #include "jwxyz-timers.h"
35 union color_bytes { // Hello, again.
40 struct jwxyz_Display {
41 const struct jwxyz_vtbl *vtbl; // Must come first.
45 struct jwxyz_sources_data *timers_data;
47 unsigned long window_background;
56 extern const struct jwxyz_vtbl image_vtbl;
59 jwxyz_image_make_display (Window w, const unsigned char *rgba_bytes)
61 Display *d = (Display *) calloc (1, sizeof(*d));
62 d->vtbl = &image_vtbl;
64 Visual *v = &d->visual;
66 Assert (rgba_bytes[3] == 3, "alpha not last");
67 unsigned long masks[4];
68 for (unsigned i = 0; i != 4; ++i) {
69 union color_bytes color;
71 color.bytes[rgba_bytes[i]] = 0xff;
72 masks[i] = color.pixel;
74 v->red_mask = masks[0];
75 v->green_mask = masks[1];
76 v->blue_mask = masks[2];
77 v->alpha_mask = masks[3];
79 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
80 d->window_background = BlackPixel(d,0);
87 jwxyz_image_free_display (Display *dpy)
89 jwxyz_sources_free (dpy->timers_data);
95 static jwxyz_sources_data *
96 display_sources_data (Display *dpy)
98 return dpy->timers_data;
105 return dpy->main_window;
109 visual (Display *dpy)
116 next_point(short *v, XPoint p, int mode)
119 case CoordModeOrigin:
123 case CoordModePrevious:
128 Assert (False, "next_point: bad mode");
133 #define SEEK_DRAWABLE(d, x, y) \
134 SEEK_XY (jwxyz_image_data(d), jwxyz_image_pitch(d), x, y)
137 DrawPoints (Display *dpy, Drawable d, GC gc,
138 XPoint *points, int count, int mode)
140 Assert (gc->gcv.function == GXcopy, "XDrawPoints: bad GC function");
142 const XRectangle *frame = jwxyz_frame (d);
144 for (unsigned i = 0; i < count; i++) {
145 next_point(v, points[i], mode);
146 if (v[0] >= 0 && v[0] < frame->width &&
147 v[1] >= 0 && v[1] < frame->height)
148 *SEEK_DRAWABLE(d, v[0], v[1]) = gc->gcv.foreground;
156 copy_area (Display *dpy, Drawable src, Drawable dst, GC gc,
157 int src_x, int src_y, unsigned int width, unsigned int height,
158 int dst_x, int dst_y)
160 jwxyz_blit (jwxyz_image_data (src), jwxyz_image_pitch (src), src_x, src_y,
161 jwxyz_image_data (dst), jwxyz_image_pitch (dst), dst_x, dst_y,
167 draw_line (Drawable d, unsigned long pixel,
168 short x0, short y0, short x1, short y1)
170 // TODO: Assert line_Width == 1, line_stipple == solid, etc.
172 const XRectangle *frame = jwxyz_frame (d);
173 if (x0 < 0 || x0 >= frame->width ||
174 x1 < 0 || x1 >= frame->width ||
175 y0 < 0 || y0 >= frame->height ||
176 y1 < 0 || y1 >= frame->height) {
177 Log ("draw_line: out of bounds");
181 int dx = abs(x1 - x0), dy = abs(y1 - y0);
183 unsigned dmod0, dmod1;
188 dpx0 = x1 > x0 ? 1 : -1;
189 dpx1 = y1 > y0 ? frame->width : -frame->width;
193 dpx0 = y1 > y0 ? frame->width : -frame->width;
194 dpx1 = x1 > x0 ? 1 : -1;
204 uint32_t *px = SEEK_DRAWABLE(d, x0, y0);
220 DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
223 short v[2] = {0, 0}, v_prev[2] = {0, 0};
224 unsigned long pixel = gc->gcv.foreground;
225 for (unsigned i = 0; i != count; ++i) {
226 next_point(v, points[i], mode);
228 draw_line (d, pixel, v_prev[0], v_prev[1], v[0], v[1]);
237 DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
239 unsigned long pixel = gc->gcv.foreground;
240 for (unsigned i = 0; i != count; ++i) {
241 XSegment *seg = &segments[i];
242 draw_line (d, pixel, seg->x1, seg->y1, seg->x2, seg->y2);
249 ClearWindow (Display *dpy, Window win)
251 Assert (win == dpy->main_window, "not a window");
252 const XRectangle *wr = jwxyz_frame (win);
253 return XClearArea (dpy, win, 0, 0, wr->width, wr->height, 0);
256 static unsigned long *
257 window_background (Display *dpy)
259 return &dpy->window_background;
263 fill_rects (Display *dpy, Drawable d, GC gc,
264 const XRectangle *rectangles, unsigned long nrectangles,
267 Assert (!gc || gc->gcv.function == GXcopy, "XDrawPoints: bad GC function");
269 const XRectangle *frame = jwxyz_frame (d);
270 void *image_data = jwxyz_image_data (d);
271 ptrdiff_t image_pitch = jwxyz_image_pitch (d);
273 for (unsigned i = 0; i != nrectangles; ++i) {
274 const XRectangle *rect = &rectangles[i];
275 unsigned x0 = rect->x >= 0 ? rect->x : 0, y0 = rect->y >= 0 ? rect->y : 0;
276 int x1 = rect->x + rect->width, y1 = rect->y + rect->height;
277 if (y1 > frame->height)
279 if (x1 > frame->width)
281 unsigned x_size = x1 - x0, y_size = y1 - y0;
282 void *dst = SEEK_XY (image_data, image_pitch, x0, y0);
284 # if __SIZEOF_WCHAR_T__ == 4
285 wmemset (dst, (wchar_t) pixel, x_size);
287 for(size_t i = 0; i != x_size; ++i)
288 ((uint32_t *)dst)[i] = pixel;
291 dst = (char *) dst + image_pitch;
298 FillPolygon (Display *dpy, Drawable d, GC gc,
299 XPoint *points, int npoints, int shape, int mode)
301 Log ("XFillPolygon: not implemented");
306 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
307 unsigned int width, unsigned int height,
308 int angle1, int angle2, Bool fill_p)
310 Log ("jwxyz_draw_arc: not implemented");
330 CreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
332 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
333 gc->depth = jwxyz_drawable_depth (d);
335 jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
336 XChangeGC (dpy, gc, mask, xgcv);
342 FreeGC (Display *dpy, GC gc)
345 XUnloadFont (dpy, gc->gcv.font);
353 PutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
354 int src_x, int src_y, int dest_x, int dest_y,
355 unsigned int w, unsigned int h)
357 const XRectangle *wr = jwxyz_frame (d);
359 Assert (gc, "no GC");
360 Assert ((w < 65535), "improbably large width");
361 Assert ((h < 65535), "improbably large height");
362 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
363 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
364 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
365 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
367 // Clip width and height to the bounds of the Drawable
369 if (dest_x + w > wr->width) {
370 if (dest_x > wr->width)
372 w = wr->width - dest_x;
374 if (dest_y + h > wr->height) {
375 if (dest_y > wr->height)
377 h = wr->height - dest_y;
379 if (w <= 0 || h <= 0)
382 // Clip width and height to the bounds of the XImage
384 if (src_x + w > ximage->width) {
385 if (src_x > ximage->width)
387 w = ximage->width - src_x;
389 if (src_y + h > ximage->height) {
390 if (src_y > ximage->height)
392 h = ximage->height - src_y;
394 if (w <= 0 || h <= 0)
399 if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
402 XGCValues *gcv = gc_gcv (gc);
404 Assert (gcv->function == GXcopy, "XPutImage: bad GC function");
405 Assert (!ximage->xoffset, "XPutImage: bad xoffset");
408 src_pitch = ximage->bytes_per_line,
409 dst_pitch = jwxyz_image_pitch (d);
411 const void *src_ptr = SEEK_XY (ximage->data, src_pitch, src_x, src_y);
412 void *dst_ptr = SEEK_XY (jwxyz_image_data (d), dst_pitch, dest_x, dest_y);
414 if (gcv->alpha_allowed_p) {
415 Assert (ximage->depth == 32, "XPutImage: depth != 32");
416 Assert (ximage->format == ZPixmap, "XPutImage: bad format");
417 Assert (ximage->bits_per_pixel == 32, "XPutImage: bad bits_per_pixel");
419 const uint8_t *src_row = src_ptr;
420 uint8_t *dst_row = dst_ptr;
422 /* Slight loss of precision here: color values may end up being one less
423 than what they should be.
426 for (unsigned x = 0; x != w; ++x) {
427 // Pixmaps don't contain alpha. (Yay.)
428 const uint8_t *src = src_row + x * 4;
429 uint8_t *dst = dst_row + x * 4;
431 // ####: This is pretty SIMD friendly.
432 // Protip: Align dst (load + store), let src be unaligned (load only)
433 uint16_t alpha = src[3], alpha1 = 0xff - src[3];
434 dst[0] = (src[0] * alpha + dst[0] * alpha1) >> 8;
435 dst[1] = (src[1] * alpha + dst[1] * alpha1) >> 8;
436 dst[2] = (src[2] * alpha + dst[2] * alpha1) >> 8;
439 src_row += src_pitch;
440 dst_row += dst_pitch;
444 Assert (ximage->depth == 1 || ximage->depth == 32,
445 "XPutImage: depth != 1 && depth != 32");
447 if (ximage->depth == 32) {
448 Assert (ximage->format == ZPixmap, "XPutImage: bad format");
449 Assert (ximage->bits_per_pixel == 32, "XPutImage: bad bits_per_pixel");
450 jwxyz_blit (ximage->data, ximage->bytes_per_line, src_x, src_y,
451 jwxyz_image_data (d), jwxyz_image_pitch (d), dest_x, dest_y,
454 Log ("XPutImage: depth == 1");
462 GetSubImage (Display *dpy, Drawable d, int x, int y,
463 unsigned int width, unsigned int height,
464 unsigned long plane_mask, int format,
465 XImage *dest_image, int dest_x, int dest_y)
467 Assert ((width < 65535), "improbably large width");
468 Assert ((height < 65535), "improbably large height");
469 Assert ((x < 65535 && x > -65535), "improbably large x");
470 Assert ((y < 65535 && y > -65535), "improbably large y");
472 Assert (dest_image->depth == 32 && jwxyz_drawable_depth (d) == 32,
473 "XGetSubImage: bad depth");
474 Assert (format == ZPixmap, "XGetSubImage: bad format");
476 jwxyz_blit (jwxyz_image_data (d), jwxyz_image_pitch (d), x, y,
477 dest_image->data, dest_image->bytes_per_line, dest_x, dest_y,
485 SetClipMask (Display *dpy, GC gc, Pixmap m)
487 Log ("TODO: No clip masks yet"); // Slip/colorbars.c needs this.
492 SetClipOrigin (Display *dpy, GC gc, int x, int y)
494 gc->gcv.clip_x_origin = x;
495 gc->gcv.clip_y_origin = y;
500 const struct jwxyz_vtbl image_vtbl = {
503 display_sources_data,
527 #endif /* JWXYZ_IMAGE -- entire file */