X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=jwxyz%2Fjwxyz-gl.c;h=369d16fbbc1ff73763668a10cc41f6245d3fcfbd;hb=c85f503f5793839a6be4c818332aca4a96927bb2;hp=66944ed25a61aa870c1661d80c09ed395f8b67f2;hpb=aa75c7476aeaa84cf3abc192b376a8b03c325213;p=xscreensaver diff --git a/jwxyz/jwxyz-gl.c b/jwxyz/jwxyz-gl.c index 66944ed2..369d16fb 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-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 @@ -15,31 +15,18 @@ Xlib and that do OpenGL-ish things that bear some resemblance to the things that Xlib might have done. - This is the version of jwxyz for Android. The version used by MacOS + This is the version of jwxyz for Android. The version used by macOS and iOS is in jwxyz.m. */ -/* Be advised, this is all very much a work in progress. - - TODO: The following should be implemented before OpenGL can be considered - practical here: - - Above all, pick the smallest not-yet working hack that utilizes the - needed functionality. - - Half-ass the drawing functions. - - [OK] What Interference needs - - Fast Pixmaps - - Whatever clipping is used in XScreenSaver (shape and/or bitmap clipping) - - Delayed context creation to support anti-aliasing/multisampling - - Everything these hacks need: - - FuzzyFlakes (needs wide lines) - - Greynetic - - [OK] Deluxe - - [OK] Get DangerBall going. - - [OK] iOS. - - [Interference, so far...] And fast, too. - - And text really needs to work for the FPS display. */ - -/* Also, Take note that OS X can actually run with 256 colors. */ +/* Be advised, this is all very much a work in progress. */ + +/* There is probably no reason to ever implement indexed-color rendering here, + even if many screenhacks still work with PseudoColor. + - OpenGL ES 1.1 (Android, iOS) doesn't support indexed color. + - macOS only provides indexed color via AGL (now deprecated), not + NSOpenGLPixelFormat. + */ /* TODO: - malloc error checking @@ -51,7 +38,6 @@ - iOS: OpenGL ES 2.0 isn't always available. Use OpenGL ES 1.1. - OS X: Drivers can go back to OpenGL 1.1 (GeForce 2 MX on 10.5.8). - Use stencil buffers (OpenGL 1.0+) for bitmap clipping masks. - - glLogicOp is an actual thing that should work for GCs. - Pixmaps can be any of the following, depending on GL implementation. - This requires offscreen rendering. Fortunately, this is always available. @@ -74,10 +60,11 @@ */ /* OpenGL hacks call a number of X11 functions, including - * XCopyArea, XDrawString, XGetImage - * XCreatePixmap, XCreateGC, XCreateImage - * XPutPixel - * Check these, of course. */ + XCopyArea, XDrawString, XGetImage + XCreatePixmap, XCreateGC, XCreateImage + XPutPixel + Check these, of course. + */ #ifdef JWXYZ_GL /* entire file */ @@ -94,30 +81,6 @@ # import # include # include - -# define NSView UIView -# define NSRect CGRect -# define NSPoint CGPoint -# define NSSize CGSize -# define NSColor UIColor -# define NSImage UIImage -# define NSEvent UIEvent -# define NSFont UIFont -# define NSGlyph CGGlyph -# define NSWindow UIWindow -# define NSMakeSize CGSizeMake -# define NSBezierPath UIBezierPath -# define colorWithDeviceRed colorWithRed - -# define NSFontTraitMask UIFontDescriptorSymbolicTraits -// The values for the flags for NSFontTraitMask and -// UIFontDescriptorSymbolicTraits match up, not that it really matters here. -# define NSBoldFontMask UIFontDescriptorTraitBold -# define NSFixedPitchFontMask UIFontDescriptorTraitMonoSpace -# define NSItalicFontMask UIFontDescriptorTraitItalic - -# define NSOpenGLContext EAGLContext - # else # include # endif @@ -135,178 +98,70 @@ # include "jwzglesI.h" #endif -#ifdef HAVE_ANDROID -# include -#endif - #include "jwxyzI.h" #include "jwxyz-timers.h" #include "yarandom.h" #include "utf8wc.h" #include "xft.h" +#include "pow2.h" -#if defined HAVE_COCOA -# include -#else - - -#ifdef HAVE_ANDROID - extern void Log(const char *fmt, ...); -#else -static void -Log (const char *fmt, ...) -{ - va_list args; - va_start (args, fmt); - vfprintf (stderr, fmt, args); - va_end (args); -} -#endif - -struct CGPoint { - float x; - float y; -}; -typedef struct CGPoint CGPoint; - -struct CGSize { - float width; - float height; -}; -typedef struct CGSize CGSize; +#define countof(x) (sizeof((x))/sizeof((*x))) -struct CGRect { - CGPoint origin; - CGSize size; +union color_bytes { + uint32_t pixel; + uint8_t bytes[4]; }; -typedef struct CGRect CGRect; -#endif - -# undef MAX -# undef MIN -# define MAX(a,b) ((a)>(b)?(a):(b)) -# define MIN(a,b) ((a)<(b)?(a):(b)) - -union color_bytes -{ - /* On 64-bit systems, high bits of the 32-bit pixel are available as scratch - space. I doubt if any screen savers need it, but just in case... */ - unsigned long pixel; - uint8_t bytes[4]; +// Use two textures: one for RGBA, one for luminance. Older Android doesn't +// seem to like it when textures change format. +enum { + texture_rgba, + texture_mono }; struct jwxyz_Display { + const struct jwxyz_vtbl *vtbl; // Must come first. + Window main_window; - Screen *screen; - int screen_count; + GLenum pixel_format, pixel_type; + Visual visual; struct jwxyz_sources_data *timers_data; Bool gl_texture_npot_p; /* Bool opengl_core_p */; GLenum gl_texture_target; -// #if defined USE_IPHONE - GLuint rect_texture; // Also can work on the desktop. -// #endif + GLuint textures[2]; // Also can work on the desktop. unsigned long window_background; -}; -struct jwxyz_Screen { - Display *dpy; - GLenum pixel_format, pixel_type; - unsigned long black, white; - Visual *visual; - int screen_number; + int gc_function; + Bool gc_alpha_allowed_p; + int gc_clip_x_origin, gc_clip_y_origin; + GLuint gc_clip_mask; + + // Alternately, there could be one queue per pixmap. + size_t queue_size, queue_capacity; + Drawable queue_drawable; + GLint queue_mode; + void *queue_vertex; + uint32_t *queue_color; + Bool queue_line_cap; }; struct jwxyz_GC { XGCValues gcv; unsigned int depth; - // 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; + GLuint clip_mask; + unsigned clip_mask_width, clip_mask_height; }; -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 - conversion itself, so alloc_color and query_color are mercifully simple. - */ -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; - - if (dpy->screen->pixel_format == GL_BGRA_EXT) { - color.pixel = color.bytes[2] | - (color.bytes[1] << 8) | - (color.bytes[0] << 16) | - (color.bytes[3] << 24); - } else { - Assert(dpy->screen->pixel_format == GL_RGBA, - "jwxyz_alloc_color: Unknown pixel_format"); - } - - return (uint32_t)color.pixel; -} - -// Converts an array of pixels ('src') from one format to another, placing the -// result in 'dest', according to the pixel conversion mode 'mode'. -void -jwxyz_query_color (Display *dpy, unsigned long pixel, uint8_t *rgba) -{ - union color_bytes color; - - if(dpy->screen->pixel_format == GL_RGBA) - { - color.pixel = pixel; - for (unsigned i = 0; i != 4; ++i) - rgba[i] = color.bytes[i]; - return; - } - - Assert (dpy->screen->pixel_format == GL_BGRA_EXT, - "jwxyz_query_color: Unknown pixel format"); - /* TODO: Cross-check with XAllocColor. */ - rgba[0] = (pixel >> 16) & 0xFF; - rgba[1] = (pixel >> 8) & 0xFF; - rgba[2] = (pixel >> 0) & 0xFF; - rgba[3] = (pixel >> 24) & 0xFF; -} - - void jwxyz_assert_display(Display *dpy) { @@ -321,6 +176,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 @@ -366,67 +224,72 @@ gl_check_ver (const struct gl_version *caps, (caps->major == gl_major && caps->minor >= gl_minor); } -/* -static GLboolean gl_check_ext(const struct gl_caps *caps, - unsigned gl_major, - unsigned gl_minor, - const char *extension) +#endif + + +static void +tex_parameters (Display *d, GLuint texture) { - return - gl_check_ver(caps, gl_major, gl_minor) || - gluCheckExtension(extension, caps->extensions); -} -*/ + // TODO: Check for (ARB|EXT|NV)_texture_rectangle. (All three are alike.) + // Rectangle textures should be present on OS X with the following exceptions: + // - Generic renderer on PowerPC OS X 10.4 and earlier + // - ATI Rage 128 + glBindTexture (d->gl_texture_target, texture); + // TODO: This is all somewhere else. Refactor. + glTexParameteri (d->gl_texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (d->gl_texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); -#endif + // This might be redundant for rectangular textures. +# ifndef HAVE_JWZGLES + const GLint wrap = GL_CLAMP; +# else // HAVE_JWZGLES + const GLint wrap = GL_CLAMP_TO_EDGE; +# endif // HAVE_JWZGLES + // In OpenGL, CLAMP_TO_EDGE is OpenGL 1.2 or GL_SGIS_texture_edge_clamp. + // This is always present with OpenGL ES. + glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_T, wrap); +} -// NSOpenGLContext *jwxyz_debug_context; +static void +tex_size (Display *dpy, unsigned *tex_w, unsigned *tex_h) +{ + if (!dpy->gl_texture_npot_p) { + *tex_w = to_pow2(*tex_w); + *tex_h = to_pow2(*tex_h); + } +} -/* 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.) +static void +tex_image (Display *dpy, GLenum internalformat, + unsigned *tex_w, unsigned *tex_h, GLenum format, GLenum type, + const void *data) +{ + unsigned w = *tex_w, h = *tex_h; + tex_size (dpy, tex_w, tex_h); - 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). + // TODO: Would using glTexSubImage2D exclusively be faster? + if (*tex_w == w && *tex_h == h) { + glTexImage2D (dpy->gl_texture_target, 0, internalformat, *tex_w, *tex_h, + 0, format, type, data); + } else { + // TODO: Sampling the last row might be a problem if src_x != 0. + glTexImage2D (dpy->gl_texture_target, 0, internalformat, *tex_w, *tex_h, + 0, format, type, NULL); + glTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, w, h, + format, type, data); + } +} - 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 +extern const struct jwxyz_vtbl gl_vtbl; Display * -jwxyz_make_display (Window w) +jwxyz_gl_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 + d->vtbl = &gl_vtbl; # ifndef HAVE_JWZGLES struct gl_version version; @@ -456,29 +319,30 @@ jwxyz_make_display (Window w) // you're gonna get for getting a texture onto the screen. # ifdef HAVE_JWZGLES /* TODO: Make BGRA work on iOS. As it is, it breaks XPutImage. (glTexImage2D, AFAIK) */ - d->screen->pixel_format = GL_RGBA; /* + d->pixel_format = GL_RGBA; /* gluCheckExtension ((const GLubyte *) "GL_APPLE_texture_format_BGRA8888", extensions) ? GL_BGRA_EXT : GL_RGBA; */ - d->screen->pixel_type = GL_UNSIGNED_BYTE; + d->pixel_type = GL_UNSIGNED_BYTE; // See also OES_read_format. # else // !HAVE_JWZGLES if (gl_check_ver (&version, 1, 2) || (gluCheckExtension ((const GLubyte *) "GL_EXT_bgra", extensions) && gluCheckExtension ((const GLubyte *) "GL_APPLE_packed_pixels", extensions))) { - d->screen->pixel_format = GL_BGRA_EXT; + // APPLE_packed_pixels is only ever available on iOS, never Android. + d->pixel_format = GL_BGRA_EXT; // Both Intel and PowerPC-era docs say to use GL_UNSIGNED_INT_8_8_8_8_REV. - d->screen->pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV; + d->pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV; } else { - d->screen->pixel_format = GL_RGBA; - d->screen->pixel_type = GL_UNSIGNED_BYTE; + d->pixel_format = GL_RGBA; + d->pixel_type = GL_UNSIGNED_BYTE; } // GL_ABGR_EXT/GL_UNSIGNED_BYTE is another possibilty that may have made more // sense on PowerPC. # endif // !HAVE_JWZGLES - // On really old systems, it would make sense to split the texture - // into subsections. + // On really old systems, it would make sense to split textures + // into subsections, to work around the maximum texture size. # ifndef HAVE_JWZGLES d->gl_texture_npot_p = gluCheckExtension ((const GLubyte *) "GL_ARB_texture_rectangle", @@ -497,117 +361,92 @@ jwxyz_make_display (Window w) d->gl_texture_target = GL_TEXTURE_2D; # endif - d->screen->black = jwxyz_alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF); - d->screen->white = jwxyz_alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF); - - 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); - v->bits_per_rgb = 8; - d->screen->visual = v; + if (d->pixel_format == GL_BGRA_EXT) { + v->red_mask = 0x00ff0000; + v->green_mask = 0x0000ff00; + v->blue_mask = 0x000000ff; + v->alpha_mask = 0xff000000; + } else { + Assert(d->pixel_format == GL_RGBA, + "jwxyz_gl_make_display: Unknown pixel_format"); + unsigned long masks[4]; + for (unsigned i = 0; i != 4; ++i) { + union color_bytes color; + color.pixel = 0; + color.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; + { - fputs((char *)glGetString(GL_VENDOR), stderr); - fputc(' ', stderr); - fputs((char *)glGetString(GL_RENDERER), stderr); - fputc(' ', stderr); - fputs((char *)glGetString(GL_VERSION), stderr); - fputc('\n', stderr); -// puts(caps.extensions); - GLint max_texture_size; + GLint max_texture_size, max_texture_units; glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size); - printf ("GL_MAX_TEXTURE_SIZE: %d\n", max_texture_size); + glGetIntegerv (GL_MAX_TEXTURE_UNITS, &max_texture_units); + Log ("GL_MAX_TEXTURE_SIZE: %d, GL_MAX_TEXTURE_UNITS: %d\n", + max_texture_size, max_texture_units); + + // OpenGL ES 1.1 and OpenGL 1.3 both promise at least 2 texture units: + // OpenGL (R) ES Common/Common-Lite Profile Specification, Version 1.1.12 (Full Specification) + // https://www.khronos.org/registry/OpenGL/specs/es/1.1/es_full_spec_1.1.pdf + // * Table 6.22. Implementation Dependent Values + // * D.2 Enhanced Texture Processing + // (OpenGL 1.2 provides multitexturing as an ARB extension, and requires 1 + // texture unit only.) + + // ...but the glGet reference page contradicts this, and says there can be + // just one. + // https://www.khronos.org/registry/OpenGL-Refpages/es1.1/xhtml/glGet.xml } - - // In case a GL hack wants to use X11 to draw offscreen, the rect_texture is available. - Assert (d->main_window == w, "Uh-oh."); - glGenTextures (1, &d->rect_texture); - // TODO: Check for (ARB|EXT|NV)_texture_rectangle. (All three are alike.) - // Rectangle textures should be present on OS X with the following exceptions: - // - Generic renderer on PowerPC OS X 10.4 and earlier - // - ATI Rage 128 - glBindTexture (d->gl_texture_target, d->rect_texture); - // TODO: This is all somewhere else. Refactor. - glTexParameteri (d->gl_texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri (d->gl_texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - // This might be redundant for rectangular textures. -# ifndef HAVE_JWZGLES - const GLint wrap = GL_CLAMP; -# else // HAVE_JWZGLES - const GLint wrap = GL_CLAMP_TO_EDGE; -# endif // HAVE_JWZGLES + glGenTextures (countof (d->textures), d->textures); - // In OpenGL, CLAMP_TO_EDGE is OpenGL 1.2 or GL_SGIS_texture_edge_clamp. - // This is always present with OpenGL ES. - glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_S, wrap); - glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_T, wrap); + for (unsigned i = 0; i != countof (d->textures); i++) { + tex_parameters (d, d->textures[i]); + } + + d->gc_function = GXcopy; + d->gc_alpha_allowed_p = False; + d->gc_clip_mask = 0; jwxyz_assert_display(d); return d; } void -jwxyz_free_display (Display *dpy) +jwxyz_gl_free_display (Display *dpy) { + Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable"); + /* TODO: Go over everything. */ - jwxyz_sources_free (dpy->timers_data); + free (dpy->queue_vertex); + free (dpy->queue_color); -# 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 + jwxyz_sources_free (dpy->timers_data); - free (dpy->screen->visual); - free (dpy->screen); free (dpy); } -/* Call this after any modification to the bits on a Pixmap or Window. - Most Pixmaps are used frequently as sources and infrequently as - destinations, so it pays to cache the data as a CGImage as needed. - */ -static void -invalidate_drawable_cache (Drawable d) -{ - /* TODO: Kill this outright. jwxyz_bind_drawable handles any potential - invalidation. - */ - - /* - if (d && d->cgi) { - abort(); - CGImageRelease (d->cgi); - d->cgi = 0; - } - */ -} - - /* Call this when the View changes size or position. */ void jwxyz_window_resized (Display *dpy) { + Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable"); + const XRectangle *new_frame = jwxyz_frame (dpy->main_window); unsigned new_width = new_frame->width; unsigned new_height = new_frame->height; @@ -620,6 +459,7 @@ jwxyz_window_resized (Display *dpy) Log("resize: %d, %d\n", new_width, new_height); + jwxyz_gl_flush (dpy); jwxyz_bind_drawable (dpy, dpy->main_window, dpy->main_window); // TODO: What does the iPhone need? @@ -644,200 +484,312 @@ jwxyz_window_resized (Display *dpy) // Stylish and attractive purple! // glClearColor (1, 0, 1, 0.5); // glClear (GL_COLOR_BUFFER_BIT); - - invalidate_drawable_cache (dpy->main_window); } -jwxyz_sources_data * +static jwxyz_sources_data * display_sources_data (Display *dpy) { return dpy->timers_data; } -Window -XRootWindow (Display *dpy, int screen) +static Window +root (Display *dpy) { - return dpy ? dpy->main_window : 0; + return dpy->main_window; } -Screen * -XDefaultScreenOfDisplay (Display *dpy) +static Visual * +visual (Display *dpy) { - return dpy ? dpy->screen : 0; + return &dpy->visual; } -Visual * -XDefaultVisualOfScreen (Screen *screen) -{ - return screen ? screen->visual : 0; -} -Display * -XDisplayOfScreen (Screen *s) -{ - return s ? s->dpy : 0; -} +/* GC attributes by usage and OpenGL implementation: -int -XDisplayNumberOfScreen (Screen *s) -{ - return 0; -} + All drawing functions: + function | glLogicOp w/ GL_COLOR_LOGIC_OP + clip_x_origin, clip_y_origin, clip_mask | Multitexturing w/ GL_TEXTURE1 -int -XScreenNumberOfScreen (Screen *s) -{ - return s? s->screen_number : 0; -} + Shape drawing functions: + foreground, background | glColor* -int -jwxyz_ScreenCount (Display *dpy) -{ - return dpy ? dpy->screen_count : 0; -} + XDrawLines, XDrawRectangles, XDrawSegments: + line_width, cap_style, join_style | Lotsa vertices -unsigned long -XBlackPixelOfScreen(Screen *screen) -{ - return screen->black; -} + XFillPolygon: + fill_rule | Multiple GL_TRIANGLE_FANs -unsigned long -XWhitePixelOfScreen(Screen *screen) -{ - return screen->white; -} + XDrawText: + font | Cocoa, then OpenGL display lists. -unsigned long -XCellsOfScreen(Screen *screen) -{ - Visual *v = screen->visual; - return v->red_mask | v->green_mask | v->blue_mask; -} + alpha_allowed_p | GL_BLEND + antialias_p | Well, there's options: + * Multisampling would work, but that's something that would need to be set + per-Pixmap, not per-GC. + * GL_POINT, LINE, and POLYGON_SMOOTH are the old-school way of doing + this, but POINT_SMOOTH is unnecessary, and POLYGON_SMOOTH is missing from + GLES 1. All three are missing from GLES 2. Word on the street is that + these are deprecated anyway. + * Tiny textures with bilinear filtering to get the same effect as LINE_ and + POLYGON_SMOOTH. A bit tricky. + * Do nothing. Android hardware is very often high-DPI enough that + anti-aliasing doesn't matter all that much. -/* GC attributes by usage and OpenGL implementation: - * - * All drawing functions: - * function | glLogicOp w/ GL_COLOR_LOGIC_OP - * clip_x_origin, clip_y_origin, clip_mask | Stencil mask - * - * Shape drawing functions: - * foreground, background | glColor* - * - * XDrawLines, XDrawRectangles, XDrawSegments: - * line_width, cap_style, join_style | Lotsa vertices - * - * XFillPolygon: - * fill_rule | Multiple GL_TRIANGLE_FANs - * - * XDrawText: - * font | Cocoa, then OpenGL display lists. - * - * alpha_allowed_p | TODO - * antialias_p | TODO - * - * Nothing, really: - * subwindow_mode -*/ + Nothing, really: + subwindow_mode + */ -static void -set_clip_mask (GC gc) +static void * +enqueue (Display *dpy, Drawable d, GC gc, int mode, size_t count, + unsigned long pixel) { - Assert (!gc->gcv.clip_mask, "set_gc: TODO"); + if (dpy->queue_size && + (!gc || /* Could allow NULL GCs here... */ + dpy->gc_function != gc->gcv.function || + dpy->gc_alpha_allowed_p != gc->gcv.alpha_allowed_p || + dpy->gc_clip_mask != gc->clip_mask || + dpy->gc_clip_x_origin != gc->gcv.clip_x_origin || + dpy->gc_clip_y_origin != gc->gcv.clip_y_origin || + dpy->queue_mode != mode || + dpy->queue_drawable != d)) { + jwxyz_gl_flush (dpy); + } + + jwxyz_bind_drawable (dpy, dpy->main_window, d); + jwxyz_gl_set_gc (dpy, gc); + + if (mode == GL_TRIANGLE_STRIP) + Assert (count, "empty triangle strip"); + // Use degenerate triangles to cut down on draw calls. + Bool prepend2 = mode == GL_TRIANGLE_STRIP && dpy->queue_size; + + // ####: Primitive restarts should be used here when (if) they're available. + if (prepend2) + count += 2; + + // TODO: Use glColor when we can get away with it. + size_t old_size = dpy->queue_size; + dpy->queue_size += count; + if (dpy->queue_size > dpy->queue_capacity) { + dpy->queue_capacity = dpy->queue_size * 2; + + uint32_t *new_color = realloc ( + dpy->queue_color, sizeof(*dpy->queue_color) * dpy->queue_capacity); + /* Allocate vertices as if they were always GLfloats. Otherwise, if + queue_vertex is allocated to hold GLshorts, then things get switched + to GLfloats, queue_vertex would be too small for the given capacity. + */ + GLshort *new_vertex = realloc ( + dpy->queue_vertex, sizeof(GLfloat) * 2 * dpy->queue_capacity); + + if (!new_color || !new_vertex) + return NULL; + + dpy->queue_color = new_color; + dpy->queue_vertex = new_vertex; + } + + dpy->queue_mode = mode; + dpy->queue_drawable = d; + + union color_bytes color; + + // Like query_color, but for bytes. + jwxyz_validate_pixel (dpy, pixel, jwxyz_drawable_depth (d), + gc ? gc->gcv.alpha_allowed_p : False); + + if (jwxyz_drawable_depth (d) == 1) { + uint8_t b = pixel ? 0xff : 0; + color.bytes[0] = b; + color.bytes[1] = b; + color.bytes[2] = b; + color.bytes[3] = 0xff; + } else { + JWXYZ_QUERY_COLOR (dpy, pixel, 0xffull, color.bytes); + } + + for (size_t i = 0; i != count; ++i) // TODO: wmemset when applicable. + dpy->queue_color[i + old_size] = color.pixel; + + void *result = (char *)dpy->queue_vertex + old_size * 2 * + (mode == GL_TRIANGLE_STRIP ? sizeof(GLfloat) : sizeof(GLshort)); + if (prepend2) { + dpy->queue_color[old_size] = dpy->queue_color[old_size - 1]; + result = (GLfloat *)result + 4; + } + return result; } + static void -set_function (int function) +finish_triangle_strip (Display *dpy, GLfloat *enqueue_result) { - Assert (function == GXcopy, "set_gc: (TODO) Stubbed gcv function"); - - /* TODO: The GL_COLOR_LOGIC_OP extension is exactly what is needed here. (OpenGL 1.1) - Fun fact: The glLogicOp opcode constants are the same as the X11 GX* function constants | GL_CLEAR. - */ - -#if 0 - switch (gc->gcv.function) { - case GXset: - case GXclear: - case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break; - case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break; - case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break; - case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break; - default: Assert(0, "unknown gcv function"); break; + if (enqueue_result != dpy->queue_vertex) { + enqueue_result[-4] = enqueue_result[-6]; + enqueue_result[-3] = enqueue_result[-5]; + enqueue_result[-2] = enqueue_result[0]; + enqueue_result[-1] = enqueue_result[1]; } -#endif } static void -set_color (Display *dpy, unsigned long pixel, unsigned int depth, - Bool alpha_allowed_p) +query_color (Display *dpy, unsigned long pixel, unsigned int depth, + Bool alpha_allowed_p, GLfloat *rgba) { jwxyz_validate_pixel (dpy, pixel, depth, alpha_allowed_p); if (depth == 1) { GLfloat f = pixel; - glColor4f (f, f, f, 1); + rgba[0] = f; + rgba[1] = f; + rgba[2] = f; + rgba[3] = 1; } else { - /* TODO: alpha_allowed_p */ - uint8_t rgba[4]; - jwxyz_query_color (dpy, pixel, rgba); -#ifdef HAVE_JWZGLES - glColor4f (rgba[0] / 255.0f, rgba[1] / 255.0f, - rgba[2] / 255.0f, rgba[3] / 255.0f); -#else - glColor4ubv (rgba); -#endif + JWXYZ_QUERY_COLOR (dpy, pixel, 1.0f, rgba); } } -/* Pushes a GC context; sets Function, ClipMask, and color. - */ + static void -set_color_gc (Display *dpy, GC gc, unsigned long color) +set_color (Display *dpy, unsigned long pixel, unsigned int depth, + Bool alpha_allowed_p) +{ + GLfloat rgba[4]; + query_color (dpy, pixel, depth, alpha_allowed_p, rgba); + glColor4f (rgba[0], rgba[1], rgba[2], rgba[3]); +} + +/* Pushes a GC context; sets Function and ClipMask. */ +void +jwxyz_gl_set_gc (Display *dpy, GC gc) { - // GC is NULL for XClearArea and XClearWindow. - unsigned int depth; int function; + Bool alpha_allowed_p; + GLuint clip_mask; + + // GC is NULL for XClearArea and XClearWindow. if (gc) { function = gc->gcv.function; - depth = gc->depth; - set_clip_mask (gc); + alpha_allowed_p = gc->gcv.alpha_allowed_p || gc->clip_mask; + clip_mask = gc->clip_mask; } else { function = GXcopy; - depth = visual_depth (NULL, NULL); - // TODO: Set null clip mask here. + alpha_allowed_p = False; + clip_mask = 0; + } + + /* GL_COLOR_LOGIC_OP: OpenGL 1.1. */ + if (function != dpy->gc_function) { + dpy->gc_function = function; + if (function != GXcopy) { + /* Fun fact: The glLogicOp opcode constants are the same as the X11 GX* + function constants | GL_CLEAR. + */ + glEnable (GL_COLOR_LOGIC_OP); + glLogicOp (gc->gcv.function | GL_CLEAR); + } else { + glDisable (GL_COLOR_LOGIC_OP); + } } - set_function (function); + /* Cocoa uses add/subtract/difference blending in place of logical ops. + It looks nice, but implementing difference blending in OpenGL appears to + require GL_KHR_blend_equation_advanced, and support for this is not + widespread. + */ - switch (function) { - case GXset: color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break; - case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break; + dpy->gc_alpha_allowed_p = alpha_allowed_p; + if (alpha_allowed_p || clip_mask) { + // TODO: Maybe move glBlendFunc to XCreatePixmap? + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_BLEND); + } else { + glDisable (GL_BLEND); } - set_color (dpy, color, depth, gc ? gc->gcv.alpha_allowed_p : False); - - /* TODO: Antialiasing. */ - /* CGContextSetShouldAntialias (cgc, antialias_p); */ -} + /* Texture units: + GL_TEXTURE0: Texture for XPutImage/XCopyArea (if applicable) + GL_TEXTURE1: Texture for clip masks (if applicable) + */ + dpy->gc_clip_mask = clip_mask; -/* Pushes a GC context; sets color to the foreground color. - */ -static void -set_fg_gc (Display *dpy, GC gc) -{ - set_color_gc (dpy, gc, gc->gcv.foreground); + glActiveTexture (GL_TEXTURE1); + if (clip_mask) { + glEnable (dpy->gl_texture_target); + glBindTexture (dpy->gl_texture_target, gc->clip_mask); + + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, + alpha_allowed_p ? GL_MODULATE : GL_REPLACE); + + glMatrixMode (GL_TEXTURE); + glLoadIdentity (); + + unsigned + tex_w = gc->clip_mask_width + 2, tex_h = gc->clip_mask_height + 2; + tex_size (dpy, &tex_w, &tex_h); + +# ifndef HAVE_JWZGLES + if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT) + { + glScalef (1, -1, 1); + } + else +# endif + { + glScalef (1.0f / tex_w, -1.0f / tex_h, 1); + } + + glTranslatef (1 - gc->gcv.clip_x_origin, + 1 - gc->gcv.clip_y_origin - (int)gc->clip_mask_height - 2, + 0); + } else { + glDisable (dpy->gl_texture_target); + } + glActiveTexture (GL_TEXTURE0); } + static void -next_point(short *v, XPoint p, int mode) +set_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color) { - switch (mode) { - case CoordModeOrigin: + jwxyz_gl_flush (dpy); + jwxyz_bind_drawable (dpy, dpy->main_window, d); + jwxyz_gl_set_gc (dpy, gc); + + unsigned int depth; + + if (gc) { + depth = gc->depth; + + switch (gc->gcv.function) { + case GXset: color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break; + case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break; + } + } else { + depth = visual_depth (NULL, NULL); + } + + set_color (dpy, color, depth, gc ? gc->gcv.alpha_allowed_p : False); +} + +/* Pushes a GC context; sets color to the foreground color. + */ +static void +set_fg_gc (Display *dpy, Drawable d, GC gc) +{ + set_color_gc (dpy, d, gc, gc->gcv.foreground); +} + +static void +next_point(short *v, XPoint p, int mode) +{ + switch (mode) { + case CoordModeOrigin: v[0] = p.x; v[1] = p.y; break; @@ -851,101 +803,127 @@ next_point(short *v, XPoint p, int mode) } } -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) { - jwxyz_bind_drawable (dpy, dpy->main_window, d); - set_fg_gc (dpy, gc); - -/* - - glBegin(GL_POINTS); - for (unsigned i = 0; i < count; i++) { - next_point(v, points[i], mode); - glVertex2f(v[0] + 0.5f, v[1] + 0.5f); - } - glEnd(); - */ - short v[2] = {0, 0}; // TODO: XPoints can be fed directly to OpenGL. - GLshort *gl_points = malloc (count * 2 * sizeof(GLshort)); // TODO: malloc returns NULL. + GLshort *gl_points = enqueue (dpy, d, gc, GL_POINTS, count, + gc->gcv.foreground); // TODO: enqueue returns NULL. for (unsigned i = 0; i < count; i++) { next_point (v, points[i], mode); gl_points[2 * i] = v[0]; gl_points[2 * i + 1] = v[1]; } - - glMatrixMode (GL_MODELVIEW); - glTranslatef (0.5, 0.5, 0); - - glEnableClientState (GL_VERTEX_ARRAY); - glDisableClientState (GL_TEXTURE_COORD_ARRAY); - glVertexPointer (2, GL_SHORT, 0, gl_points); - glDrawArrays (GL_POINTS, 0, count); - - free (gl_points); - - glLoadIdentity (); - + return 0; } static GLint -texture_internalformat(Display *dpy) +texture_internalformat (Display *dpy) { #ifdef HAVE_JWZGLES - return dpy->screen->pixel_format; + return dpy->pixel_format; #else return GL_RGBA; #endif } -static GLenum gl_pixel_type(const Display *dpy) +static GLenum +gl_pixel_type (const Display *dpy) { - return dpy->screen->pixel_type; + return dpy->pixel_type; } static void clear_texture (Display *dpy) { glTexImage2D (dpy->gl_texture_target, 0, texture_internalformat(dpy), 0, 0, - 0, dpy->screen->pixel_format, gl_pixel_type (dpy), NULL); + 0, dpy->pixel_format, gl_pixel_type (dpy), NULL); } -static void set_white (void) + +static void +vertex_pointer (Display *dpy, GLenum type, GLsizei stride, + const void *pointer) { -#ifdef HAVE_JWZGLES - glColor4f (1, 1, 1, 1); -#else - glColor3ub (0xff, 0xff, 0xff); -#endif + glVertexPointer(2, type, stride, pointer); + if (dpy->gc_clip_mask) { + glClientActiveTexture (GL_TEXTURE1); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer (2, type, stride, pointer); + glClientActiveTexture (GL_TEXTURE0); + } } -static GLsizei to_pow2 (size_t x); +void +jwxyz_gl_flush (Display *dpy) +{ + Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable"); + + if (!dpy->queue_size) + return; + + // jwxyz_bind_drawable() and jwxyz_gl_set_gc() is called in enqueue(). + + glEnableClientState (GL_COLOR_ARRAY); + glEnableClientState (GL_VERTEX_ARRAY); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + + // TODO: Use glColor instead of glColorPointer if there's just one color. + // TODO: Does OpenGL use both GL_COLOR_ARRAY and glColor at the same time? + // (Probably not.) + glColor4f (1, 1, 1, 1); + + Bool shifted = dpy->queue_mode == GL_POINTS || dpy->queue_mode == GL_LINES; + if (shifted) { + glMatrixMode (GL_MODELVIEW); + glTranslatef (0.5, 0.5, 0); + } + + glColorPointer (4, GL_UNSIGNED_BYTE, 0, dpy->queue_color); + vertex_pointer (dpy, + dpy->queue_mode == GL_TRIANGLE_STRIP ? GL_FLOAT : GL_SHORT, + 0, dpy->queue_vertex); + glDrawArrays (dpy->queue_mode, 0, dpy->queue_size); + + // TODO: This is right, right? + if (dpy->queue_mode == GL_LINES && dpy->queue_line_cap) { + Assert (!(dpy->queue_size % 2), "bad count for GL_LINES"); + glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(GLubyte) * 8, + dpy->queue_color); + vertex_pointer (dpy, GL_SHORT, sizeof(GLshort) * 4, + (GLshort *)dpy->queue_vertex + 2); + glDrawArrays (GL_POINTS, 0, dpy->queue_size / 2); + } + + if (shifted) + glLoadIdentity (); + + glDisableClientState (GL_COLOR_ARRAY); + glDisableClientState (GL_VERTEX_ARRAY); + + dpy->queue_size = 0; +} 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); @@ -954,172 +932,135 @@ jwxyz_gl_copy_area_copy_tex_image (Display *dpy, Drawable src, Drawable dst, GLint internalformat = texture_internalformat(dpy); - glBindTexture (dpy->gl_texture_target, dpy->rect_texture); + /* TODO: This probably shouldn't always be texture_rgba. */ + glBindTexture (dpy->gl_texture_target, dpy->textures[texture_rgba]); 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); + 0, dpy->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) +{ + jwxyz_gl_set_gc (dpy, gc); + + /* 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); + /* Must match what's in jwxyz_gl_copy_area_read_tex_image. */ + glBindTexture (dpy->gl_texture_target, dpy->textures[texture_rgba]); + + jwxyz_gl_draw_image (dpy, gc, dpy->gl_texture_target, tex_w, tex_h, + 0, 0, gc->depth, width, height, dst_x, dst_y, False); + + clear_texture (dpy); +} + + +void +jwxyz_gl_draw_image (Display *dpy, GC gc, GLenum gl_texture_target, + unsigned int tex_w, unsigned int tex_h, + int src_x, int src_y, int src_depth, + unsigned int width, unsigned int height, + int dst_x, int dst_y, Bool flip_y) +{ + if (!gc || src_depth == gc->depth) { + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } else { + Assert (src_depth == 1 && gc->depth == 32, + "jwxyz_gl_draw_image: bad depths"); + + set_color (dpy, gc->gcv.background, gc->depth, gc->gcv.alpha_allowed_p); + GLfloat rgba[4]; + query_color (dpy, gc->gcv.foreground, gc->depth, gc->gcv.alpha_allowed_p, + rgba); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba); + } + + glEnable (gl_texture_target); glEnableClientState (GL_TEXTURE_COORD_ARRAY); glEnableClientState (GL_VERTEX_ARRAY); - - /* TODO: Copied from XPutImage. Refactor. */ + + Assert (!glIsEnabled (GL_COLOR_ARRAY), "glIsEnabled (GL_COLOR_ARRAY)"); + Assert (!glIsEnabled (GL_NORMAL_ARRAY), "glIsEnabled (GL_NORMAL_ARRAY)"); + /* TODO: EXT_draw_texture or whatever it's called. */ - GLfloat vertices[4][2] = - { + GLfloat vertices[4][2] = { {dst_x, dst_y}, {dst_x, dst_y + height}, {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 - glVertexPointer (2, GL_FLOAT, 0, vertices); - glTexCoordPointer (2, GL_SHORT, 0, tex_coords); + GLfloat + tex_x0 = src_x, tex_y0 = src_y, + tex_x1 = src_x + width, tex_y1 = src_y; + + if (flip_y) + tex_y1 += height; + else + tex_y0 += height; + +# 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} + }; + + vertex_pointer (dpy, GL_FLOAT, 0, vertices); + glTexCoordPointer (2, GL_FLOAT, 0, tex_coords); glDrawArrays (GL_TRIANGLE_FAN, 0, 4); - - clear_texture (dpy); - - glDisable (dpy->gl_texture_target); + +//clear_texture(); + glDisable (gl_texture_target); } +#if 0 void jwxyz_gl_copy_area_read_pixels (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) { -# 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 -} - - -#if 0 -// TODO: Make sure offset works in super-sampled mode. -static void -adjust_point_for_line (GC gc, CGPoint *p) -{ - // Here's the authoritative discussion on how X draws lines: - // http://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:CreateGC:line-width - if (gc->gcv.line_width <= 1) { - /* Thin lines are "drawn using an unspecified, device-dependent - algorithm", but seriously though, Bresenham's algorithm. Bresenham's - algorithm runs to and from pixel centers. - - There's a few screenhacks (Maze, at the very least) that set line_width - to 1 when it probably should be set to 0, so it's line_width <= 1 - instead of < 1. - */ - p->x += 0.5; - p->y -= 0.5; - } else { - /* Thick lines OTOH run from the upper-left corners of pixels. This means - that a horizontal thick line of width 1 straddles two scan lines. - Aliasing requires one of these scan lines be chosen; the following - nudges the point so that the right choice is made. */ - p->y -= 1e-3; - } } #endif -int -XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2) -{ - // TODO: XDrawLine == XDrawSegments(nlines == 1), also in jwxyz.m - XSegment segment; - segment.x1 = x1; - segment.y1 = y1; - segment.x2 = x2; - segment.y2 = y2; - XDrawSegments (dpy, d, gc, &segment, 1); - - // 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, - int mode) +static int +DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, + int mode) { - jwxyz_bind_drawable (dpy, dpy->main_window, d); - set_fg_gc (dpy, gc); + set_fg_gc (dpy, d, gc); /* TODO: Thick lines * Zero-length line segments @@ -1144,7 +1085,7 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, glEnableClientState (GL_VERTEX_ARRAY); glDisableClientState (GL_TEXTURE_COORD_ARRAY); - glVertexPointer (2, GL_SHORT, 0, vertices); + vertex_pointer (dpy, GL_SHORT, 0, vertices); glDrawArrays (GL_LINE_STRIP, 0, count); free (vertices); @@ -1152,7 +1093,7 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, if (gc->gcv.cap_style != CapNotLast) { // TODO: How does this look with multisampling? // TODO: Disable me for closed loops. - glVertexPointer (2, GL_SHORT, 0, p); + vertex_pointer (dpy, GL_SHORT, 0, p); glDrawArrays (GL_POINTS, 0, 1); } @@ -1162,192 +1103,187 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, } -int -XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count) +// Turn line segment into parallelogram based on line_width +// +// TODO: Fix epicycle hack with large thickness, and truchet line segment ends +// +static void +drawThickLine (Display *dpy, Drawable d, GC gc, int line_width, + XSegment *segments) { - jwxyz_bind_drawable (dpy, dpy->main_window, d); - set_fg_gc (dpy, gc); - - /* TODO: Thick lines. */ + double dx, dy, di, m, angle; + int sx1, sx2, sy1, sy2; + + sx1 = segments->x1; + sy1 = segments->y1; + sx2 = segments->x2; + sy2 = segments->y2; + + dx = sx1 - sx2; + dy = sy1 - sy2; + di = sqrt(dx * dx + dy * dy); + dx = dx / di; + dy = dy / di; + m = dy / dx; + + angle = atan(m); + + float sn = sin(angle); + float cs = cos(angle); + float line_width_f = (float) line_width; + + float wsn = line_width_f * (sn/2); + float csn = line_width_f * (cs/2); + + float x3 = sx1 - wsn; + float y3 = sy1 + csn; + float x4 = sx1 + wsn; + float y4 = sy1 - csn; + + float x5 = sx2 - wsn; + float y5 = sy2 + csn; + float x6 = sx2 + wsn; + float y6 = sy2 - csn; + + GLfloat *coords = enqueue (dpy, d, gc, GL_TRIANGLE_STRIP, 4, + gc->gcv.foreground); + coords[0] = x3; + coords[1] = y3; + coords[2] = x4; + coords[3] = y4; + coords[4] = x5; + coords[5] = y5; + coords[6] = x6; + coords[7] = y6; + finish_triangle_strip (dpy, coords); +} + + +static int +DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count) +{ + /* TODO: Caps on thick lines. */ /* Thin lines <= 1px are offset by +0.5; thick lines are not. */ - - glMatrixMode (GL_MODELVIEW); - glTranslatef (0.5, 0.5, 0); - glEnableClientState (GL_VERTEX_ARRAY); - glDisableClientState (GL_TEXTURE_COORD_ARRAY); - - Assert (sizeof(XSegment) == sizeof(short) * 4, "XDrawSegments: Data alignment mix-up."); // TODO: Static assert here. - Assert (sizeof(GLshort) == sizeof(short), "XDrawSegments: Data alignment mix-up."); // TODO: Static assert here. - Assert (offsetof(XSegment, x1) == 0, "XDrawSegments: Data alignment mix-up."); - Assert (offsetof(XSegment, x2) == 4, "XDrawSegments: Data alignment mix-up."); - glVertexPointer (2, GL_SHORT, 0, segments); - glDrawArrays (GL_LINES, 0, count * 2); - - if (gc->gcv.cap_style != CapNotLast) { - glVertexPointer (2, GL_SHORT, sizeof(GLshort) * 4, (const GLshort *)segments + 2); - glDrawArrays (GL_POINTS, 0, count); + if (count == 1 && gc->gcv.line_width > 1) { + drawThickLine (dpy, d, gc, gc->gcv.line_width, segments); } - - glLoadIdentity (); - -/* CGRect wr = d->frame; - push_fg_gc (dpy, d, gc, NO); - 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); - segments++; + else { + if (dpy->queue_line_cap != (gc->gcv.cap_style != CapNotLast)) + jwxyz_gl_flush (dpy); + dpy->queue_line_cap = gc->gcv.cap_style != CapNotLast; + + // TODO: Static assert here. + Assert (sizeof(XSegment) == sizeof(short) * 4 && + sizeof(GLshort) == sizeof(short) && + offsetof(XSegment, x1) == 0 && + offsetof(XSegment, x2) == 4, + "XDrawSegments: Data alignment mix-up."); + + memcpy (enqueue(dpy, d, gc, GL_LINES, count * 2, gc->gcv.foreground), + segments, count * sizeof(XSegment)); } - CGContextStrokePath (cgc); - pop_gc (d, gc); - invalidate_drawable_cache (d); */ + return 0; } -int -XClearWindow (Display *dpy, Window win) +static int +ClearWindow (Display *dpy, Window win) { Assert (win == dpy->main_window, "not a window"); - const XRectangle *wr = jwxyz_frame (win); - /* TODO: Use glClear if there's no background pixmap. */ - return XClearArea (dpy, win, 0, 0, wr->width, wr->height, 0); -} -unsigned long -jwxyz_window_background (Display *dpy) -{ - return dpy->window_background; + jwxyz_gl_flush (dpy); + jwxyz_bind_drawable (dpy, win, win); + + GLfloat color[4]; + JWXYZ_QUERY_COLOR (dpy, dpy->window_background, 1.0f, color); + + glClearColor (color[0], color[1], color[2], 1); + glClear (GL_COLOR_BUFFER_BIT); + return True; } -int -XSetWindowBackground (Display *dpy, Window w, unsigned long pixel) +static unsigned long * +window_background (Display *dpy) { - Assert (w == dpy->main_window, "not a window"); - jwxyz_validate_pixel (dpy, pixel, visual_depth (NULL, NULL), False); - dpy->window_background = pixel; - return 0; + return &dpy->window_background; } -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) { - jwxyz_bind_drawable (dpy, dpy->main_window, d); - set_color_gc (dpy, gc, pixel); -/* - glBegin(GL_QUADS); - for (unsigned i = 0; i != nrectangles; ++i) { - const XRectangle *r = &rectangles[i]; - glVertex2i(r->x, r->y); - glVertex2i(r->x, r->y + r->height); - glVertex2i(r->x + r->width, r->y + r->height); - glVertex2i(r->x + r->width, r->y); - } - glEnd(); */ - - glEnableClientState (GL_VERTEX_ARRAY); - glDisableClientState (GL_TEXTURE_COORD_ARRAY); - - for (unsigned long i = 0; i != nrectangles; ++i) - { + for (unsigned long i = 0; i != nrectangles; ++i) { const XRectangle *r = &rectangles[i]; - - GLfloat coords[4][2] = - { - {r->x, r->y}, - {r->x, r->y + r->height}, - {r->x + r->width, r->y + r->height}, - {r->x + r->width, r->y} - }; - - // TODO: Several rects at once. Maybe even tune for XScreenSaver workloads. - glVertexPointer (2, GL_FLOAT, 0, coords); - jwxyz_assert_gl (); - glDrawArrays (GL_TRIANGLE_FAN, 0, 4); - jwxyz_assert_gl (); + GLfloat *coords = enqueue (dpy, d, gc, GL_TRIANGLE_STRIP, 4, pixel); + coords[0] = r->x; + coords[1] = r->y; + coords[2] = r->x; + coords[3] = r->y + r->height; + coords[4] = r->x + r->width; + coords[5] = r->y; + coords[6] = r->x + r->width; + coords[7] = r->y + r->height; + finish_triangle_strip (dpy, coords); } } -int -XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp) -{ - Assert(win == dpy->main_window, "XClearArea: not a window"); - Assert(!exp, "XClearArea: exposures unsupported"); - - 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) { - jwxyz_bind_drawable (dpy, dpy->main_window, d); - set_fg_gc(dpy, gc); + set_fg_gc(dpy, d, gc); // TODO: Re-implement the GLU tesselation functions. /* 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. - GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc. - short v[2] = {0, 0}; - - for (unsigned i = 0; i < npoints; i++) { - next_point(v, points[i], mode); - vertices[2 * i] = v[0]; - vertices[2 * i + 1] = v[1]; - } - glEnableClientState (GL_VERTEX_ARRAY); - glDisableClientState (GL_TEXTURE_COORD_ARRAY); - - glVertexPointer (2, GL_SHORT, 0, vertices); - glDrawArrays (GL_TRIANGLE_FAN, 0, npoints); + if (shape == Convex) { + + GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc. + short v[2] = {0, 0}; - 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; + for (unsigned i = 0; i < npoints; i++) { + next_point(v, points[i], mode); + vertices[2 * i] = v[0]; + vertices[2 * i + 1] = v[1]; } - - if (i == 0) - CGContextMoveToPoint (cgc, x, y); - else - CGContextAddLineToPoint (cgc, x, y); + + glEnableClientState (GL_VERTEX_ARRAY); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + + vertex_pointer (dpy, GL_SHORT, 0, vertices); + glDrawArrays (GL_TRIANGLE_FAN, 0, npoints); + + free(vertices); + + } 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(dpy, 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; } @@ -1370,13 +1306,42 @@ mod_neg(int a, unsigned b) return a < 0 ? (b - 1) - (-(a + 1) % b) : a % b; } +/* TODO: Fill in arcs with line width > 1 */ +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) +{ + int gglw = gc->gcv.line_width; + + if (fill_p || gglw <= 1) { + draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p); + } + else { + int w1, w2, h1, h2, gglwh; + w1 = width + gglw; + h1 = height + gglw; + h2 = height - gglw; + w2 = width - gglw; + gglwh = gglw / 2; + int x1 = x - gglwh; + int x2 = x + gglwh; + int y1 = y - gglwh; + int y2 = y + gglwh; + //draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p); + draw_arc_gl (dpy, d, gc, x1, y1, w1, h1, angle1, angle2, fill_p); + draw_arc_gl (dpy, d, gc, x2, y2, w2, h2, angle1, angle2, fill_p); + } + return 0; +} + + int -jwxyz_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) +draw_arc_gl (Display *dpy, Drawable d, GC gc, int x, int y, + unsigned int width, unsigned int height, + int angle1, int angle2, Bool fill_p) { - jwxyz_bind_drawable (dpy, dpy->main_window, d); - set_fg_gc(dpy, gc); + set_fg_gc(dpy, d, gc); /* Let's say the number of line segments needed to make a convincing circle is 4*sqrt(radius). (But these arcs aren't necessarily circular arcs...) */ @@ -1465,7 +1430,7 @@ jwxyz_draw_arc (Display *dpy, Drawable d, GC gc, int x, int y, glDisableClientState (GL_TEXTURE_COORD_ARRAY); glEnableClientState (GL_VERTEX_ARRAY); - glVertexPointer (2, GL_FLOAT, 0, data); + vertex_pointer (dpy, GL_FLOAT, 0, data); glDrawArrays (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP, 0, (GLsizei)((data_ptr - data) / 2)); @@ -1500,22 +1465,22 @@ jwxyz_draw_arc (Display *dpy, Drawable d, GC gc, int x, int y, } -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); @@ -1526,99 +1491,35 @@ XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv) } -int -XFreeGC (Display *dpy, GC gc) +static void +free_clip_mask (Display *dpy, GC gc) { - if (gc->gcv.font) - XUnloadFont (dpy, gc->gcv.font); - - // TODO -/* - Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup"); - if (gc->gcv.clip_mask) { - XFreePixmap (dpy, gc->gcv.clip_mask); - CGImageRelease (gc->clip_mask); + if (dpy->gc_clip_mask == gc->clip_mask) { + jwxyz_gl_flush (dpy); + dpy->gc_clip_mask = 0; + } + glDeleteTextures (1, &gc->clip_mask); } -*/ - free (gc); - return 0; } -/* -static void -flipbits (unsigned const char *in, unsigned char *out, int length) -{ - static const unsigned char table[256] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, - 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, - 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, - 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, - 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, - 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, - 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, - 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, - 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, - 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, - 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, - 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, - 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, - 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, - 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, - 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, - 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, - 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, - 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, - 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, - 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, - 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, - 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, - 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, - 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, - 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, - 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF - }; - while (length-- > 0) - *out++ = table[*in++]; -} -*/ - -// Copied and pasted from OSX/XScreenSaverView.m -static GLsizei -to_pow2 (size_t x) +static int +FreeGC (Display *dpy, GC gc) { - 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; - } + if (gc->gcv.font) + XUnloadFont (dpy, gc->gcv.font); - return 1 << log2; + free_clip_mask (dpy, gc); + free (gc); + return 0; } -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) { jwxyz_assert_display (dpy); @@ -1667,198 +1568,166 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage, if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h)) return 0; + jwxyz_gl_flush (dpy); jwxyz_bind_drawable (dpy, dpy->main_window, d); + jwxyz_gl_set_gc (dpy, gc); + int bpl = ximage->bytes_per_line; int bpp = ximage->bits_per_pixel; - /* int bsize = bpl * h; */ - char *data = ximage->data; -/* - CGRect r; - r.origin.x = wr->x + dest_x; - r.origin.y = wr->y + wr->height - dest_y - h; - r.size.width = w; - r.size.height = h; -*/ + char *tex_data; + unsigned src_w; + GLint tex_internalformat; + GLenum tex_format, tex_type; + unsigned tex_index; - Assert (gc->gcv.function == GXcopy, "XPutImage: (TODO) GC function not supported"); - Assert (!gc->gcv.clip_mask, "XPutImage: (TODO) GC clip mask not supported"); - if (bpp == 32) { - - /* Take advantage of the fact that it's ok for (bpl != w * bpp) - to create a CGImage from a sub-rectagle of the XImage. - */ - data += (src_y * bpl) + (src_x * 4); + tex_data = ximage->data + src_y * bpl + (src_x * 4); jwxyz_assert_display(dpy); /* There probably won't be any hacks that do this, but... */ Assert (!(bpl % 4), "XPutImage: bytes_per_line not divisible by four."); - unsigned src_w = bpl / 4; + tex_internalformat = texture_internalformat(dpy); + tex_format = dpy->pixel_format; + tex_type = gl_pixel_type(dpy); + tex_index = texture_rgba; /* GL_UNPACK_ROW_LENGTH is not allowed to be negative. (sigh) */ # ifndef HAVE_JWZGLES - glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w); src_w = w; + glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w); +# else + src_w = bpl / 4; # endif - glPixelStorei (GL_UNPACK_ALIGNMENT, 4); - -# if 1 // defined HAVE_JWZGLES - // Regular OpenGL uses GL_TEXTURE_RECTANGLE_EXT in place of GL_TEXTURE_2D. - // TODO: Make use of OES_draw_texture. - // TODO: Coords might be wrong; things might be upside-down or backwards - // or whatever. - - unsigned tex_w = src_w, tex_h = h; - if (!dpy->gl_texture_npot_p) { - tex_w = to_pow2(tex_w); - tex_h = to_pow2(tex_h); - } - - GLint internalformat = texture_internalformat(dpy); + // glPixelStorei (GL_UNPACK_ALIGNMENT, 4); // Probably unnecessary. + } else { + Assert (bpp == 1, "expected 1 or 32 bpp"); + Assert ((src_x % 8) == 0, + "XPutImage with non-byte-aligned 1bpp X offset not implemented"); - glBindTexture (dpy->gl_texture_target, dpy->rect_texture); + const char *src_data = ximage->data + src_y * bpl + (src_x / 8); + unsigned w8 = (w + 7) / 8; - if (tex_w == src_w && tex_h == h) { - glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h, - 0, dpy->screen->pixel_format, gl_pixel_type(dpy), data); - } else { - // TODO: Sampling the last row might be a problem if src_x != 0. - glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h, - 0, dpy->screen->pixel_format, gl_pixel_type(dpy), NULL); - glTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, src_w, h, - dpy->screen->pixel_format, gl_pixel_type(dpy), data); - } - - set_white (); - // glEnable (dpy->gl_texture_target); - // glColor4f (0.5, 0, 1, 1); - glEnable (dpy->gl_texture_target); - glEnableClientState (GL_VERTEX_ARRAY); - glEnableClientState (GL_TEXTURE_COORD_ARRAY); + src_w = w8 * 8; - // TODO: Why are these ever turned on in the first place? - glDisableClientState (GL_COLOR_ARRAY); - glDisableClientState (GL_NORMAL_ARRAY); - // glDisableClientState (GL_TEXTURE_COORD_ARRAY); + tex_data = malloc(src_w * h); - GLfloat vertices[4][2] = +#if 0 { - {dest_x, dest_y}, - {dest_x, dest_y + h}, - {dest_x + w, dest_y + h}, - {dest_x + w, dest_y} - }; - - GLfloat texcoord_w, texcoord_h; -# ifndef HAVE_JWZGLES - if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT) { - texcoord_w = w; - texcoord_h = h; - } else -# endif /* HAVE_JWZGLES */ + char s[10240]; + int x, y, o; + Log("#PI ---------- %d %d %08lx %08lx", + jwxyz_drawable_depth(d), ximage->depth, + (unsigned long)d, (unsigned long)ximage); + for (y = 0; y < ximage->height; y++) { + o = 0; + for (x = 0; x < ximage->width; x++) { + unsigned long b = XGetPixel(ximage, x, y); + s[o++] = ( (b & 0xFF000000) + ? ((b & 0x00FFFFFF) ? '#' : '-') + : ((b & 0x00FFFFFF) ? '+' : ' ')); + } + s[o] = 0; + Log("#PI [%s]",s); + } + Log("# PI ----------"); + } +#endif + uint32_t *data_out = (uint32_t *)tex_data; + for(unsigned y = h; y; --y) { + for(unsigned x = 0; x != w8; ++x) { + // TODO: Does big endian work here? + uint8_t byte = src_data[x]; + uint32_t word = byte; + word = (word & 0x3) | ((word & 0xc) << 14); + word = (word & 0x00010001) | ((word & 0x00020002) << 7); + data_out[x << 1] = (word << 8) - word; + + word = byte >> 4; + word = (word & 0x3) | ((word & 0xc) << 14); + word = (word & 0x00010001) | ((word & 0x00020002) << 7); + data_out[(x << 1) | 1] = (word << 8) - word; + } + src_data += bpl; + data_out += src_w / 4; + } +#if 0 { - texcoord_w = (double)w / tex_w; - texcoord_h = (double)h / tex_h; + char s[10240]; + int x, y, o; + Log("#P2 ----------"); + for (y = 0; y < ximage->height; y++) { + o = 0; + for (x = 0; x < ximage->width; x++) { + unsigned long b = ((uint8_t *)tex_data)[y * w + x]; + s[o++] = ( (b & 0xFF000000) + ? ((b & 0x00FFFFFF) ? '#' : '-') + : ((b & 0x00FFFFFF) ? '+' : ' ')); + } + s[o] = 0; + Log("#P2 [%s]",s); + } + Log("# P2 ----------"); } +#endif - GLfloat tex_coords[4][2]; - tex_coords[0][0] = 0; - tex_coords[0][1] = 0; - tex_coords[1][0] = 0; - tex_coords[1][1] = texcoord_h; - tex_coords[2][0] = texcoord_w; - tex_coords[2][1] = texcoord_h; - tex_coords[3][0] = texcoord_w; - tex_coords[3][1] = 0; + tex_internalformat = GL_LUMINANCE; + tex_format = GL_LUMINANCE; + tex_type = GL_UNSIGNED_BYTE; + tex_index = texture_mono; - glVertexPointer (2, GL_FLOAT, 0, vertices); - glTexCoordPointer (2, GL_FLOAT, 0, tex_coords); + // glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + } - // Respect the alpha channel in the XImage - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +# if 1 // defined HAVE_JWZGLES + // Regular OpenGL uses GL_TEXTURE_RECTANGLE_EXT in place of GL_TEXTURE_2D. + // TODO: Make use of OES_draw_texture. - glDrawArrays (GL_TRIANGLE_FAN, 0, 4); + unsigned tex_w = src_w, tex_h = h; + glBindTexture (dpy->gl_texture_target, dpy->textures[tex_index]); - glDisable (GL_BLEND); + // A fun project: reimplement xshm.c by means of a PBO using + // GL_MAP_UNSYNCHRONIZED_BIT. -// clear_texture(); - glDisable (dpy->gl_texture_target); -# else - glRasterPos2i (dest_x, dest_y); - glPixelZoom (1, -1); - jwxyz_assert_display (dpy); - glDrawPixels (w, h, dpy->screen->pixel_format, gl_pixel_type(dpy), data); -# endif - } else { // (bpp == 1) + tex_image (dpy, tex_internalformat, &tex_w, &tex_h, tex_format, tex_type, + tex_data); - // Assert(FALSE, "XPutImage: TODO"); - // Check out ximage_(get|put)pixel_1 - -#if 0 - /* To draw a 1bpp image, we use it as a mask and fill two rectangles. + if (bpp == 1) + free(tex_data); - #### However, the bit order within a byte in a 1bpp XImage is - the wrong way around from what Quartz expects, so first we - have to copy the data to reverse it. Shit! Maybe it - would be worthwhile to go through the hacks and #ifdef - each one that diddles 1bpp XImage->data directly... - */ - Assert ((src_x % 8) == 0, - "XPutImage with non-byte-aligned 1bpp X offset not implemented"); + jwxyz_gl_draw_image (dpy, gc, dpy->gl_texture_target, tex_w, tex_h, + 0, 0, bpp, w, h, dest_x, dest_y, True); +# else + glRasterPos2i (dest_x, dest_y); + glPixelZoom (1, -1); + jwxyz_assert_display (dpy); + glDrawPixels (w, h, dpy->pixel_format, gl_pixel_type(dpy), data); +# endif - data += (src_y * bpl) + (src_x / 8); // move to x,y within the data - unsigned char *flipped = (unsigned char *) malloc (bsize); - - flipbits ((unsigned char *) data, flipped, bsize); - - CGDataProviderRef prov = - CGDataProviderCreateWithData (NULL, flipped, bsize, NULL); - CGImageRef mask = CGImageMaskCreate (w, h, - 1, bpp, bpl, - prov, - NULL, /* decode[] */ - GL_FALSE); /* interpolate */ - push_fg_gc (dpy, d, gc, GL_TRUE); - - CGContextFillRect (cgc, r); // foreground color - CGContextClipToMask (cgc, r, mask); - set_color (dpy, cgc, gc->gcv.background, gc->depth, GL_FALSE, GL_TRUE); - CGContextFillRect (cgc, r); // background color - pop_gc (d, gc); - - free (flipped); - CGDataProviderRelease (prov); - CGImageRelease (mask); -#endif - } - jwxyz_assert_gl (); - invalidate_drawable_cache (d); 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 * -XGetSubImage (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) +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"); + jwxyz_gl_flush (dpy); jwxyz_bind_drawable (dpy, dpy->main_window, d); // TODO: What if this reads off the edge? What is supposed to happen? @@ -1893,7 +1762,7 @@ XGetSubImage (Display *dpy, Drawable d, int x, int y, uint32_t *dest_data = (uint32_t *)dest_image->data + pixels_per_line * dest_y + dest_x; glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height, - dpy->screen->pixel_format, gl_pixel_type(dpy), dest_data); + dpy->pixel_format, gl_pixel_type(dpy), dest_data); /* Flip this upside down. :( */ uint32_t *top = dest_data; @@ -1909,1206 +1778,348 @@ XGetSubImage (Display *dpy, Drawable d, int x, int y, } } else { - /* TODO: Actually get pixels. */ + uint32_t *rgba_image = malloc(width * height * 4); + Assert (rgba_image, "not enough memory"); + + // Must be GL_RGBA; GL_RED isn't available. + glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height, + GL_RGBA, GL_UNSIGNED_BYTE, rgba_image); Assert (!(dest_x % 8), "XGetSubImage: dest_x not byte-aligned"); - uint8_t *dest_data = + uint8_t *top = (uint8_t *)dest_image->data + dest_image->bytes_per_line * dest_y + dest_x / 8; - for (unsigned y = height / 2; y; --y) { - memset (dest_data, y & 1 ? 0x55 : 0xAA, width / 8); - dest_data += dest_image->bytes_per_line; - } - } - - 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. - */ -/* -static CGAffineTransform -exif_rotate (int rot, CGSize rect) -{ - CGAffineTransform trans = CGAffineTransformIdentity; - switch (rot) { - case 2: // flip horizontal - trans = CGAffineTransformMakeTranslation (rect.width, 0); - trans = CGAffineTransformScale (trans, -1, 1); - break; - - case 3: // rotate 180 - trans = CGAffineTransformMakeTranslation (rect.width, rect.height); - trans = CGAffineTransformRotate (trans, M_PI); - break; - - case 4: // flip vertical - trans = CGAffineTransformMakeTranslation (0, rect.height); - trans = CGAffineTransformScale (trans, 1, -1); - break; - - case 5: // transpose (UL-to-LR axis) - trans = CGAffineTransformMakeTranslation (rect.height, rect.width); - trans = CGAffineTransformScale (trans, -1, 1); - trans = CGAffineTransformRotate (trans, 3 * M_PI / 2); - break; - - case 6: // rotate 90 - trans = CGAffineTransformMakeTranslation (0, rect.width); - trans = CGAffineTransformRotate (trans, 3 * M_PI / 2); - break; - - case 7: // transverse (UR-to-LL axis) - trans = CGAffineTransformMakeScale (-1, 1); - trans = CGAffineTransformRotate (trans, M_PI / 2); - break; - - case 8: // rotate 270 - trans = CGAffineTransformMakeTranslation (rect.height, 0); - trans = CGAffineTransformRotate (trans, M_PI / 2); - break; - - default: - break; - } - - return trans; -} -*/ - -void -jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, - Bool nsimg_p, void *img_arg, - XRectangle *geom_ret, int exif_rotation) -{ - Assert (False, "jwxyz_draw_NSImage_or_CGImage: TODO stub"); -#if 0 - CGImageRef cgi; -# ifndef USE_IPHONE - CGImageSourceRef cgsrc; -# endif // USE_IPHONE - NSSize imgr; - - CGContextRef cgc = d->cgc; - - if (nsimg_p) { - - NSImage *nsimg = (NSImage *) img_arg; - imgr = [nsimg size]; - -# ifndef USE_IPHONE - // convert the NSImage to a CGImage via the toll-free-bridging - // of NSData and CFData... - // - NSData *nsdata = [NSBitmapImageRep - TIFFRepresentationOfImageRepsInArray: - [nsimg representations]]; - CFDataRef cfdata = (CFDataRef) nsdata; - cgsrc = CGImageSourceCreateWithData (cfdata, NULL); - cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL); -# else // USE_IPHONE - cgi = nsimg.CGImage; -# endif // USE_IPHONE - - } else { - cgi = (CGImageRef) img_arg; - imgr.width = CGImageGetWidth (cgi); - imgr.height = CGImageGetHeight (cgi); - } - - Bool rot_p = (exif_rotation >= 5); - - if (rot_p) - imgr = NSMakeSize (imgr.height, imgr.width); - - CGRect winr = d->frame; - float rw = winr.size.width / imgr.width; - float rh = winr.size.height / imgr.height; - float r = (rw < rh ? rw : rh); - - CGRect dst, dst2; - dst.size.width = imgr.width * r; - dst.size.height = imgr.height * r; - dst.origin.x = (winr.size.width - dst.size.width) / 2; - dst.origin.y = (winr.size.height - dst.size.height) / 2; - - dst2.origin.x = dst2.origin.y = 0; - if (rot_p) { - dst2.size.width = dst.size.height; - dst2.size.height = dst.size.width; - } else { - dst2.size = dst.size; - } - - // Clear the part not covered by the image to background or black. - // - if (d->type == WINDOW) - XClearWindow (dpy, d); - else { - jwxyz_fill_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, - drawable_depth (d) == 1 ? 0 : BlackPixel(dpy,0)); - } - - CGAffineTransform trans = - exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size); - - CGContextSaveGState (cgc); - CGContextConcatCTM (cgc, - CGAffineTransformMakeTranslation (dst.origin.x, - dst.origin.y)); - CGContextConcatCTM (cgc, trans); - //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace"); - CGContextDrawImage (cgc, dst2, cgi); - CGContextRestoreGState (cgc); - -# ifndef USE_IPHONE - if (nsimg_p) { - CFRelease (cgsrc); - CGImageRelease (cgi); - } -# endif // USE_IPHONE - - if (geom_ret) { - geom_ret->x = dst.origin.x; - geom_ret->y = dst.origin.y; - geom_ret->width = dst.size.width; - geom_ret->height = dst.size.height; - } - - invalidate_drawable_cache (d); -#endif -} - -#ifndef HAVE_JWZGLES - -/* -static void -create_rectangle_texture (GLuint *texture) -{ - glGenTextures(1, texture); - glBindTexture(GL_TEXTURE_RECTANGLE_EXT, *texture); - glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST); -} -*/ - -#endif - - #if 0 -static Pixmap -copy_pixmap (Display *dpy, Pixmap p) -{ - if (!p) return 0; - Assert (p->type == PIXMAP, "not a pixmap"); - - Pixmap p2 = 0; - - Window root; - int x, y; - unsigned int width, height, border_width, depth; - if (XGetGeometry (dpy, p, &root, - &x, &y, &width, &height, &border_width, &depth)) { - XGCValues gcv; - gcv.function = GXcopy; - GC gc = XCreateGC (dpy, p, GCFunction, &gcv); - if (gc) { - p2 = XCreatePixmap (dpy, p, width, height, depth); - if (p2) - XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0); - XFreeGC (dpy, gc); + { + char s[10240]; + Log("#GI ---------- %d %d %d x %d %08lx", + jwxyz_drawable_depth(d), dest_image->depth, + width, height, + (unsigned long) d); + for (int y = 0; y < height; y++) { + int x; + for (x = 0; x < width; x++) { + unsigned long b = rgba_image[(height-y-1) * width + x]; + s[x] = ( (b & 0xFF000000) + ? ((b & 0x00FFFFFF) ? '#' : '-') + : ((b & 0x00FFFFFF) ? '+' : ' ')); + } + s[x] = 0; + Log("#GI [%s]",s); + } + Log("#GI ----------"); } - } - - Assert (p2, "could not copy pixmap"); - - return p2; -} #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) -{ - 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)); - -# 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; + const uint32_t *bottom = rgba_image + width * (height - 1); + for (unsigned y = height; y; --y) { + memset (top, 0, width / 8); + for (unsigned x = 0; x != width; ++x) { + if (bottom[x] & 0x80) + top[x >> 3] |= (1 << (x & 7)); } - // NSLog(@"using \"%@\": %d", name, enc); - - // *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 - } - -# 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; - - name2[strlen(name2) - strlen(spc)] = 0; - - 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); - } - } - - 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]; + top += dest_image->bytes_per_line; + bottom -= width; } - families = new_families; - } -# 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; - } + free (rgba_image); } - // None of the fonts support ASCII? - return 0; + return dest_image; } -// 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) +static int +SetClipMask (Display *dpy, GC gc, Pixmap m) { - const char *s2 = strchr(s, '-'); - if (!s2) - s2 = s + strlen(s); - return s2; -} + Assert (m != dpy->main_window, "not a pixmap"); + free_clip_mask (dpy, gc); -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); + if (!m) { + gc->clip_mask = 0; + return 0; } - 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) + Assert (jwxyz_drawable_depth (m) == 1, "wrong depth for clip mask"); - xlfd_next (&s, &s2); // Pixel size (ignore) + const XRectangle *frame = jwxyz_frame (m); + gc->clip_mask_width = frame->width; + gc->clip_mask_height = frame->height; - xlfd_next (&s, &s2); // Point size - char *s3; - uintmax_t n = strtoumax(s, &s3, 10); - if (s2 == s3) - size = n / 10.0; + /* Apparently GL_ALPHA isn't a valid format for a texture used in an FBO: - 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 + (86) Are any one- or two- component formats color-renderable? - if (!family_name && !rand) - family_name = default_font_family (require); + Presently none of the one- or two- component texture formats + defined in unextended OpenGL is color-renderable. The R + and RG float formats defined by the NV_float_buffer + extension are color-renderable. - if (size < 6 || size > 1000) - size = 12; + Although an early draft of the FBO specification permitted + rendering into alpha, luminance, and intensity formats, this + this capability was pulled when it was realized that it was + under-specified exactly how rendering into these formats + would work. (specifically, how R/G/B/A map to I/L/A) - size *= scale; + - NSFontTraitMask mask = require | forbid; + ...Therefore, 1-bit drawables get to have wasted color channels. + Currently, R=G=B=grey, and the alpha channel is unused. + */ - if (rand) { - nsfont = random_font (require, mask, size, &family_name, &ps_name); - [family_name autorelease]; - } + /* There doesn't seem to be any way to move what's on one of the color + channels to the alpha channel in GLES 1.1 without leaving the GPU. + */ - if (!nsfont) - nsfont = try_font (require, mask, family_name, size, &ps_name); + /* GLES 1.1 only supports GL_CLAMP_TO_EDGE or GL_REPEAT for + GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T, so to prevent drawing outside + the clip mask pixmap, there's two options: + 1. Use glScissor. + 2. Add a black border. - // 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); - } + Option #2 is in use here. - 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; - } -} + The following converts in-place an RGBA image to alpha-only image with a + 1px black border. + */ -#endif + // Prefix/suffix: 1 row+1 pixel, rounded to nearest int32. + size_t pad0 = frame->width + 3, pad = (pad0 + 3) & ~3; + uint8_t *data = malloc(2 * pad + frame->width * frame->height * 4); -Font -XLoadFont (Display *dpy, const char *name) -{ - Font fid = (Font) calloc (1, sizeof(*fid)); - fid->refcount = 1; - fid->dpy = dpy; + uint8_t *rgba = data + pad; + uint8_t *alpha = rgba; + uint8_t *alpha0 = alpha - pad0; + memset (alpha0, 0, pad0); // Top row. - // (TODO) float scale = 1; + jwxyz_gl_flush (dpy); + jwxyz_bind_drawable (dpy, dpy->main_window, m); + glReadPixels (0, 0, frame->width, frame->height, GL_RGBA, GL_UNSIGNED_BYTE, + rgba); -# ifdef USE_IPHONE - /* Since iOS screens are physically smaller than desktop screens, scale up - the fonts to make them more readable. + for (unsigned y = 0; y != frame->height; ++y) { + for (unsigned x = 0; x != frame->width; ++x) + alpha[x] = rgba[x * 4]; - 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 + rgba += frame->width * 4; - 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; + alpha += frame->width; + alpha[0] = 0; + alpha[1] = 0; + alpha += 2; } - 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) -{ - if (--fid->refcount < 0) abort(); - if (fid->refcount > 0) return 0; + alpha -= 2; + memset (alpha, 0, pad0); // Bottom row. - if (fid->native_font) - jwxyz_release_native_font (fid->dpy, fid->native_font); + glGenTextures (1, &gc->clip_mask); + tex_parameters (dpy, gc->clip_mask); - if (fid->ps_name) - free (fid->ps_name); - if (fid->metrics.per_char) - free (fid->metrics.per_char); + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); - // #### 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); + unsigned tex_w = frame->width + 2, tex_h = frame->height + 2; + tex_image (dpy, GL_ALPHA, &tex_w, &tex_h, GL_ALPHA, GL_UNSIGNED_BYTE, + alpha0); - free (fid); - return 0; -} + free(data); -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); - } return 0; } -int -XFreeFont (Display *dpy, XFontStruct *f) +static int +SetClipOrigin (Display *dpy, GC gc, int x, int y) { - Font fid = f->fid; - XFreeFontInfo (0, f, 1); - XUnloadFont (dpy, fid); + gc->gcv.clip_x_origin = x; + gc->gcv.clip_y_origin = y; return 0; } - -int -XSetFont (Display *dpy, GC gc, Font fid) +void set_points_list(XPoint *points, int npoints, linked_point *root) { - Font font2 = copy_font (fid); - if (gc->gcv.font) - XUnloadFont (dpy, gc->gcv.font); - gc->gcv.font = font2; - return 0; -} + linked_point *current; - -XFontSet -XCreateFontSet (Display *dpy, char *name, - char ***missing_charset_list_return, - int *missing_charset_count_return, - char **def_string_return) -{ - 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; + 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; } - 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; + current->x = points[npoints-2].x; + current->y = points[npoints-2].y; + current->next = root; } -void -XFreeFontSet (Display *dpy, XFontSet set) +double compute_edge_length(linked_point * a, linked_point * b) { - XFreeFont (dpy, set->font); - free (set); -} + int xdiff, ydiff, xsq, ysq, added; + double xy_add, edge_length; -const char * -jwxyz_nativeFontName (Font f, float *size) -{ - if (size) *size = f->size; - return f->ps_name; + 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; } - -void -XFreeStringList (char **list) +double get_angle(double a, double b, double c) { - int i; - if (!list) return; - for (i = 0; list[i]; i++) - XFree (list[i]); - XFree (list); + 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; } -// Returns the verbose Unicode name of this character, like "agrave" or -// "daggerdouble". Used by fontglide debugMetrics. -// -char * -jwxyz_unicode_character_name (Font fid, unsigned long uc) +Bool is_same_slope(linked_point * a) { - /* TODO Fonts - char *ret = 0; - CTFontRef ctfont = - CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName], - [fid->nsfont pointSize], - NULL); - Assert (ctfont, @"no CTFontRef for UIFont"); - - CGGlyph cgglyph; - if (CTFontGetGlyphsForCharacters (ctfont, (UniChar *) &uc, &cgglyph, 1)) { - NSString *name = (NSString *) - CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0), - cgglyph); - ret = (name ? strdup ([name UTF8String]) : 0); - } - CFRelease (ctfont); - return ret; - */ - return NULL; -} + int abx, bcx, aby, bcy, aa, bb; + linked_point *b; + linked_point *c; + b = a->next; + c = b->next; -// 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) -{ - 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; + // 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 } - *out = 0; - NSString *nsstr = - [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding]; - free (s2); - if (latin1_pP) *latin1_pP = latin1_p; - return (nsstr ? nsstr : @""); -} -*/ -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; -} + 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; -int -XTextWidth (XFontStruct *f, const char *s, int length) -{ - int ascent, descent, dir; - XCharStruct cs; - XTextExtents (f, s, length, &dir, &ascent, &descent, &cs); - return cs.width; + return aa == bb; } - -int -XTextExtents16 (XFontStruct *f, const XChar2b *s, int length, - int *dir_ret, int *ascent_ret, int *descent_ret, - XCharStruct *cs) +void draw_three_vertices(Display *dpy, linked_point * a, Bool triangle) { - // Bool latin1_p = True; - int i, utf8_len = 0; - char *utf8 = XChar2b_to_utf8 (s, &utf8_len); // already sanitized - - for (i = 0; i < length; i++) - if (s[i].byte1 > 0) { - // latin1_p = False; - break; - } - - { - Font ff = f->fid; - Display *dpy = ff->dpy; - jwxyz_render_text (dpy, ff->native_font, utf8, strlen(utf8), - GL_TRUE, cs, 0); - } - - *dir_ret = 0; - *ascent_ret = f->ascent; - *descent_ret = f->descent; - free (utf8); - return 0; -} - - -/* "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." + linked_point *b; + linked_point *c; + GLenum drawType; - [So this means that x is the left of the ink, and width grows right. - For left-of-the-origin characters, x is negative.] + b = a->next; + c = b->next; - "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) -{ -#if 0 - Bool latin1_p; - NSString *nsstr = sanitize_utf8 (str, len, &latin1_p); - XCharStruct cs; - - utf8_metrics (set->font->fid, nsstr, &cs); + GLfloat vertices[3][2] = { + {a->x, a->y}, + {b->x, b->y}, + {c->x, c->y} + }; - /* "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." + if (triangle) { + drawType = GL_TRIANGLES; + } else { + drawType = GL_LINES; + } - 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); + glEnableClientState(GL_VERTEX_ARRAY); + vertex_pointer(dpy, GL_FLOAT, 0, vertices); + glDrawArrays(drawType, 0, 3); - return cs.width; -#endif - abort(); + free(b); // cut midpoint off from remaining polygon vertex list + a->next = c; } -static int -draw_string (Display *dpy, Drawable d, GC gc, int x, int y, - const char *str, size_t len, GLboolean utf8) +Bool is_an_ear(linked_point * a) { - 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; - } - } + double edge_ab, edge_bc, edge_ac; + double angle_a, angle_b, angle_c; + double my_pi; + linked_point *b, *c; - XPutImage (dpy, d, gc, img, 0, 0, - x + cs.lbearing, - y - cs.ascent, - w, h); - XDestroyImage (img); + b = a->next; + c = b->next; + my_pi = (double) M_PI; - return 0; -} + 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); -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); + return angle_a < my_pi && angle_b < my_pi && angle_c < my_pi; } -int -XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y, - const XChar2b *str, int len) +Bool is_three_point_loop(linked_point * head) { - 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; + return head->x == head->next->next->next->x + && head->y == head->next->next->next->y; } -void -Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc, - int x, int y, const char *str, int len) +void traverse_points_list(Display *dpy, linked_point * root) { - draw_string (dpy, d, gc, x, y, str, len, GL_TRUE); -} + linked_point *head; + head = root; + + while (!is_three_point_loop(head)) { + if (is_an_ear(head)) { + draw_three_vertices(dpy, head, True); + } else if (is_same_slope(head)) { + draw_three_vertices(dpy, head, False); + } else { + head = head->next; + } + } + // handle final three vertices in polygon + if (is_an_ear(head)) { + draw_three_vertices(dpy, head, True); + } else if (is_same_slope(head)) { + draw_three_vertices(dpy, head, False); + } else { + free(head->next->next); + free(head->next); + free(head); + Assert (False, "traverse_points_list: unknown configuration"); + } -int -XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y, - const char *str, int len) -{ - 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); + free(head->next); + free(head); } -int -XSetClipMask (Display *dpy, GC gc, Pixmap m) -{ -//#### abort(); -/* - TODO - - Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup"); +const struct jwxyz_vtbl gl_vtbl = { + root, + visual, + display_sources_data, - if (gc->gcv.clip_mask) { - XFreePixmap (dpy, gc->gcv.clip_mask); - CGImageRelease (gc->clip_mask); - } + window_background, + draw_arc, + fill_rects, + gc_gcv, + gc_depth, + jwxyz_draw_string, - 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; -} + jwxyz_gl_copy_area, -int -XSetClipOrigin (Display *dpy, GC gc, int x, int y) -{ - gc->gcv.clip_x_origin = x; - gc->gcv.clip_y_origin = y; - return 0; -} + DrawPoints, + DrawSegments, + CreateGC, + FreeGC, + ClearWindow, + SetClipMask, + SetClipOrigin, + FillPolygon, + DrawLines, + PutImage, + GetSubImage +}; #endif /* JWXYZ_GL -- entire file */