-/* xscreensaver, Copyright (c) 1991-2017 Jamie Zawinski <jwz@jwz.org>
+/* 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
struct jwxyz_Display {
+ const struct jwxyz_vtbl *vtbl; // Must come first.
+
Window main_window;
- Screen *screen;
+ CGBitmapInfo bitmap_info;
+ Visual visual;
struct jwxyz_sources_data *timers_data;
# ifndef USE_IPHONE
unsigned long window_background;
};
-struct jwxyz_Screen {
- Display *dpy;
- CGBitmapInfo bitmap_info;
- unsigned long black, white;
- Visual *visual;
-};
-
struct jwxyz_GC {
XGCValues gcv;
unsigned int depth;
};
-// 24/32bpp -> 32bpp image conversion.
+// 8/16/24/32bpp -> 32bpp image conversion.
// Any of RGBA, BGRA, ABGR, or ARGB can be represented by a rotate of 0/8/16/24
// bits and an optional byte order swap.
static const convert_mode_t CONVERT_MODE_SWAP = 0x4;
+#if defined __LITTLE_ENDIAN__
+# define PAD(r, g, b, a) ((r) | ((g) << 8) | ((b) << 16) | ((a) << 24))
+#elif defined __BIG_ENDIAN__
+# define PAD(r, g, b, a) (((r) << 24) | ((g) << 16) | ((b) << 8) | (a))
+#else
+# error "Can't determine system endianness."
+#endif
+
+
// Converts an array of pixels ('src') from one format to another, placing the
// result in 'dest', according to the pixel conversion mode 'mode'.
static void
convert_row (uint32_t *dest, const void *src, size_t count,
convert_mode_t mode, size_t src_bpp)
{
- Assert (src_bpp == 24 || src_bpp == 32, "weird bpp");
+ Assert (src_bpp == 8 || src_bpp == 24 || src_bpp == 16 || src_bpp == 32,
+ "weird bpp");
// This works OK iff src == dest or src and dest do not overlap.
- if (!mode) {
+ if (!mode && src_bpp == 32) {
if (src != dest)
memcpy (dest, src, count * 4);
return;
while (dest != dest_end) {
uint32_t x;
- if (src_bpp == 4)
- x = *(const uint32_t *)src;
- else { // src_bpp == 3
- const uint8_t *src8 = (const uint8_t *)src;
- // __LITTLE/BIG_ENDIAN__ are defined by the compiler.
-# if defined __LITTLE_ENDIAN__
- x = src8[0] | (src8[1] << 8) | (src8[2] << 16) | 0xff000000;
-# elif defined __BIG_ENDIAN__
- x = (src8[0] << 24) | (src8[1] << 16) | (src8[2] << 8) | 0xff;
-# else
-# error "Can't determine system endianness."
-# endif
+ const uint8_t *src8 = (const uint8_t *)src;
+ switch (src_bpp) {
+ case 4:
+ x = *(const uint32_t *)src;
+ break;
+ case 3:
+ x = PAD(src8[0], src8[1], src8[2], 0xff);
+ break;
+ case 2:
+ x = PAD(src8[0], src8[0], src8[0], src8[1]);
+ break;
+ case 1:
+ x = PAD(src8[0], src8[0], src8[0], 0xff);
+ break;
}
src = (const uint8_t *)src + src_bpp;
2. Clang's warning for this, -Wshift-count-overflow, only works when the
shift count is a literal constant, as opposed to an arbitrary
expression that is optimized down to a constant.
- Put together, this means that the assertions in jwxyz_make_display with
- convert_px break with the above naive rotation, but only for a release
- build.
+ Put together, this means that the assertions in
+ jwxyz_quartz_make_display with convert_px break with the above naive
+ rotation, but only for a release build.
http://blog.regehr.org/archives/1063
http://llvm.org/bugs/show_bug.cgi?id=17332
};
-uint32_t
-jwxyz_alloc_color (Display *dpy,
- uint16_t r, uint16_t g, uint16_t b, uint16_t a)
-{
- union color_bytes color;
-
- /* Instead of (int)(c / 256.0), another possibility is
- (int)(c * 255.0 / 65535.0 + 0.5). This can be calculated using only
- uint8_t integer_math(uint16_t c) {
- unsigned c0 = c + 128;
- return (c0 - (c0 >> 8)) >> 8;
- }
- */
-
- color.bytes[0] = r >> 8;
- color.bytes[1] = g >> 8;
- color.bytes[2] = b >> 8;
- color.bytes[3] = a >> 8;
-
- return
- convert_px (color.pixel,
- convert_mode_invert (convert_mode_to_rgba (dpy->screen->bitmap_info)));
-}
-
-
-void
-jwxyz_query_color (Display *dpy, unsigned long pixel, uint8_t *rgba)
-{
- union color_bytes color;
- color.pixel = convert_px ((uint32_t)pixel,
- convert_mode_to_rgba (dpy->screen->bitmap_info));
- for (unsigned i = 0; i != 4; ++i)
- rgba[i] = color.bytes[i];
-}
-
-
static void
-query_color_float (Display *dpy, unsigned long pixel, float *rgba)
+query_color_float (Display *dpy, unsigned long pixel, CGFloat *rgba)
{
- uint8_t rgba8[4];
- jwxyz_query_color (dpy, pixel, rgba8);
- for (unsigned i = 0; i != 4; ++i)
- rgba[i] = rgba8[i] * (1.0f / 255.0f);
+ JWXYZ_QUERY_COLOR (dpy, pixel, (CGFloat)1, rgba);
}
+extern const struct jwxyz_vtbl quartz_vtbl;
+
Display *
-jwxyz_make_display (Window w)
+jwxyz_quartz_make_display (Window w)
{
CGContextRef cgc = w->cgc;
Display *d = (Display *) calloc (1, sizeof(*d));
- d->screen = (Screen *) calloc (1, sizeof(Screen));
- d->screen->dpy = d;
-
- d->screen->bitmap_info = CGBitmapContextGetBitmapInfo (cgc);
- d->screen->black = jwxyz_alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF);
- d->screen->white = jwxyz_alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
+ d->vtbl = &quartz_vtbl;
+
+ d->bitmap_info = CGBitmapContextGetBitmapInfo (cgc);
# if 0
// Tests for the image conversion modes.
}
# endif
- Visual *v = (Visual *) calloc (1, sizeof(Visual));
+ Visual *v = &d->visual;
v->class = TrueColor;
- v->red_mask = jwxyz_alloc_color (d, 0xFFFF, 0x0000, 0x0000, 0x0000);
- v->green_mask = jwxyz_alloc_color (d, 0x0000, 0xFFFF, 0x0000, 0x0000);
- v->blue_mask = jwxyz_alloc_color (d, 0x0000, 0x0000, 0xFFFF, 0x0000);
- CGBitmapInfo byte_order = d->screen->bitmap_info & kCGBitmapByteOrderMask;
- Assert ( ! (d->screen->bitmap_info & kCGBitmapFloatComponents) &&
+
+ union color_bytes color;
+ convert_mode_t mode =
+ convert_mode_invert (convert_mode_to_rgba (d->bitmap_info));
+ for (unsigned i = 0; i != 4; ++i) {
+ color.pixel = 0;
+ color.bytes[i] = 0xff;
+ v->rgba_masks[i] = convert_px (color.pixel, mode);
+ }
+
+ CGBitmapInfo byte_order = d->bitmap_info & kCGBitmapByteOrderMask;
+ Assert ( ! (d->bitmap_info & kCGBitmapFloatComponents) &&
(byte_order == kCGBitmapByteOrder32Little ||
byte_order == kCGBitmapByteOrder32Big),
"invalid bits per channel");
- d->screen->visual = v;
d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
}
void
-jwxyz_free_display (Display *dpy)
+jwxyz_quartz_free_display (Display *dpy)
{
jwxyz_sources_free (dpy->timers_data);
- free (dpy->screen->visual);
- free (dpy->screen);
free (dpy);
}
CGContextFlush(dpy->main_window->cgc);
}
-jwxyz_sources_data *
+static jwxyz_sources_data *
display_sources_data (Display *dpy)
{
return dpy->timers_data;
}
-Window
-XRootWindow (Display *dpy, int screen)
-{
- return dpy ? dpy->main_window : 0;
-}
-
-Screen *
-XDefaultScreenOfDisplay (Display *dpy)
-{
- return dpy ? dpy->screen : 0;
-}
-
-Visual *
-XDefaultVisualOfScreen (Screen *screen)
-{
- return screen ? screen->visual : 0;
-}
-
-Display *
-XDisplayOfScreen (Screen *s)
-{
- return s ? s->dpy : 0;
-}
-
-int
-XDisplayNumberOfScreen (Screen *s)
-{
- return 0;
-}
-
-int
-XScreenNumberOfScreen (Screen *s)
-{
- return 0;
-}
-
-unsigned long
-XBlackPixelOfScreen(Screen *screen)
-{
- return screen->black;
-}
-
-unsigned long
-XWhitePixelOfScreen(Screen *screen)
+static Window
+root (Display *dpy)
{
- return screen->white;
+ return dpy->main_window;
}
-unsigned long
-XCellsOfScreen(Screen *screen)
+static Visual *
+visual (Display *dpy)
{
- Visual *v = screen->visual;
- return v->red_mask | v->green_mask | v->blue_mask;
+ return &dpy->visual;
}
else
CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
} else {
- float rgba[4];
+ CGFloat rgba[4];
query_color_float (dpy, argb, rgba);
if (fill_p)
CGContextSetRGBFillColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
*/
#define XDRAWPOINTS_CGDATA
-int
-XDrawPoints (Display *dpy, Drawable d, GC gc,
- XPoint *points, int count, int mode)
+static int
+DrawPoints (Display *dpy, Drawable d, GC gc,
+ XPoint *points, int count, int mode)
{
int i;
XRectangle wr = d->frame;
dpy->colorspace,
/* Host-ordered, since we're using the
address of an int as the color data. */
- dpy->screen->bitmap_info,
+ dpy->bitmap_info,
prov,
NULL, /* decode[] */
NO, /* interpolate */
}
-int
-XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
-{
- // when drawing a zero-length line, obey line-width and cap-style.
- if (x1 == x2 && y1 == y2) {
- int w = gc->gcv.line_width;
- x1 -= w/2;
- y1 -= w/2;
- if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
- return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
- else {
- if (!w)
- w = 1; // Actually show zero-length lines.
- return XFillRectangle (dpy, d, gc, x1, y1, w, w);
- }
- }
-
- CGPoint p = point_for_line (d, gc, x1, y1);
-
- push_fg_gc (dpy, d, gc, NO);
-
- CGContextRef cgc = d->cgc;
- set_line_mode (cgc, &gc->gcv);
- CGContextBeginPath (cgc);
- CGContextMoveToPoint (cgc, p.x, p.y);
- p = point_for_line(d, gc, x2, y2);
- CGContextAddLineToPoint (cgc, p.x, p.y);
- CGContextStrokePath (cgc);
- pop_gc (d, gc);
- invalidate_drawable_cache (d);
- return 0;
-}
-
-int
-XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
+static int
+DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
int mode)
{
int i;
}
-int
-XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
+static int
+DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
{
int i;
set_line_mode (cgc, &gc->gcv);
CGContextBeginPath (cgc);
for (i = 0; i < count; i++) {
- CGPoint p = point_for_line (d, gc, segments->x1, segments->y1);
- CGContextMoveToPoint (cgc, p.x, p.y);
- p = point_for_line (d, gc, segments->x2, segments->y2);
- CGContextAddLineToPoint (cgc, p.x, p.y);
+ // when drawing a zero-length line, obey line-width and cap-style.
+ if (segments->x1 == segments->x2 && segments->y1 == segments->y2) {
+ int w = gc->gcv.line_width;
+ int x1 = segments->x1 - w/2;
+ int y1 = segments->y1 - w/2;
+ if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
+ XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
+ else {
+ if (!w)
+ w = 1; // Actually show zero-length lines.
+ XFillRectangle (dpy, d, gc, x1, y1, w, w);
+ }
+ } else {
+ CGPoint p = point_for_line (d, gc, segments->x1, segments->y1);
+ CGContextMoveToPoint (cgc, p.x, p.y);
+ p = point_for_line (d, gc, segments->x2, segments->y2);
+ CGContextAddLineToPoint (cgc, p.x, p.y);
+ }
+
segments++;
}
CGContextStrokePath (cgc);
}
-int
-XClearWindow (Display *dpy, Window win)
+static int
+ClearWindow (Display *dpy, Window win)
{
Assert (win && win->type == WINDOW, "not a window");
XRectangle wr = win->frame;
return XClearArea (dpy, win, 0, 0, wr.width, wr.height, 0);
}
-unsigned long
-jwxyz_window_background (Display *dpy)
+static unsigned long *
+window_background (Display *dpy)
{
- return dpy->window_background;
+ return &dpy->window_background;
}
-int
-XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
-{
- Assert (w && w->type == WINDOW, "not a window");
- jwxyz_validate_pixel (dpy, pixel, 32, NO);
- dpy->window_background = pixel;
- return 0;
-}
-
-void
-jwxyz_fill_rects (Display *dpy, Drawable d, GC gc,
- const XRectangle *rectangles, unsigned long nrectangles,
- unsigned long pixel)
+static void
+fill_rects (Display *dpy, Drawable d, GC gc,
+ const XRectangle *rectangles, unsigned long nrectangles,
+ unsigned long pixel)
{
Assert (!gc || gc->depth == jwxyz_drawable_depth (d), "depth mismatch");
pixel = pixel ? WhitePixel(dpy, 0) : BlackPixel(dpy, 0);
size_t dst_bytes_per_row = CGBitmapContextGetBytesPerRow (d->cgc);
- void *dst = seek_xy (CGBitmapContextGetData (d->cgc),
+ void *dst = SEEK_XY (CGBitmapContextGetData (d->cgc),
dst_bytes_per_row, x, y);
Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
}
-int
-XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
-{
- Assert (win && win->type == WINDOW, "not a window");
- jwxyz_fill_rect (dpy, win, 0, x, y, w, h, dpy->window_background);
- return 0;
-}
-
-
-int
-XFillPolygon (Display *dpy, Drawable d, GC gc,
- XPoint *points, int npoints, int shape, int mode)
+static int
+FillPolygon (Display *dpy, Drawable d, GC gc,
+ XPoint *points, int npoints, int shape, int mode)
{
XRectangle wr = d->frame;
int i;
#define radians(DEG) ((DEG) * M_PI / 180.0)
#define degrees(RAD) ((RAD) * 180.0 / M_PI)
-int
-jwxyz_draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
+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)
{
}
-XGCValues *
-jwxyz_gc_gcv (GC gc)
+static XGCValues *
+gc_gcv (GC gc)
{
return &gc->gcv;
}
-unsigned int
-jwxyz_gc_depth (GC gc)
+static unsigned int
+gc_depth (GC gc)
{
return gc->depth;
}
-GC
-XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
+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);
}
-int
-XFreeGC (Display *dpy, GC gc)
+static int
+FreeGC (Display *dpy, GC gc)
{
if (gc->gcv.font)
XUnloadFont (dpy, gc->gcv.font);
}
-int
-XPutImage (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)
+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)
{
XRectangle wr = d->frame;
CGImageRef cgi = CGImageCreate (w, h,
bpp/4, bpp, bpl,
dpy->colorspace,
- dpy->screen->bitmap_info,
+ dpy->bitmap_info,
prov,
NULL, /* decode[] */
NO, /* interpolate */
}
-XImage *
-XGetSubImage (Display *dpy, Drawable d, int x, int y,
- unsigned int width, unsigned int height,
- unsigned long plane_mask, int format,
- XImage *image, int dest_x, int dest_y)
+static XImage *
+GetSubImage (Display *dpy, Drawable d, int x, int y,
+ unsigned int width, unsigned int height,
+ unsigned long plane_mask, int format,
+ XImage *image, int dest_x, int dest_y)
{
const unsigned char *data = 0;
size_t depth, ibpp, ibpl;
{
depth = jwxyz_drawable_depth (d);
- mode = convert_mode_to_rgba (dpy->screen->bitmap_info);
+ mode = convert_mode_to_rgba (dpy->bitmap_info);
ibpp = CGBitmapContextGetBitsPerPixel (cgc);
ibpl = CGBitmapContextGetBytesPerRow (cgc);
data = CGBitmapContextGetData (cgc);
mode = convert_mode_merge (mode,
convert_mode_invert (
- convert_mode_to_rgba (dpy->screen->bitmap_info)));
+ convert_mode_to_rgba (dpy->bitmap_info)));
for (yy = 0; yy < height; yy++) {
float rh = winr.height / imgr.height;
float r = (rw < rh ? rw : rh);
+ /* If the window is a goofy aspect ratio, take a middle slice of
+ the image instead. */
+ if (winr.width > winr.height * 5 ||
+ winr.width > winr.width * 5) {
+ r *= (winr.width > winr.height
+ ? winr.width / (double) winr.height
+ : winr.height / (double) winr.width);
+ // NSLog (@"weird aspect: scaling by %.1f\n", r);
+ }
+
CGRect dst, dst2;
dst.size.width = imgr.width * r;
dst.size.height = imgr.height * r;
}
+XImage *
+jwxyz_png_to_ximage (Display *dpy, Visual *visual,
+ const unsigned char *png_data, unsigned long data_size)
+{
+ NSImage *img = [[NSImage alloc] initWithData:
+ [NSData dataWithBytes:png_data
+ length:data_size]];
+#ifndef USE_IPHONE
+ NSBitmapImageRep *bm = [NSBitmapImageRep
+ imageRepWithData:
+ [NSBitmapImageRep
+ TIFFRepresentationOfImageRepsInArray:
+ [img representations]]];
+ int width = [img size].width;
+ int height = [img size].height;
+ size_t ibpp = [bm bitsPerPixel];
+ size_t ibpl = [bm bytesPerRow];
+ const unsigned char *data = [bm bitmapData];
+ convert_mode_t mode = (([bm bitmapFormat] & NSAlphaFirstBitmapFormat)
+ ? CONVERT_MODE_ROTATE_MASK
+ : 0);
+#else // USE_IPHONE
+ CGImageRef cgi = [img CGImage];
+ int width = CGImageGetWidth (cgi);
+ int height = CGImageGetHeight (cgi);
+ size_t ibpp = 32;
+ size_t ibpl = ibpp/4 * width;
+ unsigned char *data = (unsigned char *) calloc (ibpl, height);
+ const CGBitmapInfo bitmap_info =
+ kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast;
+ CGContextRef cgc =
+ CGBitmapContextCreate (data, width, height,
+ 8, /* bits per component */
+ ibpl, dpy->colorspace,
+ bitmap_info);
+ CGContextDrawImage (cgc, CGRectMake (0, 0, width, height), cgi);
+
+ convert_mode_t mode = convert_mode_to_rgba (bitmap_info);
+
+#endif // USE_IPHONE
+
+ XImage *image = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0,
+ width, height, 8, 0);
+ image->data = (char *) malloc (image->height * image->bytes_per_line);
+
+ // data points at (x,y) with ibpl rowstride.
+
+ int obpl = image->bytes_per_line;
+ const unsigned char *iline = data;
+ unsigned char *oline = (unsigned char *) image->data;
+ int yy;
+ for (yy = 0; yy < height; yy++) {
+ convert_row ((uint32_t *)oline, iline, width, mode, ibpp);
+ oline += obpl;
+ iline += ibpl;
+ }
+
+ [img release];
+
+#ifndef USE_IPHONE
+ // [bm release];
+# else
+ CGContextRelease (cgc);
+ free (data);
+# endif
+
+ return image;
+}
+
Pixmap
XCreatePixmap (Display *dpy, Drawable d,
8, /* bits per component */
width * 4, /* bpl */
dpy->colorspace,
- dpy->screen->bitmap_info);
+ dpy->bitmap_info);
Assert (p->cgc, "could not create CGBitmapContext");
return p;
}
}
-int
-jwxyz_draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
- const char *str, size_t len, int utf8_p)
+static int
+draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
+ const char *str, size_t len, int utf8_p)
{
NSString *nsstr = nsstring_from (str, len, utf8_p);
unsigned long argb = gc->gcv.foreground;
if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
- float rgba[4];
+ CGFloat rgba[4];
query_color_float (dpy, argb, rgba);
NSColor *fg = [NSColor colorWithDeviceRed:rgba[0]
green:rgba[1]
}
-int
-XSetClipMask (Display *dpy, GC gc, Pixmap m)
+static int
+SetClipMask (Display *dpy, GC gc, Pixmap m)
{
Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
return 0;
}
-int
-XSetClipOrigin (Display *dpy, GC gc, int x, int y)
+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 quartz_vtbl = {
+ root,
+ visual,
+ display_sources_data,
+
+ window_background,
+ draw_arc,
+ fill_rects,
+ gc_gcv,
+ gc_depth,
+ draw_string,
+
+ jwxyz_quartz_copy_area,
+
+ DrawPoints,
+ DrawSegments,
+ CreateGC,
+ FreeGC,
+ ClearWindow,
+ SetClipMask,
+ SetClipOrigin,
+ FillPolygon,
+ DrawLines,
+ PutImage,
+ GetSubImage
+};
+
#endif // JWXYZ_QUARTZ -- entire file