1 /* xscreensaver, Copyright (c) 1991-2018 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"
105 #define countof(x) (sizeof((x))/sizeof((*x)))
112 // Use two textures: one for RGBA, one for luminance. Older Android doesn't
113 // seem to like it when textures change format.
119 struct jwxyz_Display {
120 const struct jwxyz_vtbl *vtbl; // Must come first.
123 GLenum pixel_format, pixel_type;
125 struct jwxyz_sources_data *timers_data;
127 Bool gl_texture_npot_p;
128 /* Bool opengl_core_p */;
129 GLenum gl_texture_target;
131 GLuint textures[2]; // Also can work on the desktop.
133 unsigned long window_background;
136 Bool gc_alpha_allowed_p;
138 // Alternately, there could be one queue per pixmap.
139 size_t queue_size, queue_capacity;
140 Drawable queue_drawable;
143 uint32_t *queue_color;
152 struct jwxyz_linked_point {
159 jwxyz_assert_display(Display *dpy)
164 jwxyz_assert_drawable (dpy->main_window, dpy->main_window);
169 jwxyz_set_matrices (Display *dpy, unsigned width, unsigned height,
172 Assert (width, "no width");
173 Assert (height, "no height");
175 /* TODO: Check registration pattern from Interference with rectangles instead of points. */
177 // The projection matrix is always set as follows. The modelview matrix is
178 // usually identity, but for points and thin lines, it's translated by 0.5.
179 glMatrixMode(GL_PROJECTION);
182 # if defined(USE_IPHONE) || defined(HAVE_ANDROID)
184 if (window_p && ignore_rotation_p(dpy)) {
185 int o = (int) current_device_rotation();
186 glRotatef (-o, 0, 0, 1);
189 // glPointSize(1); // This is the default.
192 glOrthof /* TODO: Is glOrthox worth it? Signs point to no. */
196 (0, width, height, 0, -1, 1);
198 glMatrixMode(GL_MODELVIEW);
199 # endif // HAVE_MOBILE
206 // iOS always uses OpenGL ES 1.1.
212 gl_check_ver (const struct gl_version *caps,
216 return caps->major > gl_major ||
217 (caps->major == gl_major && caps->minor >= gl_minor);
223 extern const struct jwxyz_vtbl gl_vtbl;
226 jwxyz_gl_make_display (Window w)
228 Display *d = (Display *) calloc (1, sizeof(*d));
231 # ifndef HAVE_JWZGLES
232 struct gl_version version;
235 const GLubyte *version_str = glGetString (GL_VERSION);
237 /* iPhone is always OpenGL ES 1.1. */
238 if (sscanf ((const char *) version_str, "%u.%u",
239 &version.major, &version.minor) < 2)
245 # endif // !HAVE_JWZGLES
247 const GLubyte *extensions = glGetString (GL_EXTENSIONS);
250 - Apple TN2080: Understanding and Detecting OpenGL Functionality.
251 - OpenGL Programming Guide for the Mac - Best Practices for Working with
252 Texture Data - Optimal Data Formats and Types
255 // If a video adapter suports BGRA textures, then that's probably as fast as
256 // you're gonna get for getting a texture onto the screen.
258 /* TODO: Make BGRA work on iOS. As it is, it breaks XPutImage. (glTexImage2D, AFAIK) */
259 d->pixel_format = GL_RGBA; /*
260 gluCheckExtension ((const GLubyte *) "GL_APPLE_texture_format_BGRA8888",
261 extensions) ? GL_BGRA_EXT : GL_RGBA; */
262 d->pixel_type = GL_UNSIGNED_BYTE;
263 // See also OES_read_format.
264 # else // !HAVE_JWZGLES
265 if (gl_check_ver (&version, 1, 2) ||
266 (gluCheckExtension ((const GLubyte *) "GL_EXT_bgra", extensions) &&
267 gluCheckExtension ((const GLubyte *) "GL_APPLE_packed_pixels",
269 // APPLE_packed_pixels is only ever available on iOS, never Android.
270 d->pixel_format = GL_BGRA_EXT;
271 // Both Intel and PowerPC-era docs say to use GL_UNSIGNED_INT_8_8_8_8_REV.
272 d->pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
274 d->pixel_format = GL_RGBA;
275 d->pixel_type = GL_UNSIGNED_BYTE;
277 // GL_ABGR_EXT/GL_UNSIGNED_BYTE is another possibilty that may have made more
279 # endif // !HAVE_JWZGLES
281 // On really old systems, it would make sense to split textures
282 // into subsections, to work around the maximum texture size.
283 # ifndef HAVE_JWZGLES
284 d->gl_texture_npot_p = gluCheckExtension ((const GLubyte *)
285 "GL_ARB_texture_rectangle",
287 d->gl_texture_target = d->gl_texture_npot_p ?
288 GL_TEXTURE_RECTANGLE_EXT :
291 d->gl_texture_npot_p = jwzgles_gluCheckExtension
292 ((const GLubyte *) "GL_APPLE_texture_2D_limited_npot", extensions) ||
293 jwzgles_gluCheckExtension
294 ((const GLubyte *) "GL_OES_texture_npot", extensions) ||
295 jwzgles_gluCheckExtension // From PixelFlinger 1.4
296 ((const GLubyte *) "GL_ARB_texture_non_power_of_two", extensions);
298 d->gl_texture_target = GL_TEXTURE_2D;
301 Visual *v = &d->visual;
302 v->class = TrueColor;
303 if (d->pixel_format == GL_BGRA_EXT) {
304 v->rgba_masks[0] = 0x00ff0000;
305 v->rgba_masks[1] = 0x0000ff00;
306 v->rgba_masks[2] = 0x000000ff;
307 v->rgba_masks[3] = 0xff000000;
309 Assert(d->pixel_format == GL_RGBA,
310 "jwxyz_gl_make_display: Unknown pixel_format");
311 for (unsigned i = 0; i != 4; ++i) {
312 union color_bytes color;
314 color.bytes[i] = 0xff;
315 v->rgba_masks[i] = color.pixel;
319 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
321 d->window_background = BlackPixel(d,0);
325 GLint max_texture_size;
326 glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size);
327 Log ("GL_MAX_TEXTURE_SIZE: %d\n", max_texture_size);
330 glGenTextures (countof (d->textures), d->textures);
332 for (unsigned i = 0; i != countof (d->textures); i++) {
333 // TODO: Check for (ARB|EXT|NV)_texture_rectangle. (All three are alike.)
334 // Rectangle textures should be present on OS X with the following exceptions:
335 // - Generic renderer on PowerPC OS X 10.4 and earlier
337 glBindTexture (d->gl_texture_target, d->textures[i]);
338 // TODO: This is all somewhere else. Refactor.
339 glTexParameteri (d->gl_texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
340 glTexParameteri (d->gl_texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
342 // This might be redundant for rectangular textures.
343 # ifndef HAVE_JWZGLES
344 const GLint wrap = GL_CLAMP;
345 # else // HAVE_JWZGLES
346 const GLint wrap = GL_CLAMP_TO_EDGE;
347 # endif // HAVE_JWZGLES
349 // In OpenGL, CLAMP_TO_EDGE is OpenGL 1.2 or GL_SGIS_texture_edge_clamp.
350 // This is always present with OpenGL ES.
351 glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_S, wrap);
352 glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_T, wrap);
355 d->gc_function = GXcopy;
356 d->gc_alpha_allowed_p = False;
358 jwxyz_assert_display(d);
363 jwxyz_gl_free_display (Display *dpy)
365 Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable");
367 /* TODO: Go over everything. */
369 free (dpy->queue_vertex);
370 free (dpy->queue_color);
372 jwxyz_sources_free (dpy->timers_data);
378 /* Call this when the View changes size or position.
381 jwxyz_window_resized (Display *dpy)
383 Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable");
385 const XRectangle *new_frame = jwxyz_frame (dpy->main_window);
386 unsigned new_width = new_frame->width;
387 unsigned new_height = new_frame->height;
389 Assert (new_width, "jwxyz_window_resized: No width.");
390 Assert (new_height, "jwxyz_window_resized: No height.");
392 /*if (cgc) w->cgc = cgc;
393 Assert (w->cgc, "no CGContext"); */
395 Log("resize: %d, %d\n", new_width, new_height);
397 jwxyz_gl_flush (dpy);
398 jwxyz_bind_drawable (dpy, dpy->main_window, dpy->main_window);
400 // TODO: What does the iPhone need?
402 // iOS only: If the main_window is not the current_drawable, then set_matrices
403 // was already called in bind_drawable.
404 jwxyz_set_matrices (dpy, new_width, new_height, True);
408 glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
410 glDrawBuffer (GL_FRONT);
411 glClearColor (1, 0, 1, 0);
412 glClear (GL_COLOR_BUFFER_BIT);
413 glDrawBuffer (GL_BACK);
414 glClearColor (0, 1, 1, 0);
415 glClear (GL_COLOR_BUFFER_BIT);
417 glDrawBuffer (draw_buffer); */
419 // Stylish and attractive purple!
420 // glClearColor (1, 0, 1, 0.5);
421 // glClear (GL_COLOR_BUFFER_BIT);
425 static jwxyz_sources_data *
426 display_sources_data (Display *dpy)
428 return dpy->timers_data;
435 return dpy->main_window;
439 visual (Display *dpy)
445 /* GC attributes by usage and OpenGL implementation:
447 All drawing functions:
448 function | glLogicOp w/ GL_COLOR_LOGIC_OP
449 clip_x_origin, clip_y_origin, clip_mask | Stencil mask
451 Shape drawing functions:
452 foreground, background | glColor*
454 XDrawLines, XDrawRectangles, XDrawSegments:
455 line_width, cap_style, join_style | Lotsa vertices
458 fill_rule | Multiple GL_TRIANGLE_FANs
461 font | Cocoa, then OpenGL display lists.
463 alpha_allowed_p | GL_BLEND
465 antialias_p | Well, there's options:
466 * Multisampling would work, but that's something that would need to be set
467 per-Pixmap, not per-GC.
468 * GL_POINT, LINE, and POLYGON_SMOOTH are the old-school way of doing
469 this, but POINT_SMOOTH is unnecessary, and POLYGON_SMOOTH is missing from
470 GLES 1. All three are missing from GLES 2. Word on the street is that
471 these are deprecated anyway.
472 * Tiny textures with bilinear filtering to get the same effect as LINE_ and
473 POLYGON_SMOOTH. A bit tricky.
474 * Do nothing. Android hardware is very often high-DPI enough that
475 anti-aliasing doesn't matter all that much.
482 enqueue (Display *dpy, Drawable d, GC gc, int mode, size_t count,
485 if (dpy->queue_size &&
486 (dpy->gc_function != gc->gcv.function ||
487 dpy->gc_alpha_allowed_p != gc->gcv.alpha_allowed_p ||
488 dpy->queue_mode != mode ||
489 dpy->queue_drawable != d)) {
490 jwxyz_gl_flush (dpy);
493 jwxyz_bind_drawable (dpy, dpy->main_window, d);
494 jwxyz_gl_set_gc (dpy, gc);
496 if (mode == GL_TRIANGLE_STRIP)
497 Assert (count, "empty triangle strip");
498 // Use degenerate triangles to cut down on draw calls.
499 Bool prepend2 = mode == GL_TRIANGLE_STRIP && dpy->queue_size;
501 // ####: Primitive restarts should be used here when (if) they're available.
505 // TODO: Use glColor when we can get away with it.
506 size_t old_size = dpy->queue_size;
507 dpy->queue_size += count;
508 if (dpy->queue_size > dpy->queue_capacity) {
509 dpy->queue_capacity = dpy->queue_size * 2;
511 uint32_t *new_color = realloc (
512 dpy->queue_color, sizeof(*dpy->queue_color) * dpy->queue_capacity);
513 /* Allocate vertices as if they were always GLfloats. Otherwise, if
514 queue_vertex is allocated to hold GLshorts, then things get switched
515 to GLfloats, queue_vertex would be too small for the given capacity.
517 GLshort *new_vertex = realloc (
518 dpy->queue_vertex, sizeof(GLfloat) * 2 * dpy->queue_capacity);
520 if (!new_color || !new_vertex)
523 dpy->queue_color = new_color;
524 dpy->queue_vertex = new_vertex;
527 dpy->queue_mode = mode;
528 dpy->queue_drawable = d;
530 union color_bytes color;
531 // TODO: validate color
532 JWXYZ_QUERY_COLOR (dpy, pixel, 0xffull, color.bytes);
533 for (size_t i = 0; i != count; ++i) // TODO: wmemset when applicable.
534 dpy->queue_color[i + old_size] = color.pixel;
536 void *result = (char *)dpy->queue_vertex + old_size * 2 *
537 (mode == GL_TRIANGLE_STRIP ? sizeof(GLfloat) : sizeof(GLshort));
539 dpy->queue_color[old_size] = dpy->queue_color[old_size - 1];
540 result = (GLfloat *)result + 4;
547 finish_triangle_strip (Display *dpy, GLfloat *enqueue_result)
549 if (enqueue_result != dpy->queue_vertex) {
550 enqueue_result[-4] = enqueue_result[-6];
551 enqueue_result[-3] = enqueue_result[-5];
552 enqueue_result[-2] = enqueue_result[0];
553 enqueue_result[-1] = enqueue_result[1];
559 set_clip_mask (GC gc)
561 Assert (!gc->gcv.clip_mask, "set_clip_mask: TODO");
566 set_color (Display *dpy, unsigned long pixel, unsigned int depth,
567 Bool alpha_allowed_p)
569 jwxyz_validate_pixel (dpy, pixel, depth, alpha_allowed_p);
573 glColor4f (f, f, f, 1);
576 JWXYZ_QUERY_COLOR (dpy, pixel, 1.0f, rgba);
577 glColor4f (rgba[0], rgba[1], rgba[2], rgba[3]);
581 /* Pushes a GC context; sets Function and ClipMask. */
583 jwxyz_gl_set_gc (Display *dpy, GC gc)
586 Bool alpha_allowed_p;
588 // GC is NULL for XClearArea and XClearWindow.
590 function = gc->gcv.function;
591 alpha_allowed_p = gc->gcv.alpha_allowed_p;
595 alpha_allowed_p = False;
596 // TODO: Set null clip mask for NULL GC here.
599 /* GL_COLOR_LOGIC_OP: OpenGL 1.1. */
600 if (function != dpy->gc_function) {
601 dpy->gc_function = function;
602 if (function != GXcopy) {
603 /* Fun fact: The glLogicOp opcode constants are the same as the X11 GX*
604 function constants | GL_CLEAR.
606 glEnable (GL_COLOR_LOGIC_OP);
607 glLogicOp (gc->gcv.function | GL_CLEAR);
609 glDisable (GL_COLOR_LOGIC_OP);
613 /* Cocoa uses add/subtract/difference blending in place of logical ops.
614 It looks nice, but implementing difference blending in OpenGL appears to
615 require GL_KHR_blend_equation_advanced, and support for this is not
619 if (alpha_allowed_p != dpy->gc_alpha_allowed_p) {
620 dpy->gc_alpha_allowed_p = alpha_allowed_p;
621 if (gc && gc->gcv.alpha_allowed_p) {
622 // TODO: Maybe move glBlendFunc to XCreatePixmap?
623 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
626 glDisable (GL_BLEND);
633 set_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color)
635 jwxyz_gl_flush (dpy);
636 jwxyz_bind_drawable (dpy, dpy->main_window, d);
637 jwxyz_gl_set_gc (dpy, gc);
644 switch (gc->gcv.function) {
645 case GXset: color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
646 case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
649 depth = visual_depth (NULL, NULL);
652 set_color (dpy, color, depth, gc ? gc->gcv.alpha_allowed_p : False);
655 /* Pushes a GC context; sets color to the foreground color.
658 set_fg_gc (Display *dpy, Drawable d, GC gc)
660 set_color_gc (dpy, d, gc, gc->gcv.foreground);
664 next_point(short *v, XPoint p, int mode)
667 case CoordModeOrigin:
671 case CoordModePrevious:
676 Assert (False, "next_point: bad mode");
682 DrawPoints (Display *dpy, Drawable d, GC gc,
683 XPoint *points, int count, int mode)
687 // TODO: XPoints can be fed directly to OpenGL.
688 GLshort *gl_points = enqueue (dpy, d, gc, GL_POINTS, count,
689 gc->gcv.foreground); // TODO: enqueue returns NULL.
690 for (unsigned i = 0; i < count; i++) {
691 next_point (v, points[i], mode);
692 gl_points[2 * i] = v[0];
693 gl_points[2 * i + 1] = v[1];
701 texture_internalformat (Display *dpy)
704 return dpy->pixel_format;
711 gl_pixel_type (const Display *dpy)
713 return dpy->pixel_type;
717 clear_texture (Display *dpy)
719 glTexImage2D (dpy->gl_texture_target, 0, texture_internalformat(dpy), 0, 0,
720 0, dpy->pixel_format, gl_pixel_type (dpy), NULL);
727 glColor4f (1, 1, 1, 1);
729 glColor3ub (0xff, 0xff, 0xff);
735 jwxyz_gl_flush (Display *dpy)
737 Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable");
739 if (!dpy->queue_size)
742 // jwxyz_bind_drawable() and jwxyz_gl_set_gc() is called in enqueue().
744 glEnableClientState (GL_COLOR_ARRAY);
745 glEnableClientState (GL_VERTEX_ARRAY);
746 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
748 // TODO: Use glColor instead of glColorPointer if there's just one color.
749 // TODO: Does OpenGL use both GL_COLOR_ARRAY and glColor at the same time?
752 Bool shifted = dpy->queue_mode == GL_POINTS || dpy->queue_mode == GL_LINES;
754 glMatrixMode (GL_MODELVIEW);
755 glTranslatef (0.5, 0.5, 0);
758 glColorPointer (4, GL_UNSIGNED_BYTE, 0, dpy->queue_color);
760 dpy->queue_mode == GL_TRIANGLE_STRIP ? GL_FLOAT : GL_SHORT,
761 0, dpy->queue_vertex);
762 glDrawArrays (dpy->queue_mode, 0, dpy->queue_size);
764 // TODO: This is right, right?
765 if (dpy->queue_mode == GL_LINES && dpy->queue_line_cap) {
766 Assert (!(dpy->queue_size % 2), "bad count for GL_LINES");
767 glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(GLubyte) * 8,
769 glVertexPointer (2, GL_SHORT, sizeof(GLshort) * 4,
770 (GLshort *)dpy->queue_vertex + 2);
771 glDrawArrays (GL_POINTS, 0, dpy->queue_size / 2);
777 glDisableClientState (GL_COLOR_ARRAY);
778 glDisableClientState (GL_VERTEX_ARRAY);
785 jwxyz_gl_copy_area_read_tex_image (Display *dpy, unsigned src_height,
786 int src_x, int src_y,
787 unsigned int width, unsigned int height,
788 int dst_x, int dst_y)
790 # if defined HAVE_COCOA && !defined USE_IPHONE
791 /* TODO: Does this help? */
795 /* TODO: Fix TestX11 + mode_preserve with this one. */
797 unsigned tex_w = width, tex_h = height;
798 if (!dpy->gl_texture_npot_p) {
799 tex_w = to_pow2(tex_w);
800 tex_h = to_pow2(tex_h);
803 GLint internalformat = texture_internalformat(dpy);
805 /* TODO: This probably shouldn't always be texture_rgba. */
806 glBindTexture (dpy->gl_texture_target, dpy->textures[texture_rgba]);
808 if (tex_w == width && tex_h == height) {
809 glCopyTexImage2D (dpy->gl_texture_target, 0, internalformat,
810 src_x, src_height - src_y - height, width, height, 0);
812 glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
813 0, dpy->pixel_format, gl_pixel_type(dpy), NULL);
814 glCopyTexSubImage2D (dpy->gl_texture_target, 0, 0, 0,
815 src_x, src_height - src_y - height, width, height);
820 jwxyz_gl_copy_area_write_tex_image (Display *dpy, GC gc, int src_x, int src_y,
821 unsigned int width, unsigned int height,
822 int dst_x, int dst_y)
824 jwxyz_gl_set_gc (dpy, gc);
826 /* TODO: Copy-pasted from read_tex_image. */
827 unsigned tex_w = width, tex_h = height;
828 if (!dpy->gl_texture_npot_p) {
829 tex_w = to_pow2(tex_w);
830 tex_h = to_pow2(tex_h);
833 /* Must match what's in jwxyz_gl_copy_area_read_tex_image. */
834 glBindTexture (dpy->gl_texture_target, dpy->textures[texture_rgba]);
836 jwxyz_gl_draw_image (dpy->gl_texture_target, tex_w, tex_h, 0, 0,
837 width, height, dst_x, dst_y);
844 jwxyz_gl_draw_image (GLenum gl_texture_target,
845 unsigned int tex_w, unsigned int tex_h,
846 int src_x, int src_y,
847 unsigned int width, unsigned int height,
848 int dst_x, int dst_y)
851 glEnable (gl_texture_target);
853 glEnableClientState (GL_TEXTURE_COORD_ARRAY);
854 glEnableClientState (GL_VERTEX_ARRAY);
856 /* TODO: Copied from XPutImage. Refactor. */
857 /* TODO: EXT_draw_texture or whatever it's called. */
858 GLfloat vertices[4][2] =
861 {dst_x, dst_y + height},
862 {dst_x + width, dst_y + height},
863 {dst_x + width, dst_y}
867 tex_x0 = src_x, tex_y0 = src_y + height,
868 tex_x1 = src_x + width, tex_y1 = src_y;
870 # ifndef HAVE_JWZGLES
871 if (gl_texture_target != GL_TEXTURE_RECTANGLE_EXT)
874 GLfloat mx = 1.0f / tex_w, my = 1.0f / tex_h;
881 GLfloat tex_coords[4][2] =
889 glVertexPointer (2, GL_FLOAT, 0, vertices);
890 glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
891 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
893 glDisable (gl_texture_target);
897 jwxyz_gl_copy_area_read_pixels (Display *dpy, Drawable src, Drawable dst,
898 GC gc, int src_x, int src_y,
899 unsigned int width, unsigned int height,
900 int dst_x, int dst_y)
902 XImage *img = XGetImage (dpy, src, src_x, src_y, width, height, ~0, ZPixmap);
903 XPutImage (dpy, dst, gc, img, 0, 0, dst_x, dst_y, width, height);
909 DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
912 set_fg_gc (dpy, d, gc);
915 * Zero-length line segments
916 * Paths with zero length total (Remember line width, cap style.)
923 GLshort *vertices = malloc(2 * sizeof(GLshort) * count); // TODO malloc NULL sigh
925 glMatrixMode (GL_MODELVIEW);
926 glTranslatef (0.5f, 0.5f, 0);
929 for (unsigned i = 0; i < count; i++) {
930 next_point (p, points[i], mode);
931 vertices[2 * i] = p[0];
932 vertices[2 * i + 1] = p[1];
935 glEnableClientState (GL_VERTEX_ARRAY);
936 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
937 glVertexPointer (2, GL_SHORT, 0, vertices);
938 glDrawArrays (GL_LINE_STRIP, 0, count);
942 if (gc->gcv.cap_style != CapNotLast) {
943 // TODO: How does this look with multisampling?
944 // TODO: Disable me for closed loops.
945 glVertexPointer (2, GL_SHORT, 0, p);
946 glDrawArrays (GL_POINTS, 0, 1);
955 // Turn line segment into parallelogram based on line_width
957 // TODO: Fix epicycle hack with large thickness, and truchet line segment ends
960 drawThickLine (Display *dpy, Drawable d, GC gc, int line_width,
963 double dx, dy, di, m, angle;
964 int sx1, sx2, sy1, sy2;
973 di = sqrt(dx * dx + dy * dy);
980 float sn = sin(angle);
981 float cs = cos(angle);
982 float line_width_f = (float) line_width;
984 float wsn = line_width_f * (sn/2);
985 float csn = line_width_f * (cs/2);
987 float x3 = sx1 - wsn;
988 float y3 = sy1 + csn;
989 float x4 = sx1 + wsn;
990 float y4 = sy1 - csn;
992 float x5 = sx2 - wsn;
993 float y5 = sy2 + csn;
994 float x6 = sx2 + wsn;
995 float y6 = sy2 - csn;
997 GLfloat *coords = enqueue (dpy, d, gc, GL_TRIANGLE_STRIP, 4,
1007 finish_triangle_strip (dpy, coords);
1012 DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1014 /* TODO: Caps on thick lines. */
1015 /* Thin lines <= 1px are offset by +0.5; thick lines are not. */
1017 if (count == 1 && gc->gcv.line_width > 1) {
1018 drawThickLine (dpy, d, gc, gc->gcv.line_width, segments);
1021 if (dpy->queue_line_cap != (gc->gcv.cap_style != CapNotLast))
1022 jwxyz_gl_flush (dpy);
1023 dpy->queue_line_cap = gc->gcv.cap_style != CapNotLast;
1025 // TODO: Static assert here.
1026 Assert (sizeof(XSegment) == sizeof(short) * 4 &&
1027 sizeof(GLshort) == sizeof(short) &&
1028 offsetof(XSegment, x1) == 0 &&
1029 offsetof(XSegment, x2) == 4,
1030 "XDrawSegments: Data alignment mix-up.");
1032 memcpy (enqueue(dpy, d, gc, GL_LINES, count * 2, gc->gcv.foreground),
1033 segments, count * sizeof(XSegment));
1041 ClearWindow (Display *dpy, Window win)
1043 Assert (win == dpy->main_window, "not a window");
1045 jwxyz_gl_flush (dpy);
1046 jwxyz_bind_drawable (dpy, win, win);
1049 JWXYZ_QUERY_COLOR (dpy, dpy->window_background, 1.0f, color);
1051 glClearColor (color[0], color[1], color[2], 1);
1052 glClear (GL_COLOR_BUFFER_BIT);
1056 static unsigned long *
1057 window_background (Display *dpy)
1059 return &dpy->window_background;
1063 fill_rects (Display *dpy, Drawable d, GC gc,
1064 const XRectangle *rectangles, unsigned long nrectangles,
1065 unsigned long pixel)
1067 for (unsigned long i = 0; i != nrectangles; ++i) {
1068 const XRectangle *r = &rectangles[i];
1069 GLfloat *coords = enqueue (dpy, d, gc, GL_TRIANGLE_STRIP, 4, pixel);
1073 coords[3] = r->y + r->height;
1074 coords[4] = r->x + r->width;
1076 coords[6] = r->x + r->width;
1077 coords[7] = r->y + r->height;
1078 finish_triangle_strip (dpy, coords);
1084 FillPolygon (Display *dpy, Drawable d, GC gc,
1085 XPoint *points, int npoints, int shape, int mode)
1087 set_fg_gc(dpy, d, gc);
1089 // TODO: Re-implement the GLU tesselation functions.
1091 /* Complex: Pedal, and for some reason Attraction, Mountain, Qix, SpeedMine, Starfish
1092 * Nonconvex: Goop, Pacman, Rocks, Speedmine
1094 * We currently do Nonconvex with the simple-to-implement ear clipping
1095 * algorithm, but in the future we can replace that with an algorithm
1096 * with slower big-O growth
1101 // TODO: Feed vertices straight to OpenGL for CoordModeOrigin.
1103 if (shape == Convex) {
1105 GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc.
1106 short v[2] = {0, 0};
1108 for (unsigned i = 0; i < npoints; i++) {
1109 next_point(v, points[i], mode);
1110 vertices[2 * i] = v[0];
1111 vertices[2 * i + 1] = v[1];
1114 glEnableClientState (GL_VERTEX_ARRAY);
1115 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1117 glVertexPointer (2, GL_SHORT, 0, vertices);
1118 glDrawArrays (GL_TRIANGLE_FAN, 0, npoints);
1122 } else if (shape == Nonconvex) {
1124 // TODO: assert that x,y of first and last point match, as that is assumed
1127 root = (linked_point *) malloc( sizeof(linked_point) );
1128 set_points_list(points,npoints,root);
1129 traverse_points_list(root);
1132 Assert((shape == Convex || shape == Nonconvex),
1133 "XFillPolygon: (TODO) Unimplemented shape");
1139 #define radians(DEG) ((DEG) * M_PI / 180.0)
1140 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1143 arc_xy(GLfloat *p, double cx, double cy, double w2, double h2, double theta)
1145 p[0] = cos(theta) * w2 + cx;
1146 p[1] = -sin(theta) * h2 + cy;
1150 mod_neg(int a, unsigned b)
1152 /* Normal modulus is implementation defined for negative numbers. This is
1153 * well-defined such that the repeating pattern for a >= 0 is maintained for
1155 return a < 0 ? (b - 1) - (-(a + 1) % b) : a % b;
1158 /* TODO: Fill in arcs with line width > 1 */
1160 draw_arc (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 int gglw = gc->gcv.line_width;
1166 if (fill_p || gglw <= 1) {
1167 draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p);
1170 int w1, w2, h1, h2, gglwh;
1180 //draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p);
1181 draw_arc_gl (dpy, d, gc, x1, y1, w1, h1, angle1, angle2, fill_p);
1182 draw_arc_gl (dpy, d, gc, x2, y2, w2, h2, angle1, angle2, fill_p);
1189 draw_arc_gl (Display *dpy, Drawable d, GC gc, int x, int y,
1190 unsigned int width, unsigned int height,
1191 int angle1, int angle2, Bool fill_p)
1193 set_fg_gc(dpy, d, gc);
1195 /* Let's say the number of line segments needed to make a convincing circle is
1196 4*sqrt(radius). (But these arcs aren't necessarily circular arcs...) */
1198 double w2 = width * 0.5f, h2 = height * 0.5f;
1199 double a, b; /* Semi-major/minor axes. */
1208 const double two_pi = 2 * M_PI;
1210 double amb = a - b, apb = a + b;
1211 double h = (amb * amb) / (apb * apb);
1212 // TODO: Math cleanup.
1213 double C_approx = M_PI * apb * (1 + 3 * h / (10 + sqrtf(4 - 3 * h)));
1214 double segments_f = 4 * sqrtf(C_approx / (2 * M_PI));
1216 // TODO: Explain how drawing works what with the points of overlapping arcs
1220 unsigned segments_360 = segments_f;
1222 /* TODO: angle2 == 0. This is a tilted square with CapSquare. */
1223 /* TODO: color, thick lines, CapNotLast for thin lines */
1224 /* TODO: Transformations. */
1226 double segment_angle = two_pi / segments_360;
1228 const unsigned deg64 = 360 * 64;
1229 const double rad_from_deg64 = two_pi / deg64;
1236 angle1 = mod_neg(angle1, deg64); // TODO: Is this OK? Consider negative numbers.
1239 angle2 = deg64; // TODO: Handle circles special.
1242 angle1_f = angle1 * rad_from_deg64,
1243 angle2_f = angle2 * rad_from_deg64;
1245 if (angle2_f > two_pi) // TODO: Move this up.
1248 double segment1_angle_part = fmodf(angle1_f, segment_angle);
1250 unsigned segment1 = ((angle1_f - segment1_angle_part) / segment_angle) + 1.5;
1252 double angle_2r = angle2_f - segment1_angle_part;
1253 unsigned segments = angle_2r / segment_angle;
1255 GLfloat cx = x + w2, cy = y + h2;
1257 GLfloat *data = malloc((segments + 3) * sizeof(GLfloat) * 2); // TODO: Check result.
1259 GLfloat *data_ptr = data;
1266 arc_xy (data_ptr, cx, cy, w2, h2, angle1_f);
1269 for (unsigned s = 0; s != segments; ++s) {
1270 // TODO: Make sure values of theta for the following arc_xy call are between
1271 // angle1_f and angle1_f + angle2_f.
1272 arc_xy (data_ptr, cx, cy, w2, h2, (segment1 + s) * segment_angle);
1276 arc_xy (data_ptr, cx, cy, w2, h2, angle1_f + angle2_f);
1279 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1280 glEnableClientState (GL_VERTEX_ARRAY);
1282 glVertexPointer (2, GL_FLOAT, 0, data);
1283 glDrawArrays (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP,
1285 (GLsizei)((data_ptr - data) / 2));
1292 unsigned segments = segments_f * (fabs(angle2) / (360 * 64));
1294 glBegin (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP);
1296 if (fill_p /* && gc->gcv.arc_mode == ArcPieSlice */)
1297 glVertex2f (cx, cy);
1299 /* TODO: This should fix the middle points of the arc so that the starting and ending points are OK. */
1301 float to_radians = 2 * M_PI / (360 * 64);
1302 float theta = angle1 * to_radians, d_theta = angle2 * to_radians / segments;
1304 for (unsigned s = 0; s != segments + 1; ++s) /* TODO: This is the right number of segments, yes? */
1306 glVertex2f(cos(theta) * w2 + cx, -sin(theta) * h2 + cy);
1332 CreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1334 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1335 gc->depth = jwxyz_drawable_depth (d);
1337 jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
1338 XChangeGC (dpy, gc, mask, xgcv);
1344 FreeGC (Display *dpy, GC gc)
1347 XUnloadFont (dpy, gc->gcv.font);
1351 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1353 if (gc->gcv.clip_mask) {
1354 XFreePixmap (dpy, gc->gcv.clip_mask);
1355 CGImageRelease (gc->clip_mask);
1364 PutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1365 int src_x, int src_y, int dest_x, int dest_y,
1366 unsigned int w, unsigned int h)
1368 jwxyz_assert_display (dpy);
1370 const XRectangle *wr = jwxyz_frame (d);
1372 Assert (gc, "no GC");
1373 Assert ((w < 65535), "improbably large width");
1374 Assert ((h < 65535), "improbably large height");
1375 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1376 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1377 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1378 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1380 // Clip width and height to the bounds of the Drawable
1382 if (dest_x + w > wr->width) {
1383 if (dest_x > wr->width)
1385 w = wr->width - dest_x;
1387 if (dest_y + h > wr->height) {
1388 if (dest_y > wr->height)
1390 h = wr->height - dest_y;
1392 if (w <= 0 || h <= 0)
1395 // Clip width and height to the bounds of the XImage
1397 if (src_x + w > ximage->width) {
1398 if (src_x > ximage->width)
1400 w = ximage->width - src_x;
1402 if (src_y + h > ximage->height) {
1403 if (src_y > ximage->height)
1405 h = ximage->height - src_y;
1407 if (w <= 0 || h <= 0)
1410 /* Assert (d->win */
1412 if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
1415 jwxyz_gl_flush (dpy);
1416 jwxyz_bind_drawable (dpy, dpy->main_window, d);
1417 jwxyz_gl_set_gc (dpy, gc);
1419 int bpl = ximage->bytes_per_line;
1420 int bpp = ximage->bits_per_pixel;
1424 GLint tex_internalformat;
1425 GLenum tex_format, tex_type;
1429 tex_data = ximage->data + src_y * bpl + (src_x * 4);
1431 jwxyz_assert_display(dpy);
1433 /* There probably won't be any hacks that do this, but... */
1434 Assert (!(bpl % 4), "XPutImage: bytes_per_line not divisible by four.");
1436 tex_internalformat = texture_internalformat(dpy);
1437 tex_format = dpy->pixel_format;
1438 tex_type = gl_pixel_type(dpy);
1439 tex_index = texture_rgba;
1441 /* GL_UNPACK_ROW_LENGTH is not allowed to be negative. (sigh) */
1442 # ifndef HAVE_JWZGLES
1444 glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
1449 // glPixelStorei (GL_UNPACK_ALIGNMENT, 4); // Probably unnecessary.
1453 Assert (bpp == 1, "expected 1 or 32 bpp");
1454 Assert ((src_x % 8) == 0,
1455 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1457 const char *src_data = ximage->data + src_y * bpl + (src_x / 8);
1458 unsigned w8 = (w + 7) / 8;
1462 tex_data = malloc(src_w * h);
1468 Log("#PI ---------- %d %d %08lx %08lx",
1469 jwxyz_drawable_depth(d), ximage->depth,
1470 (unsigned long)d, (unsigned long)ximage);
1471 for (y = 0; y < ximage->height; y++) {
1473 for (x = 0; x < ximage->width; x++) {
1474 unsigned long b = XGetPixel(ximage, x, y);
1475 s[o++] = ( (b & 0xFF000000)
1476 ? ((b & 0x00FFFFFF) ? '#' : '-')
1477 : ((b & 0x00FFFFFF) ? '+' : ' '));
1482 Log("# PI ----------");
1485 uint32_t *data_out = (uint32_t *)tex_data;
1486 for(unsigned y = h; y; --y) {
1487 for(unsigned x = 0; x != w8; ++x) {
1488 // TODO: Does big endian work here?
1489 uint8_t byte = src_data[x];
1490 uint32_t word = byte;
1491 word = (word & 0x3) | ((word & 0xc) << 14);
1492 word = (word & 0x00010001) | ((word & 0x00020002) << 7);
1493 data_out[x << 1] = (word << 8) - word;
1496 word = (word & 0x3) | ((word & 0xc) << 14);
1497 word = (word & 0x00010001) | ((word & 0x00020002) << 7);
1498 data_out[(x << 1) | 1] = (word << 8) - word;
1501 data_out += src_w / 4;
1507 Log("#P2 ----------");
1508 for (y = 0; y < ximage->height; y++) {
1510 for (x = 0; x < ximage->width; x++) {
1511 unsigned long b = ((uint8_t *)tex_data)[y * w + x];
1512 s[o++] = ( (b & 0xFF000000)
1513 ? ((b & 0x00FFFFFF) ? '#' : '-')
1514 : ((b & 0x00FFFFFF) ? '+' : ' '));
1519 Log("# P2 ----------");
1523 tex_internalformat = GL_LUMINANCE;
1524 tex_format = GL_LUMINANCE;
1525 tex_type = GL_UNSIGNED_BYTE;
1526 tex_index = texture_mono;
1528 // glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1530 set_color (dpy, gc->gcv.foreground, gc->depth, gc->gcv.alpha_allowed_p);
1531 // TODO: Deal with the background color.
1534 # if 1 // defined HAVE_JWZGLES
1535 // Regular OpenGL uses GL_TEXTURE_RECTANGLE_EXT in place of GL_TEXTURE_2D.
1536 // TODO: Make use of OES_draw_texture.
1538 unsigned tex_w = src_w, tex_h = h;
1539 if (!dpy->gl_texture_npot_p) {
1540 tex_w = to_pow2(tex_w);
1541 tex_h = to_pow2(tex_h);
1544 glBindTexture (dpy->gl_texture_target, dpy->textures[tex_index]);
1546 // A fun project: reimplement xshm.c by means of a PBO using
1547 // GL_MAP_UNSYNCHRONIZED_BIT.
1549 // TODO: Would using glTexSubImage2D exclusively be faster?
1550 if (tex_w == src_w && tex_h == h) {
1551 glTexImage2D (dpy->gl_texture_target, 0, tex_internalformat, tex_w, tex_h,
1552 0, tex_format, tex_type, tex_data);
1554 // TODO: Sampling the last row might be a problem if src_x != 0.
1555 glTexImage2D (dpy->gl_texture_target, 0, tex_internalformat, tex_w, tex_h,
1556 0, tex_format, tex_type, NULL);
1557 glTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, src_w, h,
1558 tex_format, tex_type, tex_data);
1564 // TODO: This looks a lot like jwxyz_gl_draw_image. Refactor.
1566 // glEnable (dpy->gl_texture_target);
1567 // glColor4f (0.5, 0, 1, 1);
1568 glEnable (dpy->gl_texture_target);
1569 glEnableClientState (GL_VERTEX_ARRAY);
1570 glEnableClientState (GL_TEXTURE_COORD_ARRAY);
1572 // TODO: Why are these ever turned on in the first place?
1573 glDisableClientState (GL_COLOR_ARRAY);
1574 glDisableClientState (GL_NORMAL_ARRAY);
1575 // glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1577 GLfloat vertices[4][2] =
1580 {dest_x, dest_y + h},
1581 {dest_x + w, dest_y + h},
1582 {dest_x + w, dest_y}
1585 GLfloat texcoord_w, texcoord_h;
1586 # ifndef HAVE_JWZGLES
1587 if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT) {
1591 # endif /* HAVE_JWZGLES */
1593 texcoord_w = (double)w / tex_w;
1594 texcoord_h = (double)h / tex_h;
1597 GLfloat tex_coords[4][2];
1598 tex_coords[0][0] = 0;
1599 tex_coords[0][1] = 0;
1600 tex_coords[1][0] = 0;
1601 tex_coords[1][1] = texcoord_h;
1602 tex_coords[2][0] = texcoord_w;
1603 tex_coords[2][1] = texcoord_h;
1604 tex_coords[3][0] = texcoord_w;
1605 tex_coords[3][1] = 0;
1607 glVertexPointer (2, GL_FLOAT, 0, vertices);
1608 glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
1609 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1612 glDisable (dpy->gl_texture_target);
1614 glRasterPos2i (dest_x, dest_y);
1615 glPixelZoom (1, -1);
1616 jwxyz_assert_display (dpy);
1617 glDrawPixels (w, h, dpy->pixel_format, gl_pixel_type(dpy), data);
1625 /* At the moment only XGetImage and get_xshm_image use XGetSubImage. */
1626 /* #### Twang calls XGetImage on the window intending to get a
1627 buffer full of black. This is returning a buffer full of white
1628 instead of black for some reason. */
1630 GetSubImage (Display *dpy, Drawable d, int x, int y,
1631 unsigned int width, unsigned int height,
1632 unsigned long plane_mask, int format,
1633 XImage *dest_image, int dest_x, int dest_y)
1635 Assert ((width < 65535), "improbably large width");
1636 Assert ((height < 65535), "improbably large height");
1637 Assert ((x < 65535 && x > -65535), "improbably large x");
1638 Assert ((y < 65535 && y > -65535), "improbably large y");
1640 jwxyz_gl_flush (dpy);
1641 jwxyz_bind_drawable (dpy, dpy->main_window, d);
1643 // TODO: What if this reads off the edge? What is supposed to happen?
1646 // In case the caller tries to write off the edge.
1648 max_width = dest_image->width - dest_x,
1649 max_height = dest_image->height - dest_y;
1651 if (width > max_width) {
1655 if (height > max_height) {
1656 height = max_height;
1660 Assert (jwxyz_drawable_depth (d) == dest_image->depth, "XGetSubImage: depth mismatch");
1662 if (dest_image->depth == visual_depth (NULL, NULL)) {
1663 Assert (!(dest_image->bytes_per_line % 4), "XGetSubImage: bytes_per_line not divisible by 4");
1664 unsigned pixels_per_line = dest_image->bytes_per_line / 4;
1666 Assert (pixels_per_line == width, "XGetSubImage: (TODO) pixels_per_line != width");
1668 glPixelStorei (GL_PACK_ROW_LENGTH, pixels_per_line);
1670 glPixelStorei (GL_PACK_ALIGNMENT, 4);
1672 uint32_t *dest_data = (uint32_t *)dest_image->data + pixels_per_line * dest_y + dest_x;
1674 glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
1675 dpy->pixel_format, gl_pixel_type(dpy), dest_data);
1677 /* Flip this upside down. :( */
1678 uint32_t *top = dest_data;
1679 uint32_t *bottom = dest_data + pixels_per_line * (height - 1);
1680 for (unsigned y = height / 2; y; --y) {
1681 for (unsigned x = 0; x != width; ++x) {
1682 uint32_t px = top[x];
1686 top += pixels_per_line;
1687 bottom -= pixels_per_line;
1691 uint32_t *rgba_image = malloc(width * height * 4);
1692 Assert (rgba_image, "not enough memory");
1694 // Must be GL_RGBA; GL_RED isn't available.
1695 glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
1696 GL_RGBA, GL_UNSIGNED_BYTE, rgba_image);
1698 Assert (!(dest_x % 8), "XGetSubImage: dest_x not byte-aligned");
1700 (uint8_t *)dest_image->data + dest_image->bytes_per_line * dest_y
1705 Log("#GI ---------- %d %d %d x %d %08lx",
1706 jwxyz_drawable_depth(d), dest_image->depth,
1709 for (int y = 0; y < height; y++) {
1711 for (x = 0; x < width; x++) {
1712 unsigned long b = rgba_image[(height-y-1) * width + x];
1713 s[x] = ( (b & 0xFF000000)
1714 ? ((b & 0x00FFFFFF) ? '#' : '-')
1715 : ((b & 0x00FFFFFF) ? '+' : ' '));
1720 Log("#GI ----------");
1723 const uint32_t *bottom = rgba_image + width * (height - 1);
1724 for (unsigned y = height; y; --y) {
1725 memset (top, 0, width / 8);
1726 for (unsigned x = 0; x != width; ++x) {
1727 if (bottom[x] & 0x80)
1728 top[x >> 3] |= (1 << (x & 7));
1730 top += dest_image->bytes_per_line;
1743 copy_pixmap (Display *dpy, Pixmap p)
1746 Assert (p->type == PIXMAP, "not a pixmap");
1752 unsigned int width, height, border_width, depth;
1753 if (XGetGeometry (dpy, p, &root,
1754 &x, &y, &width, &height, &border_width, &depth)) {
1756 gcv.function = GXcopy;
1757 GC gc = XCreateGC (dpy, p, GCFunction, &gcv);
1759 p2 = XCreatePixmap (dpy, p, width, height, depth);
1761 XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0);
1766 Assert (p2, "could not copy pixmap");
1774 SetClipMask (Display *dpy, GC gc, Pixmap m)
1776 Log ("TODO: No clip masks yet");
1777 /* Protip: Do glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1778 clearing just the stencil buffer in a packed depth/stencil arrangement is
1779 slower than the above. Adreno recommends this, but other GPUs probably
1786 SetClipOrigin (Display *dpy, GC gc, int x, int y)
1788 gc->gcv.clip_x_origin = x;
1789 gc->gcv.clip_y_origin = y;
1793 void set_points_list(XPoint *points, int npoints, linked_point *root)
1795 linked_point *current;
1798 for (int i = 0; i < npoints - 2 ; i++) {
1799 current->x = points[i].x;
1800 current->y = points[i].y;
1801 current->next = (linked_point *) malloc(sizeof(linked_point));
1802 current = current->next;
1804 current->x = points[npoints-2].x;
1805 current->y = points[npoints-2].y;
1806 current->next = root;
1810 double compute_edge_length(linked_point * a, linked_point * b)
1813 int xdiff, ydiff, xsq, ysq, added;
1814 double xy_add, edge_length;
1816 xdiff = a->x - b->x;
1817 ydiff = a->y - b->y;
1818 xsq = xdiff * xdiff;
1819 ysq = ydiff * ydiff;
1821 xy_add = (double) added;
1822 edge_length = sqrt(xy_add);
1826 double get_angle(double a, double b, double c)
1828 double cos_a, i_cos_a;
1829 cos_a = (((b * b) + (c * c)) - (a * a)) / (double) (2.0 * b * c);
1830 i_cos_a = acos(cos_a);
1835 Bool is_same_slope(linked_point * a)
1838 int abx, bcx, aby, bcy, aa, bb;
1845 // test if slopes are indefinite for both line segments
1847 return b->x == c->x;
1848 } else if (b->x == c->x) {
1849 return False; // false, as ax/bx is not indefinite
1862 void draw_three_vertices(linked_point * a, Bool triangle)
1872 GLfloat vertices[3][2] = {
1879 drawType = GL_TRIANGLES;
1881 drawType = GL_LINES;
1884 glEnableClientState(GL_VERTEX_ARRAY);
1885 glVertexPointer(2, GL_FLOAT, 0, vertices);
1886 glDrawArrays(drawType, 0, 3);
1888 free(b); // cut midpoint off from remaining polygon vertex list
1893 Bool is_an_ear(linked_point * a)
1895 double edge_ab, edge_bc, edge_ac;
1896 double angle_a, angle_b, angle_c;
1898 linked_point *b, *c;
1902 my_pi = (double) M_PI;
1904 edge_ab = compute_edge_length(a, b);
1905 edge_bc = compute_edge_length(b, c);
1906 edge_ac = compute_edge_length(a, c);
1907 angle_a = get_angle(edge_bc, edge_ab, edge_ac);
1908 angle_b = get_angle(edge_ac, edge_ab, edge_bc);
1909 angle_c = get_angle(edge_ab, edge_ac, edge_bc);
1911 return angle_a < my_pi && angle_b < my_pi && angle_c < my_pi;
1915 Bool is_three_point_loop(linked_point * head)
1917 return head->x == head->next->next->next->x
1918 && head->y == head->next->next->next->y;
1922 void traverse_points_list(linked_point * root)
1927 while (!is_three_point_loop(head)) {
1928 if (is_an_ear(head)) {
1929 draw_three_vertices(head, True);
1930 } else if (is_same_slope(head)) {
1931 draw_three_vertices(head, False);
1937 // handle final three vertices in polygon
1938 if (is_an_ear(head)) {
1939 draw_three_vertices(head, True);
1940 } else if (is_same_slope(head)) {
1941 draw_three_vertices(head, False);
1943 free(head->next->next);
1946 Assert (False, "traverse_points_list: unknown configuration");
1954 const struct jwxyz_vtbl gl_vtbl = {
1957 display_sources_data,
1981 #endif /* JWXYZ_GL -- entire file */