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