X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=jwxyz%2Fjwxyz-gl.c;h=0766a4521f884f73dc107b3eacf9e87ed4f8036f;hp=2f15b1aa0199e050c6bce59b17d177561cc3ed5d;hb=4361b69d3178d7fc98d0388f9a223af6c2651aba;hpb=d6b0217f2417bd19187f0ebc389d6c5c2233b11c diff --git a/jwxyz/jwxyz-gl.c b/jwxyz/jwxyz-gl.c index 2f15b1aa..0766a452 100644 --- a/jwxyz/jwxyz-gl.c +++ b/jwxyz/jwxyz-gl.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1991-2016 Jamie Zawinski +/* xscreensaver, Copyright (c) 1991-2017 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 @@ -144,6 +144,7 @@ #include "yarandom.h" #include "utf8wc.h" #include "xft.h" +#include "pow2.h" #if defined HAVE_COCOA # include @@ -185,7 +186,6 @@ union color_bytes struct jwxyz_Display { Window main_window; Screen *screen; - int screen_count; struct jwxyz_sources_data *timers_data; Bool gl_texture_npot_p; @@ -204,7 +204,6 @@ struct jwxyz_Screen { GLenum pixel_format, pixel_type; unsigned long black, white; Visual *visual; - int screen_number; }; struct jwxyz_GC { @@ -213,24 +212,14 @@ struct jwxyz_GC { // CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask }; -struct jwxyz_Font { - Display *dpy; - char *ps_name; - void *native_font; - int refcount; // for deciding when to release the native font - float size; // points - int ascent, descent; - char *xa_font; - - // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics. - // But we need the metrics on both of them, so they go here. - XFontStruct metrics; -}; - struct jwxyz_XFontSet { XFontStruct *font; }; +struct jwxyz_linked_point { + short x, y; + linked_point *next; +}; /* XGetImage in CoreGraphics JWXYZ has to deal with funky pixel formats necessitating fast & flexible pixel conversion. OpenGL does image format @@ -307,6 +296,9 @@ void jwxyz_set_matrices (Display *dpy, unsigned width, unsigned height, Bool window_p) { + Assert (width, "no width"); + Assert (height, "no height"); + /* TODO: Check registration pattern from Interference with rectangles instead of points. */ // The projection matrix is always set as follows. The modelview matrix is @@ -369,25 +361,6 @@ static GLboolean gl_check_ext(const struct gl_caps *caps, // NSOpenGLContext *jwxyz_debug_context; -/* We keep a list of all of the Displays that have been created and not - yet freed so that they can have sensible display numbers. If three - displays are created (0, 1, 2) and then #1 is closed, then the fourth - display will be given the now-unused display number 1. (Everything in - here assumes a 1:1 Display/Screen mapping.) - - The size of this array is the most number of live displays at one time. - So if it's 20, then we'll blow up if the system has 19 monitors and also - has System Preferences open (the small preview window). - - Note that xlockmore-style savers tend to allocate big structures, so - setting this to 1000 will waste a few megabytes. Also some of them assume - that the number of screens never changes, so dynamically expanding this - array won't work. - */ -# ifndef USE_IPHONE -static Display *jwxyz_live_displays[20] = { 0, }; -# endif - Display * jwxyz_make_display (Window w) @@ -395,24 +368,6 @@ jwxyz_make_display (Window w) Display *d = (Display *) calloc (1, sizeof(*d)); d->screen = (Screen *) calloc (1, sizeof(Screen)); d->screen->dpy = d; - - d->screen_count = 1; - d->screen->screen_number = 0; -# ifndef USE_IPHONE - { - // Find the first empty slot in live_displays and plug us in. - int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays); - int i; - for (i = 0; i < size; i++) { - if (! jwxyz_live_displays[i]) - break; - } - if (i >= size) abort(); - jwxyz_live_displays[i] = d; - d->screen_count = size; - d->screen->screen_number = i; - } -# endif // !USE_IPHONE # ifndef HAVE_JWZGLES struct gl_version version; @@ -491,7 +446,6 @@ jwxyz_make_display (Window w) 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); - v->bits_per_rgb = 8; d->screen->visual = v; d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d)); @@ -547,21 +501,6 @@ jwxyz_free_display (Display *dpy) jwxyz_sources_free (dpy->timers_data); -# ifndef USE_IPHONE - { - // Find us in live_displays and clear that slot. - int size = ScreenCount(dpy); - int i; - for (i = 0; i < size; i++) { - if (dpy == jwxyz_live_displays[i]) { - jwxyz_live_displays[i] = 0; - break; - } - } - if (i >= size) abort(); - } -# endif // !USE_IPHONE - free (dpy->screen->visual); free (dpy->screen); free (dpy); @@ -675,13 +614,7 @@ XDisplayNumberOfScreen (Screen *s) int XScreenNumberOfScreen (Screen *s) { - return s? s->screen_number : 0; -} - -int -jwxyz_ScreenCount (Display *dpy) -{ - return dpy ? dpy->screen_count : 0; + return 0; } unsigned long @@ -912,26 +845,19 @@ static void set_white (void) } -static GLsizei to_pow2 (size_t x); - - void -jwxyz_gl_copy_area_copy_tex_image (Display *dpy, Drawable src, Drawable dst, - GC gc, int src_x, int src_y, +jwxyz_gl_copy_area_read_tex_image (Display *dpy, unsigned src_height, + int src_x, int src_y, unsigned int width, unsigned int height, int dst_x, int dst_y) { - const XRectangle *src_frame = jwxyz_frame (src); - - Assert(gc->gcv.function == GXcopy, "XCopyArea: Unknown function"); - - jwxyz_bind_drawable (dpy, dpy->main_window, src); - # if defined HAVE_COCOA && !defined USE_IPHONE /* TODO: Does this help? */ /* glFinish(); */ # endif + /* TODO: Fix TestX11 + mode_preserve with this one. */ + unsigned tex_w = width, tex_h = height; if (!dpy->gl_texture_npot_p) { tex_w = to_pow2(tex_w); @@ -944,20 +870,47 @@ jwxyz_gl_copy_area_copy_tex_image (Display *dpy, Drawable src, Drawable dst, if (tex_w == width && tex_h == height) { glCopyTexImage2D (dpy->gl_texture_target, 0, internalformat, - src_x, src_frame->height - src_y - height, - width, height, 0); + src_x, src_height - src_y - height, width, height, 0); } else { glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h, 0, dpy->screen->pixel_format, gl_pixel_type(dpy), NULL); glCopyTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, - src_x, src_frame->height - src_y - height, - width, height); + src_x, src_height - src_y - height, width, height); + } +} + +void +jwxyz_gl_copy_area_write_tex_image (Display *dpy, GC gc, int src_x, int src_y, + unsigned int width, unsigned int height, + int dst_x, int dst_y) +{ + Assert(gc->gcv.function == GXcopy, "XCopyArea: Unknown function"); + + /* TODO: Copy-pasted from read_tex_image. */ + unsigned tex_w = width, tex_h = height; + if (!dpy->gl_texture_npot_p) { + tex_w = to_pow2(tex_w); + tex_h = to_pow2(tex_h); } - jwxyz_bind_drawable (dpy, dpy->main_window, dst); - set_white (); glBindTexture (dpy->gl_texture_target, dpy->rect_texture); - glEnable (dpy->gl_texture_target); + + jwxyz_gl_draw_image (dpy->gl_texture_target, tex_w, tex_h, 0, 0, + width, height, dst_x, dst_y); + + clear_texture (dpy); +} + + +void +jwxyz_gl_draw_image (GLenum gl_texture_target, + unsigned int tex_w, unsigned int tex_h, + int src_x, int src_y, + unsigned int width, unsigned int height, + int dst_x, int dst_y) +{ + set_white (); + glEnable (gl_texture_target); glEnableClientState (GL_TEXTURE_COORD_ARRAY); glEnableClientState (GL_VERTEX_ARRAY); @@ -971,20 +924,35 @@ jwxyz_gl_copy_area_copy_tex_image (Display *dpy, Drawable src, Drawable dst, {dst_x + width, dst_y + height}, {dst_x + width, dst_y} }; - -#ifdef HAVE_JWZGLES - static const GLshort tex_coords[4][2] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}}; -#else - GLshort tex_coords[4][2] = {{0, height}, {0, 0}, {width, 0}, {width, height}}; -#endif + + GLfloat + tex_x0 = src_x, tex_y0 = src_y + height, + tex_x1 = src_x + width, tex_y1 = src_y; + +# ifndef HAVE_JWZGLES + if (gl_texture_target != GL_TEXTURE_RECTANGLE_EXT) +# endif + { + GLfloat mx = 1.0f / tex_w, my = 1.0f / tex_h; + tex_x0 *= mx; + tex_y0 *= my; + tex_x1 *= mx; + tex_y1 *= my; + } + + GLfloat tex_coords[4][2] = + { + {tex_x0, tex_y0}, + {tex_x0, tex_y1}, + {tex_x1, tex_y1}, + {tex_x1, tex_y0} + }; glVertexPointer (2, GL_FLOAT, 0, vertices); - glTexCoordPointer (2, GL_SHORT, 0, tex_coords); + glTexCoordPointer (2, GL_FLOAT, 0, tex_coords); glDrawArrays (GL_TRIANGLE_FAN, 0, 4); - - clear_texture (dpy); - - glDisable (dpy->gl_texture_target); + + glDisable (gl_texture_target); } void @@ -993,40 +961,9 @@ jwxyz_gl_copy_area_read_pixels (Display *dpy, Drawable src, Drawable dst, unsigned int width, unsigned int height, int dst_x, int dst_y) { -# if 1 XImage *img = XGetImage (dpy, src, src_x, src_y, width, height, ~0, ZPixmap); XPutImage (dpy, dst, gc, img, 0, 0, dst_x, dst_y, width, height); XDestroyImage (img); -# endif - -# if 0 - /* Something may or may not be broken in here. (shrug) */ - bind_drawable(dpy, src); - - /* Error checking would be nice. */ - void *pixels = malloc (src_rect.size.width * 4 * src_rect.size.height); - - glPixelStorei (GL_PACK_ROW_LENGTH, 0); - glPixelStorei (GL_PACK_ALIGNMENT, 4); - - glReadPixels (src_rect.origin.x, dst_frame.size.height - (src_rect.origin.y + src_rect.size.height), - src_rect.size.width, src_rect.size.height, - GL_RGBA, GL_UNSIGNED_BYTE, // TODO: Pick better formats. - pixels); - - bind_drawable (dpy, dst); - - glPixelZoom (1.0f, 1.0f); - - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei (GL_UNPACK_ALIGNMENT, 4); - - glRasterPos2i (dst_rect.origin.x, dst_rect.origin.y + dst_rect.size.height); - glDrawPixels (dst_rect.size.width, dst_rect.size.height, - GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - free(pixels); -# endif } @@ -1283,11 +1220,18 @@ XFillPolygon (Display *dpy, Drawable d, GC gc, /* Complex: Pedal, and for some reason Attraction, Mountain, Qix, SpeedMine, Starfish * Nonconvex: Goop, Pacman, Rocks, Speedmine + * + * We currently do Nonconvex with the simple-to-implement ear clipping + * algorithm, but in the future we can replace that with an algorithm + * with slower big-O growth + * */ - Assert(shape == Convex, "XFillPolygon: (TODO) Unimplemented shape"); // TODO: Feed vertices straight to OpenGL for CoordModeOrigin. + + if (shape == Convex) { + GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc. short v[2] = {0, 0}; @@ -1305,35 +1249,20 @@ XFillPolygon (Display *dpy, Drawable d, GC gc, free(vertices); - /* - CGRect wr = d->frame; - int i; - push_fg_gc (dpy, d, gc, YES); - CGContextRef cgc = d->cgc; - CGContextBeginPath (cgc); - float x = 0, y = 0; - for (i = 0; i < npoints; i++) { - if (i > 0 && mode == CoordModePrevious) { - x += points[i].x; - y -= points[i].y; - } else { - x = wr.origin.x + points[i].x; - y = wr.origin.y + wr.size.height - points[i].y; - } - - if (i == 0) - CGContextMoveToPoint (cgc, x, y); - else - CGContextAddLineToPoint (cgc, x, y); + } else if (shape == Nonconvex) { + + // TODO: assert that x,y of first and last point match, as that is assumed + + linked_point *root; + root = (linked_point *) malloc( sizeof(linked_point) ); + set_points_list(points,npoints,root); + traverse_points_list(root); + + } else { + Assert((shape == Convex || shape == Nonconvex), "XFillPolygon: (TODO) Unimplemented shape"); } - CGContextClosePath (cgc); - if (gc->gcv.fill_rule == EvenOddRule) - CGContextEOFillPath (cgc); - else - CGContextFillPath (cgc); - pop_gc (d, gc); - invalidate_drawable_cache (d); - */ + + return 0; } @@ -1575,31 +1504,6 @@ flipbits (unsigned const char *in, unsigned char *out, int length) } */ -// Copied and pasted from OSX/XScreenSaverView.m -static GLsizei -to_pow2 (size_t x) -{ - if (x <= 1) - return 1; - - size_t mask = (size_t)-1; - unsigned bits = sizeof(x) * CHAR_BIT; - unsigned log2 = bits; - - --x; - while (bits) { - if (!(x & mask)) { - log2 -= bits; - x <<= bits; - } - - bits >>= 1; - mask <<= bits; - } - - return 1 << log2; -} - int XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage, @@ -1832,12 +1736,11 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage, return 0; } -/* At the moment nothing actually uses XGetSubImage. */ -/* #### Actually lots of things call XGetImage, which calls XGetSubImage. - E.g., Twang calls XGetImage on the window intending to get a +/* At the moment only XGetImage and get_xshm_image use XGetSubImage. */ +/* #### Twang calls XGetImage on the window intending to get a buffer full of black. This is returning a buffer full of white instead of black for some reason. */ -static XImage * +XImage * XGetSubImage (Display *dpy, Drawable d, int x, int y, unsigned int width, unsigned int height, unsigned long plane_mask, int format, @@ -1913,19 +1816,6 @@ XGetSubImage (Display *dpy, Drawable d, int x, int y, return dest_image; } -XImage * -XGetImage (Display *dpy, Drawable d, int x, int y, - unsigned int width, unsigned int height, - unsigned long plane_mask, int format) -{ - unsigned depth = jwxyz_drawable_depth (d); - XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height, - 0, 0); - image->data = (char *) malloc (height * image->bytes_per_line); - - return XGetSubImage (dpy, d, x, y, width, height, plane_mask, format, - image, 0, 0); -} /* Returns a transformation matrix to do rotation as per the provided EXIF "Orientation" value. @@ -2131,944 +2021,257 @@ copy_pixmap (Display *dpy, Pixmap p) #endif -// This is XQueryFont, but for the XFontStruct embedded in 'Font' -// -static void -query_font (Font fid) -{ - if (!fid || !fid->native_font) { - Assert (0, "no native font in fid"); - return; - } - - int first = 32; - int last = 255; - - Display *dpy = fid->dpy; - void *native_font = fid->native_font; - - XFontStruct *f = &fid->metrics; - XCharStruct *min = &f->min_bounds; - XCharStruct *max = &f->max_bounds; - - f->fid = fid; - f->min_char_or_byte2 = first; - f->max_char_or_byte2 = last; - f->default_char = 'M'; - f->ascent = fid->ascent; - f->descent = fid->descent; - - min->width = 32767; // set to smaller values in the loop - min->ascent = 32767; - min->descent = 32767; - min->lbearing = 32767; - min->rbearing = 32767; - - f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct)); - - for (int i = first; i <= last; i++) { - XCharStruct *cs = &f->per_char[i-first]; - char s = (char) i; - jwxyz_render_text (dpy, native_font, &s, 1, GL_FALSE, cs, 0); - - max->width = MAX (max->width, cs->width); - max->ascent = MAX (max->ascent, cs->ascent); - max->descent = MAX (max->descent, cs->descent); - max->lbearing = MAX (max->lbearing, cs->lbearing); - max->rbearing = MAX (max->rbearing, cs->rbearing); - - min->width = MIN (min->width, cs->width); - min->ascent = MIN (min->ascent, cs->ascent); - min->descent = MIN (min->descent, cs->descent); - min->lbearing = MIN (min->lbearing, cs->lbearing); - min->rbearing = MIN (min->rbearing, cs->rbearing); -/* - Log (" %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d\n", - i, i, cs->width, cs->lbearing, cs->rbearing, - cs->ascent, cs->descent); - */ - } -} - - -// Since 'Font' includes the metrics, this just makes a copy of that. -// -XFontStruct * -XQueryFont (Display *dpy, Font fid) -{ - // copy XFontStruct - XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f)); - *f = fid->metrics; - f->fid = fid; - - // build XFontProps - f->n_properties = 1; - f->properties = malloc (sizeof(*f->properties) * f->n_properties); - f->properties[0].name = XA_FONT; - Assert (sizeof (f->properties[0].card32) >= sizeof (char *), - "atoms probably needs a real implementation"); - // If XInternAtom is ever implemented, use it here. - f->properties[0].card32 = (unsigned long)(char *)fid->xa_font; - - // copy XCharStruct array - int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1; - f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct)); - - memcpy (f->per_char, fid->metrics.per_char, - size * sizeof (XCharStruct)); - - return f; -} - - -static Font -copy_font (Font fid) -{ - fid->refcount++; - return fid; -} - - -#if 0 - - -static NSArray * -font_family_members (NSString *family_name) -{ -# ifndef USE_IPHONE - return [[NSFontManager sharedFontManager] - availableMembersOfFontFamily:family_name]; -# else - return [UIFont fontNamesForFamilyName:family_name]; -# endif -} - - -static NSString * -default_font_family (NSFontTraitMask require) -{ - return require & NSFixedPitchFontMask ? @"Courier" : @"Verdana"; -} - - -static NSFont * -try_font (NSFontTraitMask traits, NSFontTraitMask mask, - NSString *family_name, float size, - char **name_ret) +int +jwxyz_draw_string (Display *dpy, Drawable d, GC gc, int x, int y, + const char *str, size_t len, int utf8_p) { - Assert (size > 0, "zero font size"); - - NSArray *family_members = font_family_members (family_name); - if (!family_members.count) - family_members = font_family_members (default_font_family (traits)); + Font ff = gc->gcv.font; + XCharStruct cs; -# ifndef USE_IPHONE - for (unsigned k = 0; k != family_members.count; ++k) { - - NSArray *member = [family_members objectAtIndex:k]; - NSFontTraitMask font_mask = - [(NSNumber *)[member objectAtIndex:3] unsignedIntValue]; - - if ((font_mask & mask) == traits) { - - NSString *name = [member objectAtIndex:0]; - NSFont *f = [NSFont fontWithName:name size:size]; - if (!f) - break; - - /* Don't use this font if it (probably) doesn't include ASCII characters. - */ - NSStringEncoding enc = [f mostCompatibleStringEncoding]; - if (! (enc == NSUTF8StringEncoding || - enc == NSISOLatin1StringEncoding || - enc == NSNonLossyASCIIStringEncoding || - enc == NSISOLatin2StringEncoding || - enc == NSUnicodeStringEncoding || - enc == NSWindowsCP1250StringEncoding || - enc == NSWindowsCP1252StringEncoding || - enc == NSMacOSRomanStringEncoding)) { - // NSLog(@"skipping \"%@\": encoding = %d", name, enc); - break; - } - // NSLog(@"using \"%@\": %d", name, enc); + char *data = 0; + jwxyz_render_text (dpy, jwxyz_native_font (ff), str, len, utf8_p, &cs, &data); + int w = cs.rbearing - cs.lbearing; + int h = cs.ascent + cs.descent; - // *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]); - *name_ret = strdup (name.UTF8String); - return f; - } - } -# else // USE_IPHONE - - for (NSString *fn in family_members) { -# define MATCH(X) \ - ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \ - != NSNotFound) - - // The magic invocation for getting font names is - // [[UIFontDescriptor - // fontDescriptorWithFontAttributes:@{UIFontDescriptorNameAttribute: name}] - // symbolicTraits] - // ...but this only works on iOS 7 and later. - NSFontTraitMask font_mask = 0; - if (MATCH(@"Bold")) - font_mask |= NSBoldFontMask; - if (MATCH(@"Italic") || MATCH(@"Oblique")) - font_mask |= NSItalicFontMask; - - if ((font_mask & mask) == traits) { - - /* Check if it can do ASCII. No good way to accomplish this! - These are fonts present in iPhone Simulator as of June 2012 - that don't include ASCII. - */ - if (MATCH(@"AppleGothic") || // Korean - MATCH(@"Dingbats") || // Dingbats - MATCH(@"Emoji") || // Emoticons - MATCH(@"Geeza") || // Arabic - MATCH(@"Hebrew") || // Hebrew - MATCH(@"HiraKaku") || // Japanese - MATCH(@"HiraMin") || // Japanese - MATCH(@"Kailasa") || // Tibetan - MATCH(@"Ornaments") || // Dingbats - MATCH(@"STHeiti") // Chinese - ) - break; - - *name_ret = strdup (fn.UTF8String); - return [UIFont fontWithName:fn size:size]; - } -# undef MATCH + if (w < 0 || h < 0) abort(); + if (w == 0 || h == 0) { + if (data) free(data); + return 0; } -# endif - - return NULL; -} - - - -/* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead - of XLFD strings; also they can be comma-separated strings with multiple - font names. First one that exists wins. - */ -static NSFont * -try_native_font (const char *name, float scale, - char **name_ret, float *size_ret, char **xa_font) -{ - if (!name) return 0; - const char *spc = strrchr (name, ' '); - if (!spc) return 0; - - NSFont *f = 0; - char *token = strdup (name); - char *name2; - - while ((name2 = strtok (token, ","))) { - token = 0; - - while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n') - name2++; - - spc = strrchr (name2, ' '); - if (!spc) continue; - - int dsize = 0; - if (1 != sscanf (spc, " %d ", &dsize)) - continue; - float size = dsize; - - if (size <= 4) continue; - - size *= scale; + XImage *img = XCreateImage (dpy, dpy->screen->visual, 32, + ZPixmap, 0, data, w, h, 0, 0); - name2[strlen(name2) - strlen(spc)] = 0; + /* The image of text is a 32-bit image, in white. + Take the red channel for intensity and use that as alpha. + replace RGB with the GC's foreground color. + This expects that XPutImage respects alpha and only writes + the bits that are not masked out. + This also assumes that XPutImage expects ARGB. + */ + { + char *s = data; + char *end = s + (w * h * 4); + uint8_t rgba[4]; + jwxyz_query_color (dpy, gc->gcv.foreground, rgba); + while (s < end) { - NSString *nsname = [NSString stringWithCString:name2 - encoding:NSUTF8StringEncoding]; - f = [NSFont fontWithName:nsname size:size]; - if (f) { - *name_ret = name2; - *size_ret = size; - *xa_font = strdup (name); // Maybe this should be an XLFD? - break; - } else { - NSLog(@"No native font: \"%@\" %.0f", nsname, size); + s[3] = s[1]; + s[0] = rgba[0]; + s[1] = rgba[1]; + s[2] = rgba[2]; + s += 4; } } - free (token); - return f; -} - - -/* Returns a random font in the given size and face. - */ -static NSFont * -random_font (NSFontTraitMask traits, NSFontTraitMask mask, - float size, NSString **family_ret, char **name_ret) -{ - -# ifndef USE_IPHONE - // Providing Unbold or Unitalic in the mask for availableFontNamesWithTraits - // returns an empty list, at least on a system with default fonts only. - NSArray *families = [[NSFontManager sharedFontManager] - availableFontFamilies]; - if (!families) return 0; -# else - NSArray *families = [UIFont familyNames]; - - // There are many dups in the families array -- uniquify it. { - NSArray *sorted_families = - [families sortedArrayUsingSelector:@selector(compare:)]; - NSMutableArray *new_families = - [NSMutableArray arrayWithCapacity:sorted_families.count]; - - NSString *prev_family = nil; - for (NSString *family in sorted_families) { - if ([family compare:prev_family]) - [new_families addObject:family]; - } - - families = new_families; + Bool old_alpha = gc->gcv.alpha_allowed_p; + jwxyz_XSetAlphaAllowed (dpy, gc, True); + XPutImage (dpy, d, gc, img, 0, 0, + x + cs.lbearing, + y - cs.ascent, + w, h); + jwxyz_XSetAlphaAllowed (dpy, gc, old_alpha); + XDestroyImage (img); } -# endif // USE_IPHONE - long n = [families count]; - if (n <= 0) return 0; - - int j; - for (j = 0; j < n; j++) { - int i = random() % n; - NSString *family_name = [families objectAtIndex:i]; - - NSFont *result = try_font (traits, mask, family_name, size, name_ret); - if (result) { - [*family_ret release]; - *family_ret = family_name; - [*family_ret retain]; - return result; - } - } - - // None of the fonts support ASCII? return 0; } -// Fonts need this. XDisplayHeightMM and friends should probably be consistent -// with this as well if they're ever implemented. -static const unsigned dpi = 75; - - -static const char * -xlfd_field_end (const char *s) -{ - const char *s2 = strchr(s, '-'); - if (!s2) - s2 = s + strlen(s); - return s2; -} - - -static size_t -xlfd_next (const char **s, const char **s2) -{ - if (!**s2) { - *s = *s2; - } else { - Assert (**s2 == '-', "xlfd parse error"); - *s = *s2 + 1; - *s2 = xlfd_field_end (*s); - } - - return *s2 - *s; -} - -static NSFont * -try_xlfd_font (const char *name, float scale, - char **name_ret, float *size_ret, char **xa_font) -{ - NSFont *nsfont = 0; - NSString *family_name = nil; - NSFontTraitMask require = 0, forbid = 0; - GLboolean rand = GL_FALSE; - float size = 0; - char *ps_name = 0; - - const char *s = (name ? name : ""); - - size_t L = strlen (s); -# define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L)) -# define UNSPEC (L == 0 || L == 1 && *s == '*') - if (CMP ("6x10")) size = 8, require |= NSFixedPitchFontMask; - else if (CMP ("6x10bold")) size = 8, require |= NSFixedPitchFontMask | NSBoldFontMask; - else if (CMP ("fixed")) size = 12, require |= NSFixedPitchFontMask; - else if (CMP ("9x15")) size = 12, require |= NSFixedPitchFontMask; - else if (CMP ("9x15bold")) size = 12, require |= NSFixedPitchFontMask | NSBoldFontMask; - else if (CMP ("vga")) size = 12, require |= NSFixedPitchFontMask; - else if (CMP ("console")) size = 12, require |= NSFixedPitchFontMask; - else if (CMP ("gallant")) size = 12, require |= NSFixedPitchFontMask; - else { - - // Incorrect fields are ignored. - - if (*s == '-') - ++s; - const char *s2 = xlfd_field_end(s); - - // Foundry (ignore) - - L = xlfd_next (&s, &s2); // Family name - // This used to substitute Georgia for Times. Now it doesn't. - if (CMP ("random")) { - rand = GL_TRUE; - } else if (CMP ("fixed")) { - require |= NSFixedPitchFontMask; - family_name = @"Courier"; - } else if (!UNSPEC) { - family_name = [[[NSString alloc] initWithBytes:s - length:L - encoding:NSUTF8StringEncoding] - autorelease]; - } - - L = xlfd_next (&s, &s2); // Weight name - if (CMP ("bold") || CMP ("demibold")) - require |= NSBoldFontMask; - else if (CMP ("medium") || CMP ("regular")) - forbid |= NSBoldFontMask; - - L = xlfd_next (&s, &s2); // Slant - if (CMP ("i") || CMP ("o")) - require |= NSItalicFontMask; - else if (CMP ("r")) - forbid |= NSItalicFontMask; - - xlfd_next (&s, &s2); // Set width name (ignore) - xlfd_next (&s, &s2); // Add style name (ignore) - - xlfd_next (&s, &s2); // Pixel size (ignore) - - xlfd_next (&s, &s2); // Point size - char *s3; - uintmax_t n = strtoumax(s, &s3, 10); - if (s2 == s3) - size = n / 10.0; - - xlfd_next (&s, &s2); // Resolution X (ignore) - xlfd_next (&s, &s2); // Resolution Y (ignore) - - xlfd_next (&s, &s2); // Spacing - if (CMP ("p")) - forbid |= NSFixedPitchFontMask; - else if (CMP ("m") || CMP ("c")) - require |= NSFixedPitchFontMask; - - // Don't care about average_width or charset registry. - } -# undef CMP -# undef UNSPEC - - if (!family_name && !rand) - family_name = default_font_family (require); - - if (size < 6 || size > 1000) - size = 12; - - size *= scale; - - NSFontTraitMask mask = require | forbid; - - if (rand) { - nsfont = random_font (require, mask, size, &family_name, &ps_name); - [family_name autorelease]; - } - - if (!nsfont) - nsfont = try_font (require, mask, family_name, size, &ps_name); - - // if that didn't work, turn off attibutes until it does - // (e.g., there is no "Monaco-Bold".) - // - if (!nsfont && (mask & NSItalicFontMask)) { - require &= ~NSItalicFontMask; - mask &= ~NSItalicFontMask; - nsfont = try_font (require, mask, family_name, size, &ps_name); - } - if (!nsfont && (mask & NSBoldFontMask)) { - require &= ~NSBoldFontMask; - mask &= ~NSBoldFontMask; - nsfont = try_font (require, mask, family_name, size, &ps_name); - } - if (!nsfont && (mask & NSFixedPitchFontMask)) { - require &= ~NSFixedPitchFontMask; - mask &= ~NSFixedPitchFontMask; - nsfont = try_font (require, mask, family_name, size, &ps_name); - } - - if (nsfont) { - *name_ret = ps_name; - *size_ret = size; - float actual_size = size / scale; - asprintf(xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1", - family_name.UTF8String, - (require & NSBoldFontMask) ? "bold" : "medium", - (require & NSItalicFontMask) ? 'o' : 'r', - (unsigned)(dpi * actual_size / 72.27 + 0.5), - (unsigned)(actual_size * 10 + 0.5), dpi, dpi, - (require & NSFixedPitchFontMask) ? 'm' : 'p'); - return nsfont; - } else { - return 0; - } -} - -#endif - -Font -XLoadFont (Display *dpy, const char *name) -{ - Font fid = (Font) calloc (1, sizeof(*fid)); - fid->refcount = 1; - fid->dpy = dpy; - - // (TODO) float scale = 1; - -# ifdef USE_IPHONE - /* Since iOS screens are physically smaller than desktop screens, scale up - the fonts to make them more readable. - - Note that X11 apps on iOS also have the backbuffer sized in points - instead of pixels, resulting in an effective X11 screen size of 768x1024 - or so, even if the display has significantly higher resolution. That is - unrelated to this hack, which is really about DPI. - */ - /* scale = 2; */ -# endif - - fid->dpy = dpy; - fid->native_font = jwxyz_load_native_font (dpy, name, - &fid->ps_name, &fid->size, - &fid->ascent, &fid->descent); - if (!fid->native_font) { - free (fid); - return 0; - } - query_font (fid); - - return fid; -} - - -XFontStruct * -XLoadQueryFont (Display *dpy, const char *name) -{ - Font fid = XLoadFont (dpy, name); - if (!fid) return 0; - return XQueryFont (dpy, fid); -} - int -XUnloadFont (Display *dpy, Font fid) +XSetClipMask (Display *dpy, GC gc, Pixmap m) { - if (--fid->refcount < 0) abort(); - if (fid->refcount > 0) return 0; - - if (fid->native_font) - jwxyz_release_native_font (fid->dpy, fid->native_font); - - if (fid->ps_name) - free (fid->ps_name); - if (fid->metrics.per_char) - free (fid->metrics.per_char); - - // #### DAMMIT! I can't tell what's going wrong here, but I keep getting - // crashes in [NSFont ascender] <- query_font, and it seems to go away - // if I never release the nsfont. So, fuck it, we'll just leak fonts. - // They're probably not very big... - // - // [fid->nsfont release]; - // CFRelease (fid->nsfont); +//#### abort(); +/* + TODO - free (fid); - return 0; -} + Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup"); -int -XFreeFontInfo (char **names, XFontStruct *info, int n) -{ - int i; - if (names) { - for (i = 0; i < n; i++) - if (names[i]) free (names[i]); - free (names); - } - if (info) { - for (i = 0; i < n; i++) - if (info[i].per_char) { - free (info[i].per_char); - free (info[i].properties); - } - free (info); + if (gc->gcv.clip_mask) { + XFreePixmap (dpy, gc->gcv.clip_mask); + CGImageRelease (gc->clip_mask); } - return 0; -} -int -XFreeFont (Display *dpy, XFontStruct *f) -{ - Font fid = f->fid; - XFreeFontInfo (0, f, 1); - XUnloadFont (dpy, fid); + gc->gcv.clip_mask = copy_pixmap (dpy, m); + if (gc->gcv.clip_mask) + gc->clip_mask = + CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc); + else + gc->clip_mask = 0; +*/ + return 0; } - int -XSetFont (Display *dpy, GC gc, Font fid) +XSetClipOrigin (Display *dpy, GC gc, int x, int y) { - Font font2 = copy_font (fid); - if (gc->gcv.font) - XUnloadFont (dpy, gc->gcv.font); - gc->gcv.font = font2; + gc->gcv.clip_x_origin = x; + gc->gcv.clip_y_origin = y; return 0; } - -XFontSet -XCreateFontSet (Display *dpy, char *name, - char ***missing_charset_list_return, - int *missing_charset_count_return, - char **def_string_return) +void set_points_list(XPoint *points, int npoints, linked_point *root) { - char *name2 = strdup (name); - char *s = strchr (name, ','); - if (s) *s = 0; - XFontSet set = 0; - XFontStruct *f = XLoadQueryFont (dpy, name2); - if (f) - { - set = (XFontSet) calloc (1, sizeof(*set)); - set->font = f; - } - free (name2); - if (missing_charset_list_return) *missing_charset_list_return = 0; - if (missing_charset_count_return) *missing_charset_count_return = 0; - if (def_string_return) *def_string_return = 0; - return set; -} - + linked_point *current; -void -XFreeFontSet (Display *dpy, XFontSet set) -{ - XFreeFont (dpy, set->font); - free (set); + current = root; + for (int i = 0; i < npoints - 2 ; i++) { + current->x = points[i].x; + current->y = points[i].y; + current->next = (linked_point *) malloc(sizeof(linked_point)); + current = current->next; + } + current->x = points[npoints-2].x; + current->y = points[npoints-2].y; + current->next = root; } -const char * -jwxyz_nativeFontName (Font f, float *size) +double compute_edge_length(linked_point * a, linked_point * b) { - if (size) *size = f->size; - return f->ps_name; -} + int xdiff, ydiff, xsq, ysq, added; + double xy_add, edge_length; -void -XFreeStringList (char **list) -{ - int i; - if (!list) return; - for (i = 0; list[i]; i++) - XFree (list[i]); - XFree (list); + xdiff = a->x - b->x; + ydiff = a->y - b->y; + xsq = xdiff * xdiff; + ysq = ydiff * ydiff; + added = xsq + ysq; + xy_add = (double) added; + edge_length = sqrt(xy_add); + return edge_length; } - -// Given a UTF8 string, return an NSString. Bogus UTF8 characters are ignored. -// We have to do this because stringWithCString returns NULL if there are -// any invalid characters at all. -// -/* TODO -static NSString * -sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP) +double get_angle(double a, double b, double c) { - int out_len = in_len * 4; // length of string might increase - char *s2 = (char *) malloc (out_len); - char *out = s2; - const char *in_end = in + in_len; - const char *out_end = out + out_len; - Bool latin1_p = True; - - while (in < in_end) - { - unsigned long uc; - long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc); - long L2 = utf8_encode (uc, out, out_end - out); - in += L1; - out += L2; - if (uc > 255) latin1_p = False; - } - *out = 0; - NSString *nsstr = - [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding]; - free (s2); - if (latin1_pP) *latin1_pP = latin1_p; - return (nsstr ? nsstr : @""); + double cos_a, i_cos_a; + cos_a = (((b * b) + (c * c)) - (a * a)) / (double) (2.0 * b * c); + i_cos_a = acos(cos_a); + return i_cos_a; } -*/ -int -XTextExtents (XFontStruct *f, const char *s, int length, - int *dir_ret, int *ascent_ret, int *descent_ret, - XCharStruct *cs) -{ - // Unfortunately, adding XCharStructs together to get the extents for a - // string doesn't work: Cocoa uses non-integral character advancements, but - // XCharStruct.width is an integer. Plus that doesn't take into account - // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in - // Zapfino. - - Font ff = f->fid; - Display *dpy = ff->dpy; - jwxyz_render_text (dpy, ff->native_font, s, length, GL_FALSE, cs, 0); - *dir_ret = 0; - *ascent_ret = f->ascent; - *descent_ret = f->descent; - return 0; -} -int -XTextWidth (XFontStruct *f, const char *s, int length) +Bool is_same_slope(linked_point * a) { - int ascent, descent, dir; - XCharStruct cs; - XTextExtents (f, s, length, &dir, &ascent, &descent, &cs); - return cs.width; -} + int abx, bcx, aby, bcy, aa, bb; + linked_point *b; + linked_point *c; -int -XTextExtents16 (XFontStruct *f, const XChar2b *s, int length, - int *dir_ret, int *ascent_ret, int *descent_ret, - XCharStruct *cs) -{ - // Bool latin1_p = True; - int i, utf8_len = 0; - char *utf8 = XChar2b_to_utf8 (s, &utf8_len); // already sanitized + b = a->next; + c = b->next; - for (i = 0; i < length; i++) - if (s[i].byte1 > 0) { - // latin1_p = False; - break; + // test if slopes are indefinite for both line segments + if (a->x == b->x) { + return b->x == c->x; + } else if (b->x == c->x) { + return False; // false, as ax/bx is not indefinite } - { - Font ff = f->fid; - Display *dpy = ff->dpy; - jwxyz_render_text (dpy, ff->native_font, utf8, strlen(utf8), - GL_TRUE, cs, 0); - } + abx = a->x - b->x; + bcx = b->x - c->x; + aby = a->y - b->y; + bcy = b->y - c->y; + aa = abx * bcy; + bb = bcx * aby; - *dir_ret = 0; - *ascent_ret = f->ascent; - *descent_ret = f->descent; - free (utf8); - return 0; + return aa == bb; } - -/* "Returns the distance in pixels in the primary draw direction from - the drawing origin to the origin of the next character to be drawn." - - "overall_ink_return is set to the bbox of the string's character ink." - - "The overall_ink_return for a nondescending, horizontally drawn Latin - character is conventionally entirely above the baseline; that is, - overall_ink_return.height <= -overall_ink_return.y." - - [So this means that y is the top of the ink, and height grows down: - For above-the-baseline characters, y is negative.] - - "The overall_ink_return for a nonkerned character is entirely at, and to - the right of, the origin; that is, overall_ink_return.x >= 0." - - [So this means that x is the left of the ink, and width grows right. - For left-of-the-origin characters, x is negative.] - - "A character consisting of a single pixel at the origin would set - overall_ink_return fields y = 0, x = 0, width = 1, and height = 1." - */ -int -Xutf8TextExtents (XFontSet set, const char *str, int len, - XRectangle *overall_ink_return, - XRectangle *overall_logical_return) +void draw_three_vertices(linked_point * a, Bool triangle) { -#if 0 - Bool latin1_p; - NSString *nsstr = sanitize_utf8 (str, len, &latin1_p); - XCharStruct cs; - utf8_metrics (set->font->fid, nsstr, &cs); + linked_point *b; + linked_point *c; + GLenum drawType; - /* "The overall_logical_return is the bounding box that provides minimum - spacing to other graphical features for the string. Other graphical - features, for example, a border surrounding the text, should not - intersect this rectangle." - - So I think that means they're the same? Or maybe "ink" is the bounding - box, and "logical" is the advancement? But then why is the return value - the advancement? - */ - if (overall_ink_return) - XCharStruct_to_XmbRectangle (cs, *overall_ink_return); - if (overall_logical_return) - XCharStruct_to_XmbRectangle (cs, *overall_logical_return); - - return cs.width; -#endif - abort(); -} + b = a->next; + c = b->next; + GLfloat vertices[3][2] = { + {a->x, a->y}, + {b->x, b->y}, + {c->x, c->y} + }; -static int -draw_string (Display *dpy, Drawable d, GC gc, int x, int y, - const char *str, size_t len, GLboolean utf8) -{ - Font ff = gc->gcv.font; - XCharStruct cs; - - char *data = 0; - jwxyz_render_text (dpy, ff->native_font, str, len, utf8, &cs, &data); - int w = cs.rbearing - cs.lbearing; - int h = cs.ascent + cs.descent; - - if (w < 0 || h < 0) abort(); - if (w == 0 || h == 0) { - if (data) free(data); - return 0; - } - - XImage *img = XCreateImage (dpy, dpy->screen->visual, 32, - ZPixmap, 0, data, w, h, 0, 0); - - /* The image of text is a 32-bit image, in white. - Take the red channel for intensity and use that as alpha. - replace RGB with the GC's foreground color. - This expects that XPutImage respects alpha and only writes - the bits that are not masked out. - This also assumes that XPutImage expects ARGB. - */ - { - char *s = data; - char *end = s + (w * h * 4); - uint8_t rgba[4]; - jwxyz_query_color (dpy, gc->gcv.foreground, rgba); - while (s < end) { - - s[3] = s[1]; - s[0] = rgba[0]; - s[1] = rgba[1]; - s[2] = rgba[2]; - s += 4; + if (triangle) { + drawType = GL_TRIANGLES; + } else { + drawType = GL_LINES; } - } - - XPutImage (dpy, d, gc, img, 0, 0, - x + cs.lbearing, - y - cs.ascent, - w, h); - XDestroyImage (img); - return 0; -} + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, vertices); + glDrawArrays(drawType, 0, 3); -int -XDrawString (Display *dpy, Drawable d, GC gc, int x, int y, - const char *str, int len) -{ - return draw_string (dpy, d, gc, x, y, str, len, GL_FALSE); + free(b); // cut midpoint off from remaining polygon vertex list + a->next = c; } -int -XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y, - const XChar2b *str, int len) +Bool is_an_ear(linked_point * a) { - XChar2b *b2 = malloc ((len + 1) * sizeof(*b2)); - char *s2; - int ret; - memcpy (b2, str, len * sizeof(*b2)); - b2[len].byte1 = b2[len].byte2 = 0; - s2 = XChar2b_to_utf8 (b2, 0); - free (b2); - ret = draw_string (dpy, d, gc, x, y, s2, strlen(s2), GL_TRUE); - free (s2); - return ret; -} + double edge_ab, edge_bc, edge_ac; + double angle_a, angle_b, angle_c; + double my_pi; + linked_point *b, *c; + b = a->next; + c = b->next; + my_pi = (double) M_PI; -void -Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc, - int x, int y, const char *str, int len) -{ - draw_string (dpy, d, gc, x, y, str, len, GL_TRUE); + edge_ab = compute_edge_length(a, b); + edge_bc = compute_edge_length(b, c); + edge_ac = compute_edge_length(a, c); + angle_a = get_angle(edge_bc, edge_ab, edge_ac); + angle_b = get_angle(edge_ac, edge_ab, edge_bc); + angle_c = get_angle(edge_ab, edge_ac, edge_bc); + + return angle_a < my_pi && angle_b < my_pi && angle_c < my_pi; } -int -XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y, - const char *str, int len) +Bool is_three_point_loop(linked_point * head) { - int ascent, descent, dir; - XCharStruct cs; - XTextExtents (&gc->gcv.font->metrics, str, len, - &dir, &ascent, &descent, &cs); - jwxyz_fill_rect (dpy, d, gc, - x + MIN (0, cs.lbearing), - y - MAX (0, ascent), - MAX (MAX (0, cs.rbearing) - - MIN (0, cs.lbearing), - cs.width), - MAX (0, ascent) + MAX (0, descent), - gc->gcv.background); - return XDrawString (dpy, d, gc, x, y, str, len); + return head->x == head->next->next->next->x + && head->y == head->next->next->next->y; } -int -XSetClipMask (Display *dpy, GC gc, Pixmap m) +void traverse_points_list(linked_point * root) { -//#### abort(); -/* - TODO + linked_point *head; + head = root; - Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup"); + while (!is_three_point_loop(head)) { + if (is_an_ear(head)) { + draw_three_vertices(head, True); + } else if (is_same_slope(head)) { + draw_three_vertices(head, False); + } else { + head = head->next; + } + } - if (gc->gcv.clip_mask) { - XFreePixmap (dpy, gc->gcv.clip_mask); - CGImageRelease (gc->clip_mask); - } + // handle final three vertices in polygon + if (is_an_ear(head)) { + draw_three_vertices(head, True); + } else if (is_same_slope(head)) { + draw_three_vertices(head, False); + } else { + free(head->next->next); + free(head->next); + free(head); + Assert (False, "traverse_points_list: unknown configuration"); + } - gc->gcv.clip_mask = copy_pixmap (dpy, m); - if (gc->gcv.clip_mask) - gc->clip_mask = - CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc); - else - gc->clip_mask = 0; -*/ - - return 0; + free(head->next); + free(head); } -int -XSetClipOrigin (Display *dpy, GC gc, int x, int y) -{ - gc->gcv.clip_x_origin = x; - gc->gcv.clip_y_origin = y; - return 0; -} + #endif /* JWXYZ_GL -- entire file */