summaryrefslogtreecommitdiffstats
path: root/jwxyz/jwxyz-image.c
diff options
context:
space:
mode:
Diffstat (limited to 'jwxyz/jwxyz-image.c')
-rw-r--r--jwxyz/jwxyz-image.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/jwxyz/jwxyz-image.c b/jwxyz/jwxyz-image.c
new file mode 100644
index 0000000..b1b3332
--- /dev/null
+++ b/jwxyz/jwxyz-image.c
@@ -0,0 +1,527 @@
+/* xscreensaver, Copyright (c) 1991-2018 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* JWXYZ Is Not Xlib.
+
+ But it's a bunch of function definitions that bear some resemblance to
+ Xlib and that do things to an XImage that bear some resemblance to the
+ things that Xlib might have done.
+
+ This handles things when jwxyz-gl.c can't.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef JWXYZ_IMAGE /* entire file */
+
+#include "jwxyzI.h"
+#include "jwxyz.h"
+#include "jwxyz-timers.h"
+#include "pow2.h"
+
+#include <wchar.h>
+
+
+union color_bytes { // Hello, again.
+ uint32_t pixel;
+ uint8_t bytes[4];
+};
+
+struct jwxyz_Display {
+ const struct jwxyz_vtbl *vtbl; // Must come first.
+
+ Window main_window;
+ Visual visual;
+ struct jwxyz_sources_data *timers_data;
+
+ unsigned long window_background;
+};
+
+struct jwxyz_GC {
+ XGCValues gcv;
+ unsigned int depth;
+};
+
+
+extern const struct jwxyz_vtbl image_vtbl;
+
+Display *
+jwxyz_image_make_display (Window w, const unsigned char *rgba_bytes)
+{
+ Display *d = (Display *) calloc (1, sizeof(*d));
+ d->vtbl = &image_vtbl;
+
+ Visual *v = &d->visual;
+ v->class = TrueColor;
+ Assert (rgba_bytes[3] == 3, "alpha not last");
+ unsigned long masks[4];
+ for (unsigned i = 0; i != 4; ++i) {
+ union color_bytes color;
+ color.pixel = 0;
+ color.bytes[rgba_bytes[i]] = 0xff;
+ masks[i] = color.pixel;
+ }
+ v->red_mask = masks[0];
+ v->green_mask = masks[1];
+ v->blue_mask = masks[2];
+ v->alpha_mask = masks[3];
+
+ d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
+ d->window_background = BlackPixel(d,0);
+ d->main_window = w;
+
+ return d;
+}
+
+void
+jwxyz_image_free_display (Display *dpy)
+{
+ jwxyz_sources_free (dpy->timers_data);
+
+ free (dpy);
+}
+
+
+static jwxyz_sources_data *
+display_sources_data (Display *dpy)
+{
+ return dpy->timers_data;
+}
+
+
+static Window
+root (Display *dpy)
+{
+ return dpy->main_window;
+}
+
+static Visual *
+visual (Display *dpy)
+{
+ return &dpy->visual;
+}
+
+
+static void
+next_point(short *v, XPoint p, int mode)
+{
+ switch (mode) {
+ case CoordModeOrigin:
+ v[0] = p.x;
+ v[1] = p.y;
+ break;
+ case CoordModePrevious:
+ v[0] += p.x;
+ v[1] += p.y;
+ break;
+ default:
+ Assert (False, "next_point: bad mode");
+ break;
+ }
+}
+
+#define SEEK_DRAWABLE(d, x, y) \
+ SEEK_XY (jwxyz_image_data(d), jwxyz_image_pitch(d), x, y)
+
+static int
+DrawPoints (Display *dpy, Drawable d, GC gc,
+ XPoint *points, int count, int mode)
+{
+ Assert (gc->gcv.function == GXcopy, "XDrawPoints: bad GC function");
+
+ const XRectangle *frame = jwxyz_frame (d);
+ short v[2] = {0, 0};
+ for (unsigned i = 0; i < count; i++) {
+ next_point(v, points[i], mode);
+ if (v[0] >= 0 && v[0] < frame->width &&
+ v[1] >= 0 && v[1] < frame->height)
+ *SEEK_DRAWABLE(d, v[0], v[1]) = gc->gcv.foreground;
+ }
+
+ return 0;
+}
+
+
+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)
+{
+ jwxyz_blit (jwxyz_image_data (src), jwxyz_image_pitch (src), src_x, src_y,
+ jwxyz_image_data (dst), jwxyz_image_pitch (dst), dst_x, dst_y,
+ width, height);
+}
+
+
+static void
+draw_line (Drawable d, unsigned long pixel,
+ short x0, short y0, short x1, short y1)
+{
+// TODO: Assert line_Width == 1, line_stipple == solid, etc.
+
+ const XRectangle *frame = jwxyz_frame (d);
+ if (x0 < 0 || x0 >= frame->width ||
+ x1 < 0 || x1 >= frame->width ||
+ y0 < 0 || y0 >= frame->height ||
+ y1 < 0 || y1 >= frame->height) {
+ Log ("draw_line: out of bounds");
+ return;
+ }
+
+ int dx = abs(x1 - x0), dy = abs(y1 - y0);
+
+ unsigned dmod0, dmod1;
+ int dpx0, dpx1;
+ if (dx > dy) {
+ dmod0 = dy;
+ dmod1 = dx;
+ dpx0 = x1 > x0 ? 1 : -1;
+ dpx1 = y1 > y0 ? frame->width : -frame->width;
+ } else {
+ dmod0 = dx;
+ dmod1 = dy;
+ dpx0 = y1 > y0 ? frame->width : -frame->width;
+ dpx1 = x1 > x0 ? 1 : -1;
+ }
+
+ unsigned n = dmod1;
+ unsigned mod = n;
+ ++n;
+
+ dmod0 <<= 1;
+ dmod1 <<= 1;
+
+ uint32_t *px = SEEK_DRAWABLE(d, x0, y0);
+
+ for(; n; --n) {
+ *px = pixel;
+
+ mod += dmod0;
+ if(mod > dmod1) {
+ mod -= dmod1;
+ px += dpx1;
+ }
+
+ px += dpx0;
+ }
+}
+
+static int
+DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
+ int mode)
+{
+ short v[2] = {0, 0}, v_prev[2] = {0, 0};
+ unsigned long pixel = gc->gcv.foreground;
+ for (unsigned i = 0; i != count; ++i) {
+ next_point(v, points[i], mode);
+ if (i)
+ draw_line (d, pixel, v_prev[0], v_prev[1], v[0], v[1]);
+ v_prev[0] = v[0];
+ v_prev[1] = v[1];
+ }
+ return 0;
+}
+
+
+static int
+DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
+{
+ unsigned long pixel = gc->gcv.foreground;
+ for (unsigned i = 0; i != count; ++i) {
+ XSegment *seg = &segments[i];
+ draw_line (d, pixel, seg->x1, seg->y1, seg->x2, seg->y2);
+ }
+ return 0;
+}
+
+
+static int
+ClearWindow (Display *dpy, Window win)
+{
+ Assert (win == dpy->main_window, "not a window");
+ const XRectangle *wr = jwxyz_frame (win);
+ return XClearArea (dpy, win, 0, 0, wr->width, wr->height, 0);
+}
+
+static unsigned long *
+window_background (Display *dpy)
+{
+ return &dpy->window_background;
+}
+
+static void
+fill_rects (Display *dpy, Drawable d, GC gc,
+ const XRectangle *rectangles, unsigned long nrectangles,
+ unsigned long pixel)
+{
+ Assert (!gc || gc->gcv.function == GXcopy, "XDrawPoints: bad GC function");
+
+ const XRectangle *frame = jwxyz_frame (d);
+ void *image_data = jwxyz_image_data (d);
+ ptrdiff_t image_pitch = jwxyz_image_pitch (d);
+
+ for (unsigned i = 0; i != nrectangles; ++i) {
+ const XRectangle *rect = &rectangles[i];
+ unsigned x0 = rect->x >= 0 ? rect->x : 0, y0 = rect->y >= 0 ? rect->y : 0;
+ int x1 = rect->x + rect->width, y1 = rect->y + rect->height;
+ if (y1 > frame->height)
+ y1 = frame->height;
+ if (x1 > frame->width)
+ x1 = frame->width;
+ unsigned x_size = x1 - x0, y_size = y1 - y0;
+ void *dst = SEEK_XY (image_data, image_pitch, x0, y0);
+ while (y_size) {
+# if __SIZEOF_WCHAR_T__ == 4
+ wmemset (dst, (wchar_t) pixel, x_size);
+# else
+ for(size_t i = 0; i != x_size; ++i)
+ ((uint32_t *)dst)[i] = pixel;
+# endif
+ --y_size;
+ dst = (char *) dst + image_pitch;
+ }
+ }
+}
+
+
+static int
+FillPolygon (Display *dpy, Drawable d, GC gc,
+ XPoint *points, int npoints, int shape, int mode)
+{
+ Log ("XFillPolygon: not implemented");
+ return 0;
+}
+
+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)
+{
+ Log ("jwxyz_draw_arc: not implemented");
+ return 0;
+}
+
+
+static XGCValues *
+gc_gcv (GC gc)
+{
+ return &gc->gcv;
+}
+
+
+static unsigned int
+gc_depth (GC gc)
+{
+ return gc->depth;
+}
+
+
+static GC
+CreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
+{
+ struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
+ gc->depth = jwxyz_drawable_depth (d);
+
+ jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
+ XChangeGC (dpy, gc, mask, xgcv);
+ return gc;
+}
+
+
+static int
+FreeGC (Display *dpy, GC gc)
+{
+ if (gc->gcv.font)
+ XUnloadFont (dpy, gc->gcv.font);
+
+ free (gc);
+ return 0;
+}
+
+
+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)
+{
+ const XRectangle *wr = jwxyz_frame (d);
+
+ Assert (gc, "no GC");
+ Assert ((w < 65535), "improbably large width");
+ Assert ((h < 65535), "improbably large height");
+ Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
+ Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
+ Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
+ Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
+
+ // Clip width and height to the bounds of the Drawable
+ //
+ if (dest_x + w > wr->width) {
+ if (dest_x > wr->width)
+ return 0;
+ w = wr->width - dest_x;
+ }
+ if (dest_y + h > wr->height) {
+ if (dest_y > wr->height)
+ return 0;
+ h = wr->height - dest_y;
+ }
+ if (w <= 0 || h <= 0)
+ return 0;
+
+ // Clip width and height to the bounds of the XImage
+ //
+ if (src_x + w > ximage->width) {
+ if (src_x > ximage->width)
+ return 0;
+ w = ximage->width - src_x;
+ }
+ if (src_y + h > ximage->height) {
+ if (src_y > ximage->height)
+ return 0;
+ h = ximage->height - src_y;
+ }
+ if (w <= 0 || h <= 0)
+ return 0;
+
+ /* Assert (d->win */
+
+ if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
+ return 0;
+
+ XGCValues *gcv = gc_gcv (gc);
+
+ Assert (gcv->function == GXcopy, "XPutImage: bad GC function");
+ Assert (!ximage->xoffset, "XPutImage: bad xoffset");
+
+ ptrdiff_t
+ src_pitch = ximage->bytes_per_line,
+ dst_pitch = jwxyz_image_pitch (d);
+
+ const void *src_ptr = SEEK_XY (ximage->data, src_pitch, src_x, src_y);
+ void *dst_ptr = SEEK_XY (jwxyz_image_data (d), dst_pitch, dest_x, dest_y);
+
+ if (gcv->alpha_allowed_p) {
+ Assert (ximage->depth == 32, "XPutImage: depth != 32");
+ Assert (ximage->format == ZPixmap, "XPutImage: bad format");
+ Assert (ximage->bits_per_pixel == 32, "XPutImage: bad bits_per_pixel");
+
+ const uint8_t *src_row = src_ptr;
+ uint8_t *dst_row = dst_ptr;
+
+ /* Slight loss of precision here: color values may end up being one less
+ than what they should be.
+ */
+ while (h) {
+ for (unsigned x = 0; x != w; ++x) {
+ // Pixmaps don't contain alpha. (Yay.)
+ const uint8_t *src = src_row + x * 4;
+ uint8_t *dst = dst_row + x * 4;
+
+ // ####: This is pretty SIMD friendly.
+ // Protip: Align dst (load + store), let src be unaligned (load only)
+ uint16_t alpha = src[3], alpha1 = 0xff - src[3];
+ dst[0] = (src[0] * alpha + dst[0] * alpha1) >> 8;
+ dst[1] = (src[1] * alpha + dst[1] * alpha1) >> 8;
+ dst[2] = (src[2] * alpha + dst[2] * alpha1) >> 8;
+ }
+
+ src_row += src_pitch;
+ dst_row += dst_pitch;
+ --h;
+ }
+ } else {
+ Assert (ximage->depth == 1 || ximage->depth == 32,
+ "XPutImage: depth != 1 && depth != 32");
+
+ if (ximage->depth == 32) {
+ Assert (ximage->format == ZPixmap, "XPutImage: bad format");
+ Assert (ximage->bits_per_pixel == 32, "XPutImage: bad bits_per_pixel");
+ jwxyz_blit (ximage->data, ximage->bytes_per_line, src_x, src_y,
+ jwxyz_image_data (d), jwxyz_image_pitch (d), dest_x, dest_y,
+ w, h);
+ } else {
+ Log ("XPutImage: depth == 1");
+ }
+ }
+
+ return 0;
+}
+
+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)
+{
+ Assert ((width < 65535), "improbably large width");
+ Assert ((height < 65535), "improbably large height");
+ Assert ((x < 65535 && x > -65535), "improbably large x");
+ Assert ((y < 65535 && y > -65535), "improbably large y");
+
+ Assert (dest_image->depth == 32 && jwxyz_drawable_depth (d) == 32,
+ "XGetSubImage: bad depth");
+ Assert (format == ZPixmap, "XGetSubImage: bad format");
+
+ jwxyz_blit (jwxyz_image_data (d), jwxyz_image_pitch (d), x, y,
+ dest_image->data, dest_image->bytes_per_line, dest_x, dest_y,
+ width, height);
+
+ return dest_image;
+}
+
+
+static int
+SetClipMask (Display *dpy, GC gc, Pixmap m)
+{
+ Log ("TODO: No clip masks yet"); // Slip/colorbars.c needs this.
+ return 0;
+}
+
+static int
+SetClipOrigin (Display *dpy, GC gc, int x, int y)
+{
+ gc->gcv.clip_x_origin = x;
+ gc->gcv.clip_y_origin = y;
+ return 0;
+}
+
+
+const struct jwxyz_vtbl image_vtbl = {
+ root,
+ visual,
+ display_sources_data,
+
+ window_background,
+ draw_arc,
+ fill_rects,
+ gc_gcv,
+ gc_depth,
+ jwxyz_draw_string,
+
+ copy_area,
+
+ DrawPoints,
+ DrawSegments,
+ CreateGC,
+ FreeGC,
+ ClearWindow,
+ SetClipMask,
+ SetClipOrigin,
+ FillPolygon,
+ DrawLines,
+ PutImage,
+ GetSubImage
+};
+
+#endif /* JWXYZ_IMAGE -- entire file */