X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=jwxyz%2Fjwxyz-image.c;fp=jwxyz%2Fjwxyz-image.c;h=a419965be594b9b9388602a65c0f9cdc3603d700;hp=0000000000000000000000000000000000000000;hb=78add6e627ee5f10e1fa6f3852602ea5066eee5a;hpb=39809ded547bdbb08207d3e514950425215b4410 diff --git a/jwxyz/jwxyz-image.c b/jwxyz/jwxyz-image.c new file mode 100644 index 00000000..a419965b --- /dev/null +++ b/jwxyz/jwxyz-image.c @@ -0,0 +1,522 @@ +/* xscreensaver, Copyright (c) 1991-2018 Jamie Zawinski + * + * 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 + + +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"); + for (unsigned i = 0; i != 4; ++i) { + union color_bytes color; + color.pixel = 0; + color.bytes[rgba_bytes[i]] = 0xff; + v->rgba_masks[i] = color.pixel; + } + + 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 */