From http://www.jwz.org/xscreensaver/xscreensaver-5.40.tar.gz
[xscreensaver] / jwxyz / jwxyz-gl.c
1 /* xscreensaver, Copyright (c) 1991-2018 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* JWXYZ Is Not Xlib.
13
14    But it's a bunch of function definitions that bear some resemblance to
15    Xlib and that do OpenGL-ish things that bear some resemblance to the
16    things that Xlib might have done.
17
18    This is the version of jwxyz for Android.  The version used by macOS
19    and iOS is in jwxyz.m.
20  */
21
22 /* Be advised, this is all very much a work in progress. */
23
24 /* There is probably no reason to ever implement indexed-color rendering here,
25    even if many screenhacks still work with PseudoColor.
26    - OpenGL ES 1.1 (Android, iOS) doesn't support indexed color.
27    - macOS only provides indexed color via AGL (now deprecated), not
28      NSOpenGLPixelFormat.
29  */
30
31 /* TODO:
32    - malloc error checking
33    - Check max texture sizes for XGet/PutImage, XCopyArea.
34    - Optional 5:5:5 16-bit color
35 */
36
37 /* Techniques & notes:
38    - iOS: OpenGL ES 2.0 isn't always available. Use OpenGL ES 1.1.
39    - OS X: Drivers can go back to OpenGL 1.1 (GeForce 2 MX on 10.5.8).
40    - Use stencil buffers (OpenGL 1.0+) for bitmap clipping masks.
41    - Pixmaps can be any of the following, depending on GL implementation.
42      - This requires offscreen rendering. Fortunately, this is always
43        available.
44        - OS X: Drawable objects, including: pixel buffers and offscreen
45          memory.
46          - Offscreen buffers w/ CGLSetOffScreen (10.0+)
47          - http://lists.apple.com/archives/mac-opengl/2002/Jun/msg00087.html
48            provides a very ugly solution for hardware-accelerated offscreen
49            rendering with CGLSetParameter(*, kCGLCPSurfaceTexture, *) on OS X
50            10.1+. Apple docs say it's actually for OS X 10.3+, instead.
51          - Pixel buffers w/ CGLSetPBuffer (10.3+, now deprecated)
52            - Requires APPLE_pixel_buffer.
53              - Available in software on x86 only.
54              - Always available on hardware.
55            - Need to blit? Use OpenGL pixel buffers. (GL_PIXEL_PACK_BUFFER)
56          - Framebuffer objects w/ GL_(ARB|EXT)_framebuffer_object
57            - TODO: Can EXT_framebuffers be different sizes?
58            - Preferred on 10.7+
59        - iOS: Use OES_framebuffer_object, it's always present.
60  */
61
62 /* OpenGL hacks call a number of X11 functions, including
63    XCopyArea, XDrawString, XGetImage
64    XCreatePixmap, XCreateGC, XCreateImage
65    XPutPixel
66    Check these, of course.
67  */
68
69 #ifdef JWXYZ_GL /* entire file */
70
71 #include <math.h>
72 #include <limits.h>
73 #include <stddef.h>
74 #include <stdlib.h>
75 #include <stdint.h>
76 #include <string.h>
77 #include <wchar.h>
78
79 #ifdef HAVE_COCOA
80 # ifdef USE_IPHONE
81 #  import <QuartzCore/QuartzCore.h>
82 #  include <OpenGLES/ES1/gl.h>
83 #  include <OpenGLES/ES1/glext.h>
84 # else
85 #  include <OpenGL/glu.h>
86 # endif
87 #else
88 /* TODO: Does this work on iOS? */
89 # ifndef HAVE_JWZGLES
90 #  include <gl/glu.h>
91 # else
92 #  include <GLES/gl.h>
93 #  include <GLES/glext.h>
94 # endif
95 #endif
96
97 #ifdef HAVE_JWZGLES
98 # include "jwzglesI.h"
99 #endif
100
101 #include "jwxyzI.h"
102 #include "jwxyz-timers.h"
103 #include "yarandom.h"
104 #include "utf8wc.h"
105 #include "xft.h"
106 #include "pow2.h"
107
108 #define countof(x) (sizeof((x))/sizeof((*x)))
109
110 union color_bytes {
111   uint32_t pixel;
112   uint8_t bytes[4];
113 };
114
115 // Use two textures: one for RGBA, one for luminance. Older Android doesn't
116 // seem to like it when textures change format.
117 enum {
118   texture_rgba,
119   texture_mono
120 };
121
122 struct jwxyz_Display {
123   const struct jwxyz_vtbl *vtbl; // Must come first.
124
125   Window main_window;
126   GLenum pixel_format, pixel_type;
127   Visual visual;
128   struct jwxyz_sources_data *timers_data;
129
130   Bool gl_texture_npot_p;
131   /* Bool opengl_core_p */;
132   GLenum gl_texture_target;
133   
134   GLuint textures[2]; // Also can work on the desktop.
135
136   unsigned long window_background;
137
138   int gc_function;
139   Bool gc_alpha_allowed_p;
140   int gc_clip_x_origin, gc_clip_y_origin;
141   GLuint gc_clip_mask;
142
143   // Alternately, there could be one queue per pixmap.
144   size_t queue_size, queue_capacity;
145   Drawable queue_drawable;
146   GLint queue_mode;
147   void *queue_vertex;
148   uint32_t *queue_color;
149   Bool queue_line_cap;
150 };
151
152 struct jwxyz_GC {
153   XGCValues gcv;
154   unsigned int depth;
155   GLuint clip_mask;
156   unsigned clip_mask_width, clip_mask_height;
157 };
158
159 struct jwxyz_linked_point {
160     short x, y;
161     linked_point *next;
162 };
163
164
165 void
166 jwxyz_assert_display(Display *dpy)
167 {
168   if(!dpy)
169     return;
170   jwxyz_assert_gl ();
171   jwxyz_assert_drawable (dpy->main_window, dpy->main_window);
172 }
173
174
175 void
176 jwxyz_set_matrices (Display *dpy, unsigned width, unsigned height,
177                     Bool window_p)
178 {
179   Assert (width, "no width");
180   Assert (height, "no height");
181
182   /* TODO: Check registration pattern from Interference with rectangles instead of points. */
183
184   // The projection matrix is always set as follows. The modelview matrix is
185   // usually identity, but for points and thin lines, it's translated by 0.5.
186   glMatrixMode(GL_PROJECTION);
187   glLoadIdentity();
188   
189 # if defined(USE_IPHONE) || defined(HAVE_ANDROID)
190
191   if (window_p && ignore_rotation_p(dpy)) {
192     int o = (int) current_device_rotation();
193     glRotatef (-o, 0, 0, 1);
194   }
195
196   // glPointSize(1); // This is the default.
197   
198 #ifdef HAVE_JWZGLES
199   glOrthof /* TODO: Is glOrthox worth it? Signs point to no. */
200 #else
201   glOrtho
202 #endif
203     (0, width, height, 0, -1, 1);
204
205   glMatrixMode(GL_MODELVIEW);
206 # endif // HAVE_MOBILE
207 }
208
209 #ifndef HAVE_JWZGLES
210
211 struct gl_version
212 {
213   // iOS always uses OpenGL ES 1.1.
214   unsigned major;
215   unsigned minor;
216 };
217
218 static GLboolean
219 gl_check_ver (const struct gl_version *caps,
220               unsigned gl_major,
221               unsigned gl_minor)
222 {
223   return caps->major > gl_major ||
224            (caps->major == gl_major && caps->minor >= gl_minor);
225 }
226
227 #endif
228
229
230 static void
231 tex_parameters (Display *d, GLuint texture)
232 {
233   // TODO: Check for (ARB|EXT|NV)_texture_rectangle. (All three are alike.)
234   // Rectangle textures should be present on OS X with the following exceptions:
235   // - Generic renderer on PowerPC OS X 10.4 and earlier
236   // - ATI Rage 128
237   glBindTexture (d->gl_texture_target, texture);
238   // TODO: This is all somewhere else. Refactor.
239   glTexParameteri (d->gl_texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
240   glTexParameteri (d->gl_texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
241
242   // This might be redundant for rectangular textures.
243 # ifndef HAVE_JWZGLES
244   const GLint wrap = GL_CLAMP;
245 # else  // HAVE_JWZGLES
246   const GLint wrap = GL_CLAMP_TO_EDGE;
247 # endif // HAVE_JWZGLES
248
249   // In OpenGL, CLAMP_TO_EDGE is OpenGL 1.2 or GL_SGIS_texture_edge_clamp.
250   // This is always present with OpenGL ES.
251   glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_S, wrap);
252   glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_T, wrap);
253 }
254
255 static void
256 tex_size (Display *dpy, unsigned *tex_w, unsigned *tex_h)
257 {
258   if (!dpy->gl_texture_npot_p) {
259     *tex_w = to_pow2(*tex_w);
260     *tex_h = to_pow2(*tex_h);
261   }
262 }
263
264 static void
265 tex_image (Display *dpy, GLenum internalformat,
266            unsigned *tex_w, unsigned *tex_h, GLenum format, GLenum type,
267            const void *data)
268 {
269   unsigned w = *tex_w, h = *tex_h;
270   tex_size (dpy, tex_w, tex_h);
271
272   // TODO: Would using glTexSubImage2D exclusively be faster?
273   if (*tex_w == w && *tex_h == h) {
274     glTexImage2D (dpy->gl_texture_target, 0, internalformat, *tex_w, *tex_h,
275                   0, format, type, data);
276   } else {
277     // TODO: Sampling the last row might be a problem if src_x != 0.
278     glTexImage2D (dpy->gl_texture_target, 0, internalformat, *tex_w, *tex_h,
279                   0, format, type, NULL);
280     glTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, w, h,
281                      format, type, data);
282   }
283 }
284
285
286 extern const struct jwxyz_vtbl gl_vtbl;
287
288 Display *
289 jwxyz_gl_make_display (Window w)
290 {
291   Display *d = (Display *) calloc (1, sizeof(*d));
292   d->vtbl = &gl_vtbl;
293
294 # ifndef HAVE_JWZGLES
295   struct gl_version version;
296
297   {
298     const GLubyte *version_str = glGetString (GL_VERSION);
299
300     /* iPhone is always OpenGL ES 1.1. */
301     if (sscanf ((const char *) version_str, "%u.%u",
302                 &version.major, &version.minor) < 2)
303     {
304       version.major = 1;
305       version.minor = 1;
306     }
307   }
308 # endif // !HAVE_JWZGLES
309
310   const GLubyte *extensions = glGetString (GL_EXTENSIONS);
311
312   /* See:
313      - Apple TN2080: Understanding and Detecting OpenGL Functionality.
314      - OpenGL Programming Guide for the Mac - Best Practices for Working with
315        Texture Data - Optimal Data Formats and Types
316    */
317
318   // If a video adapter suports BGRA textures, then that's probably as fast as
319   // you're gonna get for getting a texture onto the screen.
320 # ifdef HAVE_JWZGLES
321   /* TODO: Make BGRA work on iOS. As it is, it breaks XPutImage. (glTexImage2D, AFAIK) */
322   d->pixel_format = GL_RGBA; /*
323     gluCheckExtension ((const GLubyte *) "GL_APPLE_texture_format_BGRA8888",
324                        extensions) ? GL_BGRA_EXT : GL_RGBA; */
325   d->pixel_type = GL_UNSIGNED_BYTE;
326   // See also OES_read_format.
327 # else  // !HAVE_JWZGLES
328   if (gl_check_ver (&version, 1, 2) ||
329       (gluCheckExtension ((const GLubyte *) "GL_EXT_bgra", extensions) &&
330        gluCheckExtension ((const GLubyte *) "GL_APPLE_packed_pixels",
331                           extensions))) {
332     // APPLE_packed_pixels is only ever available on iOS, never Android.
333     d->pixel_format = GL_BGRA_EXT;
334     // Both Intel and PowerPC-era docs say to use GL_UNSIGNED_INT_8_8_8_8_REV.
335     d->pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
336   } else {
337     d->pixel_format = GL_RGBA;
338     d->pixel_type = GL_UNSIGNED_BYTE;
339   }
340   // GL_ABGR_EXT/GL_UNSIGNED_BYTE is another possibilty that may have made more
341   // sense on PowerPC.
342 # endif // !HAVE_JWZGLES
343
344   // On really old systems, it would make sense to split textures
345   // into subsections, to work around the maximum texture size.
346 # ifndef HAVE_JWZGLES
347   d->gl_texture_npot_p = gluCheckExtension ((const GLubyte *)
348                                             "GL_ARB_texture_rectangle",
349                                             extensions);
350   d->gl_texture_target = d->gl_texture_npot_p ?
351                          GL_TEXTURE_RECTANGLE_EXT :
352                          GL_TEXTURE_2D;
353 # else
354   d->gl_texture_npot_p = jwzgles_gluCheckExtension
355       ((const GLubyte *) "GL_APPLE_texture_2D_limited_npot", extensions) ||
356     jwzgles_gluCheckExtension
357       ((const GLubyte *) "GL_OES_texture_npot", extensions) ||
358     jwzgles_gluCheckExtension // From PixelFlinger 1.4
359       ((const GLubyte *) "GL_ARB_texture_non_power_of_two", extensions);
360
361   d->gl_texture_target = GL_TEXTURE_2D;
362 # endif
363
364   Visual *v = &d->visual;
365   v->class      = TrueColor;
366   if (d->pixel_format == GL_BGRA_EXT) {
367     v->red_mask   = 0x00ff0000;
368     v->green_mask = 0x0000ff00;
369     v->blue_mask  = 0x000000ff;
370     v->alpha_mask = 0xff000000;
371   } else {
372     Assert(d->pixel_format == GL_RGBA,
373            "jwxyz_gl_make_display: Unknown pixel_format");
374     unsigned long masks[4];
375     for (unsigned i = 0; i != 4; ++i) {
376       union color_bytes color;
377       color.pixel = 0;
378       color.bytes[i] = 0xff;
379       masks[i] = color.pixel;
380     }
381     v->red_mask   = masks[0];
382     v->green_mask = masks[1];
383     v->blue_mask  = masks[2];
384     v->alpha_mask = masks[3];
385   }
386
387   d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
388
389   d->window_background = BlackPixel(d,0);
390
391   d->main_window = w;
392
393   {
394     GLint max_texture_size, max_texture_units;
395     glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size);
396     glGetIntegerv (GL_MAX_TEXTURE_UNITS, &max_texture_units);
397     Log ("GL_MAX_TEXTURE_SIZE: %d, GL_MAX_TEXTURE_UNITS: %d\n",
398          max_texture_size, max_texture_units);
399
400     // OpenGL ES 1.1 and OpenGL 1.3 both promise at least 2 texture units:
401     // OpenGL (R) ES Common/Common-Lite Profile Specification, Version 1.1.12 (Full Specification)
402     // https://www.khronos.org/registry/OpenGL/specs/es/1.1/es_full_spec_1.1.pdf
403     // * Table 6.22. Implementation Dependent Values
404     // * D.2 Enhanced Texture Processing
405     // (OpenGL 1.2 provides multitexturing as an ARB extension, and requires 1
406     // texture unit only.)
407
408     // ...but the glGet reference page contradicts this, and says there can be
409     // just one.
410     // https://www.khronos.org/registry/OpenGL-Refpages/es1.1/xhtml/glGet.xml
411   }
412
413   glGenTextures (countof (d->textures), d->textures);
414
415   for (unsigned i = 0; i != countof (d->textures); i++) {
416     tex_parameters (d, d->textures[i]);
417   }
418
419   d->gc_function = GXcopy;
420   d->gc_alpha_allowed_p = False;
421   d->gc_clip_mask = 0;
422
423   jwxyz_assert_display(d);
424   return d;
425 }
426
427 void
428 jwxyz_gl_free_display (Display *dpy)
429 {
430   Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable");
431
432   /* TODO: Go over everything. */
433
434   free (dpy->queue_vertex);
435   free (dpy->queue_color);
436
437   jwxyz_sources_free (dpy->timers_data);
438
439   free (dpy);
440 }
441
442
443 /* Call this when the View changes size or position.
444  */
445 void
446 jwxyz_window_resized (Display *dpy)
447 {
448   Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable");
449
450   const XRectangle *new_frame = jwxyz_frame (dpy->main_window);
451   unsigned new_width = new_frame->width;
452   unsigned new_height = new_frame->height;
453
454   Assert (new_width, "jwxyz_window_resized: No width.");
455   Assert (new_height, "jwxyz_window_resized: No height.");
456
457 /*if (cgc) w->cgc = cgc;
458   Assert (w->cgc, "no CGContext"); */
459
460   Log("resize: %d, %d\n", new_width, new_height);
461
462   jwxyz_gl_flush (dpy);
463   jwxyz_bind_drawable (dpy, dpy->main_window, dpy->main_window);
464
465   // TODO: What does the iPhone need?
466   
467   // iOS only: If the main_window is not the current_drawable, then set_matrices
468   // was already called in bind_drawable.
469   jwxyz_set_matrices (dpy, new_width, new_height, True);
470
471 /*
472   GLint draw_buffer;
473   glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
474   
475   glDrawBuffer (GL_FRONT);
476   glClearColor (1, 0, 1, 0);
477   glClear (GL_COLOR_BUFFER_BIT);
478   glDrawBuffer (GL_BACK);
479   glClearColor (0, 1, 1, 0);
480   glClear (GL_COLOR_BUFFER_BIT);
481   
482   glDrawBuffer (draw_buffer); */
483   
484   // Stylish and attractive purple!
485   // glClearColor (1, 0, 1, 0.5);
486   // glClear (GL_COLOR_BUFFER_BIT);
487 }
488
489
490 static jwxyz_sources_data *
491 display_sources_data (Display *dpy)
492 {
493   return dpy->timers_data;
494 }
495
496
497 static Window
498 root (Display *dpy)
499 {
500   return dpy->main_window;
501 }
502
503 static Visual *
504 visual (Display *dpy)
505 {
506   return &dpy->visual;
507 }
508
509
510 /* GC attributes by usage and OpenGL implementation:
511
512    All drawing functions:
513    function                                | glLogicOp w/ GL_COLOR_LOGIC_OP
514    clip_x_origin, clip_y_origin, clip_mask | Multitexturing w/ GL_TEXTURE1
515
516    Shape drawing functions:
517    foreground, background                  | glColor*
518
519    XDrawLines, XDrawRectangles, XDrawSegments:
520    line_width, cap_style, join_style       | Lotsa vertices
521
522    XFillPolygon:
523    fill_rule                               | Multiple GL_TRIANGLE_FANs
524
525    XDrawText:
526    font                                    | Cocoa, then OpenGL display lists.
527
528    alpha_allowed_p                         | GL_BLEND
529
530    antialias_p                             | Well, there's options:
531    * Multisampling would work, but that's something that would need to be set
532      per-Pixmap, not per-GC.
533    * GL_POINT, LINE, and POLYGON_SMOOTH are the old-school way of doing
534      this, but POINT_SMOOTH is unnecessary, and POLYGON_SMOOTH is missing from
535      GLES 1. All three are missing from GLES 2. Word on the street is that
536      these are deprecated anyway.
537    * Tiny textures with bilinear filtering to get the same effect as LINE_ and
538      POLYGON_SMOOTH. A bit tricky.
539    * Do nothing. Android hardware is very often high-DPI enough that
540      anti-aliasing doesn't matter all that much.
541
542    Nothing, really:
543    subwindow_mode
544  */
545
546 static void *
547 enqueue (Display *dpy, Drawable d, GC gc, int mode, size_t count,
548          unsigned long pixel)
549 {
550   if (dpy->queue_size &&
551       (!gc || /* Could allow NULL GCs here... */
552        dpy->gc_function != gc->gcv.function ||
553        dpy->gc_alpha_allowed_p != gc->gcv.alpha_allowed_p ||
554        dpy->gc_clip_mask != gc->clip_mask ||
555        dpy->gc_clip_x_origin != gc->gcv.clip_x_origin ||
556        dpy->gc_clip_y_origin != gc->gcv.clip_y_origin ||
557        dpy->queue_mode != mode ||
558        dpy->queue_drawable != d)) {
559     jwxyz_gl_flush (dpy);
560   }
561
562   jwxyz_bind_drawable (dpy, dpy->main_window, d);
563   jwxyz_gl_set_gc (dpy, gc);
564
565   if (mode == GL_TRIANGLE_STRIP)
566     Assert (count, "empty triangle strip");
567   // Use degenerate triangles to cut down on draw calls.
568   Bool prepend2 = mode == GL_TRIANGLE_STRIP && dpy->queue_size;
569
570   // ####: Primitive restarts should be used here when (if) they're available.
571   if (prepend2)
572     count += 2;
573
574   // TODO: Use glColor when we can get away with it.
575   size_t old_size = dpy->queue_size;
576   dpy->queue_size += count;
577   if (dpy->queue_size > dpy->queue_capacity) {
578     dpy->queue_capacity = dpy->queue_size * 2;
579
580     uint32_t *new_color = realloc (
581       dpy->queue_color, sizeof(*dpy->queue_color) * dpy->queue_capacity);
582     /* Allocate vertices as if they were always GLfloats. Otherwise, if
583        queue_vertex is allocated to hold GLshorts, then things get switched
584        to GLfloats, queue_vertex would be too small for the given capacity.
585      */
586     GLshort *new_vertex = realloc (
587       dpy->queue_vertex, sizeof(GLfloat) * 2 * dpy->queue_capacity);
588
589     if (!new_color || !new_vertex)
590       return NULL;
591
592     dpy->queue_color = new_color;
593     dpy->queue_vertex = new_vertex;
594   }
595
596   dpy->queue_mode = mode;
597   dpy->queue_drawable = d;
598
599   union color_bytes color;
600
601   // Like query_color, but for bytes.
602   jwxyz_validate_pixel (dpy, pixel, jwxyz_drawable_depth (d),
603                         gc ? gc->gcv.alpha_allowed_p : False);
604
605   if (jwxyz_drawable_depth (d) == 1) {
606     uint8_t b = pixel ? 0xff : 0;
607     color.bytes[0] = b;
608     color.bytes[1] = b;
609     color.bytes[2] = b;
610     color.bytes[3] = 0xff;
611   } else {
612     JWXYZ_QUERY_COLOR (dpy, pixel, 0xffull, color.bytes);
613   }
614
615   for (size_t i = 0; i != count; ++i) // TODO: wmemset when applicable.
616     dpy->queue_color[i + old_size] = color.pixel;
617
618   void *result = (char *)dpy->queue_vertex + old_size * 2 *
619     (mode == GL_TRIANGLE_STRIP ? sizeof(GLfloat) : sizeof(GLshort));
620   if (prepend2) {
621     dpy->queue_color[old_size] = dpy->queue_color[old_size - 1];
622     result = (GLfloat *)result + 4;
623   }
624   return result;
625 }
626
627
628 static void
629 finish_triangle_strip (Display *dpy, GLfloat *enqueue_result)
630 {
631   if (enqueue_result != dpy->queue_vertex) {
632     enqueue_result[-4] = enqueue_result[-6];
633     enqueue_result[-3] = enqueue_result[-5];
634     enqueue_result[-2] = enqueue_result[0];
635     enqueue_result[-1] = enqueue_result[1];
636   }
637 }
638
639
640 static void
641 query_color (Display *dpy, unsigned long pixel, unsigned int depth,
642              Bool alpha_allowed_p, GLfloat *rgba)
643 {
644   jwxyz_validate_pixel (dpy, pixel, depth, alpha_allowed_p);
645
646   if (depth == 1) {
647     GLfloat f = pixel;
648     rgba[0] = f;
649     rgba[1] = f;
650     rgba[2] = f;
651     rgba[3] = 1;
652   } else {
653     JWXYZ_QUERY_COLOR (dpy, pixel, 1.0f, rgba);
654   }
655 }
656
657
658 static void
659 set_color (Display *dpy, unsigned long pixel, unsigned int depth,
660            Bool alpha_allowed_p)
661 {
662   GLfloat rgba[4];
663   query_color (dpy, pixel, depth, alpha_allowed_p, rgba);
664   glColor4f (rgba[0], rgba[1], rgba[2], rgba[3]);
665 }
666
667 /* Pushes a GC context; sets Function and ClipMask. */
668 void
669 jwxyz_gl_set_gc (Display *dpy, GC gc)
670 {
671   int function;
672   Bool alpha_allowed_p;
673   GLuint clip_mask;
674
675   // GC is NULL for XClearArea and XClearWindow.
676   if (gc) {
677     function = gc->gcv.function;
678     alpha_allowed_p = gc->gcv.alpha_allowed_p || gc->clip_mask;
679     clip_mask = gc->clip_mask;
680   } else {
681     function = GXcopy;
682     alpha_allowed_p = False;
683     clip_mask = 0;
684   }
685
686   /* GL_COLOR_LOGIC_OP: OpenGL 1.1. */
687   if (function != dpy->gc_function) {
688     dpy->gc_function = function;
689     if (function != GXcopy) {
690       /* Fun fact: The glLogicOp opcode constants are the same as the X11 GX*
691          function constants | GL_CLEAR.
692        */
693       glEnable (GL_COLOR_LOGIC_OP);
694       glLogicOp (gc->gcv.function | GL_CLEAR);
695     } else {
696       glDisable (GL_COLOR_LOGIC_OP);
697     }
698   }
699
700   /* Cocoa uses add/subtract/difference blending in place of logical ops.
701      It looks nice, but implementing difference blending in OpenGL appears to
702      require GL_KHR_blend_equation_advanced, and support for this is not
703      widespread.
704    */
705
706   dpy->gc_alpha_allowed_p = alpha_allowed_p;
707   if (alpha_allowed_p || clip_mask) {
708     // TODO: Maybe move glBlendFunc to XCreatePixmap?
709     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
710     glEnable (GL_BLEND);
711   } else {
712     glDisable (GL_BLEND);
713   }
714
715   /* Texture units:
716      GL_TEXTURE0: Texture for XPutImage/XCopyArea (if applicable)
717      GL_TEXTURE1: Texture for clip masks (if applicable)
718    */
719   dpy->gc_clip_mask = clip_mask;
720
721   glActiveTexture (GL_TEXTURE1);
722   if (clip_mask) {
723     glEnable (dpy->gl_texture_target);
724     glBindTexture (dpy->gl_texture_target, gc->clip_mask);
725
726     glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
727                alpha_allowed_p ? GL_MODULATE : GL_REPLACE);
728
729     glMatrixMode (GL_TEXTURE);
730     glLoadIdentity ();
731
732     unsigned
733       tex_w = gc->clip_mask_width + 2, tex_h = gc->clip_mask_height + 2;
734     tex_size (dpy, &tex_w, &tex_h);
735
736 # ifndef HAVE_JWZGLES
737     if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT)
738     {
739       glScalef (1, -1, 1);
740     }
741     else
742 # endif
743     {
744       glScalef (1.0f / tex_w, -1.0f / tex_h, 1);
745     }
746
747     glTranslatef (1 - gc->gcv.clip_x_origin,
748                   1 - gc->gcv.clip_y_origin - (int)gc->clip_mask_height - 2,
749                   0);
750   } else {
751     glDisable (dpy->gl_texture_target);
752   }
753   glActiveTexture (GL_TEXTURE0);
754 }
755
756
757 static void
758 set_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color)
759 {
760   jwxyz_gl_flush (dpy);
761   jwxyz_bind_drawable (dpy, dpy->main_window, d);
762   jwxyz_gl_set_gc (dpy, gc);
763
764   unsigned int depth;
765
766   if (gc) {
767     depth = gc->depth;
768
769     switch (gc->gcv.function) {
770       case GXset:   color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
771       case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
772     }
773   } else {
774     depth = visual_depth (NULL, NULL);
775   }
776
777   set_color (dpy, color, depth, gc ? gc->gcv.alpha_allowed_p : False);
778 }
779
780 /* Pushes a GC context; sets color to the foreground color.
781  */
782 static void
783 set_fg_gc (Display *dpy, Drawable d, GC gc)
784 {
785   set_color_gc (dpy, d, gc, gc->gcv.foreground);
786 }
787
788 static void
789 next_point(short *v, XPoint p, int mode)
790 {
791   switch (mode) {
792     case CoordModeOrigin:
793       v[0] = p.x;
794       v[1] = p.y;
795       break;
796     case CoordModePrevious:
797       v[0] += p.x;
798       v[1] += p.y;
799       break;
800     default:
801       Assert (False, "next_point: bad mode");
802       break;
803   }
804 }
805
806 static int
807 DrawPoints (Display *dpy, Drawable d, GC gc,
808             XPoint *points, int count, int mode)
809 {
810   short v[2] = {0, 0};
811
812   // TODO: XPoints can be fed directly to OpenGL.
813   GLshort *gl_points = enqueue (dpy, d, gc, GL_POINTS, count,
814                                 gc->gcv.foreground); // TODO: enqueue returns NULL.
815   for (unsigned i = 0; i < count; i++) {
816     next_point (v, points[i], mode);
817     gl_points[2 * i] = v[0];
818     gl_points[2 * i + 1] = v[1];
819   }
820
821   return 0;
822 }
823
824
825 static GLint
826 texture_internalformat (Display *dpy)
827 {
828 #ifdef HAVE_JWZGLES
829   return dpy->pixel_format;
830 #else
831   return GL_RGBA;
832 #endif
833 }
834
835 static GLenum
836 gl_pixel_type (const Display *dpy)
837 {
838   return dpy->pixel_type;
839 }
840
841 static void
842 clear_texture (Display *dpy)
843 {
844   glTexImage2D (dpy->gl_texture_target, 0, texture_internalformat(dpy), 0, 0,
845                 0, dpy->pixel_format, gl_pixel_type (dpy), NULL);
846 }
847
848
849 static void
850 vertex_pointer (Display *dpy, GLenum type, GLsizei stride,
851                 const void *pointer)
852 {
853   glVertexPointer(2, type, stride, pointer);
854   if (dpy->gc_clip_mask) {
855     glClientActiveTexture (GL_TEXTURE1);
856     glEnableClientState (GL_TEXTURE_COORD_ARRAY);
857     glTexCoordPointer (2, type, stride, pointer);
858     glClientActiveTexture (GL_TEXTURE0);
859   }
860 }
861
862
863 void
864 jwxyz_gl_flush (Display *dpy)
865 {
866   Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable");
867
868   if (!dpy->queue_size)
869     return;
870
871   // jwxyz_bind_drawable() and jwxyz_gl_set_gc() is called in enqueue().
872
873   glEnableClientState (GL_COLOR_ARRAY);
874   glEnableClientState (GL_VERTEX_ARRAY);
875   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
876
877   // TODO: Use glColor instead of glColorPointer if there's just one color.
878   // TODO: Does OpenGL use both GL_COLOR_ARRAY and glColor at the same time?
879   //       (Probably not.)
880   glColor4f (1, 1, 1, 1);
881
882   Bool shifted = dpy->queue_mode == GL_POINTS || dpy->queue_mode == GL_LINES;
883   if (shifted) {
884     glMatrixMode (GL_MODELVIEW);
885     glTranslatef (0.5, 0.5, 0);
886   }
887
888   glColorPointer (4, GL_UNSIGNED_BYTE, 0, dpy->queue_color);
889   vertex_pointer (dpy,
890                   dpy->queue_mode == GL_TRIANGLE_STRIP ? GL_FLOAT : GL_SHORT,
891                   0, dpy->queue_vertex);
892   glDrawArrays (dpy->queue_mode, 0, dpy->queue_size);
893
894   // TODO: This is right, right?
895   if (dpy->queue_mode == GL_LINES && dpy->queue_line_cap) {
896     Assert (!(dpy->queue_size % 2), "bad count for GL_LINES");
897     glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(GLubyte) * 8,
898                     dpy->queue_color);
899     vertex_pointer (dpy, GL_SHORT, sizeof(GLshort) * 4,
900                     (GLshort *)dpy->queue_vertex + 2);
901     glDrawArrays (GL_POINTS, 0, dpy->queue_size / 2);
902   }
903
904   if (shifted)
905     glLoadIdentity ();
906
907   glDisableClientState (GL_COLOR_ARRAY);
908   glDisableClientState (GL_VERTEX_ARRAY);
909
910   dpy->queue_size = 0;
911 }
912
913
914 void
915 jwxyz_gl_copy_area_read_tex_image (Display *dpy, unsigned src_height,
916                                    int src_x, int src_y,
917                                    unsigned int width, unsigned int height,
918                                    int dst_x, int dst_y)
919 {
920 #  if defined HAVE_COCOA && !defined USE_IPHONE
921   /* TODO: Does this help? */
922   /* glFinish(); */
923 #  endif
924
925   /* TODO: Fix TestX11 + mode_preserve with this one. */
926
927   unsigned tex_w = width, tex_h = height;
928   if (!dpy->gl_texture_npot_p) {
929     tex_w = to_pow2(tex_w);
930     tex_h = to_pow2(tex_h);
931   }
932
933   GLint internalformat = texture_internalformat(dpy);
934
935   /* TODO: This probably shouldn't always be texture_rgba. */
936   glBindTexture (dpy->gl_texture_target, dpy->textures[texture_rgba]);
937
938   if (tex_w == width && tex_h == height) {
939     glCopyTexImage2D (dpy->gl_texture_target, 0, internalformat,
940                       src_x, src_height - src_y - height, width, height, 0);
941   } else {
942     glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
943                   0, dpy->pixel_format, gl_pixel_type(dpy), NULL);
944     glCopyTexSubImage2D (dpy->gl_texture_target, 0, 0, 0,
945                          src_x, src_height - src_y - height, width, height);
946   }
947 }
948
949 void
950 jwxyz_gl_copy_area_write_tex_image (Display *dpy, GC gc, int src_x, int src_y,
951                                     unsigned int width, unsigned int height,
952                                     int dst_x, int dst_y)
953 {
954   jwxyz_gl_set_gc (dpy, gc);
955
956   /* TODO: Copy-pasted from read_tex_image. */
957   unsigned tex_w = width, tex_h = height;
958   if (!dpy->gl_texture_npot_p) {
959     tex_w = to_pow2(tex_w);
960     tex_h = to_pow2(tex_h);
961   }
962
963   /* Must match what's in jwxyz_gl_copy_area_read_tex_image. */
964   glBindTexture (dpy->gl_texture_target, dpy->textures[texture_rgba]);
965
966   jwxyz_gl_draw_image (dpy, gc, dpy->gl_texture_target, tex_w, tex_h,
967                        0, 0, gc->depth, width, height, dst_x, dst_y, False);
968
969   clear_texture (dpy);
970 }
971
972
973 void
974 jwxyz_gl_draw_image (Display *dpy, GC gc, GLenum gl_texture_target,
975                      unsigned int tex_w, unsigned int tex_h,
976                      int src_x, int src_y, int src_depth,
977                      unsigned int width, unsigned int height,
978                      int dst_x, int dst_y, Bool flip_y)
979 {
980   if (!gc || src_depth == gc->depth) {
981     glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
982   } else {
983     Assert (src_depth == 1 && gc->depth == 32,
984             "jwxyz_gl_draw_image: bad depths");
985
986     set_color (dpy, gc->gcv.background, gc->depth, gc->gcv.alpha_allowed_p);
987
988     GLfloat rgba[4];
989     query_color (dpy, gc->gcv.foreground, gc->depth, gc->gcv.alpha_allowed_p,
990                  rgba);
991     glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
992     glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba);
993   }
994
995   glEnable (gl_texture_target);
996   glEnableClientState (GL_TEXTURE_COORD_ARRAY);
997   glEnableClientState (GL_VERTEX_ARRAY);
998
999   Assert (!glIsEnabled (GL_COLOR_ARRAY), "glIsEnabled (GL_COLOR_ARRAY)");
1000   Assert (!glIsEnabled (GL_NORMAL_ARRAY), "glIsEnabled (GL_NORMAL_ARRAY)");
1001
1002   /* TODO: EXT_draw_texture or whatever it's called. */
1003   GLfloat vertices[4][2] = {
1004     {dst_x, dst_y},
1005     {dst_x, dst_y + height},
1006     {dst_x + width, dst_y + height},
1007     {dst_x + width, dst_y}
1008   };
1009
1010   GLfloat
1011     tex_x0 = src_x, tex_y0 = src_y,
1012     tex_x1 = src_x + width, tex_y1 = src_y;
1013
1014   if (flip_y)
1015     tex_y1 += height;
1016   else
1017     tex_y0 += height;
1018
1019 # ifndef HAVE_JWZGLES
1020   if (gl_texture_target != GL_TEXTURE_RECTANGLE_EXT)
1021 # endif
1022   {
1023     GLfloat mx = 1.0f / tex_w, my = 1.0f / tex_h;
1024     tex_x0 *= mx;
1025     tex_y0 *= my;
1026     tex_x1 *= mx;
1027     tex_y1 *= my;
1028   }
1029
1030   GLfloat tex_coords[4][2] = {
1031     {tex_x0, tex_y0},
1032     {tex_x0, tex_y1},
1033     {tex_x1, tex_y1},
1034     {tex_x1, tex_y0}
1035   };
1036
1037   vertex_pointer (dpy, GL_FLOAT, 0, vertices);
1038   glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
1039   glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1040
1041 //clear_texture();
1042   glDisable (gl_texture_target);
1043 }
1044
1045 #if 0
1046 void
1047 jwxyz_gl_copy_area_read_pixels (Display *dpy, Drawable src, Drawable dst,
1048                                 GC gc, int src_x, int src_y,
1049                                 unsigned int width, unsigned int height,
1050                                 int dst_x, int dst_y)
1051 {
1052   XImage *img = XGetImage (dpy, src, src_x, src_y, width, height, ~0, ZPixmap);
1053   XPutImage (dpy, dst, gc, img, 0, 0, dst_x, dst_y, width, height);
1054   XDestroyImage (img);
1055 }
1056 #endif
1057
1058
1059 static int
1060 DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1061            int mode)
1062 {
1063   set_fg_gc (dpy, d, gc);
1064
1065   /* TODO: Thick lines
1066    * Zero-length line segments
1067    * Paths with zero length total (Remember line width, cap style.)
1068    * Closed loops
1069    */
1070   
1071   if (!count)
1072     return 0;
1073
1074   GLshort *vertices = malloc(2 * sizeof(GLshort) * count); // TODO malloc NULL sigh
1075   
1076   glMatrixMode (GL_MODELVIEW);
1077   glTranslatef (0.5f, 0.5f, 0);
1078   
1079   short p[2] = {0, 0};
1080   for (unsigned i = 0; i < count; i++) {
1081     next_point (p, points[i], mode);
1082     vertices[2 * i] = p[0];
1083     vertices[2 * i + 1] = p[1];
1084   }
1085   
1086   glEnableClientState (GL_VERTEX_ARRAY);
1087   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1088   vertex_pointer (dpy, GL_SHORT, 0, vertices);
1089   glDrawArrays (GL_LINE_STRIP, 0, count);
1090   
1091   free (vertices);
1092
1093   if (gc->gcv.cap_style != CapNotLast) {
1094     // TODO: How does this look with multisampling?
1095     // TODO: Disable me for closed loops.
1096     vertex_pointer (dpy, GL_SHORT, 0, p);
1097     glDrawArrays (GL_POINTS, 0, 1);
1098   }
1099
1100   glLoadIdentity ();
1101   
1102   return 0;
1103 }
1104
1105
1106 // Turn line segment into parallelogram based on line_width
1107 //
1108 // TODO: Fix epicycle hack with large thickness, and truchet line segment ends
1109 //
1110 static void
1111 drawThickLine (Display *dpy, Drawable d, GC gc, int line_width,
1112                XSegment *segments)
1113 {
1114     double dx, dy, di, m, angle;
1115     int sx1, sx2, sy1, sy2;
1116
1117     sx1 = segments->x1;
1118     sy1 = segments->y1;
1119     sx2 = segments->x2;
1120     sy2 = segments->y2;
1121
1122     dx = sx1 - sx2;
1123     dy = sy1 - sy2;
1124     di = sqrt(dx * dx + dy * dy);
1125     dx = dx / di;
1126     dy = dy / di;
1127     m = dy / dx;
1128
1129     angle = atan(m); 
1130
1131     float sn = sin(angle);
1132     float cs = cos(angle);
1133     float line_width_f = (float) line_width;
1134
1135     float wsn = line_width_f * (sn/2);
1136     float csn = line_width_f * (cs/2);
1137
1138     float x3 = sx1 - wsn;
1139     float y3 = sy1 + csn;
1140     float x4 = sx1 + wsn;
1141     float y4 = sy1 - csn;
1142
1143     float x5 = sx2 - wsn;
1144     float y5 = sy2 + csn;
1145     float x6 = sx2 + wsn;
1146     float y6 = sy2 - csn;
1147
1148     GLfloat *coords = enqueue (dpy, d, gc, GL_TRIANGLE_STRIP, 4,
1149                                gc->gcv.foreground);
1150     coords[0] = x3;
1151     coords[1] = y3;
1152     coords[2] = x4;
1153     coords[3] = y4;
1154     coords[4] = x5;
1155     coords[5] = y5;
1156     coords[6] = x6;
1157     coords[7] = y6;
1158     finish_triangle_strip (dpy, coords);
1159 }
1160
1161
1162 static int
1163 DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1164 {
1165   /* TODO: Caps on thick lines. */
1166   /* Thin lines <= 1px are offset by +0.5; thick lines are not. */
1167
1168   if (count == 1 && gc->gcv.line_width > 1) {
1169     drawThickLine (dpy, d, gc, gc->gcv.line_width, segments);
1170   }
1171   else {
1172     if (dpy->queue_line_cap != (gc->gcv.cap_style != CapNotLast))
1173       jwxyz_gl_flush (dpy);
1174     dpy->queue_line_cap = gc->gcv.cap_style != CapNotLast;
1175
1176     // TODO: Static assert here.
1177     Assert (sizeof(XSegment) == sizeof(short) * 4 &&
1178             sizeof(GLshort) == sizeof(short) &&
1179             offsetof(XSegment, x1) == 0 &&
1180             offsetof(XSegment, x2) == 4,
1181             "XDrawSegments: Data alignment mix-up.");
1182
1183     memcpy (enqueue(dpy, d, gc, GL_LINES, count * 2, gc->gcv.foreground),
1184             segments, count * sizeof(XSegment));
1185   }
1186
1187   return 0;
1188 }
1189
1190
1191 static int
1192 ClearWindow (Display *dpy, Window win)
1193 {
1194   Assert (win == dpy->main_window, "not a window");
1195
1196   jwxyz_gl_flush (dpy);
1197   jwxyz_bind_drawable (dpy, win, win);
1198
1199   GLfloat color[4];
1200   JWXYZ_QUERY_COLOR (dpy, dpy->window_background, 1.0f, color);
1201
1202   glClearColor (color[0], color[1], color[2], 1);
1203   glClear (GL_COLOR_BUFFER_BIT);
1204   return True;
1205 }
1206
1207 static unsigned long *
1208 window_background (Display *dpy)
1209 {
1210   return &dpy->window_background;
1211 }
1212
1213 static void
1214 fill_rects (Display *dpy, Drawable d, GC gc,
1215             const XRectangle *rectangles, unsigned long nrectangles,
1216             unsigned long pixel)
1217 {
1218   for (unsigned long i = 0; i != nrectangles; ++i) {
1219     const XRectangle *r = &rectangles[i];
1220     GLfloat *coords = enqueue (dpy, d, gc, GL_TRIANGLE_STRIP, 4, pixel);
1221     coords[0] = r->x;
1222     coords[1] = r->y;
1223     coords[2] = r->x;
1224     coords[3] = r->y + r->height;
1225     coords[4] = r->x + r->width;
1226     coords[5] = r->y;
1227     coords[6] = r->x + r->width;
1228     coords[7] = r->y + r->height;
1229     finish_triangle_strip (dpy, coords);
1230   }
1231 }
1232
1233
1234 static int
1235 FillPolygon (Display *dpy, Drawable d, GC gc,
1236              XPoint *points, int npoints, int shape, int mode)
1237 {
1238   set_fg_gc(dpy, d, gc);
1239   
1240   // TODO: Re-implement the GLU tesselation functions.
1241
1242   /* Complex: Pedal, and for some reason Attraction, Mountain, Qix, SpeedMine, Starfish
1243    * Nonconvex: Goop, Pacman, Rocks, Speedmine
1244    *
1245    * We currently do Nonconvex with the simple-to-implement ear clipping
1246    * algorithm, but in the future we can replace that with an algorithm
1247    * with slower big-O growth
1248    *
1249    */
1250   
1251   
1252   // TODO: Feed vertices straight to OpenGL for CoordModeOrigin.
1253
1254   if (shape == Convex) {
1255
1256     GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc.
1257     short v[2] = {0, 0};
1258   
1259     for (unsigned i = 0; i < npoints; i++) {
1260       next_point(v, points[i], mode);
1261       vertices[2 * i] = v[0];
1262       vertices[2 * i + 1] = v[1];
1263     }
1264
1265     glEnableClientState (GL_VERTEX_ARRAY);
1266     glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1267
1268     vertex_pointer (dpy, GL_SHORT, 0, vertices);
1269     glDrawArrays (GL_TRIANGLE_FAN, 0, npoints);
1270
1271     free(vertices);
1272
1273   } else if (shape == Nonconvex) {
1274
1275     // TODO: assert that x,y of first and last point match, as that is assumed
1276
1277     linked_point *root;
1278     root = (linked_point *) malloc( sizeof(linked_point) );
1279     set_points_list(points,npoints,root);
1280     traverse_points_list(dpy, root);
1281
1282   } else {
1283     Assert((shape == Convex || shape == Nonconvex),
1284            "XFillPolygon: (TODO) Unimplemented shape");
1285   }
1286
1287   return 0;
1288 }
1289
1290 #define radians(DEG) ((DEG) * M_PI / 180.0)
1291 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1292
1293 static void
1294 arc_xy(GLfloat *p, double cx, double cy, double w2, double h2, double theta)
1295 {
1296   p[0] = cos(theta) * w2 + cx;
1297   p[1] = -sin(theta) * h2 + cy;
1298 }
1299
1300 static unsigned
1301 mod_neg(int a, unsigned b)
1302 {
1303   /* Normal modulus is implementation defined for negative numbers. This is 
1304    * well-defined such that the repeating pattern for a >= 0 is maintained for 
1305    * a < 0. */
1306   return a < 0 ? (b - 1) - (-(a + 1) % b) : a % b;
1307 }
1308
1309 /* TODO: Fill in arcs with line width > 1 */
1310 static int
1311 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1312           unsigned int width, unsigned int height,
1313           int angle1, int angle2, Bool fill_p)
1314 {
1315     int gglw = gc->gcv.line_width;
1316
1317     if (fill_p || gglw <= 1) {
1318         draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p);
1319     }
1320     else {
1321         int w1, w2, h1, h2, gglwh;
1322         w1 = width + gglw;
1323         h1 = height + gglw;
1324         h2 = height - gglw;
1325         w2 = width - gglw;
1326         gglwh = gglw / 2;
1327         int x1 = x - gglwh;
1328         int x2 = x + gglwh;
1329         int y1 = y - gglwh;
1330         int y2 = y + gglwh;
1331         //draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p);
1332         draw_arc_gl (dpy, d, gc, x1, y1, w1, h1, angle1, angle2, fill_p);
1333         draw_arc_gl (dpy, d, gc, x2, y2, w2, h2, angle1, angle2, fill_p);
1334     }
1335     return 0;
1336 }
1337
1338
1339 int
1340 draw_arc_gl (Display *dpy, Drawable d, GC gc, int x, int y,
1341           unsigned int width, unsigned int height,
1342           int angle1, int angle2, Bool fill_p)
1343 {
1344   set_fg_gc(dpy, d, gc);
1345
1346   /* Let's say the number of line segments needed to make a convincing circle is
1347      4*sqrt(radius). (But these arcs aren't necessarily circular arcs...) */
1348
1349   double w2 = width * 0.5f, h2 = height * 0.5f;
1350   double a, b; /* Semi-major/minor axes. */
1351   if(w2 > h2) {
1352     a = w2;
1353     b = h2;
1354   } else {
1355     a = h2;
1356     b = w2;
1357   }
1358   
1359   const double two_pi = 2 * M_PI;
1360
1361   double amb = a - b, apb = a + b;
1362   double h = (amb * amb) / (apb * apb);
1363   // TODO: Math cleanup.
1364   double C_approx = M_PI * apb * (1 + 3 * h / (10 + sqrtf(4 - 3 * h)));
1365   double segments_f = 4 * sqrtf(C_approx / (2 * M_PI));
1366
1367   // TODO: Explain how drawing works what with the points of overlapping arcs
1368   // matching up.
1369  
1370 #if 1
1371   unsigned segments_360 = segments_f;
1372   
1373   /* TODO: angle2 == 0. This is a tilted square with CapSquare. */
1374   /* TODO: color, thick lines, CapNotLast for thin lines */
1375   /* TODO: Transformations. */
1376
1377   double segment_angle = two_pi / segments_360;
1378
1379   const unsigned deg64 = 360 * 64;
1380   const double rad_from_deg64 = two_pi / deg64;
1381   
1382   if (angle2 < 0) {
1383     angle1 += angle2;
1384     angle2 = -angle2;
1385   }
1386
1387   angle1 = mod_neg(angle1, deg64); // TODO: Is this OK? Consider negative numbers.
1388
1389   if (angle2 > deg64)
1390     angle2 = deg64; // TODO: Handle circles special.
1391   
1392   double
1393     angle1_f = angle1 * rad_from_deg64,
1394     angle2_f = angle2 * rad_from_deg64;
1395   
1396   if (angle2_f > two_pi) // TODO: Move this up.
1397     angle2_f = two_pi;
1398   
1399   double segment1_angle_part = fmodf(angle1_f, segment_angle);
1400   
1401   unsigned segment1 = ((angle1_f - segment1_angle_part) / segment_angle) + 1.5;
1402
1403   double angle_2r = angle2_f - segment1_angle_part;
1404   unsigned segments = angle_2r / segment_angle;
1405   
1406   GLfloat cx = x + w2, cy = y + h2;
1407
1408   GLfloat *data = malloc((segments + 3) * sizeof(GLfloat) * 2); // TODO: Check result.
1409   
1410   GLfloat *data_ptr = data;
1411   if (fill_p) {
1412     data_ptr[0] = cx;
1413     data_ptr[1] = cy;
1414     data_ptr += 2;
1415   }
1416   
1417   arc_xy (data_ptr, cx, cy, w2, h2, angle1_f);
1418   data_ptr += 2;
1419   
1420   for (unsigned s = 0; s != segments; ++s) {
1421     // TODO: Make sure values of theta for the following arc_xy call are between
1422     // angle1_f and angle1_f + angle2_f.
1423     arc_xy (data_ptr, cx, cy, w2, h2, (segment1 + s) * segment_angle);
1424     data_ptr += 2;
1425   }
1426   
1427   arc_xy (data_ptr, cx, cy, w2, h2, angle1_f + angle2_f);
1428   data_ptr += 2;
1429
1430   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1431   glEnableClientState (GL_VERTEX_ARRAY);
1432   
1433   vertex_pointer (dpy, GL_FLOAT, 0, data);
1434   glDrawArrays (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP,
1435                 0,
1436                 (GLsizei)((data_ptr - data) / 2));
1437
1438   free(data);
1439   
1440 #endif
1441   
1442 #if 0
1443   unsigned segments = segments_f * (fabs(angle2) / (360 * 64));
1444  
1445   glBegin (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP);
1446   
1447   if (fill_p /* && gc->gcv.arc_mode == ArcPieSlice */)
1448     glVertex2f (cx, cy);
1449   
1450   /* TODO: This should fix the middle points of the arc so that the starting and ending points are OK. */
1451   
1452   float to_radians = 2 * M_PI / (360 * 64);
1453   float theta = angle1 * to_radians, d_theta = angle2 * to_radians / segments;
1454   
1455   for (unsigned s = 0; s != segments + 1; ++s) /* TODO: This is the right number of segments, yes? */
1456   {
1457     glVertex2f(cos(theta) * w2 + cx, -sin(theta) * h2 + cy);
1458     theta += d_theta;
1459   }
1460   
1461   glEnd ();
1462 #endif
1463   
1464   return 0;
1465 }
1466
1467
1468 static XGCValues *
1469 gc_gcv (GC gc)
1470 {
1471   return &gc->gcv;
1472 }
1473
1474
1475 static unsigned int
1476 gc_depth (GC gc)
1477 {
1478   return gc->depth;
1479 }
1480
1481
1482 static GC
1483 CreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1484 {
1485   struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1486   gc->depth = jwxyz_drawable_depth (d);
1487
1488   jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
1489   XChangeGC (dpy, gc, mask, xgcv);
1490   return gc;
1491 }
1492
1493
1494 static void
1495 free_clip_mask (Display *dpy, GC gc)
1496 {
1497   if (gc->gcv.clip_mask) {
1498     if (dpy->gc_clip_mask == gc->clip_mask) {
1499       jwxyz_gl_flush (dpy);
1500       dpy->gc_clip_mask = 0;
1501     }
1502     glDeleteTextures (1, &gc->clip_mask);
1503   }
1504 }
1505
1506
1507 static int
1508 FreeGC (Display *dpy, GC gc)
1509 {
1510   if (gc->gcv.font)
1511     XUnloadFont (dpy, gc->gcv.font);
1512
1513   free_clip_mask (dpy, gc);
1514   free (gc);
1515   return 0;
1516 }
1517
1518
1519 static int
1520 PutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1521           int src_x, int src_y, int dest_x, int dest_y,
1522           unsigned int w, unsigned int h)
1523 {
1524   jwxyz_assert_display (dpy);
1525  
1526   const XRectangle *wr = jwxyz_frame (d);
1527
1528   Assert (gc, "no GC");
1529   Assert ((w < 65535), "improbably large width");
1530   Assert ((h < 65535), "improbably large height");
1531   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
1532   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
1533   Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1534   Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1535
1536   // Clip width and height to the bounds of the Drawable
1537   //
1538   if (dest_x + w > wr->width) {
1539     if (dest_x > wr->width)
1540       return 0;
1541     w = wr->width - dest_x;
1542   }
1543   if (dest_y + h > wr->height) {
1544     if (dest_y > wr->height)
1545       return 0;
1546     h = wr->height - dest_y;
1547   }
1548   if (w <= 0 || h <= 0)
1549     return 0;
1550
1551   // Clip width and height to the bounds of the XImage
1552   //
1553   if (src_x + w > ximage->width) {
1554     if (src_x > ximage->width)
1555       return 0;
1556     w = ximage->width - src_x;
1557   }
1558   if (src_y + h > ximage->height) {
1559     if (src_y > ximage->height)
1560       return 0;
1561     h = ximage->height - src_y;
1562   }
1563   if (w <= 0 || h <= 0)
1564     return 0;
1565
1566   /* Assert (d->win */
1567
1568   if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
1569     return 0;
1570
1571   jwxyz_gl_flush (dpy);
1572   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1573   jwxyz_gl_set_gc (dpy, gc);
1574
1575   int bpl = ximage->bytes_per_line;
1576   int bpp = ximage->bits_per_pixel;
1577
1578   char *tex_data;
1579   unsigned src_w;
1580   GLint tex_internalformat;
1581   GLenum tex_format, tex_type;
1582   unsigned tex_index;
1583
1584   if (bpp == 32) {
1585     tex_data = ximage->data + src_y * bpl + (src_x * 4);
1586
1587     jwxyz_assert_display(dpy);
1588     
1589     /* There probably won't be any hacks that do this, but... */
1590     Assert (!(bpl % 4), "XPutImage: bytes_per_line not divisible by four.");
1591     
1592     tex_internalformat = texture_internalformat(dpy);
1593     tex_format = dpy->pixel_format;
1594     tex_type = gl_pixel_type(dpy);
1595     tex_index = texture_rgba;
1596
1597     /* GL_UNPACK_ROW_LENGTH is not allowed to be negative. (sigh) */
1598 # ifndef HAVE_JWZGLES
1599     src_w = w;
1600     glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
1601 # else
1602     src_w = bpl / 4;
1603 # endif
1604
1605     // glPixelStorei (GL_UNPACK_ALIGNMENT, 4); // Probably unnecessary.
1606   } else {
1607     Assert (bpp == 1, "expected 1 or 32 bpp");
1608     Assert ((src_x % 8) == 0,
1609             "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1610
1611     const char *src_data = ximage->data + src_y * bpl + (src_x / 8);
1612     unsigned w8 = (w + 7) / 8;
1613
1614     src_w = w8 * 8;
1615
1616     tex_data = malloc(src_w * h);
1617
1618 #if 0
1619     {
1620       char s[10240];
1621       int x, y, o;
1622       Log("#PI ---------- %d %d %08lx %08lx",
1623           jwxyz_drawable_depth(d), ximage->depth,
1624           (unsigned long)d, (unsigned long)ximage);
1625       for (y = 0; y < ximage->height; y++) {
1626         o = 0;
1627         for (x = 0; x < ximage->width; x++) {
1628           unsigned long b = XGetPixel(ximage, x, y);
1629           s[o++] = (   (b & 0xFF000000)
1630                     ? ((b & 0x00FFFFFF) ? '#' : '-')
1631                     : ((b & 0x00FFFFFF) ? '+' : ' '));
1632         }
1633         s[o] = 0;
1634         Log("#PI [%s]",s);
1635       }
1636       Log("# PI ----------");
1637     }
1638 #endif
1639     uint32_t *data_out = (uint32_t *)tex_data;
1640     for(unsigned y = h; y; --y) {
1641       for(unsigned x = 0; x != w8; ++x) {
1642         // TODO: Does big endian work here?
1643         uint8_t byte = src_data[x];
1644         uint32_t word = byte;
1645         word = (word & 0x3) | ((word & 0xc) << 14);
1646         word = (word & 0x00010001) | ((word & 0x00020002) << 7);
1647         data_out[x << 1] = (word << 8) - word;
1648
1649         word = byte >> 4;
1650         word = (word & 0x3) | ((word & 0xc) << 14);
1651         word = (word & 0x00010001) | ((word & 0x00020002) << 7);
1652         data_out[(x << 1) | 1] = (word << 8) - word;
1653       }
1654       src_data += bpl;
1655       data_out += src_w / 4;
1656     }
1657 #if 0
1658     {
1659       char s[10240];
1660       int x, y, o;
1661       Log("#P2 ----------");
1662       for (y = 0; y < ximage->height; y++) {
1663         o = 0;
1664         for (x = 0; x < ximage->width; x++) {
1665           unsigned long b = ((uint8_t *)tex_data)[y * w + x];
1666           s[o++] = (   (b & 0xFF000000)
1667                     ? ((b & 0x00FFFFFF) ? '#' : '-')
1668                     : ((b & 0x00FFFFFF) ? '+' : ' '));
1669         }
1670         s[o] = 0;
1671         Log("#P2 [%s]",s);
1672       }
1673       Log("# P2 ----------");
1674     }
1675 #endif
1676
1677     tex_internalformat = GL_LUMINANCE;
1678     tex_format = GL_LUMINANCE;
1679     tex_type = GL_UNSIGNED_BYTE;
1680     tex_index = texture_mono;
1681
1682     // glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1683   }
1684
1685 # if 1 // defined HAVE_JWZGLES
1686   // Regular OpenGL uses GL_TEXTURE_RECTANGLE_EXT in place of GL_TEXTURE_2D.
1687   // TODO: Make use of OES_draw_texture.
1688
1689   unsigned tex_w = src_w, tex_h = h;
1690   glBindTexture (dpy->gl_texture_target, dpy->textures[tex_index]);
1691
1692   // A fun project: reimplement xshm.c by means of a PBO using
1693   // GL_MAP_UNSYNCHRONIZED_BIT.
1694
1695   tex_image (dpy, tex_internalformat, &tex_w, &tex_h, tex_format, tex_type,
1696              tex_data);
1697
1698   if (bpp == 1)
1699     free(tex_data);
1700
1701   jwxyz_gl_draw_image (dpy, gc, dpy->gl_texture_target, tex_w, tex_h,
1702                        0, 0, bpp, w, h, dest_x, dest_y, True);
1703 # else
1704   glRasterPos2i (dest_x, dest_y);
1705   glPixelZoom (1, -1);
1706   jwxyz_assert_display (dpy);
1707   glDrawPixels (w, h, dpy->pixel_format, gl_pixel_type(dpy), data);
1708 # endif
1709
1710   jwxyz_assert_gl ();
1711
1712   return 0;
1713 }
1714
1715 /* At the moment only XGetImage and get_xshm_image use XGetSubImage. */
1716 /* #### Twang calls XGetImage on the window intending to get a
1717    buffer full of black.  This is returning a buffer full of white
1718    instead of black for some reason. */
1719 static XImage *
1720 GetSubImage (Display *dpy, Drawable d, int x, int y,
1721              unsigned int width, unsigned int height,
1722              unsigned long plane_mask, int format,
1723              XImage *dest_image, int dest_x, int dest_y)
1724 {
1725   Assert ((width  < 65535), "improbably large width");
1726   Assert ((height < 65535), "improbably large height");
1727   Assert ((x < 65535 && x > -65535), "improbably large x");
1728   Assert ((y < 65535 && y > -65535), "improbably large y");
1729
1730   jwxyz_gl_flush (dpy);
1731   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1732   
1733   // TODO: What if this reads off the edge? What is supposed to happen?
1734
1735   {
1736     // In case the caller tries to write off the edge.
1737     int
1738       max_width = dest_image->width - dest_x,
1739       max_height = dest_image->height - dest_y;
1740
1741     if (width > max_width) {
1742       width = max_width;
1743     }
1744     
1745     if (height > max_height) {
1746       height = max_height;
1747     }
1748   }
1749   
1750   Assert (jwxyz_drawable_depth (d) == dest_image->depth, "XGetSubImage: depth mismatch");
1751   
1752   if (dest_image->depth == visual_depth (NULL, NULL)) {
1753     Assert (!(dest_image->bytes_per_line % 4), "XGetSubImage: bytes_per_line not divisible by 4");
1754     unsigned pixels_per_line = dest_image->bytes_per_line / 4;
1755 #ifdef HAVE_JWZGLES
1756     Assert (pixels_per_line == width, "XGetSubImage: (TODO) pixels_per_line != width");
1757 #else
1758     glPixelStorei (GL_PACK_ROW_LENGTH, pixels_per_line);
1759 #endif
1760     glPixelStorei (GL_PACK_ALIGNMENT, 4);
1761     
1762     uint32_t *dest_data = (uint32_t *)dest_image->data + pixels_per_line * dest_y + dest_x;
1763     
1764     glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
1765                   dpy->pixel_format, gl_pixel_type(dpy), dest_data);
1766
1767     /* Flip this upside down. :( */
1768     uint32_t *top = dest_data;
1769     uint32_t *bottom = dest_data + pixels_per_line * (height - 1);
1770     for (unsigned y = height / 2; y; --y) {
1771       for (unsigned x = 0; x != width; ++x) {
1772         uint32_t px = top[x];
1773         top[x] = bottom[x];
1774         bottom[x] = px;
1775       }
1776       top += pixels_per_line;
1777       bottom -= pixels_per_line;
1778     }
1779   } else {
1780
1781     uint32_t *rgba_image = malloc(width * height * 4);
1782     Assert (rgba_image, "not enough memory");
1783
1784     // Must be GL_RGBA; GL_RED isn't available.
1785     glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
1786                   GL_RGBA, GL_UNSIGNED_BYTE, rgba_image);
1787
1788     Assert (!(dest_x % 8), "XGetSubImage: dest_x not byte-aligned");
1789     uint8_t *top =
1790       (uint8_t *)dest_image->data + dest_image->bytes_per_line * dest_y
1791       + dest_x / 8;
1792 #if 0
1793     {
1794       char s[10240];
1795       Log("#GI ---------- %d %d  %d x %d %08lx", 
1796           jwxyz_drawable_depth(d), dest_image->depth,
1797           width, height,
1798           (unsigned long) d);
1799       for (int y = 0; y < height; y++) {
1800         int x;
1801         for (x = 0; x < width; x++) {
1802           unsigned long b = rgba_image[(height-y-1) * width + x];
1803           s[x] = (   (b & 0xFF000000)
1804                   ? ((b & 0x00FFFFFF) ? '#' : '-')
1805                   : ((b & 0x00FFFFFF) ? '+' : ' '));
1806         }
1807         s[x] = 0;
1808         Log("#GI [%s]",s);
1809       }
1810       Log("#GI ----------");
1811     }
1812 #endif
1813     const uint32_t *bottom = rgba_image + width * (height - 1);
1814     for (unsigned y = height; y; --y) {
1815       memset (top, 0, width / 8);
1816       for (unsigned x = 0; x != width; ++x) {
1817         if (bottom[x] & 0x80)
1818           top[x >> 3] |= (1 << (x & 7));
1819       }
1820       top += dest_image->bytes_per_line;
1821       bottom -= width;
1822     }
1823
1824     free (rgba_image);
1825   }
1826
1827   return dest_image;
1828 }
1829
1830
1831 static int
1832 SetClipMask (Display *dpy, GC gc, Pixmap m)
1833 {
1834   Assert (m != dpy->main_window, "not a pixmap");
1835
1836   free_clip_mask (dpy, gc);
1837
1838   if (!m) {
1839     gc->clip_mask = 0;
1840     return 0;
1841   }
1842
1843   Assert (jwxyz_drawable_depth (m) == 1, "wrong depth for clip mask");
1844
1845   const XRectangle *frame = jwxyz_frame (m);
1846   gc->clip_mask_width = frame->width;
1847   gc->clip_mask_height = frame->height;
1848
1849   /* Apparently GL_ALPHA isn't a valid format for a texture used in an FBO:
1850
1851     (86) Are any one- or two- component formats color-renderable?
1852
1853             Presently none of the one- or two- component texture formats
1854             defined in unextended OpenGL is color-renderable.  The R
1855             and RG float formats defined by the NV_float_buffer
1856             extension are color-renderable.
1857
1858             Although an early draft of the FBO specification permitted
1859             rendering into alpha, luminance, and intensity formats, this
1860             this capability was pulled when it was realized that it was
1861             under-specified exactly how rendering into these formats
1862             would work.  (specifically, how R/G/B/A map to I/L/A)
1863
1864      <https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_framebuffer_object.txt>
1865
1866      ...Therefore, 1-bit drawables get to have wasted color channels.
1867      Currently, R=G=B=grey, and the alpha channel is unused.
1868    */
1869
1870   /* There doesn't seem to be any way to move what's on one of the color
1871      channels to the alpha channel in GLES 1.1 without leaving the GPU.
1872    */
1873
1874   /* GLES 1.1 only supports GL_CLAMP_TO_EDGE or GL_REPEAT for
1875      GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T, so to prevent drawing outside
1876      the clip mask pixmap, there's two options:
1877      1. Use glScissor.
1878      2. Add a black border.
1879
1880      Option #2 is in use here.
1881
1882      The following converts in-place an RGBA image to alpha-only image with a
1883      1px black border.
1884    */
1885
1886   // Prefix/suffix: 1 row+1 pixel, rounded to nearest int32.
1887   size_t pad0 = frame->width + 3, pad = (pad0 + 3) & ~3;
1888   uint8_t *data = malloc(2 * pad + frame->width * frame->height * 4);
1889
1890   uint8_t *rgba = data + pad;
1891   uint8_t *alpha = rgba;
1892   uint8_t *alpha0 = alpha - pad0;
1893   memset (alpha0, 0, pad0); // Top row.
1894
1895   jwxyz_gl_flush (dpy);
1896   jwxyz_bind_drawable (dpy, dpy->main_window, m);
1897   glReadPixels (0, 0, frame->width, frame->height, GL_RGBA, GL_UNSIGNED_BYTE,
1898                 rgba);
1899
1900   for (unsigned y = 0; y != frame->height; ++y) {
1901     for (unsigned x = 0; x != frame->width; ++x)
1902       alpha[x] = rgba[x * 4];
1903
1904     rgba += frame->width * 4;
1905
1906     alpha += frame->width;
1907     alpha[0] = 0;
1908     alpha[1] = 0;
1909     alpha += 2;
1910   }
1911
1912   alpha -= 2;
1913   memset (alpha, 0, pad0); // Bottom row.
1914
1915   glGenTextures (1, &gc->clip_mask);
1916   tex_parameters (dpy, gc->clip_mask);
1917
1918   glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1919
1920   unsigned tex_w = frame->width + 2, tex_h = frame->height + 2;
1921   tex_image (dpy, GL_ALPHA, &tex_w, &tex_h, GL_ALPHA, GL_UNSIGNED_BYTE,
1922              alpha0);
1923
1924   free(data);
1925
1926   return 0;
1927 }
1928
1929 static int
1930 SetClipOrigin (Display *dpy, GC gc, int x, int y)
1931 {
1932   gc->gcv.clip_x_origin = x;
1933   gc->gcv.clip_y_origin = y;
1934   return 0;
1935 }
1936
1937 void set_points_list(XPoint *points, int npoints, linked_point *root)
1938 {
1939     linked_point *current;  
1940
1941     current = root;
1942     for (int i = 0; i < npoints - 2 ; i++) {
1943         current->x = points[i].x;
1944         current->y = points[i].y;
1945         current->next = (linked_point *) malloc(sizeof(linked_point)); 
1946         current = current->next;
1947     }
1948     current->x = points[npoints-2].x;
1949     current->y = points[npoints-2].y;
1950     current->next = root;
1951 }
1952
1953
1954 double compute_edge_length(linked_point * a, linked_point * b)
1955 {
1956
1957     int xdiff, ydiff, xsq, ysq, added;
1958     double xy_add, edge_length;
1959
1960     xdiff = a->x - b->x;
1961     ydiff = a->y - b->y;
1962     xsq = xdiff * xdiff;
1963     ysq = ydiff * ydiff;
1964     added = xsq + ysq;
1965     xy_add = (double) added;
1966     edge_length = sqrt(xy_add);
1967     return edge_length;
1968 }
1969
1970 double get_angle(double a, double b, double c)
1971 {
1972     double cos_a, i_cos_a;
1973     cos_a = (((b * b) + (c * c)) - (a * a)) / (double) (2.0 * b * c);
1974     i_cos_a = acos(cos_a);
1975     return i_cos_a;
1976 }
1977
1978
1979 Bool is_same_slope(linked_point * a)
1980 {
1981
1982     int abx, bcx, aby, bcy, aa, bb;
1983     linked_point *b;
1984     linked_point *c;
1985
1986     b = a->next;
1987     c = b->next;
1988
1989     // test if slopes are indefinite for both line segments
1990     if (a->x == b->x) {
1991         return b->x == c->x;
1992     } else if (b->x == c->x) {
1993         return False;   // false, as ax/bx is not indefinite
1994     }
1995
1996     abx = a->x - b->x;
1997     bcx = b->x - c->x;
1998     aby = a->y - b->y;
1999     bcy = b->y - c->y;
2000     aa = abx * bcy;
2001     bb = bcx * aby;
2002
2003     return aa == bb;
2004 }
2005
2006 void draw_three_vertices(Display *dpy, linked_point * a, Bool triangle)
2007 {
2008
2009     linked_point *b;
2010     linked_point *c;
2011     GLenum drawType;
2012
2013     b = a->next;
2014     c = b->next;
2015
2016     GLfloat vertices[3][2] = {
2017         {a->x, a->y},
2018         {b->x, b->y},
2019         {c->x, c->y}
2020     };
2021
2022     if (triangle) {
2023         drawType = GL_TRIANGLES;
2024     } else {
2025         drawType = GL_LINES;
2026     }
2027
2028     glEnableClientState(GL_VERTEX_ARRAY);
2029     vertex_pointer(dpy, GL_FLOAT, 0, vertices);
2030     glDrawArrays(drawType, 0, 3);
2031
2032     free(b);  // cut midpoint off from remaining polygon vertex list
2033     a->next = c;
2034 }
2035
2036
2037 Bool is_an_ear(linked_point * a)
2038 {
2039     double edge_ab, edge_bc, edge_ac;
2040     double angle_a, angle_b, angle_c;
2041     double my_pi;
2042     linked_point *b, *c;
2043
2044     b = a->next;
2045     c = b->next;
2046     my_pi = (double) M_PI;
2047
2048     edge_ab = compute_edge_length(a, b);
2049     edge_bc = compute_edge_length(b, c);
2050     edge_ac = compute_edge_length(a, c);
2051     angle_a = get_angle(edge_bc, edge_ab, edge_ac);
2052     angle_b = get_angle(edge_ac, edge_ab, edge_bc);
2053     angle_c = get_angle(edge_ab, edge_ac, edge_bc);
2054
2055     return angle_a < my_pi && angle_b < my_pi && angle_c < my_pi;
2056 }
2057
2058
2059 Bool is_three_point_loop(linked_point * head)
2060 {
2061     return head->x == head->next->next->next->x
2062         && head->y == head->next->next->next->y;
2063 }
2064
2065
2066 void traverse_points_list(Display *dpy, linked_point * root)
2067 {
2068     linked_point *head;
2069     head = root;
2070
2071     while (!is_three_point_loop(head)) {
2072         if (is_an_ear(head)) {
2073             draw_three_vertices(dpy, head, True);
2074         } else if (is_same_slope(head)) {
2075             draw_three_vertices(dpy, head, False);
2076         } else {
2077             head = head->next;
2078         }
2079     }
2080
2081     // handle final three vertices in polygon
2082     if (is_an_ear(head)) {
2083         draw_three_vertices(dpy, head, True);
2084     } else if (is_same_slope(head)) {
2085         draw_three_vertices(dpy, head, False);
2086     } else {
2087         free(head->next->next);
2088         free(head->next);
2089         free(head);
2090         Assert (False, "traverse_points_list: unknown configuration");
2091     }
2092
2093     free(head->next);
2094     free(head);
2095 }
2096
2097
2098 const struct jwxyz_vtbl gl_vtbl = {
2099   root,
2100   visual,
2101   display_sources_data,
2102
2103   window_background,
2104   draw_arc,
2105   fill_rects,
2106   gc_gcv,
2107   gc_depth,
2108   jwxyz_draw_string,
2109
2110   jwxyz_gl_copy_area,
2111
2112   DrawPoints,
2113   DrawSegments,
2114   CreateGC,
2115   FreeGC,
2116   ClearWindow,
2117   SetClipMask,
2118   SetClipOrigin,
2119   FillPolygon,
2120   DrawLines,
2121   PutImage,
2122   GetSubImage
2123 };
2124
2125 #endif /* JWXYZ_GL -- entire file */