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 /* FWIW, at one point macOS 10.x could actually run with 256 colors. It might
25 not be able to do this anymore, though.
29 - malloc error checking
30 - Check max texture sizes for XGet/PutImage, XCopyArea.
31 - Optional 5:5:5 16-bit color
34 /* Techniques & notes:
35 - iOS: OpenGL ES 2.0 isn't always available. Use OpenGL ES 1.1.
36 - OS X: Drivers can go back to OpenGL 1.1 (GeForce 2 MX on 10.5.8).
37 - Use stencil buffers (OpenGL 1.0+) for bitmap clipping masks.
38 - Pixmaps can be any of the following, depending on GL implementation.
39 - This requires offscreen rendering. Fortunately, this is always
41 - OS X: Drawable objects, including: pixel buffers and offscreen
43 - Offscreen buffers w/ CGLSetOffScreen (10.0+)
44 - http://lists.apple.com/archives/mac-opengl/2002/Jun/msg00087.html
45 provides a very ugly solution for hardware-accelerated offscreen
46 rendering with CGLSetParameter(*, kCGLCPSurfaceTexture, *) on OS X
47 10.1+. Apple docs say it's actually for OS X 10.3+, instead.
48 - Pixel buffers w/ CGLSetPBuffer (10.3+, now deprecated)
49 - Requires APPLE_pixel_buffer.
50 - Available in software on x86 only.
51 - Always available on hardware.
52 - Need to blit? Use OpenGL pixel buffers. (GL_PIXEL_PACK_BUFFER)
53 - Framebuffer objects w/ GL_(ARB|EXT)_framebuffer_object
54 - TODO: Can EXT_framebuffers be different sizes?
56 - iOS: Use OES_framebuffer_object, it's always present.
59 /* OpenGL hacks call a number of X11 functions, including
60 XCopyArea, XDrawString, XGetImage
61 XCreatePixmap, XCreateGC, XCreateImage
63 Check these, of course.
66 #ifdef JWXYZ_GL /* entire file */
78 # import <QuartzCore/QuartzCore.h>
79 # include <OpenGLES/ES1/gl.h>
80 # include <OpenGLES/ES1/glext.h>
82 # include <OpenGL/glu.h>
85 /* TODO: Does this work on iOS? */
90 # include <GLES/glext.h>
95 # include "jwzglesI.h"
99 #include "jwxyz-timers.h"
100 #include "yarandom.h"
110 struct jwxyz_Display {
111 const struct jwxyz_vtbl *vtbl; // Must come first.
114 GLenum pixel_format, pixel_type;
116 struct jwxyz_sources_data *timers_data;
118 Bool gl_texture_npot_p;
119 /* Bool opengl_core_p */;
120 GLenum gl_texture_target;
122 // #if defined USE_IPHONE
123 GLuint rect_texture; // Also can work on the desktop.
126 unsigned long window_background;
129 Bool gc_alpha_allowed_p;
131 // Alternately, there could be one queue per pixmap.
132 size_t queue_size, queue_capacity;
133 Drawable queue_drawable;
135 GLshort *queue_vertex;
136 uint32_t *queue_color;
145 struct jwxyz_linked_point {
152 jwxyz_assert_display(Display *dpy)
157 jwxyz_assert_drawable (dpy->main_window, dpy->main_window);
162 jwxyz_set_matrices (Display *dpy, unsigned width, unsigned height,
165 Assert (width, "no width");
166 Assert (height, "no height");
168 /* TODO: Check registration pattern from Interference with rectangles instead of points. */
170 // The projection matrix is always set as follows. The modelview matrix is
171 // usually identity, but for points and thin lines, it's translated by 0.5.
172 glMatrixMode(GL_PROJECTION);
175 # if defined(USE_IPHONE) || defined(HAVE_ANDROID)
177 if (window_p && ignore_rotation_p(dpy)) {
178 int o = (int) current_device_rotation();
179 glRotatef (-o, 0, 0, 1);
182 // glPointSize(1); // This is the default.
185 glOrthof /* TODO: Is glOrthox worth it? Signs point to no. */
189 (0, width, height, 0, -1, 1);
191 glMatrixMode(GL_MODELVIEW);
192 # endif // HAVE_MOBILE
199 // iOS always uses OpenGL ES 1.1.
205 gl_check_ver (const struct gl_version *caps,
209 return caps->major > gl_major ||
210 (caps->major == gl_major && caps->minor >= gl_minor);
216 extern const struct jwxyz_vtbl gl_vtbl;
219 jwxyz_gl_make_display (Window w)
221 Display *d = (Display *) calloc (1, sizeof(*d));
224 # ifndef HAVE_JWZGLES
225 struct gl_version version;
228 const GLubyte *version_str = glGetString (GL_VERSION);
230 /* iPhone is always OpenGL ES 1.1. */
231 if (sscanf ((const char *) version_str, "%u.%u",
232 &version.major, &version.minor) < 2)
238 # endif // !HAVE_JWZGLES
240 const GLubyte *extensions = glGetString (GL_EXTENSIONS);
243 - Apple TN2080: Understanding and Detecting OpenGL Functionality.
244 - OpenGL Programming Guide for the Mac - Best Practices for Working with
245 Texture Data - Optimal Data Formats and Types
248 // If a video adapter suports BGRA textures, then that's probably as fast as
249 // you're gonna get for getting a texture onto the screen.
251 /* TODO: Make BGRA work on iOS. As it is, it breaks XPutImage. (glTexImage2D, AFAIK) */
252 d->pixel_format = GL_RGBA; /*
253 gluCheckExtension ((const GLubyte *) "GL_APPLE_texture_format_BGRA8888",
254 extensions) ? GL_BGRA_EXT : GL_RGBA; */
255 d->pixel_type = GL_UNSIGNED_BYTE;
256 // See also OES_read_format.
257 # else // !HAVE_JWZGLES
258 if (gl_check_ver (&version, 1, 2) ||
259 (gluCheckExtension ((const GLubyte *) "GL_EXT_bgra", extensions) &&
260 gluCheckExtension ((const GLubyte *) "GL_APPLE_packed_pixels",
262 // APPLE_packed_pixels is only ever available on iOS, never Android.
263 d->pixel_format = GL_BGRA_EXT;
264 // Both Intel and PowerPC-era docs say to use GL_UNSIGNED_INT_8_8_8_8_REV.
265 d->pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
267 d->pixel_format = GL_RGBA;
268 d->pixel_type = GL_UNSIGNED_BYTE;
270 // GL_ABGR_EXT/GL_UNSIGNED_BYTE is another possibilty that may have made more
272 # endif // !HAVE_JWZGLES
274 // On really old systems, it would make sense to split textures
275 // into subsections, to work around the maximum texture size.
276 # ifndef HAVE_JWZGLES
277 d->gl_texture_npot_p = gluCheckExtension ((const GLubyte *)
278 "GL_ARB_texture_rectangle",
280 d->gl_texture_target = d->gl_texture_npot_p ?
281 GL_TEXTURE_RECTANGLE_EXT :
284 d->gl_texture_npot_p = jwzgles_gluCheckExtension
285 ((const GLubyte *) "GL_APPLE_texture_2D_limited_npot", extensions) ||
286 jwzgles_gluCheckExtension
287 ((const GLubyte *) "GL_OES_texture_npot", extensions) ||
288 jwzgles_gluCheckExtension // From PixelFlinger 1.4
289 ((const GLubyte *) "GL_ARB_texture_non_power_of_two", extensions);
291 d->gl_texture_target = GL_TEXTURE_2D;
294 Visual *v = &d->visual;
295 v->class = TrueColor;
296 if (d->pixel_format == GL_BGRA_EXT) {
297 v->rgba_masks[0] = 0x00ff0000;
298 v->rgba_masks[1] = 0x0000ff00;
299 v->rgba_masks[2] = 0x000000ff;
300 v->rgba_masks[3] = 0xff000000;
302 Assert(d->pixel_format == GL_RGBA,
303 "jwxyz_gl_make_display: Unknown pixel_format");
304 for (unsigned i = 0; i != 4; ++i) {
305 union color_bytes color;
307 color.bytes[i] = 0xff;
308 v->rgba_masks[i] = color.pixel;
312 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
314 d->window_background = BlackPixel(d,0);
318 GLint max_texture_size;
319 glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size);
320 Log ("GL_MAX_TEXTURE_SIZE: %d\n", max_texture_size);
323 // In case a GL hack wants to use X11 to draw offscreen, the rect_texture is available.
324 Assert (d->main_window == w, "Uh-oh.");
325 glGenTextures (1, &d->rect_texture);
326 // TODO: Check for (ARB|EXT|NV)_texture_rectangle. (All three are alike.)
327 // Rectangle textures should be present on OS X with the following exceptions:
328 // - Generic renderer on PowerPC OS X 10.4 and earlier
330 glBindTexture (d->gl_texture_target, d->rect_texture);
331 // TODO: This is all somewhere else. Refactor.
332 glTexParameteri (d->gl_texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
333 glTexParameteri (d->gl_texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
335 // This might be redundant for rectangular textures.
336 # ifndef HAVE_JWZGLES
337 const GLint wrap = GL_CLAMP;
338 # else // HAVE_JWZGLES
339 const GLint wrap = GL_CLAMP_TO_EDGE;
340 # endif // HAVE_JWZGLES
342 // In OpenGL, CLAMP_TO_EDGE is OpenGL 1.2 or GL_SGIS_texture_edge_clamp.
343 // This is always present with OpenGL ES.
344 glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_S, wrap);
345 glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_T, wrap);
347 d->gc_function = GXcopy;
348 d->gc_alpha_allowed_p = False;
350 jwxyz_assert_display(d);
355 jwxyz_gl_free_display (Display *dpy)
357 /* TODO: Go over everything. */
359 free (dpy->queue_vertex);
360 free (dpy->queue_color);
362 jwxyz_sources_free (dpy->timers_data);
368 /* Call this when the View changes size or position.
371 jwxyz_window_resized (Display *dpy)
373 const XRectangle *new_frame = jwxyz_frame (dpy->main_window);
374 unsigned new_width = new_frame->width;
375 unsigned new_height = new_frame->height;
377 Assert (new_width, "jwxyz_window_resized: No width.");
378 Assert (new_height, "jwxyz_window_resized: No height.");
380 /*if (cgc) w->cgc = cgc;
381 Assert (w->cgc, "no CGContext"); */
383 Log("resize: %d, %d\n", new_width, new_height);
385 jwxyz_gl_flush (dpy);
386 jwxyz_bind_drawable (dpy, dpy->main_window, dpy->main_window);
388 // TODO: What does the iPhone need?
390 // iOS only: If the main_window is not the current_drawable, then set_matrices
391 // was already called in bind_drawable.
392 jwxyz_set_matrices (dpy, new_width, new_height, True);
396 glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
398 glDrawBuffer (GL_FRONT);
399 glClearColor (1, 0, 1, 0);
400 glClear (GL_COLOR_BUFFER_BIT);
401 glDrawBuffer (GL_BACK);
402 glClearColor (0, 1, 1, 0);
403 glClear (GL_COLOR_BUFFER_BIT);
405 glDrawBuffer (draw_buffer); */
407 // Stylish and attractive purple!
408 // glClearColor (1, 0, 1, 0.5);
409 // glClear (GL_COLOR_BUFFER_BIT);
413 static jwxyz_sources_data *
414 display_sources_data (Display *dpy)
416 return dpy->timers_data;
423 return dpy->main_window;
427 visual (Display *dpy)
433 /* GC attributes by usage and OpenGL implementation:
435 All drawing functions:
436 function | glLogicOp w/ GL_COLOR_LOGIC_OP
437 clip_x_origin, clip_y_origin, clip_mask | Stencil mask
439 Shape drawing functions:
440 foreground, background | glColor*
442 XDrawLines, XDrawRectangles, XDrawSegments:
443 line_width, cap_style, join_style | Lotsa vertices
446 fill_rule | Multiple GL_TRIANGLE_FANs
449 font | Cocoa, then OpenGL display lists.
451 alpha_allowed_p | GL_BLEND
453 antialias_p | Well, there's options:
454 * Multisampling would work, but that's something that would need to be set
455 per-Pixmap, not per-GC.
456 * GL_POINT, LINE, and POLYGON_SMOOTH are the old-school way of doing
457 this, but POINT_SMOOTH is unnecessary, and POLYGON_SMOOTH is missing from
458 GLES 1. All three are missing from GLES 2. Word on the street is that
459 these are deprecated anyway.
460 * Tiny textures with bilinear filtering to get the same effect as LINE_ and
461 POLYGON_SMOOTH. A bit tricky.
462 * Do nothing. Android hardware is very often high-DPI enough that
463 anti-aliasing doesn't matter all that much.
470 enqueue (Display *dpy, Drawable d, GC gc, int mode, size_t count)
472 if (dpy->queue_size &&
473 (dpy->gc_function != gc->gcv.function ||
474 dpy->gc_alpha_allowed_p != gc->gcv.alpha_allowed_p ||
475 dpy->queue_mode != mode ||
476 dpy->queue_drawable != d)) {
477 jwxyz_gl_flush (dpy);
480 jwxyz_bind_drawable (dpy, dpy->main_window, d);
481 jwxyz_gl_set_gc (dpy, gc);
483 // TODO: Use glColor when we can get away with it.
484 size_t old_size = dpy->queue_size;
485 dpy->queue_size += count;
486 if (dpy->queue_size > dpy->queue_capacity) {
487 dpy->queue_capacity = dpy->queue_size * 2;
489 uint32_t *new_color = realloc (
490 dpy->queue_color, sizeof(*dpy->queue_color) * dpy->queue_capacity);
491 GLshort *new_vertex = realloc (
492 dpy->queue_vertex, sizeof(*dpy->queue_vertex) * 2 * dpy->queue_capacity);
494 if (!new_color || !new_vertex)
497 dpy->queue_color = new_color;
498 dpy->queue_vertex = new_vertex;
501 dpy->queue_mode = mode;
502 dpy->queue_drawable = d;
504 union color_bytes color;
505 // TODO: validate color
506 JWXYZ_QUERY_COLOR (dpy, gc->gcv.foreground, 0xffull, color.bytes);
507 for (size_t i = 0; i != count; ++i) // TODO: wmemset when applicable.
508 dpy->queue_color[i + old_size] = color.pixel;
510 return dpy->queue_vertex + old_size * 2;
515 set_clip_mask (GC gc)
517 Assert (!gc->gcv.clip_mask, "set_clip_mask: TODO");
522 set_color (Display *dpy, unsigned long pixel, unsigned int depth,
523 Bool alpha_allowed_p)
525 jwxyz_validate_pixel (dpy, pixel, depth, alpha_allowed_p);
529 glColor4f (f, f, f, 1);
532 JWXYZ_QUERY_COLOR (dpy, pixel, 1.0f, rgba);
533 glColor4f (rgba[0], rgba[1], rgba[2], rgba[3]);
537 /* Pushes a GC context; sets Function and ClipMask. */
539 jwxyz_gl_set_gc (Display *dpy, GC gc)
542 Bool alpha_allowed_p;
544 // GC is NULL for XClearArea and XClearWindow.
546 function = gc->gcv.function;
547 alpha_allowed_p = gc->gcv.alpha_allowed_p;
551 alpha_allowed_p = False;
552 // TODO: Set null clip mask for NULL GC here.
555 /* GL_COLOR_LOGIC_OP: OpenGL 1.1. */
556 if (function != dpy->gc_function) {
557 dpy->gc_function = function;
558 if (function != GXcopy) {
559 /* Fun fact: The glLogicOp opcode constants are the same as the X11 GX*
560 function constants | GL_CLEAR.
562 glEnable (GL_COLOR_LOGIC_OP);
563 glLogicOp (gc->gcv.function | GL_CLEAR);
565 glDisable (GL_COLOR_LOGIC_OP);
569 /* Cocoa uses add/subtract/difference blending in place of logical ops.
570 It looks nice, but implementing difference blending in OpenGL appears to
571 require GL_KHR_blend_equation_advanced, and support for this is not
575 if (alpha_allowed_p != dpy->gc_alpha_allowed_p) {
576 dpy->gc_alpha_allowed_p = alpha_allowed_p;
577 if (gc && gc->gcv.alpha_allowed_p) {
578 // TODO: Maybe move glBlendFunc to XCreatePixmap?
579 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
582 glDisable (GL_BLEND);
589 set_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color)
591 jwxyz_gl_flush (dpy);
592 jwxyz_bind_drawable (dpy, dpy->main_window, d);
593 jwxyz_gl_set_gc (dpy, gc);
600 switch (gc->gcv.function) {
601 case GXset: color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
602 case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
605 depth = visual_depth (NULL, NULL);
608 set_color (dpy, color, depth, gc ? gc->gcv.alpha_allowed_p : False);
611 /* Pushes a GC context; sets color to the foreground color.
614 set_fg_gc (Display *dpy, Drawable d, GC gc)
616 set_color_gc (dpy, d, gc, gc->gcv.foreground);
620 next_point(short *v, XPoint p, int mode)
623 case CoordModeOrigin:
627 case CoordModePrevious:
632 Assert (False, "next_point: bad mode");
638 DrawPoints (Display *dpy, Drawable d, GC gc,
639 XPoint *points, int count, int mode)
643 // TODO: XPoints can be fed directly to OpenGL.
644 GLshort *gl_points = enqueue (dpy, d, gc, GL_POINTS, count); // TODO: enqueue returns NULL.
645 for (unsigned i = 0; i < count; i++) {
646 next_point (v, points[i], mode);
647 gl_points[2 * i] = v[0];
648 gl_points[2 * i + 1] = v[1];
656 texture_internalformat (Display *dpy)
659 return dpy->pixel_format;
666 gl_pixel_type (const Display *dpy)
668 return dpy->pixel_type;
672 clear_texture (Display *dpy)
674 glTexImage2D (dpy->gl_texture_target, 0, texture_internalformat(dpy), 0, 0,
675 0, dpy->pixel_format, gl_pixel_type (dpy), NULL);
682 glColor4f (1, 1, 1, 1);
684 glColor3ub (0xff, 0xff, 0xff);
690 jwxyz_gl_flush (Display *dpy)
692 if (!dpy->queue_size)
695 // jwxyz_bind_drawable() and jwxyz_gl_set_gc() is called in enqueue().
697 glEnableClientState (GL_COLOR_ARRAY);
698 glEnableClientState (GL_VERTEX_ARRAY);
699 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
701 // TODO: Use glColor instead of glColorPointer if there's just one color.
702 // TODO: Does OpenGL use both GL_COLOR_ARRAY and glColor at the same time?
705 Bool shifted = dpy->queue_mode == GL_POINTS || dpy->queue_mode == GL_LINES;
707 glMatrixMode (GL_MODELVIEW);
708 glTranslatef (0.5, 0.5, 0);
711 glColorPointer (4, GL_UNSIGNED_BYTE, 0, dpy->queue_color);
712 glVertexPointer (2, GL_SHORT, 0, dpy->queue_vertex);
713 glDrawArrays (dpy->queue_mode, 0, dpy->queue_size);
715 // TODO: This is right, right?
716 if (dpy->queue_mode == GL_LINES && dpy->queue_line_cap) {
717 Assert (!(dpy->queue_size % 2), "bad count for GL_LINES");
718 glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(GLubyte) * 8,
720 glVertexPointer (2, GL_SHORT, sizeof(GLshort) * 4, dpy->queue_vertex + 2);
721 glDrawArrays (GL_POINTS, 0, dpy->queue_size / 2);
727 glDisableClientState (GL_COLOR_ARRAY);
728 glDisableClientState (GL_VERTEX_ARRAY);
735 jwxyz_gl_copy_area_read_tex_image (Display *dpy, unsigned src_height,
736 int src_x, int src_y,
737 unsigned int width, unsigned int height,
738 int dst_x, int dst_y)
740 # if defined HAVE_COCOA && !defined USE_IPHONE
741 /* TODO: Does this help? */
745 /* TODO: Fix TestX11 + mode_preserve with this one. */
747 unsigned tex_w = width, tex_h = height;
748 if (!dpy->gl_texture_npot_p) {
749 tex_w = to_pow2(tex_w);
750 tex_h = to_pow2(tex_h);
753 GLint internalformat = texture_internalformat(dpy);
755 glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
757 if (tex_w == width && tex_h == height) {
758 glCopyTexImage2D (dpy->gl_texture_target, 0, internalformat,
759 src_x, src_height - src_y - height, width, height, 0);
761 glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
762 0, dpy->pixel_format, gl_pixel_type(dpy), NULL);
763 glCopyTexSubImage2D (dpy->gl_texture_target, 0, 0, 0,
764 src_x, src_height - src_y - height, width, height);
769 jwxyz_gl_copy_area_write_tex_image (Display *dpy, GC gc, int src_x, int src_y,
770 unsigned int width, unsigned int height,
771 int dst_x, int dst_y)
773 jwxyz_gl_set_gc (dpy, gc);
775 /* TODO: Copy-pasted from read_tex_image. */
776 unsigned tex_w = width, tex_h = height;
777 if (!dpy->gl_texture_npot_p) {
778 tex_w = to_pow2(tex_w);
779 tex_h = to_pow2(tex_h);
782 glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
784 jwxyz_gl_draw_image (dpy->gl_texture_target, tex_w, tex_h, 0, 0,
785 width, height, dst_x, dst_y);
792 jwxyz_gl_draw_image (GLenum gl_texture_target,
793 unsigned int tex_w, unsigned int tex_h,
794 int src_x, int src_y,
795 unsigned int width, unsigned int height,
796 int dst_x, int dst_y)
799 glEnable (gl_texture_target);
801 glEnableClientState (GL_TEXTURE_COORD_ARRAY);
802 glEnableClientState (GL_VERTEX_ARRAY);
804 /* TODO: Copied from XPutImage. Refactor. */
805 /* TODO: EXT_draw_texture or whatever it's called. */
806 GLfloat vertices[4][2] =
809 {dst_x, dst_y + height},
810 {dst_x + width, dst_y + height},
811 {dst_x + width, dst_y}
815 tex_x0 = src_x, tex_y0 = src_y + height,
816 tex_x1 = src_x + width, tex_y1 = src_y;
818 # ifndef HAVE_JWZGLES
819 if (gl_texture_target != GL_TEXTURE_RECTANGLE_EXT)
822 GLfloat mx = 1.0f / tex_w, my = 1.0f / tex_h;
829 GLfloat tex_coords[4][2] =
837 glVertexPointer (2, GL_FLOAT, 0, vertices);
838 glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
839 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
841 glDisable (gl_texture_target);
845 jwxyz_gl_copy_area_read_pixels (Display *dpy, Drawable src, Drawable dst,
846 GC gc, int src_x, int src_y,
847 unsigned int width, unsigned int height,
848 int dst_x, int dst_y)
850 XImage *img = XGetImage (dpy, src, src_x, src_y, width, height, ~0, ZPixmap);
851 XPutImage (dpy, dst, gc, img, 0, 0, dst_x, dst_y, width, height);
857 DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
860 set_fg_gc (dpy, d, gc);
863 * Zero-length line segments
864 * Paths with zero length total (Remember line width, cap style.)
871 GLshort *vertices = malloc(2 * sizeof(GLshort) * count); // TODO malloc NULL sigh
873 glMatrixMode (GL_MODELVIEW);
874 glTranslatef (0.5f, 0.5f, 0);
877 for (unsigned i = 0; i < count; i++) {
878 next_point (p, points[i], mode);
879 vertices[2 * i] = p[0];
880 vertices[2 * i + 1] = p[1];
883 glEnableClientState (GL_VERTEX_ARRAY);
884 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
885 glVertexPointer (2, GL_SHORT, 0, vertices);
886 glDrawArrays (GL_LINE_STRIP, 0, count);
890 if (gc->gcv.cap_style != CapNotLast) {
891 // TODO: How does this look with multisampling?
892 // TODO: Disable me for closed loops.
893 glVertexPointer (2, GL_SHORT, 0, p);
894 glDrawArrays (GL_POINTS, 0, 1);
903 // Turn line segment into parallelogram based on line_width
905 // TODO: Fix epicycle hack with large thickness, and truchet line segment ends
907 static void drawThickLine(int line_width, XSegment *segments)
909 double dx, dy, di, m, angle;
910 int sx1, sx2, sy1, sy2;
919 di = sqrt(dx * dx + dy * dy);
926 float sn = sin(angle);
927 float cs = cos(angle);
928 float line_width_f = (float) line_width;
930 float wsn = line_width_f * (sn/2);
931 float csn = line_width_f * (cs/2);
933 float x3 = sx1 - wsn;
934 float y3 = sy1 + csn;
935 float x4 = sx1 + wsn;
936 float y4 = sy1 - csn;
938 float x5 = sx2 - wsn;
939 float y5 = sy2 + csn;
940 float x6 = sx2 + wsn;
941 float y6 = sy2 - csn;
943 GLfloat coords[4][2] =
951 glEnableClientState (GL_VERTEX_ARRAY);
952 glVertexPointer (2, GL_FLOAT, 0, coords);
954 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
960 DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
962 /* TODO: Caps on thick lines. */
963 /* Thin lines <= 1px are offset by +0.5; thick lines are not. */
965 if (count == 1 && gc->gcv.line_width > 1) {
966 set_fg_gc (dpy, d, gc);
967 drawThickLine(gc->gcv.line_width,segments);
970 if (dpy->queue_line_cap != (gc->gcv.cap_style != CapNotLast))
971 jwxyz_gl_flush (dpy);
972 dpy->queue_line_cap = gc->gcv.cap_style != CapNotLast;
974 // TODO: Static assert here.
975 Assert (sizeof(XSegment) == sizeof(short) * 4 &&
976 sizeof(GLshort) == sizeof(short) &&
977 offsetof(XSegment, x1) == 0 &&
978 offsetof(XSegment, x2) == 4,
979 "XDrawSegments: Data alignment mix-up.");
981 memcpy (enqueue(dpy, d, gc, GL_LINES, count * 2), segments,
982 count * sizeof(XSegment));
990 ClearWindow (Display *dpy, Window win)
992 Assert (win == dpy->main_window, "not a window");
994 jwxyz_gl_flush (dpy);
995 jwxyz_bind_drawable (dpy, win, win);
998 JWXYZ_QUERY_COLOR (dpy, dpy->window_background, 1.0f, color);
1000 glClearColor (color[0], color[1], color[2], 1);
1001 glClear (GL_COLOR_BUFFER_BIT);
1005 static unsigned long *
1006 window_background (Display *dpy)
1008 return &dpy->window_background;
1012 fill_rects (Display *dpy, Drawable d, GC gc,
1013 const XRectangle *rectangles, unsigned long nrectangles,
1014 unsigned long pixel)
1016 set_color_gc (dpy, d, gc, pixel);
1020 for (unsigned i = 0; i != nrectangles; ++i) {
1021 const XRectangle *r = &rectangles[i];
1022 glVertex2i(r->x, r->y);
1023 glVertex2i(r->x, r->y + r->height);
1024 glVertex2i(r->x + r->width, r->y + r->height);
1025 glVertex2i(r->x + r->width, r->y);
1030 glEnableClientState (GL_VERTEX_ARRAY);
1031 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1033 for (unsigned long i = 0; i != nrectangles; ++i)
1035 const XRectangle *r = &rectangles[i];
1037 GLfloat coords[4][2] =
1040 {r->x, r->y + r->height},
1041 {r->x + r->width, r->y + r->height},
1042 {r->x + r->width, r->y}
1045 // TODO: Several rects at once. Maybe even tune for XScreenSaver workloads.
1046 glVertexPointer (2, GL_FLOAT, 0, coords);
1048 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1055 FillPolygon (Display *dpy, Drawable d, GC gc,
1056 XPoint *points, int npoints, int shape, int mode)
1058 set_fg_gc(dpy, d, gc);
1060 // TODO: Re-implement the GLU tesselation functions.
1062 /* Complex: Pedal, and for some reason Attraction, Mountain, Qix, SpeedMine, Starfish
1063 * Nonconvex: Goop, Pacman, Rocks, Speedmine
1065 * We currently do Nonconvex with the simple-to-implement ear clipping
1066 * algorithm, but in the future we can replace that with an algorithm
1067 * with slower big-O growth
1072 // TODO: Feed vertices straight to OpenGL for CoordModeOrigin.
1074 if (shape == Convex) {
1076 GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc.
1077 short v[2] = {0, 0};
1079 for (unsigned i = 0; i < npoints; i++) {
1080 next_point(v, points[i], mode);
1081 vertices[2 * i] = v[0];
1082 vertices[2 * i + 1] = v[1];
1085 glEnableClientState (GL_VERTEX_ARRAY);
1086 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1088 glVertexPointer (2, GL_SHORT, 0, vertices);
1089 glDrawArrays (GL_TRIANGLE_FAN, 0, npoints);
1093 } else if (shape == Nonconvex) {
1095 // TODO: assert that x,y of first and last point match, as that is assumed
1098 root = (linked_point *) malloc( sizeof(linked_point) );
1099 set_points_list(points,npoints,root);
1100 traverse_points_list(root);
1103 Assert((shape == Convex || shape == Nonconvex),
1104 "XFillPolygon: (TODO) Unimplemented shape");
1110 #define radians(DEG) ((DEG) * M_PI / 180.0)
1111 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1114 arc_xy(GLfloat *p, double cx, double cy, double w2, double h2, double theta)
1116 p[0] = cos(theta) * w2 + cx;
1117 p[1] = -sin(theta) * h2 + cy;
1121 mod_neg(int a, unsigned b)
1123 /* Normal modulus is implementation defined for negative numbers. This is
1124 * well-defined such that the repeating pattern for a >= 0 is maintained for
1126 return a < 0 ? (b - 1) - (-(a + 1) % b) : a % b;
1129 /* TODO: Fill in arcs with line width > 1 */
1131 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1132 unsigned int width, unsigned int height,
1133 int angle1, int angle2, Bool fill_p)
1135 int gglw = gc->gcv.line_width;
1137 if (fill_p || gglw <= 1) {
1138 draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p);
1141 int w1, w2, h1, h2, gglwh;
1151 //draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p);
1152 draw_arc_gl (dpy, d, gc, x1, y1, w1, h1, angle1, angle2, fill_p);
1153 draw_arc_gl (dpy, d, gc, x2, y2, w2, h2, angle1, angle2, fill_p);
1160 draw_arc_gl (Display *dpy, Drawable d, GC gc, int x, int y,
1161 unsigned int width, unsigned int height,
1162 int angle1, int angle2, Bool fill_p)
1164 set_fg_gc(dpy, d, gc);
1166 /* Let's say the number of line segments needed to make a convincing circle is
1167 4*sqrt(radius). (But these arcs aren't necessarily circular arcs...) */
1169 double w2 = width * 0.5f, h2 = height * 0.5f;
1170 double a, b; /* Semi-major/minor axes. */
1179 const double two_pi = 2 * M_PI;
1181 double amb = a - b, apb = a + b;
1182 double h = (amb * amb) / (apb * apb);
1183 // TODO: Math cleanup.
1184 double C_approx = M_PI * apb * (1 + 3 * h / (10 + sqrtf(4 - 3 * h)));
1185 double segments_f = 4 * sqrtf(C_approx / (2 * M_PI));
1187 // TODO: Explain how drawing works what with the points of overlapping arcs
1191 unsigned segments_360 = segments_f;
1193 /* TODO: angle2 == 0. This is a tilted square with CapSquare. */
1194 /* TODO: color, thick lines, CapNotLast for thin lines */
1195 /* TODO: Transformations. */
1197 double segment_angle = two_pi / segments_360;
1199 const unsigned deg64 = 360 * 64;
1200 const double rad_from_deg64 = two_pi / deg64;
1207 angle1 = mod_neg(angle1, deg64); // TODO: Is this OK? Consider negative numbers.
1210 angle2 = deg64; // TODO: Handle circles special.
1213 angle1_f = angle1 * rad_from_deg64,
1214 angle2_f = angle2 * rad_from_deg64;
1216 if (angle2_f > two_pi) // TODO: Move this up.
1219 double segment1_angle_part = fmodf(angle1_f, segment_angle);
1221 unsigned segment1 = ((angle1_f - segment1_angle_part) / segment_angle) + 1.5;
1223 double angle_2r = angle2_f - segment1_angle_part;
1224 unsigned segments = angle_2r / segment_angle;
1226 GLfloat cx = x + w2, cy = y + h2;
1228 GLfloat *data = malloc((segments + 3) * sizeof(GLfloat) * 2); // TODO: Check result.
1230 GLfloat *data_ptr = data;
1237 arc_xy (data_ptr, cx, cy, w2, h2, angle1_f);
1240 for (unsigned s = 0; s != segments; ++s) {
1241 // TODO: Make sure values of theta for the following arc_xy call are between
1242 // angle1_f and angle1_f + angle2_f.
1243 arc_xy (data_ptr, cx, cy, w2, h2, (segment1 + s) * segment_angle);
1247 arc_xy (data_ptr, cx, cy, w2, h2, angle1_f + angle2_f);
1250 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1251 glEnableClientState (GL_VERTEX_ARRAY);
1253 glVertexPointer (2, GL_FLOAT, 0, data);
1254 glDrawArrays (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP,
1256 (GLsizei)((data_ptr - data) / 2));
1263 unsigned segments = segments_f * (fabs(angle2) / (360 * 64));
1265 glBegin (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP);
1267 if (fill_p /* && gc->gcv.arc_mode == ArcPieSlice */)
1268 glVertex2f (cx, cy);
1270 /* TODO: This should fix the middle points of the arc so that the starting and ending points are OK. */
1272 float to_radians = 2 * M_PI / (360 * 64);
1273 float theta = angle1 * to_radians, d_theta = angle2 * to_radians / segments;
1275 for (unsigned s = 0; s != segments + 1; ++s) /* TODO: This is the right number of segments, yes? */
1277 glVertex2f(cos(theta) * w2 + cx, -sin(theta) * h2 + cy);
1303 CreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1305 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1306 gc->depth = jwxyz_drawable_depth (d);
1308 jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
1309 XChangeGC (dpy, gc, mask, xgcv);
1315 FreeGC (Display *dpy, GC gc)
1318 XUnloadFont (dpy, gc->gcv.font);
1322 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1324 if (gc->gcv.clip_mask) {
1325 XFreePixmap (dpy, gc->gcv.clip_mask);
1326 CGImageRelease (gc->clip_mask);
1335 PutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1336 int src_x, int src_y, int dest_x, int dest_y,
1337 unsigned int w, unsigned int h)
1339 jwxyz_assert_display (dpy);
1341 const XRectangle *wr = jwxyz_frame (d);
1343 Assert (gc, "no GC");
1344 Assert ((w < 65535), "improbably large width");
1345 Assert ((h < 65535), "improbably large height");
1346 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1347 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1348 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1349 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1351 // Clip width and height to the bounds of the Drawable
1353 if (dest_x + w > wr->width) {
1354 if (dest_x > wr->width)
1356 w = wr->width - dest_x;
1358 if (dest_y + h > wr->height) {
1359 if (dest_y > wr->height)
1361 h = wr->height - dest_y;
1363 if (w <= 0 || h <= 0)
1366 // Clip width and height to the bounds of the XImage
1368 if (src_x + w > ximage->width) {
1369 if (src_x > ximage->width)
1371 w = ximage->width - src_x;
1373 if (src_y + h > ximage->height) {
1374 if (src_y > ximage->height)
1376 h = ximage->height - src_y;
1378 if (w <= 0 || h <= 0)
1381 /* Assert (d->win */
1383 if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
1386 jwxyz_gl_flush (dpy);
1387 jwxyz_bind_drawable (dpy, dpy->main_window, d);
1388 jwxyz_gl_set_gc (dpy, gc);
1390 int bpl = ximage->bytes_per_line;
1391 int bpp = ximage->bits_per_pixel;
1395 GLint tex_internalformat;
1396 GLenum tex_format, tex_type;
1399 tex_data = ximage->data + src_y * bpl + (src_x * 4);
1401 jwxyz_assert_display(dpy);
1403 /* There probably won't be any hacks that do this, but... */
1404 Assert (!(bpl % 4), "XPutImage: bytes_per_line not divisible by four.");
1406 tex_internalformat = texture_internalformat(dpy);
1407 tex_format = dpy->pixel_format;
1408 tex_type = gl_pixel_type(dpy);
1410 /* GL_UNPACK_ROW_LENGTH is not allowed to be negative. (sigh) */
1411 # ifndef HAVE_JWZGLES
1413 glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
1418 // glPixelStorei (GL_UNPACK_ALIGNMENT, 4); // Probably unnecessary.
1422 Assert (bpp == 1, "expected 1 or 32 bpp");
1423 Assert ((src_x % 8) == 0,
1424 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1426 const char *src_data = ximage->data + src_y * bpl + (src_x / 8);
1427 unsigned w8 = (w + 7) / 8;
1431 tex_data = malloc(src_w * h);
1433 uint32_t *data_out = (uint32_t *)tex_data;
1434 for(unsigned y = h; y; --y) {
1435 for(unsigned x = 0; x != w8; ++x) {
1436 // TODO: Does big endian work here?
1437 uint8_t byte = src_data[x];
1438 uint32_t word = byte;
1439 word = (word & 0x3) | ((word & 0xc) << 14);
1440 word = (word & 0x00010001) | ((word & 0x00020002) << 7);
1441 data_out[x << 1] = (word << 8) - word;
1444 word = (word & 0x3) | ((word & 0xc) << 14);
1445 word = (word & 0x00010001) | ((word & 0x00020002) << 7);
1446 data_out[(x << 1) | 1] = (word << 8) - word;
1449 data_out += src_w / 4;
1452 tex_internalformat = GL_LUMINANCE;
1453 tex_format = GL_LUMINANCE;
1454 tex_type = GL_UNSIGNED_BYTE;
1456 // glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1458 set_color (dpy, gc->gcv.foreground, gc->depth, gc->gcv.alpha_allowed_p);
1459 // TODO: Deal with the background color.
1462 # if 1 // defined HAVE_JWZGLES
1463 // Regular OpenGL uses GL_TEXTURE_RECTANGLE_EXT in place of GL_TEXTURE_2D.
1464 // TODO: Make use of OES_draw_texture.
1466 unsigned tex_w = src_w, tex_h = h;
1467 if (!dpy->gl_texture_npot_p) {
1468 tex_w = to_pow2(tex_w);
1469 tex_h = to_pow2(tex_h);
1472 glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
1474 // A fun project: reimplement xshm.c by means of a PBO using
1475 // GL_MAP_UNSYNCHRONIZED_BIT.
1477 // TODO: Would using glTexSubImage2D exclusively be faster?
1478 if (tex_w == src_w && tex_h == h) {
1479 glTexImage2D (dpy->gl_texture_target, 0, tex_internalformat, tex_w, tex_h,
1480 0, tex_format, tex_type, tex_data);
1482 // TODO: Sampling the last row might be a problem if src_x != 0.
1483 glTexImage2D (dpy->gl_texture_target, 0, tex_internalformat, tex_w, tex_h,
1484 0, tex_format, tex_type, NULL);
1485 glTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, src_w, h,
1486 tex_format, tex_type, tex_data);
1492 // TODO: This looks a lot like jwxyz_gl_draw_image. Refactor.
1494 // glEnable (dpy->gl_texture_target);
1495 // glColor4f (0.5, 0, 1, 1);
1496 glEnable (dpy->gl_texture_target);
1497 glEnableClientState (GL_VERTEX_ARRAY);
1498 glEnableClientState (GL_TEXTURE_COORD_ARRAY);
1500 // TODO: Why are these ever turned on in the first place?
1501 glDisableClientState (GL_COLOR_ARRAY);
1502 glDisableClientState (GL_NORMAL_ARRAY);
1503 // glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1505 GLfloat vertices[4][2] =
1508 {dest_x, dest_y + h},
1509 {dest_x + w, dest_y + h},
1510 {dest_x + w, dest_y}
1513 GLfloat texcoord_w, texcoord_h;
1514 # ifndef HAVE_JWZGLES
1515 if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT) {
1519 # endif /* HAVE_JWZGLES */
1521 texcoord_w = (double)w / tex_w;
1522 texcoord_h = (double)h / tex_h;
1525 GLfloat tex_coords[4][2];
1526 tex_coords[0][0] = 0;
1527 tex_coords[0][1] = 0;
1528 tex_coords[1][0] = 0;
1529 tex_coords[1][1] = texcoord_h;
1530 tex_coords[2][0] = texcoord_w;
1531 tex_coords[2][1] = texcoord_h;
1532 tex_coords[3][0] = texcoord_w;
1533 tex_coords[3][1] = 0;
1535 glVertexPointer (2, GL_FLOAT, 0, vertices);
1536 glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
1537 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1540 glDisable (dpy->gl_texture_target);
1542 glRasterPos2i (dest_x, dest_y);
1543 glPixelZoom (1, -1);
1544 jwxyz_assert_display (dpy);
1545 glDrawPixels (w, h, dpy->pixel_format, gl_pixel_type(dpy), data);
1553 /* At the moment only XGetImage and get_xshm_image use XGetSubImage. */
1554 /* #### Twang calls XGetImage on the window intending to get a
1555 buffer full of black. This is returning a buffer full of white
1556 instead of black for some reason. */
1558 GetSubImage (Display *dpy, Drawable d, int x, int y,
1559 unsigned int width, unsigned int height,
1560 unsigned long plane_mask, int format,
1561 XImage *dest_image, int dest_x, int dest_y)
1563 Assert ((width < 65535), "improbably large width");
1564 Assert ((height < 65535), "improbably large height");
1565 Assert ((x < 65535 && x > -65535), "improbably large x");
1566 Assert ((y < 65535 && y > -65535), "improbably large y");
1568 jwxyz_gl_flush (dpy);
1569 jwxyz_bind_drawable (dpy, dpy->main_window, d);
1571 // TODO: What if this reads off the edge? What is supposed to happen?
1574 // In case the caller tries to write off the edge.
1576 max_width = dest_image->width - dest_x,
1577 max_height = dest_image->height - dest_y;
1579 if (width > max_width) {
1583 if (height > max_height) {
1584 height = max_height;
1588 Assert (jwxyz_drawable_depth (d) == dest_image->depth, "XGetSubImage: depth mismatch");
1590 if (dest_image->depth == visual_depth (NULL, NULL)) {
1591 Assert (!(dest_image->bytes_per_line % 4), "XGetSubImage: bytes_per_line not divisible by 4");
1592 unsigned pixels_per_line = dest_image->bytes_per_line / 4;
1594 Assert (pixels_per_line == width, "XGetSubImage: (TODO) pixels_per_line != width");
1596 glPixelStorei (GL_PACK_ROW_LENGTH, pixels_per_line);
1598 glPixelStorei (GL_PACK_ALIGNMENT, 4);
1600 uint32_t *dest_data = (uint32_t *)dest_image->data + pixels_per_line * dest_y + dest_x;
1602 glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
1603 dpy->pixel_format, gl_pixel_type(dpy), dest_data);
1605 /* Flip this upside down. :( */
1606 uint32_t *top = dest_data;
1607 uint32_t *bottom = dest_data + pixels_per_line * (height - 1);
1608 for (unsigned y = height / 2; y; --y) {
1609 for (unsigned x = 0; x != width; ++x) {
1610 uint32_t px = top[x];
1614 top += pixels_per_line;
1615 bottom -= pixels_per_line;
1619 /* TODO: Actually get pixels. */
1621 Assert (!(dest_x % 8), "XGetSubImage: dest_x not byte-aligned");
1622 uint8_t *dest_data =
1623 (uint8_t *)dest_image->data + dest_image->bytes_per_line * dest_y
1625 for (unsigned y = height / 2; y; --y) {
1626 memset (dest_data, y & 1 ? 0x55 : 0xAA, width / 8);
1627 dest_data += dest_image->bytes_per_line;
1637 copy_pixmap (Display *dpy, Pixmap p)
1640 Assert (p->type == PIXMAP, "not a pixmap");
1646 unsigned int width, height, border_width, depth;
1647 if (XGetGeometry (dpy, p, &root,
1648 &x, &y, &width, &height, &border_width, &depth)) {
1650 gcv.function = GXcopy;
1651 GC gc = XCreateGC (dpy, p, GCFunction, &gcv);
1653 p2 = XCreatePixmap (dpy, p, width, height, depth);
1655 XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0);
1660 Assert (p2, "could not copy pixmap");
1668 SetClipMask (Display *dpy, GC gc, Pixmap m)
1670 Log ("TODO: No clip masks yet");
1671 /* Protip: Do glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1672 clearing just the stencil buffer in a packed depth/stencil arrangement is
1673 slower than the above. Adreno recommends this, but other GPUs probably
1680 SetClipOrigin (Display *dpy, GC gc, int x, int y)
1682 gc->gcv.clip_x_origin = x;
1683 gc->gcv.clip_y_origin = y;
1687 void set_points_list(XPoint *points, int npoints, linked_point *root)
1689 linked_point *current;
1692 for (int i = 0; i < npoints - 2 ; i++) {
1693 current->x = points[i].x;
1694 current->y = points[i].y;
1695 current->next = (linked_point *) malloc(sizeof(linked_point));
1696 current = current->next;
1698 current->x = points[npoints-2].x;
1699 current->y = points[npoints-2].y;
1700 current->next = root;
1704 double compute_edge_length(linked_point * a, linked_point * b)
1707 int xdiff, ydiff, xsq, ysq, added;
1708 double xy_add, edge_length;
1710 xdiff = a->x - b->x;
1711 ydiff = a->y - b->y;
1712 xsq = xdiff * xdiff;
1713 ysq = ydiff * ydiff;
1715 xy_add = (double) added;
1716 edge_length = sqrt(xy_add);
1720 double get_angle(double a, double b, double c)
1722 double cos_a, i_cos_a;
1723 cos_a = (((b * b) + (c * c)) - (a * a)) / (double) (2.0 * b * c);
1724 i_cos_a = acos(cos_a);
1729 Bool is_same_slope(linked_point * a)
1732 int abx, bcx, aby, bcy, aa, bb;
1739 // test if slopes are indefinite for both line segments
1741 return b->x == c->x;
1742 } else if (b->x == c->x) {
1743 return False; // false, as ax/bx is not indefinite
1756 void draw_three_vertices(linked_point * a, Bool triangle)
1766 GLfloat vertices[3][2] = {
1773 drawType = GL_TRIANGLES;
1775 drawType = GL_LINES;
1778 glEnableClientState(GL_VERTEX_ARRAY);
1779 glVertexPointer(2, GL_FLOAT, 0, vertices);
1780 glDrawArrays(drawType, 0, 3);
1782 free(b); // cut midpoint off from remaining polygon vertex list
1787 Bool is_an_ear(linked_point * a)
1789 double edge_ab, edge_bc, edge_ac;
1790 double angle_a, angle_b, angle_c;
1792 linked_point *b, *c;
1796 my_pi = (double) M_PI;
1798 edge_ab = compute_edge_length(a, b);
1799 edge_bc = compute_edge_length(b, c);
1800 edge_ac = compute_edge_length(a, c);
1801 angle_a = get_angle(edge_bc, edge_ab, edge_ac);
1802 angle_b = get_angle(edge_ac, edge_ab, edge_bc);
1803 angle_c = get_angle(edge_ab, edge_ac, edge_bc);
1805 return angle_a < my_pi && angle_b < my_pi && angle_c < my_pi;
1809 Bool is_three_point_loop(linked_point * head)
1811 return head->x == head->next->next->next->x
1812 && head->y == head->next->next->next->y;
1816 void traverse_points_list(linked_point * root)
1821 while (!is_three_point_loop(head)) {
1822 if (is_an_ear(head)) {
1823 draw_three_vertices(head, True);
1824 } else if (is_same_slope(head)) {
1825 draw_three_vertices(head, False);
1831 // handle final three vertices in polygon
1832 if (is_an_ear(head)) {
1833 draw_three_vertices(head, True);
1834 } else if (is_same_slope(head)) {
1835 draw_three_vertices(head, False);
1837 free(head->next->next);
1840 Assert (False, "traverse_points_list: unknown configuration");
1848 const struct jwxyz_vtbl gl_vtbl = {
1851 display_sources_data,
1875 #endif /* JWXYZ_GL -- entire file */