1 /* xscreensaver, Copyright (c) 1991-2018 Jamie Zawinski <jwz@jwz.org>
2 *
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
9 * implied warranty.
10 */
11
12 /* JWXYZ Is Not Xlib.
13
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.
17
18 This handles things when jwxyz-gl.c can't.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #ifdef JWXYZ_IMAGE /* entire file */
26
27 #include "jwxyzI.h"
28 #include "jwxyz.h"
29 #include "jwxyz-timers.h"
30 #include "pow2.h"
31
32 #include <wchar.h>
33
34
35 union color_bytes { // Hello, again.
36 uint32_t pixel;
37 uint8_t bytes[4];
38 };
39
40 struct jwxyz_Display {
41 const struct jwxyz_vtbl *vtbl; // Must come first.
42
43 Window main_window;
44 Visual visual;
45 struct jwxyz_sources_data *timers_data;
46
47 unsigned long window_background;
48 };
49
50 struct jwxyz_GC {
51 XGCValues gcv;
52 unsigned int depth;
53 };
54
55
56 extern const struct jwxyz_vtbl image_vtbl;
57
58 Display *
jwxyz_image_make_display(Window w,const unsigned char * rgba_bytes)59 jwxyz_image_make_display (Window w, const unsigned char *rgba_bytes)
60 {
61 Display *d = (Display *) calloc (1, sizeof(*d));
62 d->vtbl = &image_vtbl;
63
64 Visual *v = &d->visual;
65 v->class = TrueColor;
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;
70 color.pixel = 0;
71 color.bytes[rgba_bytes[i]] = 0xff;
72 masks[i] = color.pixel;
73 }
74 v->red_mask = masks[0];
75 v->green_mask = masks[1];
76 v->blue_mask = masks[2];
77 v->alpha_mask = masks[3];
78
79 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
80 d->window_background = BlackPixel(d,0);
81 d->main_window = w;
82
83 return d;
84 }
85
86 void
jwxyz_image_free_display(Display * dpy)87 jwxyz_image_free_display (Display *dpy)
88 {
89 jwxyz_sources_free (dpy->timers_data);
90
91 free (dpy);
92 }
93
94
95 static jwxyz_sources_data *
display_sources_data(Display * dpy)96 display_sources_data (Display *dpy)
97 {
98 return dpy->timers_data;
99 }
100
101
102 static Window
root(Display * dpy)103 root (Display *dpy)
104 {
105 return dpy->main_window;
106 }
107
108 static Visual *
visual(Display * dpy)109 visual (Display *dpy)
110 {
111 return &dpy->visual;
112 }
113
114
115 static void
next_point(short * v,XPoint p,int mode)116 next_point(short *v, XPoint p, int mode)
117 {
118 switch (mode) {
119 case CoordModeOrigin:
120 v[0] = p.x;
121 v[1] = p.y;
122 break;
123 case CoordModePrevious:
124 v[0] += p.x;
125 v[1] += p.y;
126 break;
127 default:
128 Assert (False, "next_point: bad mode");
129 break;
130 }
131 }
132
133 #define SEEK_DRAWABLE(d, x, y) \
134 SEEK_XY (jwxyz_image_data(d), jwxyz_image_pitch(d), x, y)
135
136 static int
DrawPoints(Display * dpy,Drawable d,GC gc,XPoint * points,int count,int mode)137 DrawPoints (Display *dpy, Drawable d, GC gc,
138 XPoint *points, int count, int mode)
139 {
140 Assert (gc->gcv.function == GXcopy, "XDrawPoints: bad GC function");
141
142 const XRectangle *frame = jwxyz_frame (d);
143 short v[2] = {0, 0};
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;
149 }
150
151 return 0;
152 }
153
154
155 static void
copy_area(Display * dpy,Drawable src,Drawable dst,GC gc,int src_x,int src_y,unsigned int width,unsigned int height,int dst_x,int dst_y)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)
159 {
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,
162 width, height);
163 }
164
165
166 static void
draw_line(Drawable d,unsigned long pixel,short x0,short y0,short x1,short y1)167 draw_line (Drawable d, unsigned long pixel,
168 short x0, short y0, short x1, short y1)
169 {
170 // TODO: Assert line_Width == 1, line_stipple == solid, etc.
171
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");
178 return;
179 }
180
181 int dx = abs(x1 - x0), dy = abs(y1 - y0);
182
183 unsigned dmod0, dmod1;
184 int dpx0, dpx1;
185 if (dx > dy) {
186 dmod0 = dy;
187 dmod1 = dx;
188 dpx0 = x1 > x0 ? 1 : -1;
189 dpx1 = y1 > y0 ? frame->width : -frame->width;
190 } else {
191 dmod0 = dx;
192 dmod1 = dy;
193 dpx0 = y1 > y0 ? frame->width : -frame->width;
194 dpx1 = x1 > x0 ? 1 : -1;
195 }
196
197 unsigned n = dmod1;
198 unsigned mod = n;
199 ++n;
200
201 dmod0 <<= 1;
202 dmod1 <<= 1;
203
204 uint32_t *px = SEEK_DRAWABLE(d, x0, y0);
205
206 for(; n; --n) {
207 *px = pixel;
208
209 mod += dmod0;
210 if(mod > dmod1) {
211 mod -= dmod1;
212 px += dpx1;
213 }
214
215 px += dpx0;
216 }
217 }
218
219 static int
DrawLines(Display * dpy,Drawable d,GC gc,XPoint * points,int count,int mode)220 DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
221 int mode)
222 {
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);
227 if (i)
228 draw_line (d, pixel, v_prev[0], v_prev[1], v[0], v[1]);
229 v_prev[0] = v[0];
230 v_prev[1] = v[1];
231 }
232 return 0;
233 }
234
235
236 static int
DrawSegments(Display * dpy,Drawable d,GC gc,XSegment * segments,int count)237 DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
238 {
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);
243 }
244 return 0;
245 }
246
247
248 static int
ClearWindow(Display * dpy,Window win)249 ClearWindow (Display *dpy, Window win)
250 {
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);
254 }
255
256 static unsigned long *
window_background(Display * dpy)257 window_background (Display *dpy)
258 {
259 return &dpy->window_background;
260 }
261
262 static void
fill_rects(Display * dpy,Drawable d,GC gc,const XRectangle * rectangles,unsigned long nrectangles,unsigned long pixel)263 fill_rects (Display *dpy, Drawable d, GC gc,
264 const XRectangle *rectangles, unsigned long nrectangles,
265 unsigned long pixel)
266 {
267 Assert (!gc || gc->gcv.function == GXcopy, "XDrawPoints: bad GC function");
268
269 const XRectangle *frame = jwxyz_frame (d);
270 void *image_data = jwxyz_image_data (d);
271 ptrdiff_t image_pitch = jwxyz_image_pitch (d);
272
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)
278 y1 = frame->height;
279 if (x1 > frame->width)
280 x1 = frame->width;
281 unsigned x_size = x1 - x0, y_size = y1 - y0;
282 void *dst = SEEK_XY (image_data, image_pitch, x0, y0);
283 while (y_size) {
284 # if __SIZEOF_WCHAR_T__ == 4
285 wmemset (dst, (wchar_t) pixel, x_size);
286 # else
287 for(size_t i = 0; i != x_size; ++i)
288 ((uint32_t *)dst)[i] = pixel;
289 # endif
290 --y_size;
291 dst = (char *) dst + image_pitch;
292 }
293 }
294 }
295
296
297 static int
FillPolygon(Display * dpy,Drawable d,GC gc,XPoint * points,int npoints,int shape,int mode)298 FillPolygon (Display *dpy, Drawable d, GC gc,
299 XPoint *points, int npoints, int shape, int mode)
300 {
301 Log ("XFillPolygon: not implemented");
302 return 0;
303 }
304
305 static int
draw_arc(Display * dpy,Drawable d,GC gc,int x,int y,unsigned int width,unsigned int height,int angle1,int angle2,Bool fill_p)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)
309 {
310 Log ("jwxyz_draw_arc: not implemented");
311 return 0;
312 }
313
314
315 static XGCValues *
gc_gcv(GC gc)316 gc_gcv (GC gc)
317 {
318 return &gc->gcv;
319 }
320
321
322 static unsigned int
gc_depth(GC gc)323 gc_depth (GC gc)
324 {
325 return gc->depth;
326 }
327
328
329 static GC
CreateGC(Display * dpy,Drawable d,unsigned long mask,XGCValues * xgcv)330 CreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
331 {
332 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
333 gc->depth = jwxyz_drawable_depth (d);
334
335 jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
336 XChangeGC (dpy, gc, mask, xgcv);
337 return gc;
338 }
339
340
341 static int
FreeGC(Display * dpy,GC gc)342 FreeGC (Display *dpy, GC gc)
343 {
344 if (gc->gcv.font)
345 XUnloadFont (dpy, gc->gcv.font);
346
347 free (gc);
348 return 0;
349 }
350
351
352 static int
PutImage(Display * dpy,Drawable d,GC gc,XImage * ximage,int src_x,int src_y,int dest_x,int dest_y,unsigned int w,unsigned int h)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)
356 {
357 const XRectangle *wr = jwxyz_frame (d);
358
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");
366
367 // Clip width and height to the bounds of the Drawable
368 //
369 if (dest_x + w > wr->width) {
370 if (dest_x > wr->width)
371 return 0;
372 w = wr->width - dest_x;
373 }
374 if (dest_y + h > wr->height) {
375 if (dest_y > wr->height)
376 return 0;
377 h = wr->height - dest_y;
378 }
379 if (w <= 0 || h <= 0)
380 return 0;
381
382 // Clip width and height to the bounds of the XImage
383 //
384 if (src_x + w > ximage->width) {
385 if (src_x > ximage->width)
386 return 0;
387 w = ximage->width - src_x;
388 }
389 if (src_y + h > ximage->height) {
390 if (src_y > ximage->height)
391 return 0;
392 h = ximage->height - src_y;
393 }
394 if (w <= 0 || h <= 0)
395 return 0;
396
397 /* Assert (d->win */
398
399 if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
400 return 0;
401
402 XGCValues *gcv = gc_gcv (gc);
403
404 Assert (gcv->function == GXcopy, "XPutImage: bad GC function");
405 Assert (!ximage->xoffset, "XPutImage: bad xoffset");
406
407 ptrdiff_t
408 src_pitch = ximage->bytes_per_line,
409 dst_pitch = jwxyz_image_pitch (d);
410
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);
413
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");
418
419 const uint8_t *src_row = src_ptr;
420 uint8_t *dst_row = dst_ptr;
421
422 /* Slight loss of precision here: color values may end up being one less
423 than what they should be.
424 */
425 while (h) {
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;
430
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;
437 }
438
439 src_row += src_pitch;
440 dst_row += dst_pitch;
441 --h;
442 }
443 } else {
444 Assert (ximage->depth == 1 || ximage->depth == 32,
445 "XPutImage: depth != 1 && depth != 32");
446
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,
452 w, h);
453 } else {
454 Log ("XPutImage: depth == 1");
455 }
456 }
457
458 return 0;
459 }
460
461 static XImage *
GetSubImage(Display * dpy,Drawable d,int x,int y,unsigned int width,unsigned int height,unsigned long plane_mask,int format,XImage * dest_image,int dest_x,int dest_y)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)
466 {
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");
471
472 Assert (dest_image->depth == 32 && jwxyz_drawable_depth (d) == 32,
473 "XGetSubImage: bad depth");
474 Assert (format == ZPixmap, "XGetSubImage: bad format");
475
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,
478 width, height);
479
480 return dest_image;
481 }
482
483
484 static int
SetClipMask(Display * dpy,GC gc,Pixmap m)485 SetClipMask (Display *dpy, GC gc, Pixmap m)
486 {
487 Log ("TODO: No clip masks yet"); // Slip/colorbars.c needs this.
488 return 0;
489 }
490
491 static int
SetClipOrigin(Display * dpy,GC gc,int x,int y)492 SetClipOrigin (Display *dpy, GC gc, int x, int y)
493 {
494 gc->gcv.clip_x_origin = x;
495 gc->gcv.clip_y_origin = y;
496 return 0;
497 }
498
499
500 const struct jwxyz_vtbl image_vtbl = {
501 root,
502 visual,
503 display_sources_data,
504
505 window_background,
506 draw_arc,
507 fill_rects,
508 gc_gcv,
509 gc_depth,
510 jwxyz_draw_string,
511
512 copy_area,
513
514 DrawPoints,
515 DrawSegments,
516 CreateGC,
517 FreeGC,
518 ClearWindow,
519 SetClipMask,
520 SetClipOrigin,
521 FillPolygon,
522 DrawLines,
523 PutImage,
524 GetSubImage
525 };
526
527 #endif /* JWXYZ_IMAGE -- entire file */
528