From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / jwxyz / jwxyz-gl.c
1 /* xscreensaver, Copyright (c) 1991-2017 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    TODO: The following should be implemented before OpenGL can be considered
25    practical here:
26    - Above all, pick the smallest not-yet working hack that utilizes the
27      needed functionality.
28      - Half-ass the drawing functions.
29    - [OK] What Interference needs
30    - Fast Pixmaps
31    - Whatever clipping is used in XScreenSaver (shape and/or bitmap clipping)
32    - Delayed context creation to support anti-aliasing/multisampling
33    - Everything these hacks need:
34      - FuzzyFlakes (needs wide lines)
35      - Greynetic
36      - [OK] Deluxe
37    - [OK] Get DangerBall going.
38    - [OK] iOS.
39      - [Interference, so far...] And fast, too.
40    - And text really needs to work for the FPS display. */
41
42 /* Also, Take note that OS X can actually run with 256 colors. */
43
44 /* TODO:
45    - malloc error checking
46    - Check max texture sizes for XGet/PutImage, XCopyArea.
47    - Optional 5:5:5 16-bit color
48 */
49
50 /* Techniques & notes:
51    - iOS: OpenGL ES 2.0 isn't always available. Use OpenGL ES 1.1.
52    - OS X: Drivers can go back to OpenGL 1.1 (GeForce 2 MX on 10.5.8).
53    - Use stencil buffers (OpenGL 1.0+) for bitmap clipping masks.
54    - glLogicOp is an actual thing that should work for GCs.
55    - Pixmaps can be any of the following, depending on GL implementation.
56      - This requires offscreen rendering. Fortunately, this is always
57        available.
58        - OS X: Drawable objects, including: pixel buffers and offscreen
59          memory.
60          - Offscreen buffers w/ CGLSetOffScreen (10.0+)
61          - http://lists.apple.com/archives/mac-opengl/2002/Jun/msg00087.html
62            provides a very ugly solution for hardware-accelerated offscreen
63            rendering with CGLSetParameter(*, kCGLCPSurfaceTexture, *) on OS X
64            10.1+. Apple docs say it's actually for OS X 10.3+, instead.
65          - Pixel buffers w/ CGLSetPBuffer (10.3+, now deprecated)
66            - Requires APPLE_pixel_buffer.
67              - Available in software on x86 only.
68              - Always available on hardware.
69            - Need to blit? Use OpenGL pixel buffers. (GL_PIXEL_PACK_BUFFER)
70          - Framebuffer objects w/ GL_(ARB|EXT)_framebuffer_object
71            - TODO: Can EXT_framebuffers be different sizes?
72            - Preferred on 10.7+
73        - iOS: Use OES_framebuffer_object, it's always present.
74  */
75
76 /* OpenGL hacks call a number of X11 functions, including
77  * XCopyArea, XDrawString, XGetImage
78  * XCreatePixmap, XCreateGC, XCreateImage
79  * XPutPixel
80  * Check these, of course. */
81
82 #ifdef JWXYZ_GL /* entire file */
83
84 #include <math.h>
85 #include <limits.h>
86 #include <stddef.h>
87 #include <stdlib.h>
88 #include <stdint.h>
89 #include <string.h>
90 #include <wchar.h>
91
92 #ifdef HAVE_COCOA
93 # ifdef USE_IPHONE
94 #  import <QuartzCore/QuartzCore.h>
95 #  include <OpenGLES/ES1/gl.h>
96 #  include <OpenGLES/ES1/glext.h>
97
98 #  define NSView  UIView
99 #  define NSRect  CGRect
100 #  define NSPoint CGPoint
101 #  define NSSize  CGSize
102 #  define NSColor UIColor
103 #  define NSImage UIImage
104 #  define NSEvent UIEvent
105 #  define NSFont  UIFont
106 #  define NSGlyph CGGlyph
107 #  define NSWindow UIWindow
108 #  define NSMakeSize   CGSizeMake
109 #  define NSBezierPath UIBezierPath
110 #  define colorWithDeviceRed colorWithRed
111
112 #  define NSFontTraitMask      UIFontDescriptorSymbolicTraits
113 // The values for the flags for NSFontTraitMask and
114 // UIFontDescriptorSymbolicTraits match up, not that it really matters here.
115 #  define NSBoldFontMask       UIFontDescriptorTraitBold
116 #  define NSFixedPitchFontMask UIFontDescriptorTraitMonoSpace
117 #  define NSItalicFontMask     UIFontDescriptorTraitItalic
118
119 #  define NSOpenGLContext EAGLContext
120
121 # else
122 #  include <OpenGL/glu.h>
123 # endif
124 #else
125 /* TODO: Does this work on iOS? */
126 # ifndef HAVE_JWZGLES
127 #  include <gl/glu.h>
128 # else
129 #  include <GLES/gl.h>
130 #  include <GLES/glext.h>
131 # endif
132 #endif
133
134 #ifdef HAVE_JWZGLES
135 # include "jwzglesI.h"
136 #endif
137
138 #ifdef HAVE_ANDROID
139 # include <android/log.h>
140 #endif
141
142 #include "jwxyzI.h"
143 #include "jwxyz-timers.h"
144 #include "yarandom.h"
145 #include "utf8wc.h"
146 #include "xft.h"
147 #include "pow2.h"
148
149 #if defined HAVE_COCOA
150 # include <CoreGraphics/CGGeometry.h>
151 #else
152
153 struct CGPoint {
154     float x;
155     float y;
156 };
157 typedef struct CGPoint CGPoint;
158
159 struct CGSize {
160     float width;
161     float height;
162 };
163 typedef struct CGSize CGSize;
164
165 struct CGRect {
166     CGPoint origin;
167     CGSize size;
168 };
169 typedef struct CGRect CGRect;
170
171 #endif
172
173 # undef MAX
174 # undef MIN
175 # define MAX(a,b) ((a)>(b)?(a):(b))
176 # define MIN(a,b) ((a)<(b)?(a):(b))
177
178 union color_bytes
179 {
180   /* On 64-bit systems, high bits of the 32-bit pixel are available as scratch
181      space. I doubt if any screen savers need it, but just in case... */
182   unsigned long pixel;
183   uint8_t bytes[4];
184 };
185
186 struct jwxyz_Display {
187   Window main_window;
188   Screen *screen;
189   struct jwxyz_sources_data *timers_data;
190
191   Bool gl_texture_npot_p;
192   /* Bool opengl_core_p */;
193   GLenum gl_texture_target;
194   
195 // #if defined USE_IPHONE
196   GLuint rect_texture; // Also can work on the desktop.
197 // #endif
198
199   unsigned long window_background;
200 };
201
202 struct jwxyz_Screen {
203   Display *dpy;
204   GLenum pixel_format, pixel_type;
205   unsigned long black, white;
206   Visual *visual;
207 };
208
209 struct jwxyz_GC {
210   XGCValues gcv;
211   unsigned int depth;
212   // CGImageRef clip_mask;  // CGImage copy of the Pixmap in gcv.clip_mask
213 };
214
215 struct jwxyz_XFontSet {
216   XFontStruct *font;
217 };
218
219 struct jwxyz_linked_point {
220     short x, y;
221     linked_point *next;
222 };
223
224 /* XGetImage in CoreGraphics JWXYZ has to deal with funky pixel formats
225    necessitating fast & flexible pixel conversion. OpenGL does image format
226    conversion itself, so alloc_color and query_color are mercifully simple.
227  */
228 uint32_t
229 jwxyz_alloc_color (Display *dpy,
230                    uint16_t r, uint16_t g, uint16_t b, uint16_t a)
231 {
232   union color_bytes color;
233
234   /* Instead of (int)(c / 256.0), another possibility is
235      (int)(c * 255.0 / 65535.0 + 0.5). This can be calculated using only
236      uint8_t integer_math(uint16_t c) {
237        unsigned c0 = c + 128;
238        return (c0 - (c0 >> 8)) >> 8;
239      }
240    */
241
242   color.bytes[0] = r >> 8;
243   color.bytes[1] = g >> 8;
244   color.bytes[2] = b >> 8;
245   color.bytes[3] = a >> 8;
246
247   if (dpy->screen->pixel_format == GL_BGRA_EXT) {
248     color.pixel = color.bytes[2] |
249                   (color.bytes[1] << 8) |
250                   (color.bytes[0] << 16) |
251                   (color.bytes[3] << 24);
252   } else {
253     Assert(dpy->screen->pixel_format == GL_RGBA,
254            "jwxyz_alloc_color: Unknown pixel_format");
255   }
256
257   return (uint32_t)color.pixel;
258 }
259
260 // Converts an array of pixels ('src') from one format to another, placing the
261 // result in 'dest', according to the pixel conversion mode 'mode'.
262 void
263 jwxyz_query_color (Display *dpy, unsigned long pixel, uint8_t *rgba)
264 {
265   union color_bytes color;
266
267   if(dpy->screen->pixel_format == GL_RGBA)
268   {
269     color.pixel = pixel;
270     for (unsigned i = 0; i != 4; ++i)
271       rgba[i] = color.bytes[i];
272     return;
273   }
274
275   Assert (dpy->screen->pixel_format == GL_BGRA_EXT,
276           "jwxyz_query_color: Unknown pixel format");
277   /* TODO: Cross-check with XAllocColor. */
278   rgba[0] = (pixel >> 16) & 0xFF;
279   rgba[1] = (pixel >>  8) & 0xFF;
280   rgba[2] = (pixel >>  0) & 0xFF;
281   rgba[3] = (pixel >> 24) & 0xFF;
282 }
283
284
285 void
286 jwxyz_assert_display(Display *dpy)
287 {
288   if(!dpy)
289     return;
290   jwxyz_assert_gl ();
291   jwxyz_assert_drawable (dpy->main_window, dpy->main_window);
292 }
293
294
295 void
296 jwxyz_set_matrices (Display *dpy, unsigned width, unsigned height,
297                     Bool window_p)
298 {
299   Assert (width, "no width");
300   Assert (height, "no height");
301
302   /* TODO: Check registration pattern from Interference with rectangles instead of points. */
303
304   // The projection matrix is always set as follows. The modelview matrix is
305   // usually identity, but for points and thin lines, it's translated by 0.5.
306   glMatrixMode(GL_PROJECTION);
307   glLoadIdentity();
308   
309 # if defined(USE_IPHONE) || defined(HAVE_ANDROID)
310
311   if (window_p && ignore_rotation_p(dpy)) {
312     int o = (int) current_device_rotation();
313     glRotatef (-o, 0, 0, 1);
314   }
315
316   // glPointSize(1); // This is the default.
317   
318 #ifdef HAVE_JWZGLES
319   glOrthof /* TODO: Is glOrthox worth it? Signs point to no. */
320 #else
321   glOrtho
322 #endif
323     (0, width, height, 0, -1, 1);
324
325   glMatrixMode(GL_MODELVIEW);
326 # endif // HAVE_MOBILE
327 }
328
329 #ifndef HAVE_JWZGLES
330
331 struct gl_version
332 {
333   // iOS always uses OpenGL ES 1.1.
334   unsigned major;
335   unsigned minor;
336 };
337
338 static GLboolean
339 gl_check_ver (const struct gl_version *caps,
340               unsigned gl_major,
341               unsigned gl_minor)
342 {
343   return caps->major > gl_major ||
344            (caps->major == gl_major && caps->minor >= gl_minor);
345 }
346
347 /*
348 static GLboolean gl_check_ext(const struct gl_caps *caps,
349                               unsigned gl_major,
350                               unsigned gl_minor,
351                               const char *extension)
352 {
353   return
354     gl_check_ver(caps, gl_major, gl_minor) ||
355     gluCheckExtension(extension, caps->extensions);
356 }
357 */
358
359 #endif
360
361
362 // NSOpenGLContext *jwxyz_debug_context;
363
364
365 Display *
366 jwxyz_make_display (Window w)
367 {
368   Display *d = (Display *) calloc (1, sizeof(*d));
369   d->screen = (Screen *) calloc (1, sizeof(Screen));
370   d->screen->dpy = d;
371
372 # ifndef HAVE_JWZGLES
373   struct gl_version version;
374
375   {
376     const GLubyte *version_str = glGetString (GL_VERSION);
377
378     /* iPhone is always OpenGL ES 1.1. */
379     if (sscanf ((const char *) version_str, "%u.%u",
380                 &version.major, &version.minor) < 2)
381     {
382       version.major = 1;
383       version.minor = 1;
384     }
385   }
386 # endif // !HAVE_JWZGLES
387
388   const GLubyte *extensions = glGetString (GL_EXTENSIONS);
389
390   /* See:
391      - Apple TN2080: Understanding and Detecting OpenGL Functionality.
392      - OpenGL Programming Guide for the Mac - Best Practices for Working with
393        Texture Data - Optimal Data Formats and Types
394    */
395
396   // If a video adapter suports BGRA textures, then that's probably as fast as
397   // you're gonna get for getting a texture onto the screen.
398 # ifdef HAVE_JWZGLES
399   /* TODO: Make BGRA work on iOS. As it is, it breaks XPutImage. (glTexImage2D, AFAIK) */
400   d->screen->pixel_format = GL_RGBA; /*
401     gluCheckExtension ((const GLubyte *) "GL_APPLE_texture_format_BGRA8888",
402                        extensions) ? GL_BGRA_EXT : GL_RGBA; */
403   d->screen->pixel_type = GL_UNSIGNED_BYTE;
404   // See also OES_read_format.
405 # else  // !HAVE_JWZGLES
406   if (gl_check_ver (&version, 1, 2) ||
407       (gluCheckExtension ((const GLubyte *) "GL_EXT_bgra", extensions) &&
408        gluCheckExtension ((const GLubyte *) "GL_APPLE_packed_pixels",
409                           extensions))) {
410     d->screen->pixel_format = GL_BGRA_EXT;
411     // Both Intel and PowerPC-era docs say to use GL_UNSIGNED_INT_8_8_8_8_REV.
412     d->screen->pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
413   } else {
414     d->screen->pixel_format = GL_RGBA;
415     d->screen->pixel_type = GL_UNSIGNED_BYTE;
416   }
417   // GL_ABGR_EXT/GL_UNSIGNED_BYTE is another possibilty that may have made more
418   // sense on PowerPC.
419 # endif // !HAVE_JWZGLES
420
421   // On really old systems, it would make sense to split the texture
422   // into subsections.
423 # ifndef HAVE_JWZGLES
424   d->gl_texture_npot_p = gluCheckExtension ((const GLubyte *)
425                                             "GL_ARB_texture_rectangle",
426                                             extensions);
427   d->gl_texture_target = d->gl_texture_npot_p ?
428                          GL_TEXTURE_RECTANGLE_EXT :
429                          GL_TEXTURE_2D;
430 # else
431   d->gl_texture_npot_p = jwzgles_gluCheckExtension
432       ((const GLubyte *) "GL_APPLE_texture_2D_limited_npot", extensions) ||
433     jwzgles_gluCheckExtension
434       ((const GLubyte *) "GL_OES_texture_npot", extensions) ||
435     jwzgles_gluCheckExtension // From PixelFlinger 1.4
436       ((const GLubyte *) "GL_ARB_texture_non_power_of_two", extensions);
437
438   d->gl_texture_target = GL_TEXTURE_2D;
439 # endif
440
441   d->screen->black = jwxyz_alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF);
442   d->screen->white = jwxyz_alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
443
444   Visual *v = (Visual *) calloc (1, sizeof(Visual));
445   v->class      = TrueColor;
446   v->red_mask   = jwxyz_alloc_color (d, 0xFFFF, 0x0000, 0x0000, 0x0000);
447   v->green_mask = jwxyz_alloc_color (d, 0x0000, 0xFFFF, 0x0000, 0x0000);
448   v->blue_mask  = jwxyz_alloc_color (d, 0x0000, 0x0000, 0xFFFF, 0x0000);
449   d->screen->visual = v;
450
451   d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
452
453   d->window_background = BlackPixel(d,0);
454
455   d->main_window = w;
456   {
457     fputs((char *)glGetString(GL_VENDOR), stderr);
458     fputc(' ', stderr);
459     fputs((char *)glGetString(GL_RENDERER), stderr);
460     fputc(' ', stderr);
461     fputs((char *)glGetString(GL_VERSION), stderr);
462     fputc('\n', stderr);
463 //  puts(caps.extensions);
464     GLint max_texture_size;
465     glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size);
466     printf ("GL_MAX_TEXTURE_SIZE: %d\n", max_texture_size);
467   }
468  
469   // In case a GL hack wants to use X11 to draw offscreen, the rect_texture is available.
470   Assert (d->main_window == w, "Uh-oh.");
471   glGenTextures (1, &d->rect_texture);
472   // TODO: Check for (ARB|EXT|NV)_texture_rectangle. (All three are alike.)
473   // Rectangle textures should be present on OS X with the following exceptions:
474   // - Generic renderer on PowerPC OS X 10.4 and earlier
475   // - ATI Rage 128
476   glBindTexture (d->gl_texture_target, d->rect_texture);
477   // TODO: This is all somewhere else. Refactor.
478   glTexParameteri (d->gl_texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
479   glTexParameteri (d->gl_texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
480
481   // This might be redundant for rectangular textures.
482 # ifndef HAVE_JWZGLES
483   const GLint wrap = GL_CLAMP;
484 # else  // HAVE_JWZGLES
485   const GLint wrap = GL_CLAMP_TO_EDGE;
486 # endif // HAVE_JWZGLES
487
488   // In OpenGL, CLAMP_TO_EDGE is OpenGL 1.2 or GL_SGIS_texture_edge_clamp.
489   // This is always present with OpenGL ES.
490   glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_S, wrap);
491   glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_T, wrap);
492
493   jwxyz_assert_display(d);
494   return d;
495 }
496
497 void
498 jwxyz_free_display (Display *dpy)
499 {
500   /* TODO: Go over everything. */
501
502   jwxyz_sources_free (dpy->timers_data);
503
504   free (dpy->screen->visual);
505   free (dpy->screen);
506   free (dpy);
507 }
508
509
510 /* Call this after any modification to the bits on a Pixmap or Window.
511    Most Pixmaps are used frequently as sources and infrequently as
512    destinations, so it pays to cache the data as a CGImage as needed.
513  */
514 static void
515 invalidate_drawable_cache (Drawable d)
516 {
517   /* TODO: Kill this outright. jwxyz_bind_drawable handles any potential 
518      invalidation.
519    */
520
521   /*
522   if (d && d->cgi) {
523     abort();
524     CGImageRelease (d->cgi);
525     d->cgi = 0;
526   }
527  */
528 }
529
530
531 /* Call this when the View changes size or position.
532  */
533 void
534 jwxyz_window_resized (Display *dpy)
535 {
536   const XRectangle *new_frame = jwxyz_frame (dpy->main_window);
537   unsigned new_width = new_frame->width;
538   unsigned new_height = new_frame->height;
539
540   Assert (new_width, "jwxyz_window_resized: No width.");
541   Assert (new_height, "jwxyz_window_resized: No height.");
542
543 /*if (cgc) w->cgc = cgc;
544   Assert (w->cgc, "no CGContext"); */
545
546   Log("resize: %d, %d\n", new_width, new_height);
547
548   jwxyz_bind_drawable (dpy, dpy->main_window, dpy->main_window);
549
550   // TODO: What does the iPhone need?
551   
552   // iOS only: If the main_window is not the current_drawable, then set_matrices
553   // was already called in bind_drawable.
554   jwxyz_set_matrices (dpy, new_width, new_height, True);
555
556 /*
557   GLint draw_buffer;
558   glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
559   
560   glDrawBuffer (GL_FRONT);
561   glClearColor (1, 0, 1, 0);
562   glClear (GL_COLOR_BUFFER_BIT);
563   glDrawBuffer (GL_BACK);
564   glClearColor (0, 1, 1, 0);
565   glClear (GL_COLOR_BUFFER_BIT);
566   
567   glDrawBuffer (draw_buffer); */
568   
569   // Stylish and attractive purple!
570   // glClearColor (1, 0, 1, 0.5);
571   // glClear (GL_COLOR_BUFFER_BIT);
572   
573   invalidate_drawable_cache (dpy->main_window);
574 }
575
576
577 jwxyz_sources_data *
578 display_sources_data (Display *dpy)
579 {
580   return dpy->timers_data;
581 }
582
583
584 Window
585 XRootWindow (Display *dpy, int screen)
586 {
587   return dpy ? dpy->main_window : 0;
588 }
589
590 Screen *
591 XDefaultScreenOfDisplay (Display *dpy)
592 {
593   return dpy ? dpy->screen : 0;
594 }
595
596 Visual *
597 XDefaultVisualOfScreen (Screen *screen)
598 {
599   return screen ? screen->visual : 0;
600 }
601
602 Display *
603 XDisplayOfScreen (Screen *s)
604 {
605   return s ? s->dpy : 0;
606 }
607
608 int
609 XDisplayNumberOfScreen (Screen *s)
610 {
611   return 0;
612 }
613
614 int
615 XScreenNumberOfScreen (Screen *s)
616 {
617   return 0;
618 }
619
620 unsigned long
621 XBlackPixelOfScreen(Screen *screen)
622 {
623   return screen->black;
624 }
625
626 unsigned long
627 XWhitePixelOfScreen(Screen *screen)
628 {
629   return screen->white;
630 }
631
632 unsigned long
633 XCellsOfScreen(Screen *screen)
634 {
635   Visual *v = screen->visual;
636   return v->red_mask | v->green_mask | v->blue_mask;
637 }
638
639
640 /* GC attributes by usage and OpenGL implementation:
641  *
642  * All drawing functions:
643  * function                                | glLogicOp w/ GL_COLOR_LOGIC_OP
644  * clip_x_origin, clip_y_origin, clip_mask | Stencil mask
645  *
646  * Shape drawing functions:
647  * foreground, background                  | glColor*
648  *
649  * XDrawLines, XDrawRectangles, XDrawSegments:
650  * line_width, cap_style, join_style       | Lotsa vertices
651  *
652  * XFillPolygon:
653  * fill_rule                               | Multiple GL_TRIANGLE_FANs
654  *
655  * XDrawText:
656  * font                                    | Cocoa, then OpenGL display lists.
657  *
658  * alpha_allowed_p                         | TODO
659  * antialias_p                             | TODO
660  *
661  * Nothing, really:
662  * subwindow_mode
663 */
664
665 static void
666 set_clip_mask (GC gc)
667 {
668   Assert (!gc->gcv.clip_mask, "set_gc: TODO");
669 }
670
671 static void
672 set_function (int function)
673 {
674   Assert (function == GXcopy, "set_gc: (TODO) Stubbed gcv function");
675   
676   /* TODO: The GL_COLOR_LOGIC_OP extension is exactly what is needed here. (OpenGL 1.1)
677    Fun fact: The glLogicOp opcode constants are the same as the X11 GX* function constants | GL_CLEAR.
678    */
679   
680 #if 0
681   switch (gc->gcv.function) {
682     case GXset:
683     case GXclear:
684     case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/   break;
685     case GXxor:   CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
686     case GXor:    CGContextSetBlendMode (cgc, kCGBlendModeLighten);    break;
687     case GXand:   CGContextSetBlendMode (cgc, kCGBlendModeDarken);     break;
688     default: Assert(0, "unknown gcv function"); break;
689   }
690 #endif
691 }
692
693
694 static void
695 set_color (Display *dpy, unsigned long pixel, unsigned int depth,
696            Bool alpha_allowed_p)
697 {
698   jwxyz_validate_pixel (dpy, pixel, depth, alpha_allowed_p);
699
700   if (depth == 1) {
701     GLfloat f = pixel;
702     glColor4f (f, f, f, 1);
703   } else {
704     /* TODO: alpha_allowed_p */
705     uint8_t rgba[4];
706     jwxyz_query_color (dpy, pixel, rgba);
707 #ifdef HAVE_JWZGLES
708     glColor4f (rgba[0] / 255.0f, rgba[1] / 255.0f,
709                rgba[2] / 255.0f, rgba[3] / 255.0f);
710 #else
711     glColor4ubv (rgba);
712 #endif
713   }
714 }
715
716 /* Pushes a GC context; sets Function, ClipMask, and color.
717  */
718 static void
719 set_color_gc (Display *dpy, GC gc, unsigned long color)
720 {
721   // GC is NULL for XClearArea and XClearWindow.
722   unsigned int depth;
723   int function;
724   if (gc) {
725     function = gc->gcv.function;
726     depth = gc->depth;
727     set_clip_mask (gc);
728   } else {
729     function = GXcopy;
730     depth = visual_depth (NULL, NULL);
731     // TODO: Set null clip mask here.
732   }
733
734   set_function (function);
735
736   switch (function) {
737     case GXset:   color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
738     case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
739   }
740
741   set_color (dpy, color, depth, gc ? gc->gcv.alpha_allowed_p : False);
742   
743   /* TODO: Antialiasing. */
744   /* CGContextSetShouldAntialias (cgc, antialias_p); */
745 }
746
747 /* Pushes a GC context; sets color to the foreground color.
748  */
749 static void
750 set_fg_gc (Display *dpy, GC gc)
751 {
752   set_color_gc (dpy, gc, gc->gcv.foreground);
753 }
754
755 static void
756 next_point(short *v, XPoint p, int mode)
757 {
758   switch (mode) {
759     case CoordModeOrigin:
760       v[0] = p.x;
761       v[1] = p.y;
762       break;
763     case CoordModePrevious:
764       v[0] += p.x;
765       v[1] += p.y;
766       break;
767     default:
768       Assert (False, "next_point: bad mode");
769       break;
770   }
771 }
772
773 int
774 XDrawPoints (Display *dpy, Drawable d, GC gc, 
775              XPoint *points, int count, int mode)
776 {
777   jwxyz_bind_drawable (dpy, dpy->main_window, d);
778   set_fg_gc (dpy, gc);
779
780 /*
781   
782   glBegin(GL_POINTS);
783   for (unsigned i = 0; i < count; i++) {
784     next_point(v, points[i], mode);
785     glVertex2f(v[0] + 0.5f, v[1] + 0.5f);
786   }
787   glEnd();
788  */
789
790   short v[2] = {0, 0};
791
792   // TODO: XPoints can be fed directly to OpenGL.
793   GLshort *gl_points = malloc (count * 2 * sizeof(GLshort)); // TODO: malloc returns NULL.
794   for (unsigned i = 0; i < count; i++) {
795     next_point (v, points[i], mode);
796     gl_points[2 * i] = v[0];
797     gl_points[2 * i + 1] = v[1];
798   }
799   
800   glMatrixMode (GL_MODELVIEW);
801   glTranslatef (0.5, 0.5, 0);
802   
803   glEnableClientState (GL_VERTEX_ARRAY);
804   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
805   glVertexPointer (2, GL_SHORT, 0, gl_points);
806   glDrawArrays (GL_POINTS, 0, count);
807   
808   free (gl_points);
809   
810   glLoadIdentity ();
811   
812   return 0;
813 }
814
815
816 static GLint
817 texture_internalformat(Display *dpy)
818 {
819 #ifdef HAVE_JWZGLES
820   return dpy->screen->pixel_format;
821 #else
822   return GL_RGBA;
823 #endif
824 }
825
826 static GLenum gl_pixel_type(const Display *dpy)
827 {
828   return dpy->screen->pixel_type;
829 }
830
831 static void
832 clear_texture (Display *dpy)
833 {
834   glTexImage2D (dpy->gl_texture_target, 0, texture_internalformat(dpy), 0, 0,
835                 0, dpy->screen->pixel_format, gl_pixel_type (dpy), NULL);
836 }
837
838 static void set_white (void)
839 {
840 #ifdef HAVE_JWZGLES
841   glColor4f (1, 1, 1, 1);
842 #else
843   glColor3ub (0xff, 0xff, 0xff);
844 #endif
845 }
846
847
848 void
849 jwxyz_gl_copy_area_read_tex_image (Display *dpy, unsigned src_height,
850                                    int src_x, int src_y,
851                                    unsigned int width, unsigned int height,
852                                    int dst_x, int dst_y)
853 {
854 #  if defined HAVE_COCOA && !defined USE_IPHONE
855   /* TODO: Does this help? */
856   /* glFinish(); */
857 #  endif
858
859   /* TODO: Fix TestX11 + mode_preserve with this one. */
860
861   unsigned tex_w = width, tex_h = height;
862   if (!dpy->gl_texture_npot_p) {
863     tex_w = to_pow2(tex_w);
864     tex_h = to_pow2(tex_h);
865   }
866
867   GLint internalformat = texture_internalformat(dpy);
868
869   glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
870
871   if (tex_w == width && tex_h == height) {
872     glCopyTexImage2D (dpy->gl_texture_target, 0, internalformat,
873                       src_x, src_height - src_y - height, width, height, 0);
874   } else {
875     glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
876                   0, dpy->screen->pixel_format, gl_pixel_type(dpy), NULL);
877     glCopyTexSubImage2D (dpy->gl_texture_target, 0, 0, 0,
878                          src_x, src_height - src_y - height, width, height);
879   }
880 }
881
882 void
883 jwxyz_gl_copy_area_write_tex_image (Display *dpy, GC gc, int src_x, int src_y,
884                                     unsigned int width, unsigned int height,
885                                     int dst_x, int dst_y)
886 {
887   Assert(gc->gcv.function == GXcopy, "XCopyArea: Unknown function");
888
889   /* TODO: Copy-pasted from read_tex_image. */
890   unsigned tex_w = width, tex_h = height;
891   if (!dpy->gl_texture_npot_p) {
892     tex_w = to_pow2(tex_w);
893     tex_h = to_pow2(tex_h);
894   }
895
896   glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
897
898   jwxyz_gl_draw_image (dpy->gl_texture_target, tex_w, tex_h, 0, 0,
899                        width, height, dst_x, dst_y);
900
901   clear_texture (dpy);
902 }
903
904
905 void
906 jwxyz_gl_draw_image (GLenum gl_texture_target,
907                      unsigned int tex_w, unsigned int tex_h,
908                      int src_x, int src_y,
909                      unsigned int width, unsigned int height,
910                      int dst_x, int dst_y)
911 {
912   set_white ();
913   glEnable (gl_texture_target);
914
915   glEnableClientState (GL_TEXTURE_COORD_ARRAY);
916   glEnableClientState (GL_VERTEX_ARRAY);
917     
918   /* TODO: Copied from XPutImage. Refactor. */
919   /* TODO: EXT_draw_texture or whatever it's called. */
920   GLfloat vertices[4][2] =
921   {
922     {dst_x, dst_y},
923     {dst_x, dst_y + height},
924     {dst_x + width, dst_y + height},
925     {dst_x + width, dst_y}
926   };
927
928   GLfloat
929     tex_x0 = src_x, tex_y0 = src_y + height,
930     tex_x1 = src_x + width, tex_y1 = src_y;
931
932 # ifndef HAVE_JWZGLES
933   if (gl_texture_target != GL_TEXTURE_RECTANGLE_EXT)
934 # endif
935   {
936     GLfloat mx = 1.0f / tex_w, my = 1.0f / tex_h;
937     tex_x0 *= mx;
938     tex_y0 *= my;
939     tex_x1 *= mx;
940     tex_y1 *= my;
941   }
942
943   GLfloat tex_coords[4][2] =
944   {
945     {tex_x0, tex_y0},
946     {tex_x0, tex_y1},
947     {tex_x1, tex_y1},
948     {tex_x1, tex_y0}
949   };
950
951   glVertexPointer (2, GL_FLOAT, 0, vertices);
952   glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
953   glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
954
955   glDisable (gl_texture_target);
956 }
957
958 void
959 jwxyz_gl_copy_area_read_pixels (Display *dpy, Drawable src, Drawable dst,
960                                 GC gc, int src_x, int src_y,
961                                 unsigned int width, unsigned int height,
962                                 int dst_x, int dst_y)
963 {
964   XImage *img = XGetImage (dpy, src, src_x, src_y, width, height, ~0, ZPixmap);
965   XPutImage (dpy, dst, gc, img, 0, 0, dst_x, dst_y, width, height);
966   XDestroyImage (img);
967 }
968
969
970 #if 0
971 // TODO: Make sure offset works in super-sampled mode.
972 static void
973 adjust_point_for_line (GC gc, CGPoint *p)
974 {
975   // Here's the authoritative discussion on how X draws lines:
976   // http://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:CreateGC:line-width
977   if (gc->gcv.line_width <= 1) {
978     /* Thin lines are "drawn using an unspecified, device-dependent
979        algorithm", but seriously though, Bresenham's algorithm. Bresenham's
980        algorithm runs to and from pixel centers.
981
982        There's a few screenhacks (Maze, at the very least) that set line_width
983        to 1 when it probably should be set to 0, so it's line_width <= 1
984        instead of < 1.
985      */
986     p->x += 0.5;
987     p->y -= 0.5;
988   } else {
989     /* Thick lines OTOH run from the upper-left corners of pixels. This means
990        that a horizontal thick line of width 1 straddles two scan lines.
991        Aliasing requires one of these scan lines be chosen; the following
992        nudges the point so that the right choice is made. */
993     p->y -= 1e-3;
994   }
995 }
996 #endif
997
998
999 int
1000 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1001 {
1002   // TODO: XDrawLine == XDrawSegments(nlines == 1), also in jwxyz.m
1003   XSegment segment;
1004   segment.x1 = x1;
1005   segment.y1 = y1;
1006   segment.x2 = x2;
1007   segment.y2 = y2;
1008   XDrawSegments (dpy, d, gc, &segment, 1);
1009
1010   // when drawing a zero-length line, obey line-width and cap-style.
1011 /* if (x1 == x2 && y1 == y2) {
1012     int w = gc->gcv.line_width;
1013     x1 -= w/2;
1014     y1 -= w/2;
1015     if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1016       return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1017     else {
1018       if (!w)
1019         w = 1; // Actually show zero-length lines.
1020       return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1021     }
1022   }
1023
1024   CGPoint p = point_for_line (d, gc, x1, y1);
1025
1026   push_fg_gc (dpy, d, gc, NO);
1027
1028   CGContextRef cgc = d->cgc;
1029   set_line_mode (cgc, &gc->gcv);
1030   CGContextBeginPath (cgc);
1031   CGContextMoveToPoint (cgc, p.x, p.y);
1032   p = point_for_line(d, gc, x2, y2);
1033   CGContextAddLineToPoint (cgc, p.x, p.y);
1034   CGContextStrokePath (cgc);
1035   pop_gc (d, gc);
1036   invalidate_drawable_cache (d); */
1037   return 0;
1038 }
1039
1040 int
1041 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1042             int mode)
1043 {
1044   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1045   set_fg_gc (dpy, gc);
1046
1047   /* TODO: Thick lines
1048    * Zero-length line segments
1049    * Paths with zero length total (Remember line width, cap style.)
1050    * Closed loops
1051    */
1052   
1053   if (!count)
1054     return 0;
1055
1056   GLshort *vertices = malloc(2 * sizeof(GLshort) * count); // TODO malloc NULL sigh
1057   
1058   glMatrixMode (GL_MODELVIEW);
1059   glTranslatef (0.5f, 0.5f, 0);
1060   
1061   short p[2] = {0, 0};
1062   for (unsigned i = 0; i < count; i++) {
1063     next_point (p, points[i], mode);
1064     vertices[2 * i] = p[0];
1065     vertices[2 * i + 1] = p[1];
1066   }
1067   
1068   glEnableClientState (GL_VERTEX_ARRAY);
1069   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1070   glVertexPointer (2, GL_SHORT, 0, vertices);
1071   glDrawArrays (GL_LINE_STRIP, 0, count);
1072   
1073   free (vertices);
1074
1075   if (gc->gcv.cap_style != CapNotLast) {
1076     // TODO: How does this look with multisampling?
1077     // TODO: Disable me for closed loops.
1078     glVertexPointer (2, GL_SHORT, 0, p);
1079     glDrawArrays (GL_POINTS, 0, 1);
1080   }
1081
1082   glLoadIdentity ();
1083   
1084   return 0;
1085 }
1086
1087
1088 int
1089 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1090 {
1091   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1092   set_fg_gc (dpy, gc);
1093   
1094   /* TODO: Thick lines. */
1095   /* Thin lines <= 1px are offset by +0.5; thick lines are not. */
1096   
1097   glMatrixMode (GL_MODELVIEW);
1098   glTranslatef (0.5, 0.5, 0);
1099
1100   glEnableClientState (GL_VERTEX_ARRAY);
1101   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1102   
1103   Assert (sizeof(XSegment) == sizeof(short) * 4, "XDrawSegments: Data alignment mix-up."); // TODO: Static assert here.
1104   Assert (sizeof(GLshort) == sizeof(short), "XDrawSegments: Data alignment mix-up."); // TODO: Static assert here.
1105   Assert (offsetof(XSegment, x1) == 0, "XDrawSegments: Data alignment mix-up.");
1106   Assert (offsetof(XSegment, x2) == 4, "XDrawSegments: Data alignment mix-up.");
1107   glVertexPointer (2, GL_SHORT, 0, segments);
1108   glDrawArrays (GL_LINES, 0, count * 2);
1109   
1110   if (gc->gcv.cap_style != CapNotLast) {
1111     glVertexPointer (2, GL_SHORT, sizeof(GLshort) * 4, (const GLshort *)segments + 2);
1112     glDrawArrays (GL_POINTS, 0, count);
1113   }
1114   
1115   glLoadIdentity ();
1116   
1117 /* CGRect wr = d->frame;
1118   push_fg_gc (dpy, d, gc, NO);
1119   set_line_mode (cgc, &gc->gcv);
1120   CGContextBeginPath (cgc);
1121   for (i = 0; i < count; i++) {
1122     CGPoint p = point_for_line (d, gc, segments->x1, segments->y1);
1123     CGContextMoveToPoint (cgc, p.x, p.y);
1124     p = point_for_line (d, gc, segments->x2, segments->y2);
1125     CGContextAddLineToPoint (cgc, p.x, p.y);
1126     segments++;
1127   }
1128   CGContextStrokePath (cgc);
1129   pop_gc (d, gc);
1130   invalidate_drawable_cache (d); */
1131   return 0;
1132 }
1133
1134
1135 int
1136 XClearWindow (Display *dpy, Window win)
1137 {
1138   Assert (win == dpy->main_window, "not a window");
1139   const XRectangle *wr = jwxyz_frame (win);
1140   /* TODO: Use glClear if there's no background pixmap. */
1141   return XClearArea (dpy, win, 0, 0, wr->width, wr->height, 0);
1142 }
1143
1144 unsigned long
1145 jwxyz_window_background (Display *dpy)
1146 {
1147   return dpy->window_background;
1148 }
1149
1150 int
1151 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1152 {
1153   Assert (w == dpy->main_window, "not a window");
1154   jwxyz_validate_pixel (dpy, pixel, visual_depth (NULL, NULL), False);
1155   dpy->window_background = pixel;
1156   return 0;
1157 }
1158
1159 void
1160 jwxyz_fill_rects (Display *dpy, Drawable d, GC gc,
1161                   const XRectangle *rectangles, unsigned long nrectangles,
1162                   unsigned long pixel)
1163 {
1164   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1165   set_color_gc (dpy, gc, pixel);
1166 /*
1167   glBegin(GL_QUADS);
1168   for (unsigned i = 0; i != nrectangles; ++i) {
1169     const XRectangle *r = &rectangles[i];
1170     glVertex2i(r->x, r->y);
1171     glVertex2i(r->x, r->y + r->height);
1172     glVertex2i(r->x + r->width, r->y + r->height);
1173     glVertex2i(r->x + r->width, r->y);
1174   }
1175   glEnd(); */
1176   
1177   glEnableClientState (GL_VERTEX_ARRAY);
1178   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1179     
1180   for (unsigned long i = 0; i != nrectangles; ++i)
1181   {
1182     const XRectangle *r = &rectangles[i];
1183     
1184     GLfloat coords[4][2] =
1185     {
1186       {r->x, r->y},
1187       {r->x, r->y + r->height},
1188       {r->x + r->width, r->y + r->height},
1189       {r->x + r->width, r->y}
1190     };
1191     
1192     // TODO: Several rects at once. Maybe even tune for XScreenSaver workloads.
1193     glVertexPointer (2, GL_FLOAT, 0, coords);
1194     jwxyz_assert_gl ();
1195     glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1196     jwxyz_assert_gl ();
1197   }
1198 }
1199
1200
1201 int
1202 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1203 {
1204   Assert(win == dpy->main_window, "XClearArea: not a window");
1205   Assert(!exp, "XClearArea: exposures unsupported");
1206
1207   jwxyz_fill_rect (dpy, win, 0, x, y, w, h, dpy->window_background);
1208   return 0;
1209 }
1210
1211
1212 int
1213 XFillPolygon (Display *dpy, Drawable d, GC gc, 
1214               XPoint *points, int npoints, int shape, int mode)
1215 {
1216   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1217   set_fg_gc(dpy, gc);
1218   
1219   // TODO: Re-implement the GLU tesselation functions.
1220
1221   /* Complex: Pedal, and for some reason Attraction, Mountain, Qix, SpeedMine, Starfish
1222    * Nonconvex: Goop, Pacman, Rocks, Speedmine
1223    *
1224    * We currently do Nonconvex with the simple-to-implement ear clipping
1225    * algorithm, but in the future we can replace that with an algorithm
1226    * with slower big-O growth
1227    *
1228    */
1229   
1230   
1231   // TODO: Feed vertices straight to OpenGL for CoordModeOrigin.
1232
1233   if (shape == Convex) {
1234
1235   GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc.
1236   short v[2] = {0, 0};
1237   
1238   for (unsigned i = 0; i < npoints; i++) {
1239     next_point(v, points[i], mode);
1240     vertices[2 * i] = v[0];
1241     vertices[2 * i + 1] = v[1];
1242   }
1243
1244   glEnableClientState (GL_VERTEX_ARRAY);
1245   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1246   
1247   glVertexPointer (2, GL_SHORT, 0, vertices);
1248   glDrawArrays (GL_TRIANGLE_FAN, 0, npoints);
1249   
1250   free(vertices);
1251
1252   } else if (shape == Nonconvex) {
1253
1254   // TODO: assert that x,y of first and last point match, as that is assumed
1255
1256   linked_point *root;
1257   root = (linked_point *) malloc( sizeof(linked_point) );
1258   set_points_list(points,npoints,root);
1259   traverse_points_list(root);
1260
1261   } else {
1262     Assert((shape == Convex || shape == Nonconvex), "XFillPolygon: (TODO) Unimplemented shape");
1263   }
1264   
1265
1266   return 0;
1267 }
1268
1269 #define radians(DEG) ((DEG) * M_PI / 180.0)
1270 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1271
1272 static void
1273 arc_xy(GLfloat *p, double cx, double cy, double w2, double h2, double theta)
1274 {
1275   p[0] = cos(theta) * w2 + cx;
1276   p[1] = -sin(theta) * h2 + cy;
1277 }
1278
1279 static unsigned
1280 mod_neg(int a, unsigned b)
1281 {
1282   /* Normal modulus is implementation defined for negative numbers. This is 
1283    * well-defined such that the repeating pattern for a >= 0 is maintained for 
1284    * a < 0. */
1285   return a < 0 ? (b - 1) - (-(a + 1) % b) : a % b;
1286 }
1287
1288 int
1289 jwxyz_draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1290                 unsigned int width, unsigned int height,
1291                 int angle1, int angle2, Bool fill_p)
1292 {
1293   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1294   set_fg_gc(dpy, gc);
1295
1296   /* Let's say the number of line segments needed to make a convincing circle is
1297      4*sqrt(radius). (But these arcs aren't necessarily circular arcs...) */
1298
1299   double w2 = width * 0.5f, h2 = height * 0.5f;
1300   double a, b; /* Semi-major/minor axes. */
1301   if(w2 > h2) {
1302     a = w2;
1303     b = h2;
1304   } else {
1305     a = h2;
1306     b = w2;
1307   }
1308   
1309   const double two_pi = 2 * M_PI;
1310
1311   double amb = a - b, apb = a + b;
1312   double h = (amb * amb) / (apb * apb);
1313   // TODO: Math cleanup.
1314   double C_approx = M_PI * apb * (1 + 3 * h / (10 + sqrtf(4 - 3 * h)));
1315   double segments_f = 4 * sqrtf(C_approx / (2 * M_PI));
1316
1317   // TODO: Explain how drawing works what with the points of overlapping arcs
1318   // matching up.
1319  
1320 #if 1
1321   unsigned segments_360 = segments_f;
1322   
1323   /* TODO: angle2 == 0. This is a tilted square with CapSquare. */
1324   /* TODO: color, thick lines, CapNotLast for thin lines */
1325   /* TODO: Transformations. */
1326
1327   double segment_angle = two_pi / segments_360;
1328
1329   const unsigned deg64 = 360 * 64;
1330   const double rad_from_deg64 = two_pi / deg64;
1331   
1332   if (angle2 < 0) {
1333     angle1 += angle2;
1334     angle2 = -angle2;
1335   }
1336
1337   angle1 = mod_neg(angle1, deg64); // TODO: Is this OK? Consider negative numbers.
1338
1339   if (angle2 > deg64)
1340     angle2 = deg64; // TODO: Handle circles special.
1341   
1342   double
1343     angle1_f = angle1 * rad_from_deg64,
1344     angle2_f = angle2 * rad_from_deg64;
1345   
1346   if (angle2_f > two_pi) // TODO: Move this up.
1347     angle2_f = two_pi;
1348   
1349   double segment1_angle_part = fmodf(angle1_f, segment_angle);
1350   
1351   unsigned segment1 = ((angle1_f - segment1_angle_part) / segment_angle) + 1.5;
1352
1353   double angle_2r = angle2_f - segment1_angle_part;
1354   unsigned segments = angle_2r / segment_angle;
1355   
1356   GLfloat cx = x + w2, cy = y + h2;
1357
1358   GLfloat *data = malloc((segments + 3) * sizeof(GLfloat) * 2); // TODO: Check result.
1359   
1360   GLfloat *data_ptr = data;
1361   if (fill_p) {
1362     data_ptr[0] = cx;
1363     data_ptr[1] = cy;
1364     data_ptr += 2;
1365   }
1366   
1367   arc_xy (data_ptr, cx, cy, w2, h2, angle1_f);
1368   data_ptr += 2;
1369   
1370   for (unsigned s = 0; s != segments; ++s) {
1371     // TODO: Make sure values of theta for the following arc_xy call are between
1372     // angle1_f and angle1_f + angle2_f.
1373     arc_xy (data_ptr, cx, cy, w2, h2, (segment1 + s) * segment_angle);
1374     data_ptr += 2;
1375   }
1376   
1377   arc_xy (data_ptr, cx, cy, w2, h2, angle1_f + angle2_f);
1378   data_ptr += 2;
1379
1380   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1381   glEnableClientState (GL_VERTEX_ARRAY);
1382   
1383   glVertexPointer (2, GL_FLOAT, 0, data);
1384   glDrawArrays (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP,
1385                 0,
1386                 (GLsizei)((data_ptr - data) / 2));
1387
1388   free(data);
1389   
1390 #endif
1391   
1392 #if 0
1393   unsigned segments = segments_f * (fabs(angle2) / (360 * 64));
1394  
1395   glBegin (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP);
1396   
1397   if (fill_p /* && gc->gcv.arc_mode == ArcPieSlice */)
1398     glVertex2f (cx, cy);
1399   
1400   /* TODO: This should fix the middle points of the arc so that the starting and ending points are OK. */
1401   
1402   float to_radians = 2 * M_PI / (360 * 64);
1403   float theta = angle1 * to_radians, d_theta = angle2 * to_radians / segments;
1404   
1405   for (unsigned s = 0; s != segments + 1; ++s) /* TODO: This is the right number of segments, yes? */
1406   {
1407     glVertex2f(cos(theta) * w2 + cx, -sin(theta) * h2 + cy);
1408     theta += d_theta;
1409   }
1410   
1411   glEnd ();
1412 #endif
1413   
1414   return 0;
1415 }
1416
1417
1418 XGCValues *
1419 jwxyz_gc_gcv (GC gc)
1420 {
1421   return &gc->gcv;
1422 }
1423
1424
1425 unsigned int
1426 jwxyz_gc_depth (GC gc)
1427 {
1428   return gc->depth;
1429 }
1430
1431
1432 GC
1433 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1434 {
1435   struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1436   gc->depth = jwxyz_drawable_depth (d);
1437
1438   jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
1439   XChangeGC (dpy, gc, mask, xgcv);
1440   return gc;
1441 }
1442
1443
1444 int
1445 XFreeGC (Display *dpy, GC gc)
1446 {
1447   if (gc->gcv.font)
1448     XUnloadFont (dpy, gc->gcv.font);
1449
1450   // TODO
1451 /*
1452   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1453
1454   if (gc->gcv.clip_mask) {
1455     XFreePixmap (dpy, gc->gcv.clip_mask);
1456     CGImageRelease (gc->clip_mask);
1457   }
1458 */
1459   free (gc);
1460   return 0;
1461 }
1462
1463
1464 /*
1465 static void
1466 flipbits (unsigned const char *in, unsigned char *out, int length)
1467 {
1468   static const unsigned char table[256] = {
1469     0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 
1470     0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 
1471     0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 
1472     0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 
1473     0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 
1474     0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 
1475     0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 
1476     0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
1477     0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 
1478     0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
1479     0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 
1480     0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 
1481     0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 
1482     0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
1483     0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 
1484     0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 
1485     0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 
1486     0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 
1487     0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 
1488     0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
1489     0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 
1490     0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 
1491     0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 
1492     0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 
1493     0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 
1494     0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
1495     0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1496     0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 
1497     0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 
1498     0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
1499     0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 
1500     0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1501   };
1502   while (length-- > 0)
1503     *out++ = table[*in++];
1504 }
1505 */
1506
1507
1508 int
1509 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1510            int src_x, int src_y, int dest_x, int dest_y,
1511            unsigned int w, unsigned int h)
1512 {
1513   jwxyz_assert_display (dpy);
1514  
1515   const XRectangle *wr = jwxyz_frame (d);
1516
1517   Assert (gc, "no GC");
1518   Assert ((w < 65535), "improbably large width");
1519   Assert ((h < 65535), "improbably large height");
1520   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
1521   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
1522   Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1523   Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1524
1525   // Clip width and height to the bounds of the Drawable
1526   //
1527   if (dest_x + w > wr->width) {
1528     if (dest_x > wr->width)
1529       return 0;
1530     w = wr->width - dest_x;
1531   }
1532   if (dest_y + h > wr->height) {
1533     if (dest_y > wr->height)
1534       return 0;
1535     h = wr->height - dest_y;
1536   }
1537   if (w <= 0 || h <= 0)
1538     return 0;
1539
1540   // Clip width and height to the bounds of the XImage
1541   //
1542   if (src_x + w > ximage->width) {
1543     if (src_x > ximage->width)
1544       return 0;
1545     w = ximage->width - src_x;
1546   }
1547   if (src_y + h > ximage->height) {
1548     if (src_y > ximage->height)
1549       return 0;
1550     h = ximage->height - src_y;
1551   }
1552   if (w <= 0 || h <= 0)
1553     return 0;
1554
1555   /* Assert (d->win */
1556
1557   if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
1558     return 0;
1559
1560   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1561   int bpl = ximage->bytes_per_line;
1562   int bpp = ximage->bits_per_pixel;
1563   /* int bsize = bpl * h; */
1564   char *data = ximage->data;
1565
1566 /*
1567   CGRect r;
1568   r.origin.x = wr->x + dest_x;
1569   r.origin.y = wr->y + wr->height - dest_y - h;
1570   r.size.width = w;
1571   r.size.height = h;
1572 */
1573
1574   Assert (gc->gcv.function == GXcopy, "XPutImage: (TODO) GC function not supported");
1575   Assert (!gc->gcv.clip_mask, "XPutImage: (TODO) GC clip mask not supported");
1576   
1577   if (bpp == 32) {
1578
1579     /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1580        to create a CGImage from a sub-rectagle of the XImage.
1581      */
1582     data += (src_y * bpl) + (src_x * 4);
1583
1584     jwxyz_assert_display(dpy);
1585     
1586     /* There probably won't be any hacks that do this, but... */
1587     Assert (!(bpl % 4), "XPutImage: bytes_per_line not divisible by four.");
1588     
1589     unsigned src_w = bpl / 4;
1590
1591     /* GL_UNPACK_ROW_LENGTH is not allowed to be negative. (sigh) */
1592 # ifndef HAVE_JWZGLES
1593     glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
1594     src_w = w;
1595 # endif
1596
1597     glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1598
1599 # if 1 // defined HAVE_JWZGLES
1600     // Regular OpenGL uses GL_TEXTURE_RECTANGLE_EXT in place of GL_TEXTURE_2D.
1601     // TODO: Make use of OES_draw_texture.
1602     // TODO: Coords might be wrong; things might be upside-down or backwards
1603     //       or whatever.
1604
1605     unsigned tex_w = src_w, tex_h = h;
1606     if (!dpy->gl_texture_npot_p) {
1607       tex_w = to_pow2(tex_w);
1608       tex_h = to_pow2(tex_h);
1609     }
1610
1611     GLint internalformat = texture_internalformat(dpy);
1612
1613     glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
1614
1615     if (tex_w == src_w && tex_h == h) {
1616       glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
1617                     0, dpy->screen->pixel_format, gl_pixel_type(dpy), data);
1618     } else {
1619       // TODO: Sampling the last row might be a problem if src_x != 0.
1620       glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
1621                     0, dpy->screen->pixel_format, gl_pixel_type(dpy), NULL);
1622       glTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, src_w, h,
1623                        dpy->screen->pixel_format, gl_pixel_type(dpy), data);
1624     }
1625     
1626     set_white ();
1627     // glEnable (dpy->gl_texture_target);
1628     // glColor4f (0.5, 0, 1, 1);
1629     glEnable (dpy->gl_texture_target);
1630     glEnableClientState (GL_VERTEX_ARRAY);
1631     glEnableClientState (GL_TEXTURE_COORD_ARRAY);
1632
1633     // TODO: Why are these ever turned on in the first place?
1634     glDisableClientState (GL_COLOR_ARRAY);
1635     glDisableClientState (GL_NORMAL_ARRAY);
1636     // glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1637
1638     GLfloat vertices[4][2] =
1639     {
1640       {dest_x, dest_y},
1641       {dest_x, dest_y + h},
1642       {dest_x + w, dest_y + h},
1643       {dest_x + w, dest_y}
1644     };
1645
1646     GLfloat texcoord_w, texcoord_h;
1647 #  ifndef HAVE_JWZGLES
1648     if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT) {
1649       texcoord_w = w;
1650       texcoord_h = h;
1651     } else
1652 #  endif /* HAVE_JWZGLES */
1653     {
1654       texcoord_w = (double)w / tex_w;
1655       texcoord_h = (double)h / tex_h;
1656     }
1657
1658     GLfloat tex_coords[4][2];
1659     tex_coords[0][0] = 0;
1660     tex_coords[0][1] = 0;
1661     tex_coords[1][0] = 0;
1662     tex_coords[1][1] = texcoord_h;
1663     tex_coords[2][0] = texcoord_w;
1664     tex_coords[2][1] = texcoord_h;
1665     tex_coords[3][0] = texcoord_w;
1666     tex_coords[3][1] = 0;
1667
1668     glVertexPointer (2, GL_FLOAT, 0, vertices);
1669     glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
1670
1671     // Respect the alpha channel in the XImage if we're using alpha.
1672     if (gc->gcv.alpha_allowed_p) {
1673       glEnable (GL_BLEND);
1674       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1675     }
1676
1677     glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1678
1679     if (gc->gcv.alpha_allowed_p)
1680       glDisable (GL_BLEND);
1681
1682 //  clear_texture();
1683     glDisable (dpy->gl_texture_target);
1684 # else
1685     glRasterPos2i (dest_x, dest_y);
1686     glPixelZoom (1, -1);
1687     jwxyz_assert_display (dpy);
1688     glDrawPixels (w, h, dpy->screen->pixel_format, gl_pixel_type(dpy), data);
1689 # endif
1690   } else {   // (bpp == 1)
1691
1692     // Assert(FALSE, "XPutImage: TODO");
1693     // Check out ximage_(get|put)pixel_1
1694     
1695 #if 0
1696     /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1697
1698        #### However, the bit order within a byte in a 1bpp XImage is
1699             the wrong way around from what Quartz expects, so first we
1700             have to copy the data to reverse it.  Shit!  Maybe it
1701             would be worthwhile to go through the hacks and #ifdef
1702             each one that diddles 1bpp XImage->data directly...
1703      */
1704     Assert ((src_x % 8) == 0,
1705             "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1706
1707     data += (src_y * bpl) + (src_x / 8);   // move to x,y within the data
1708     unsigned char *flipped = (unsigned char *) malloc (bsize);
1709
1710     flipbits ((unsigned char *) data, flipped, bsize);
1711
1712     CGDataProviderRef prov = 
1713       CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1714     CGImageRef mask = CGImageMaskCreate (w, h, 
1715                                          1, bpp, bpl,
1716                                          prov,
1717                                          NULL,  /* decode[] */
1718                                          GL_FALSE); /* interpolate */
1719     push_fg_gc (dpy, d, gc, GL_TRUE);
1720
1721     CGContextFillRect (cgc, r);                         // foreground color
1722     CGContextClipToMask (cgc, r, mask);
1723     set_color (dpy, cgc, gc->gcv.background, gc->depth, GL_FALSE, GL_TRUE);
1724     CGContextFillRect (cgc, r);                         // background color
1725     pop_gc (d, gc);
1726
1727     free (flipped);
1728     CGDataProviderRelease (prov);
1729     CGImageRelease (mask);
1730 #endif
1731   }
1732  
1733   jwxyz_assert_gl ();
1734   invalidate_drawable_cache (d);
1735
1736   return 0;
1737 }
1738
1739 /* At the moment only XGetImage and get_xshm_image use XGetSubImage. */
1740 /* #### Twang calls XGetImage on the window intending to get a
1741    buffer full of black.  This is returning a buffer full of white
1742    instead of black for some reason. */
1743 XImage *
1744 XGetSubImage (Display *dpy, Drawable d, int x, int y,
1745               unsigned int width, unsigned int height,
1746               unsigned long plane_mask, int format,
1747               XImage *dest_image, int dest_x, int dest_y)
1748 {
1749   Assert ((width  < 65535), "improbably large width");
1750   Assert ((height < 65535), "improbably large height");
1751   Assert ((x < 65535 && x > -65535), "improbably large x");
1752   Assert ((y < 65535 && y > -65535), "improbably large y");
1753
1754   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1755   
1756   // TODO: What if this reads off the edge? What is supposed to happen?
1757
1758   {
1759     // In case the caller tries to write off the edge.
1760     int
1761       max_width = dest_image->width - dest_x,
1762       max_height = dest_image->height - dest_y;
1763
1764     if (width > max_width) {
1765       width = max_width;
1766     }
1767     
1768     if (height > max_height) {
1769       height = max_height;
1770     }
1771   }
1772   
1773   Assert (jwxyz_drawable_depth (d) == dest_image->depth, "XGetSubImage: depth mismatch");
1774   
1775   if (dest_image->depth == visual_depth (NULL, NULL)) {
1776     Assert (!(dest_image->bytes_per_line % 4), "XGetSubImage: bytes_per_line not divisible by 4");
1777     unsigned pixels_per_line = dest_image->bytes_per_line / 4;
1778 #ifdef HAVE_JWZGLES
1779     Assert (pixels_per_line == width, "XGetSubImage: (TODO) pixels_per_line != width");
1780 #else
1781     glPixelStorei (GL_PACK_ROW_LENGTH, pixels_per_line);
1782 #endif
1783     glPixelStorei (GL_PACK_ALIGNMENT, 4);
1784     
1785     uint32_t *dest_data = (uint32_t *)dest_image->data + pixels_per_line * dest_y + dest_x;
1786     
1787     glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
1788                   dpy->screen->pixel_format, gl_pixel_type(dpy), dest_data);
1789
1790     /* Flip this upside down. :( */
1791     uint32_t *top = dest_data;
1792     uint32_t *bottom = dest_data + pixels_per_line * (height - 1);
1793     for (unsigned y = height / 2; y; --y) {
1794       for (unsigned x = 0; x != width; ++x) {
1795         uint32_t px = top[x];
1796         top[x] = bottom[x];
1797         bottom[x] = px;
1798       }
1799       top += pixels_per_line;
1800       bottom -= pixels_per_line;
1801     }
1802   } else {
1803
1804     /* TODO: Actually get pixels. */
1805
1806     Assert (!(dest_x % 8), "XGetSubImage: dest_x not byte-aligned");
1807     uint8_t *dest_data =
1808       (uint8_t *)dest_image->data + dest_image->bytes_per_line * dest_y
1809       + dest_x / 8;
1810     for (unsigned y = height / 2; y; --y) {
1811       memset (dest_data, y & 1 ? 0x55 : 0xAA, width / 8);
1812       dest_data += dest_image->bytes_per_line;
1813     }
1814   }
1815
1816   return dest_image;
1817 }
1818
1819
1820 /* Returns a transformation matrix to do rotation as per the provided
1821    EXIF "Orientation" value.
1822  */
1823 /*
1824 static CGAffineTransform
1825 exif_rotate (int rot, CGSize rect)
1826 {
1827   CGAffineTransform trans = CGAffineTransformIdentity;
1828   switch (rot) {
1829   case 2:               // flip horizontal
1830     trans = CGAffineTransformMakeTranslation (rect.width, 0);
1831     trans = CGAffineTransformScale (trans, -1, 1);
1832     break;
1833
1834   case 3:               // rotate 180
1835     trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1836     trans = CGAffineTransformRotate (trans, M_PI);
1837     break;
1838
1839   case 4:               // flip vertical
1840     trans = CGAffineTransformMakeTranslation (0, rect.height);
1841     trans = CGAffineTransformScale (trans, 1, -1);
1842     break;
1843
1844   case 5:               // transpose (UL-to-LR axis)
1845     trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
1846     trans = CGAffineTransformScale (trans, -1, 1);
1847     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1848     break;
1849
1850   case 6:               // rotate 90
1851     trans = CGAffineTransformMakeTranslation (0, rect.width);
1852     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1853     break;
1854
1855   case 7:               // transverse (UR-to-LL axis)
1856     trans = CGAffineTransformMakeScale (-1, 1);
1857     trans = CGAffineTransformRotate (trans, M_PI / 2);
1858     break;
1859
1860   case 8:               // rotate 270
1861     trans = CGAffineTransformMakeTranslation (rect.height, 0);
1862     trans = CGAffineTransformRotate (trans, M_PI / 2);
1863     break;
1864
1865   default: 
1866     break;
1867   }
1868
1869   return trans;
1870 }
1871 */
1872
1873 void
1874 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, 
1875                                 Bool nsimg_p, void *img_arg,
1876                                XRectangle *geom_ret, int exif_rotation)
1877 {
1878   Assert (False, "jwxyz_draw_NSImage_or_CGImage: TODO stub");
1879 #if 0
1880   CGImageRef cgi;
1881 # ifndef USE_IPHONE
1882   CGImageSourceRef cgsrc;
1883 # endif // USE_IPHONE
1884   NSSize imgr;
1885
1886   CGContextRef cgc = d->cgc;
1887
1888   if (nsimg_p) {
1889
1890     NSImage *nsimg = (NSImage *) img_arg;
1891     imgr = [nsimg size];
1892
1893 # ifndef USE_IPHONE
1894     // convert the NSImage to a CGImage via the toll-free-bridging 
1895     // of NSData and CFData...
1896     //
1897     NSData *nsdata = [NSBitmapImageRep
1898                        TIFFRepresentationOfImageRepsInArray:
1899                          [nsimg representations]];
1900     CFDataRef cfdata = (CFDataRef) nsdata;
1901     cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
1902     cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
1903 # else  // USE_IPHONE
1904     cgi = nsimg.CGImage;
1905 # endif // USE_IPHONE
1906
1907   } else {
1908     cgi = (CGImageRef) img_arg;
1909     imgr.width  = CGImageGetWidth (cgi);
1910     imgr.height = CGImageGetHeight (cgi);
1911   }
1912
1913   Bool rot_p = (exif_rotation >= 5);
1914
1915   if (rot_p)
1916     imgr = NSMakeSize (imgr.height, imgr.width);
1917
1918   CGRect winr = d->frame;
1919   float rw = winr.size.width  / imgr.width;
1920   float rh = winr.size.height / imgr.height;
1921   float r = (rw < rh ? rw : rh);
1922
1923   CGRect dst, dst2;
1924   dst.size.width  = imgr.width  * r;
1925   dst.size.height = imgr.height * r;
1926   dst.origin.x = (winr.size.width  - dst.size.width)  / 2;
1927   dst.origin.y = (winr.size.height - dst.size.height) / 2;
1928
1929   dst2.origin.x = dst2.origin.y = 0;
1930   if (rot_p) {
1931     dst2.size.width = dst.size.height; 
1932     dst2.size.height = dst.size.width;
1933   } else {
1934     dst2.size = dst.size;
1935   }
1936
1937   // Clear the part not covered by the image to background or black.
1938   //
1939   if (d->type == WINDOW)
1940     XClearWindow (dpy, d);
1941   else {
1942     jwxyz_fill_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height,
1943                      drawable_depth (d) == 1 ? 0 : BlackPixel(dpy,0));
1944   }
1945
1946   CGAffineTransform trans = 
1947     exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
1948
1949   CGContextSaveGState (cgc);
1950   CGContextConcatCTM (cgc, 
1951                       CGAffineTransformMakeTranslation (dst.origin.x,
1952                                                         dst.origin.y));
1953   CGContextConcatCTM (cgc, trans);
1954   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1955   CGContextDrawImage (cgc, dst2, cgi);
1956   CGContextRestoreGState (cgc);
1957
1958 # ifndef USE_IPHONE
1959   if (nsimg_p) {
1960     CFRelease (cgsrc);
1961     CGImageRelease (cgi);
1962   }
1963 # endif // USE_IPHONE
1964
1965   if (geom_ret) {
1966     geom_ret->x = dst.origin.x;
1967     geom_ret->y = dst.origin.y;
1968     geom_ret->width  = dst.size.width;
1969     geom_ret->height = dst.size.height;
1970   }
1971
1972   invalidate_drawable_cache (d);
1973 #endif
1974 }
1975
1976 #ifndef HAVE_JWZGLES
1977
1978 /*
1979 static void
1980 create_rectangle_texture (GLuint *texture)
1981 {
1982   glGenTextures(1, texture);
1983   glBindTexture(GL_TEXTURE_RECTANGLE_EXT, *texture);
1984   glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1985   glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1986 }
1987 */
1988
1989 #endif
1990
1991
1992 #if 0
1993 static Pixmap
1994 copy_pixmap (Display *dpy, Pixmap p)
1995 {
1996   if (!p) return 0;
1997   Assert (p->type == PIXMAP, "not a pixmap");
1998
1999   Pixmap p2 = 0;
2000
2001   Window root;
2002   int x, y;
2003   unsigned int width, height, border_width, depth;
2004   if (XGetGeometry (dpy, p, &root,
2005                     &x, &y, &width, &height, &border_width, &depth)) {
2006     XGCValues gcv;
2007     gcv.function = GXcopy;
2008     GC gc = XCreateGC (dpy, p, GCFunction, &gcv);
2009     if (gc) {
2010       p2 = XCreatePixmap (dpy, p, width, height, depth);
2011       if (p2)
2012         XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0);
2013       XFreeGC (dpy, gc);
2014     }
2015   }
2016
2017   Assert (p2, "could not copy pixmap");
2018
2019   return p2;
2020 }
2021 #endif
2022
2023
2024 int
2025 jwxyz_draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2026                    const char *str, size_t len, int utf8_p)
2027 {
2028   Font ff = gc->gcv.font;
2029   XCharStruct cs;
2030
2031   char *data = 0;
2032   jwxyz_render_text (dpy, jwxyz_native_font (ff), str, len, utf8_p, &cs, &data);
2033   int w = cs.rbearing - cs.lbearing;
2034   int h = cs.ascent + cs.descent;
2035
2036   if (w < 0 || h < 0) abort();
2037   if (w == 0 || h == 0) {
2038     if (data) free(data);
2039     return 0;
2040   }
2041
2042   XImage *img = XCreateImage (dpy, dpy->screen->visual, 32,
2043                               ZPixmap, 0, data, w, h, 0, 0);
2044
2045   /* The image of text is a 32-bit image, in white.
2046      Take the red channel for intensity and use that as alpha.
2047      replace RGB with the GC's foreground color.
2048      This expects that XPutImage respects alpha and only writes
2049      the bits that are not masked out.
2050      This also assumes that XPutImage expects ARGB.
2051    */
2052   {
2053     char *s = data;
2054     char *end = s + (w * h * 4);
2055     uint8_t rgba[4];
2056     jwxyz_query_color (dpy, gc->gcv.foreground, rgba);
2057     while (s < end) {
2058
2059       s[3] = s[1];
2060       s[0] = rgba[0];
2061       s[1] = rgba[1];
2062       s[2] = rgba[2];
2063       s += 4;
2064     }
2065   }
2066
2067   {
2068     Bool old_alpha = gc->gcv.alpha_allowed_p;
2069     jwxyz_XSetAlphaAllowed (dpy, gc, True);
2070     XPutImage (dpy, d, gc, img, 0, 0,
2071                x + cs.lbearing,
2072                y - cs.ascent,
2073                w, h);
2074     jwxyz_XSetAlphaAllowed (dpy, gc, old_alpha);
2075     XDestroyImage (img);
2076   }
2077
2078   return 0;
2079 }
2080
2081
2082 int
2083 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2084 {
2085 //####  abort();
2086 /*
2087   TODO
2088
2089   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2090
2091   if (gc->gcv.clip_mask) {
2092     XFreePixmap (dpy, gc->gcv.clip_mask);
2093     CGImageRelease (gc->clip_mask);
2094   }
2095
2096   gc->gcv.clip_mask = copy_pixmap (dpy, m);
2097   if (gc->gcv.clip_mask)
2098     gc->clip_mask =
2099       CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2100   else
2101     gc->clip_mask = 0;
2102 */
2103   
2104   return 0;
2105 }
2106
2107 int
2108 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2109 {
2110   gc->gcv.clip_x_origin = x;
2111   gc->gcv.clip_y_origin = y;
2112   return 0;
2113 }
2114
2115 void set_points_list(XPoint *points, int npoints, linked_point *root)
2116 {
2117     linked_point *current;  
2118
2119     current = root;
2120     for (int i = 0; i < npoints - 2 ; i++) {
2121         current->x = points[i].x;
2122         current->y = points[i].y;
2123         current->next = (linked_point *) malloc(sizeof(linked_point)); 
2124         current = current->next;
2125     }
2126     current->x = points[npoints-2].x;
2127     current->y = points[npoints-2].y;
2128     current->next = root;
2129 }
2130
2131
2132 double compute_edge_length(linked_point * a, linked_point * b)
2133 {
2134
2135     int xdiff, ydiff, xsq, ysq, added;
2136     double xy_add, edge_length;
2137
2138     xdiff = a->x - b->x;
2139     ydiff = a->y - b->y;
2140     xsq = xdiff * xdiff;
2141     ysq = ydiff * ydiff;
2142     added = xsq + ysq;
2143     xy_add = (double) added;
2144     edge_length = sqrt(xy_add);
2145     return edge_length;
2146 }
2147
2148 double get_angle(double a, double b, double c)
2149 {
2150     double cos_a, i_cos_a;
2151     cos_a = (((b * b) + (c * c)) - (a * a)) / (double) (2.0 * b * c);
2152     i_cos_a = acos(cos_a);
2153     return i_cos_a;
2154 }
2155
2156
2157 Bool is_same_slope(linked_point * a)
2158 {
2159
2160     int abx, bcx, aby, bcy, aa, bb;
2161     linked_point *b;
2162     linked_point *c;
2163
2164     b = a->next;
2165     c = b->next;
2166
2167     // test if slopes are indefinite for both line segments
2168     if (a->x == b->x) {
2169         return b->x == c->x;
2170     } else if (b->x == c->x) {
2171         return False;   // false, as ax/bx is not indefinite
2172     }
2173
2174     abx = a->x - b->x;
2175     bcx = b->x - c->x;
2176     aby = a->y - b->y;
2177     bcy = b->y - c->y;
2178     aa = abx * bcy;
2179     bb = bcx * aby;
2180
2181     return aa == bb;
2182 }
2183
2184 void draw_three_vertices(linked_point * a, Bool triangle)
2185 {
2186
2187     linked_point *b;
2188     linked_point *c;
2189     GLenum drawType;
2190
2191     b = a->next;
2192     c = b->next;
2193
2194     GLfloat vertices[3][2] = {
2195         {a->x, a->y},
2196         {b->x, b->y},
2197         {c->x, c->y}
2198     };
2199
2200     if (triangle) {
2201         drawType = GL_TRIANGLES;
2202     } else {
2203         drawType = GL_LINES;
2204     }
2205
2206     glEnableClientState(GL_VERTEX_ARRAY);
2207     glVertexPointer(2, GL_FLOAT, 0, vertices);
2208     glDrawArrays(drawType, 0, 3);
2209
2210     free(b);  // cut midpoint off from remaining polygon vertex list
2211     a->next = c;
2212 }
2213
2214
2215 Bool is_an_ear(linked_point * a)
2216 {
2217     double edge_ab, edge_bc, edge_ac;
2218     double angle_a, angle_b, angle_c;
2219     double my_pi;
2220     linked_point *b, *c;
2221
2222     b = a->next;
2223     c = b->next;
2224     my_pi = (double) M_PI;
2225
2226     edge_ab = compute_edge_length(a, b);
2227     edge_bc = compute_edge_length(b, c);
2228     edge_ac = compute_edge_length(a, c);
2229     angle_a = get_angle(edge_bc, edge_ab, edge_ac);
2230     angle_b = get_angle(edge_ac, edge_ab, edge_bc);
2231     angle_c = get_angle(edge_ab, edge_ac, edge_bc);
2232
2233     return angle_a < my_pi && angle_b < my_pi && angle_c < my_pi;
2234 }
2235
2236
2237 Bool is_three_point_loop(linked_point * head)
2238 {
2239     return head->x == head->next->next->next->x
2240         && head->y == head->next->next->next->y;
2241 }
2242
2243
2244 void traverse_points_list(linked_point * root)
2245 {
2246     linked_point *head;
2247     head = root;
2248
2249     while (!is_three_point_loop(head)) {
2250         if (is_an_ear(head)) {
2251             draw_three_vertices(head, True);
2252         } else if (is_same_slope(head)) {
2253             draw_three_vertices(head, False);
2254         } else {
2255             head = head->next;
2256         }
2257     }
2258
2259     // handle final three vertices in polygon
2260     if (is_an_ear(head)) {
2261         draw_three_vertices(head, True);
2262     } else if (is_same_slope(head)) {
2263         draw_three_vertices(head, False);
2264     } else {
2265         free(head->next->next);
2266         free(head->next);
2267         free(head);
2268         Assert (False, "traverse_points_list: unknown configuration");
2269     }
2270
2271     free(head->next);
2272     free(head);
2273 }
2274
2275
2276
2277 #endif /* JWXYZ_GL -- entire file */