1 /* xscreensaver, Copyright (c) 1991-2017 Jamie Zawinski <jwz@jwz.org>
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
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.
18 This is the version of jwxyz for Android. The version used by MacOS
19 and iOS is in jwxyz.m.
22 /* Be advised, this is all very much a work in progress.
24 TODO: The following should be implemented before OpenGL can be considered
26 - Above all, pick the smallest not-yet working hack that utilizes the
28 - Half-ass the drawing functions.
29 - [OK] What Interference needs
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)
37 - [OK] Get DangerBall going.
39 - [Interference, so far...] And fast, too.
40 - And text really needs to work for the FPS display. */
42 /* Also, Take note that OS X can actually run with 256 colors. */
45 - malloc error checking
46 - Check max texture sizes for XGet/PutImage, XCopyArea.
47 - Optional 5:5:5 16-bit color
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
58 - OS X: Drawable objects, including: pixel buffers and offscreen
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?
73 - iOS: Use OES_framebuffer_object, it's always present.
76 /* OpenGL hacks call a number of X11 functions, including
77 * XCopyArea, XDrawString, XGetImage
78 * XCreatePixmap, XCreateGC, XCreateImage
80 * Check these, of course. */
82 #ifdef JWXYZ_GL /* entire file */
94 # import <QuartzCore/QuartzCore.h>
95 # include <OpenGLES/ES1/gl.h>
96 # include <OpenGLES/ES1/glext.h>
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
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
119 # define NSOpenGLContext EAGLContext
122 # include <OpenGL/glu.h>
125 /* TODO: Does this work on iOS? */
126 # ifndef HAVE_JWZGLES
129 # include <GLES/gl.h>
130 # include <GLES/glext.h>
135 # include "jwzglesI.h"
139 # include <android/log.h>
143 #include "jwxyz-timers.h"
144 #include "yarandom.h"
149 #if defined HAVE_COCOA
150 # include <CoreGraphics/CGGeometry.h>
157 typedef struct CGPoint CGPoint;
163 typedef struct CGSize CGSize;
169 typedef struct CGRect CGRect;
175 # define MAX(a,b) ((a)>(b)?(a):(b))
176 # define MIN(a,b) ((a)<(b)?(a):(b))
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... */
186 struct jwxyz_Display {
189 struct jwxyz_sources_data *timers_data;
191 Bool gl_texture_npot_p;
192 /* Bool opengl_core_p */;
193 GLenum gl_texture_target;
195 // #if defined USE_IPHONE
196 GLuint rect_texture; // Also can work on the desktop.
199 unsigned long window_background;
202 struct jwxyz_Screen {
204 GLenum pixel_format, pixel_type;
205 unsigned long black, white;
212 // CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
215 struct jwxyz_XFontSet {
219 struct jwxyz_linked_point {
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.
229 jwxyz_alloc_color (Display *dpy,
230 uint16_t r, uint16_t g, uint16_t b, uint16_t a)
232 union color_bytes color;
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;
242 color.bytes[0] = r >> 8;
243 color.bytes[1] = g >> 8;
244 color.bytes[2] = b >> 8;
245 color.bytes[3] = a >> 8;
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);
253 Assert(dpy->screen->pixel_format == GL_RGBA,
254 "jwxyz_alloc_color: Unknown pixel_format");
257 return (uint32_t)color.pixel;
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'.
263 jwxyz_query_color (Display *dpy, unsigned long pixel, uint8_t *rgba)
265 union color_bytes color;
267 if(dpy->screen->pixel_format == GL_RGBA)
270 for (unsigned i = 0; i != 4; ++i)
271 rgba[i] = color.bytes[i];
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;
286 jwxyz_assert_display(Display *dpy)
291 jwxyz_assert_drawable (dpy->main_window, dpy->main_window);
296 jwxyz_set_matrices (Display *dpy, unsigned width, unsigned height,
299 Assert (width, "no width");
300 Assert (height, "no height");
302 /* TODO: Check registration pattern from Interference with rectangles instead of points. */
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);
309 # if defined(USE_IPHONE) || defined(HAVE_ANDROID)
311 if (window_p && ignore_rotation_p(dpy)) {
312 int o = (int) current_device_rotation();
313 glRotatef (-o, 0, 0, 1);
316 // glPointSize(1); // This is the default.
319 glOrthof /* TODO: Is glOrthox worth it? Signs point to no. */
323 (0, width, height, 0, -1, 1);
325 glMatrixMode(GL_MODELVIEW);
326 # endif // HAVE_MOBILE
333 // iOS always uses OpenGL ES 1.1.
339 gl_check_ver (const struct gl_version *caps,
343 return caps->major > gl_major ||
344 (caps->major == gl_major && caps->minor >= gl_minor);
348 static GLboolean gl_check_ext(const struct gl_caps *caps,
351 const char *extension)
354 gl_check_ver(caps, gl_major, gl_minor) ||
355 gluCheckExtension(extension, caps->extensions);
362 // NSOpenGLContext *jwxyz_debug_context;
366 jwxyz_make_display (Window w)
368 Display *d = (Display *) calloc (1, sizeof(*d));
369 d->screen = (Screen *) calloc (1, sizeof(Screen));
372 # ifndef HAVE_JWZGLES
373 struct gl_version version;
376 const GLubyte *version_str = glGetString (GL_VERSION);
378 /* iPhone is always OpenGL ES 1.1. */
379 if (sscanf ((const char *) version_str, "%u.%u",
380 &version.major, &version.minor) < 2)
386 # endif // !HAVE_JWZGLES
388 const GLubyte *extensions = glGetString (GL_EXTENSIONS);
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
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.
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",
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;
414 d->screen->pixel_format = GL_RGBA;
415 d->screen->pixel_type = GL_UNSIGNED_BYTE;
417 // GL_ABGR_EXT/GL_UNSIGNED_BYTE is another possibilty that may have made more
419 # endif // !HAVE_JWZGLES
421 // On really old systems, it would make sense to split the texture
423 # ifndef HAVE_JWZGLES
424 d->gl_texture_npot_p = gluCheckExtension ((const GLubyte *)
425 "GL_ARB_texture_rectangle",
427 d->gl_texture_target = d->gl_texture_npot_p ?
428 GL_TEXTURE_RECTANGLE_EXT :
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);
438 d->gl_texture_target = GL_TEXTURE_2D;
441 d->screen->black = jwxyz_alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF);
442 d->screen->white = jwxyz_alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
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;
451 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
453 d->window_background = BlackPixel(d,0);
457 fputs((char *)glGetString(GL_VENDOR), stderr);
459 fputs((char *)glGetString(GL_RENDERER), stderr);
461 fputs((char *)glGetString(GL_VERSION), 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);
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
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);
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
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);
493 jwxyz_assert_display(d);
498 jwxyz_free_display (Display *dpy)
500 /* TODO: Go over everything. */
502 jwxyz_sources_free (dpy->timers_data);
504 free (dpy->screen->visual);
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.
515 invalidate_drawable_cache (Drawable d)
517 /* TODO: Kill this outright. jwxyz_bind_drawable handles any potential
524 CGImageRelease (d->cgi);
531 /* Call this when the View changes size or position.
534 jwxyz_window_resized (Display *dpy)
536 const XRectangle *new_frame = jwxyz_frame (dpy->main_window);
537 unsigned new_width = new_frame->width;
538 unsigned new_height = new_frame->height;
540 Assert (new_width, "jwxyz_window_resized: No width.");
541 Assert (new_height, "jwxyz_window_resized: No height.");
543 /*if (cgc) w->cgc = cgc;
544 Assert (w->cgc, "no CGContext"); */
546 Log("resize: %d, %d\n", new_width, new_height);
548 jwxyz_bind_drawable (dpy, dpy->main_window, dpy->main_window);
550 // TODO: What does the iPhone need?
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);
558 glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
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);
567 glDrawBuffer (draw_buffer); */
569 // Stylish and attractive purple!
570 // glClearColor (1, 0, 1, 0.5);
571 // glClear (GL_COLOR_BUFFER_BIT);
573 invalidate_drawable_cache (dpy->main_window);
578 display_sources_data (Display *dpy)
580 return dpy->timers_data;
585 XRootWindow (Display *dpy, int screen)
587 return dpy ? dpy->main_window : 0;
591 XDefaultScreenOfDisplay (Display *dpy)
593 return dpy ? dpy->screen : 0;
597 XDefaultVisualOfScreen (Screen *screen)
599 return screen ? screen->visual : 0;
603 XDisplayOfScreen (Screen *s)
605 return s ? s->dpy : 0;
609 XDisplayNumberOfScreen (Screen *s)
615 XScreenNumberOfScreen (Screen *s)
621 XBlackPixelOfScreen(Screen *screen)
623 return screen->black;
627 XWhitePixelOfScreen(Screen *screen)
629 return screen->white;
633 XCellsOfScreen(Screen *screen)
635 Visual *v = screen->visual;
636 return v->red_mask | v->green_mask | v->blue_mask;
640 /* GC attributes by usage and OpenGL implementation:
642 * All drawing functions:
643 * function | glLogicOp w/ GL_COLOR_LOGIC_OP
644 * clip_x_origin, clip_y_origin, clip_mask | Stencil mask
646 * Shape drawing functions:
647 * foreground, background | glColor*
649 * XDrawLines, XDrawRectangles, XDrawSegments:
650 * line_width, cap_style, join_style | Lotsa vertices
653 * fill_rule | Multiple GL_TRIANGLE_FANs
656 * font | Cocoa, then OpenGL display lists.
658 * alpha_allowed_p | TODO
666 set_clip_mask (GC gc)
668 Assert (!gc->gcv.clip_mask, "set_gc: TODO");
672 set_function (int function)
674 Assert (function == GXcopy, "set_gc: (TODO) Stubbed gcv function");
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.
681 switch (gc->gcv.function) {
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;
695 set_color (Display *dpy, unsigned long pixel, unsigned int depth,
696 Bool alpha_allowed_p)
698 jwxyz_validate_pixel (dpy, pixel, depth, alpha_allowed_p);
702 glColor4f (f, f, f, 1);
704 /* TODO: alpha_allowed_p */
706 jwxyz_query_color (dpy, pixel, rgba);
708 glColor4f (rgba[0] / 255.0f, rgba[1] / 255.0f,
709 rgba[2] / 255.0f, rgba[3] / 255.0f);
716 /* Pushes a GC context; sets Function, ClipMask, and color.
719 set_color_gc (Display *dpy, GC gc, unsigned long color)
721 // GC is NULL for XClearArea and XClearWindow.
725 function = gc->gcv.function;
730 depth = visual_depth (NULL, NULL);
731 // TODO: Set null clip mask here.
734 set_function (function);
737 case GXset: color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
738 case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
741 set_color (dpy, color, depth, gc ? gc->gcv.alpha_allowed_p : False);
743 /* TODO: Antialiasing. */
744 /* CGContextSetShouldAntialias (cgc, antialias_p); */
747 /* Pushes a GC context; sets color to the foreground color.
750 set_fg_gc (Display *dpy, GC gc)
752 set_color_gc (dpy, gc, gc->gcv.foreground);
756 next_point(short *v, XPoint p, int mode)
759 case CoordModeOrigin:
763 case CoordModePrevious:
768 Assert (False, "next_point: bad mode");
774 XDrawPoints (Display *dpy, Drawable d, GC gc,
775 XPoint *points, int count, int mode)
777 jwxyz_bind_drawable (dpy, dpy->main_window, d);
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);
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];
800 glMatrixMode (GL_MODELVIEW);
801 glTranslatef (0.5, 0.5, 0);
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);
817 texture_internalformat(Display *dpy)
820 return dpy->screen->pixel_format;
826 static GLenum gl_pixel_type(const Display *dpy)
828 return dpy->screen->pixel_type;
832 clear_texture (Display *dpy)
834 glTexImage2D (dpy->gl_texture_target, 0, texture_internalformat(dpy), 0, 0,
835 0, dpy->screen->pixel_format, gl_pixel_type (dpy), NULL);
838 static void set_white (void)
841 glColor4f (1, 1, 1, 1);
843 glColor3ub (0xff, 0xff, 0xff);
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)
854 # if defined HAVE_COCOA && !defined USE_IPHONE
855 /* TODO: Does this help? */
859 /* TODO: Fix TestX11 + mode_preserve with this one. */
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);
867 GLint internalformat = texture_internalformat(dpy);
869 glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
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);
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);
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)
887 Assert(gc->gcv.function == GXcopy, "XCopyArea: Unknown function");
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);
896 glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
898 jwxyz_gl_draw_image (dpy->gl_texture_target, tex_w, tex_h, 0, 0,
899 width, height, dst_x, dst_y);
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)
913 glEnable (gl_texture_target);
915 glEnableClientState (GL_TEXTURE_COORD_ARRAY);
916 glEnableClientState (GL_VERTEX_ARRAY);
918 /* TODO: Copied from XPutImage. Refactor. */
919 /* TODO: EXT_draw_texture or whatever it's called. */
920 GLfloat vertices[4][2] =
923 {dst_x, dst_y + height},
924 {dst_x + width, dst_y + height},
925 {dst_x + width, dst_y}
929 tex_x0 = src_x, tex_y0 = src_y + height,
930 tex_x1 = src_x + width, tex_y1 = src_y;
932 # ifndef HAVE_JWZGLES
933 if (gl_texture_target != GL_TEXTURE_RECTANGLE_EXT)
936 GLfloat mx = 1.0f / tex_w, my = 1.0f / tex_h;
943 GLfloat tex_coords[4][2] =
951 glVertexPointer (2, GL_FLOAT, 0, vertices);
952 glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
953 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
955 glDisable (gl_texture_target);
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)
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);
971 // TODO: Make sure offset works in super-sampled mode.
973 adjust_point_for_line (GC gc, CGPoint *p)
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.
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
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. */
1000 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1002 // TODO: XDrawLine == XDrawSegments(nlines == 1), also in jwxyz.m
1008 XDrawSegments (dpy, d, gc, &segment, 1);
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;
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);
1019 w = 1; // Actually show zero-length lines.
1020 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1024 CGPoint p = point_for_line (d, gc, x1, y1);
1026 push_fg_gc (dpy, d, gc, NO);
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);
1036 invalidate_drawable_cache (d); */
1041 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1044 jwxyz_bind_drawable (dpy, dpy->main_window, d);
1045 set_fg_gc (dpy, gc);
1047 /* TODO: Thick lines
1048 * Zero-length line segments
1049 * Paths with zero length total (Remember line width, cap style.)
1056 GLshort *vertices = malloc(2 * sizeof(GLshort) * count); // TODO malloc NULL sigh
1058 glMatrixMode (GL_MODELVIEW);
1059 glTranslatef (0.5f, 0.5f, 0);
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];
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);
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);
1089 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1091 jwxyz_bind_drawable (dpy, dpy->main_window, d);
1092 set_fg_gc (dpy, gc);
1094 /* TODO: Thick lines. */
1095 /* Thin lines <= 1px are offset by +0.5; thick lines are not. */
1097 glMatrixMode (GL_MODELVIEW);
1098 glTranslatef (0.5, 0.5, 0);
1100 glEnableClientState (GL_VERTEX_ARRAY);
1101 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
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);
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);
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);
1128 CGContextStrokePath (cgc);
1130 invalidate_drawable_cache (d); */
1136 XClearWindow (Display *dpy, Window win)
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);
1145 jwxyz_window_background (Display *dpy)
1147 return dpy->window_background;
1151 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
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;
1160 jwxyz_fill_rects (Display *dpy, Drawable d, GC gc,
1161 const XRectangle *rectangles, unsigned long nrectangles,
1162 unsigned long pixel)
1164 jwxyz_bind_drawable (dpy, dpy->main_window, d);
1165 set_color_gc (dpy, gc, pixel);
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);
1177 glEnableClientState (GL_VERTEX_ARRAY);
1178 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1180 for (unsigned long i = 0; i != nrectangles; ++i)
1182 const XRectangle *r = &rectangles[i];
1184 GLfloat coords[4][2] =
1187 {r->x, r->y + r->height},
1188 {r->x + r->width, r->y + r->height},
1189 {r->x + r->width, r->y}
1192 // TODO: Several rects at once. Maybe even tune for XScreenSaver workloads.
1193 glVertexPointer (2, GL_FLOAT, 0, coords);
1195 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1202 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1204 Assert(win == dpy->main_window, "XClearArea: not a window");
1205 Assert(!exp, "XClearArea: exposures unsupported");
1207 jwxyz_fill_rect (dpy, win, 0, x, y, w, h, dpy->window_background);
1213 XFillPolygon (Display *dpy, Drawable d, GC gc,
1214 XPoint *points, int npoints, int shape, int mode)
1216 jwxyz_bind_drawable (dpy, dpy->main_window, d);
1219 // TODO: Re-implement the GLU tesselation functions.
1221 /* Complex: Pedal, and for some reason Attraction, Mountain, Qix, SpeedMine, Starfish
1222 * Nonconvex: Goop, Pacman, Rocks, Speedmine
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
1231 // TODO: Feed vertices straight to OpenGL for CoordModeOrigin.
1233 if (shape == Convex) {
1235 GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc.
1236 short v[2] = {0, 0};
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];
1244 glEnableClientState (GL_VERTEX_ARRAY);
1245 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1247 glVertexPointer (2, GL_SHORT, 0, vertices);
1248 glDrawArrays (GL_TRIANGLE_FAN, 0, npoints);
1252 } else if (shape == Nonconvex) {
1254 // TODO: assert that x,y of first and last point match, as that is assumed
1257 root = (linked_point *) malloc( sizeof(linked_point) );
1258 set_points_list(points,npoints,root);
1259 traverse_points_list(root);
1262 Assert((shape == Convex || shape == Nonconvex), "XFillPolygon: (TODO) Unimplemented shape");
1269 #define radians(DEG) ((DEG) * M_PI / 180.0)
1270 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1273 arc_xy(GLfloat *p, double cx, double cy, double w2, double h2, double theta)
1275 p[0] = cos(theta) * w2 + cx;
1276 p[1] = -sin(theta) * h2 + cy;
1280 mod_neg(int a, unsigned b)
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
1285 return a < 0 ? (b - 1) - (-(a + 1) % b) : a % b;
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)
1293 jwxyz_bind_drawable (dpy, dpy->main_window, d);
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...) */
1299 double w2 = width * 0.5f, h2 = height * 0.5f;
1300 double a, b; /* Semi-major/minor axes. */
1309 const double two_pi = 2 * M_PI;
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));
1317 // TODO: Explain how drawing works what with the points of overlapping arcs
1321 unsigned segments_360 = segments_f;
1323 /* TODO: angle2 == 0. This is a tilted square with CapSquare. */
1324 /* TODO: color, thick lines, CapNotLast for thin lines */
1325 /* TODO: Transformations. */
1327 double segment_angle = two_pi / segments_360;
1329 const unsigned deg64 = 360 * 64;
1330 const double rad_from_deg64 = two_pi / deg64;
1337 angle1 = mod_neg(angle1, deg64); // TODO: Is this OK? Consider negative numbers.
1340 angle2 = deg64; // TODO: Handle circles special.
1343 angle1_f = angle1 * rad_from_deg64,
1344 angle2_f = angle2 * rad_from_deg64;
1346 if (angle2_f > two_pi) // TODO: Move this up.
1349 double segment1_angle_part = fmodf(angle1_f, segment_angle);
1351 unsigned segment1 = ((angle1_f - segment1_angle_part) / segment_angle) + 1.5;
1353 double angle_2r = angle2_f - segment1_angle_part;
1354 unsigned segments = angle_2r / segment_angle;
1356 GLfloat cx = x + w2, cy = y + h2;
1358 GLfloat *data = malloc((segments + 3) * sizeof(GLfloat) * 2); // TODO: Check result.
1360 GLfloat *data_ptr = data;
1367 arc_xy (data_ptr, cx, cy, w2, h2, angle1_f);
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);
1377 arc_xy (data_ptr, cx, cy, w2, h2, angle1_f + angle2_f);
1380 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1381 glEnableClientState (GL_VERTEX_ARRAY);
1383 glVertexPointer (2, GL_FLOAT, 0, data);
1384 glDrawArrays (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP,
1386 (GLsizei)((data_ptr - data) / 2));
1393 unsigned segments = segments_f * (fabs(angle2) / (360 * 64));
1395 glBegin (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP);
1397 if (fill_p /* && gc->gcv.arc_mode == ArcPieSlice */)
1398 glVertex2f (cx, cy);
1400 /* TODO: This should fix the middle points of the arc so that the starting and ending points are OK. */
1402 float to_radians = 2 * M_PI / (360 * 64);
1403 float theta = angle1 * to_radians, d_theta = angle2 * to_radians / segments;
1405 for (unsigned s = 0; s != segments + 1; ++s) /* TODO: This is the right number of segments, yes? */
1407 glVertex2f(cos(theta) * w2 + cx, -sin(theta) * h2 + cy);
1419 jwxyz_gc_gcv (GC gc)
1426 jwxyz_gc_depth (GC gc)
1433 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1435 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1436 gc->depth = jwxyz_drawable_depth (d);
1438 jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
1439 XChangeGC (dpy, gc, mask, xgcv);
1445 XFreeGC (Display *dpy, GC gc)
1448 XUnloadFont (dpy, gc->gcv.font);
1452 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1454 if (gc->gcv.clip_mask) {
1455 XFreePixmap (dpy, gc->gcv.clip_mask);
1456 CGImageRelease (gc->clip_mask);
1466 flipbits (unsigned const char *in, unsigned char *out, int length)
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
1502 while (length-- > 0)
1503 *out++ = table[*in++];
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)
1513 jwxyz_assert_display (dpy);
1515 const XRectangle *wr = jwxyz_frame (d);
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");
1525 // Clip width and height to the bounds of the Drawable
1527 if (dest_x + w > wr->width) {
1528 if (dest_x > wr->width)
1530 w = wr->width - dest_x;
1532 if (dest_y + h > wr->height) {
1533 if (dest_y > wr->height)
1535 h = wr->height - dest_y;
1537 if (w <= 0 || h <= 0)
1540 // Clip width and height to the bounds of the XImage
1542 if (src_x + w > ximage->width) {
1543 if (src_x > ximage->width)
1545 w = ximage->width - src_x;
1547 if (src_y + h > ximage->height) {
1548 if (src_y > ximage->height)
1550 h = ximage->height - src_y;
1552 if (w <= 0 || h <= 0)
1555 /* Assert (d->win */
1557 if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
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;
1568 r.origin.x = wr->x + dest_x;
1569 r.origin.y = wr->y + wr->height - dest_y - h;
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");
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.
1582 data += (src_y * bpl) + (src_x * 4);
1584 jwxyz_assert_display(dpy);
1586 /* There probably won't be any hacks that do this, but... */
1587 Assert (!(bpl % 4), "XPutImage: bytes_per_line not divisible by four.");
1589 unsigned src_w = bpl / 4;
1591 /* GL_UNPACK_ROW_LENGTH is not allowed to be negative. (sigh) */
1592 # ifndef HAVE_JWZGLES
1593 glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
1597 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
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
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);
1611 GLint internalformat = texture_internalformat(dpy);
1613 glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
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);
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);
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);
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);
1638 GLfloat vertices[4][2] =
1641 {dest_x, dest_y + h},
1642 {dest_x + w, dest_y + h},
1643 {dest_x + w, dest_y}
1646 GLfloat texcoord_w, texcoord_h;
1647 # ifndef HAVE_JWZGLES
1648 if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT) {
1652 # endif /* HAVE_JWZGLES */
1654 texcoord_w = (double)w / tex_w;
1655 texcoord_h = (double)h / tex_h;
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;
1668 glVertexPointer (2, GL_FLOAT, 0, vertices);
1669 glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
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);
1677 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1679 if (gc->gcv.alpha_allowed_p)
1680 glDisable (GL_BLEND);
1683 glDisable (dpy->gl_texture_target);
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);
1690 } else { // (bpp == 1)
1692 // Assert(FALSE, "XPutImage: TODO");
1693 // Check out ximage_(get|put)pixel_1
1696 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
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...
1704 Assert ((src_x % 8) == 0,
1705 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1707 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1708 unsigned char *flipped = (unsigned char *) malloc (bsize);
1710 flipbits ((unsigned char *) data, flipped, bsize);
1712 CGDataProviderRef prov =
1713 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1714 CGImageRef mask = CGImageMaskCreate (w, h,
1717 NULL, /* decode[] */
1718 GL_FALSE); /* interpolate */
1719 push_fg_gc (dpy, d, gc, GL_TRUE);
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
1728 CGDataProviderRelease (prov);
1729 CGImageRelease (mask);
1734 invalidate_drawable_cache (d);
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. */
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)
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");
1754 jwxyz_bind_drawable (dpy, dpy->main_window, d);
1756 // TODO: What if this reads off the edge? What is supposed to happen?
1759 // In case the caller tries to write off the edge.
1761 max_width = dest_image->width - dest_x,
1762 max_height = dest_image->height - dest_y;
1764 if (width > max_width) {
1768 if (height > max_height) {
1769 height = max_height;
1773 Assert (jwxyz_drawable_depth (d) == dest_image->depth, "XGetSubImage: depth mismatch");
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;
1779 Assert (pixels_per_line == width, "XGetSubImage: (TODO) pixels_per_line != width");
1781 glPixelStorei (GL_PACK_ROW_LENGTH, pixels_per_line);
1783 glPixelStorei (GL_PACK_ALIGNMENT, 4);
1785 uint32_t *dest_data = (uint32_t *)dest_image->data + pixels_per_line * dest_y + dest_x;
1787 glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
1788 dpy->screen->pixel_format, gl_pixel_type(dpy), dest_data);
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];
1799 top += pixels_per_line;
1800 bottom -= pixels_per_line;
1804 /* TODO: Actually get pixels. */
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
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;
1820 /* Returns a transformation matrix to do rotation as per the provided
1821 EXIF "Orientation" value.
1824 static CGAffineTransform
1825 exif_rotate (int rot, CGSize rect)
1827 CGAffineTransform trans = CGAffineTransformIdentity;
1829 case 2: // flip horizontal
1830 trans = CGAffineTransformMakeTranslation (rect.width, 0);
1831 trans = CGAffineTransformScale (trans, -1, 1);
1834 case 3: // rotate 180
1835 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1836 trans = CGAffineTransformRotate (trans, M_PI);
1839 case 4: // flip vertical
1840 trans = CGAffineTransformMakeTranslation (0, rect.height);
1841 trans = CGAffineTransformScale (trans, 1, -1);
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);
1850 case 6: // rotate 90
1851 trans = CGAffineTransformMakeTranslation (0, rect.width);
1852 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1855 case 7: // transverse (UR-to-LL axis)
1856 trans = CGAffineTransformMakeScale (-1, 1);
1857 trans = CGAffineTransformRotate (trans, M_PI / 2);
1860 case 8: // rotate 270
1861 trans = CGAffineTransformMakeTranslation (rect.height, 0);
1862 trans = CGAffineTransformRotate (trans, M_PI / 2);
1874 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
1875 Bool nsimg_p, void *img_arg,
1876 XRectangle *geom_ret, int exif_rotation)
1878 Assert (False, "jwxyz_draw_NSImage_or_CGImage: TODO stub");
1882 CGImageSourceRef cgsrc;
1883 # endif // USE_IPHONE
1886 CGContextRef cgc = d->cgc;
1890 NSImage *nsimg = (NSImage *) img_arg;
1891 imgr = [nsimg size];
1894 // convert the NSImage to a CGImage via the toll-free-bridging
1895 // of NSData and CFData...
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
1908 cgi = (CGImageRef) img_arg;
1909 imgr.width = CGImageGetWidth (cgi);
1910 imgr.height = CGImageGetHeight (cgi);
1913 Bool rot_p = (exif_rotation >= 5);
1916 imgr = NSMakeSize (imgr.height, imgr.width);
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);
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;
1929 dst2.origin.x = dst2.origin.y = 0;
1931 dst2.size.width = dst.size.height;
1932 dst2.size.height = dst.size.width;
1934 dst2.size = dst.size;
1937 // Clear the part not covered by the image to background or black.
1939 if (d->type == WINDOW)
1940 XClearWindow (dpy, d);
1942 jwxyz_fill_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height,
1943 drawable_depth (d) == 1 ? 0 : BlackPixel(dpy,0));
1946 CGAffineTransform trans =
1947 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
1949 CGContextSaveGState (cgc);
1950 CGContextConcatCTM (cgc,
1951 CGAffineTransformMakeTranslation (dst.origin.x,
1953 CGContextConcatCTM (cgc, trans);
1954 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1955 CGContextDrawImage (cgc, dst2, cgi);
1956 CGContextRestoreGState (cgc);
1961 CGImageRelease (cgi);
1963 # endif // USE_IPHONE
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;
1972 invalidate_drawable_cache (d);
1976 #ifndef HAVE_JWZGLES
1980 create_rectangle_texture (GLuint *texture)
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);
1994 copy_pixmap (Display *dpy, Pixmap p)
1997 Assert (p->type == PIXMAP, "not a pixmap");
2003 unsigned int width, height, border_width, depth;
2004 if (XGetGeometry (dpy, p, &root,
2005 &x, &y, &width, &height, &border_width, &depth)) {
2007 gcv.function = GXcopy;
2008 GC gc = XCreateGC (dpy, p, GCFunction, &gcv);
2010 p2 = XCreatePixmap (dpy, p, width, height, depth);
2012 XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0);
2017 Assert (p2, "could not copy pixmap");
2025 jwxyz_draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2026 const char *str, size_t len, int utf8_p)
2028 Font ff = gc->gcv.font;
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;
2036 if (w < 0 || h < 0) abort();
2037 if (w == 0 || h == 0) {
2038 if (data) free(data);
2042 XImage *img = XCreateImage (dpy, dpy->screen->visual, 32,
2043 ZPixmap, 0, data, w, h, 0, 0);
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.
2054 char *end = s + (w * h * 4);
2056 jwxyz_query_color (dpy, gc->gcv.foreground, rgba);
2068 Bool old_alpha = gc->gcv.alpha_allowed_p;
2069 jwxyz_XSetAlphaAllowed (dpy, gc, True);
2070 XPutImage (dpy, d, gc, img, 0, 0,
2074 jwxyz_XSetAlphaAllowed (dpy, gc, old_alpha);
2075 XDestroyImage (img);
2083 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2089 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2091 if (gc->gcv.clip_mask) {
2092 XFreePixmap (dpy, gc->gcv.clip_mask);
2093 CGImageRelease (gc->clip_mask);
2096 gc->gcv.clip_mask = copy_pixmap (dpy, m);
2097 if (gc->gcv.clip_mask)
2099 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2108 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2110 gc->gcv.clip_x_origin = x;
2111 gc->gcv.clip_y_origin = y;
2115 void set_points_list(XPoint *points, int npoints, linked_point *root)
2117 linked_point *current;
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;
2126 current->x = points[npoints-2].x;
2127 current->y = points[npoints-2].y;
2128 current->next = root;
2132 double compute_edge_length(linked_point * a, linked_point * b)
2135 int xdiff, ydiff, xsq, ysq, added;
2136 double xy_add, edge_length;
2138 xdiff = a->x - b->x;
2139 ydiff = a->y - b->y;
2140 xsq = xdiff * xdiff;
2141 ysq = ydiff * ydiff;
2143 xy_add = (double) added;
2144 edge_length = sqrt(xy_add);
2148 double get_angle(double a, double b, double c)
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);
2157 Bool is_same_slope(linked_point * a)
2160 int abx, bcx, aby, bcy, aa, bb;
2167 // test if slopes are indefinite for both line segments
2169 return b->x == c->x;
2170 } else if (b->x == c->x) {
2171 return False; // false, as ax/bx is not indefinite
2184 void draw_three_vertices(linked_point * a, Bool triangle)
2194 GLfloat vertices[3][2] = {
2201 drawType = GL_TRIANGLES;
2203 drawType = GL_LINES;
2206 glEnableClientState(GL_VERTEX_ARRAY);
2207 glVertexPointer(2, GL_FLOAT, 0, vertices);
2208 glDrawArrays(drawType, 0, 3);
2210 free(b); // cut midpoint off from remaining polygon vertex list
2215 Bool is_an_ear(linked_point * a)
2217 double edge_ab, edge_bc, edge_ac;
2218 double angle_a, angle_b, angle_c;
2220 linked_point *b, *c;
2224 my_pi = (double) M_PI;
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);
2233 return angle_a < my_pi && angle_b < my_pi && angle_c < my_pi;
2237 Bool is_three_point_loop(linked_point * head)
2239 return head->x == head->next->next->next->x
2240 && head->y == head->next->next->next->y;
2244 void traverse_points_list(linked_point * root)
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);
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);
2265 free(head->next->next);
2268 Assert (False, "traverse_points_list: unknown configuration");
2277 #endif /* JWXYZ_GL -- entire file */