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 /* There is probably no reason to ever implement indexed-color rendering here,
25 even if many screenhacks still work with PseudoColor.
26 - OpenGL ES 1.1 (Android, iOS) doesn't support indexed color.
27 - macOS only provides indexed color via AGL (now deprecated), not
32 - malloc error checking
33 - Check max texture sizes for XGet/PutImage, XCopyArea.
34 - Optional 5:5:5 16-bit color
37 /* Techniques & notes:
38 - iOS: OpenGL ES 2.0 isn't always available. Use OpenGL ES 1.1.
39 - OS X: Drivers can go back to OpenGL 1.1 (GeForce 2 MX on 10.5.8).
40 - Use stencil buffers (OpenGL 1.0+) for bitmap clipping masks.
41 - Pixmaps can be any of the following, depending on GL implementation.
42 - This requires offscreen rendering. Fortunately, this is always
44 - OS X: Drawable objects, including: pixel buffers and offscreen
46 - Offscreen buffers w/ CGLSetOffScreen (10.0+)
47 - http://lists.apple.com/archives/mac-opengl/2002/Jun/msg00087.html
48 provides a very ugly solution for hardware-accelerated offscreen
49 rendering with CGLSetParameter(*, kCGLCPSurfaceTexture, *) on OS X
50 10.1+. Apple docs say it's actually for OS X 10.3+, instead.
51 - Pixel buffers w/ CGLSetPBuffer (10.3+, now deprecated)
52 - Requires APPLE_pixel_buffer.
53 - Available in software on x86 only.
54 - Always available on hardware.
55 - Need to blit? Use OpenGL pixel buffers. (GL_PIXEL_PACK_BUFFER)
56 - Framebuffer objects w/ GL_(ARB|EXT)_framebuffer_object
57 - TODO: Can EXT_framebuffers be different sizes?
59 - iOS: Use OES_framebuffer_object, it's always present.
62 /* OpenGL hacks call a number of X11 functions, including
63 XCopyArea, XDrawString, XGetImage
64 XCreatePixmap, XCreateGC, XCreateImage
66 Check these, of course.
69 #ifdef JWXYZ_GL /* entire file */
81 # import <QuartzCore/QuartzCore.h>
82 # include <OpenGLES/ES1/gl.h>
83 # include <OpenGLES/ES1/glext.h>
85 # include <OpenGL/glu.h>
88 /* TODO: Does this work on iOS? */
93 # include <GLES/glext.h>
98 # include "jwzglesI.h"
102 #include "jwxyz-timers.h"
103 #include "yarandom.h"
108 #define countof(x) (sizeof((x))/sizeof((*x)))
115 // Use two textures: one for RGBA, one for luminance. Older Android doesn't
116 // seem to like it when textures change format.
122 struct jwxyz_Display {
123 const struct jwxyz_vtbl *vtbl; // Must come first.
126 GLenum pixel_format, pixel_type;
128 struct jwxyz_sources_data *timers_data;
130 Bool gl_texture_npot_p;
131 /* Bool opengl_core_p */;
132 GLenum gl_texture_target;
134 GLuint textures[2]; // Also can work on the desktop.
136 unsigned long window_background;
139 Bool gc_alpha_allowed_p;
140 int gc_clip_x_origin, gc_clip_y_origin;
143 // Alternately, there could be one queue per pixmap.
144 size_t queue_size, queue_capacity;
145 Drawable queue_drawable;
148 uint32_t *queue_color;
156 unsigned clip_mask_width, clip_mask_height;
159 struct jwxyz_linked_point {
166 jwxyz_assert_display(Display *dpy)
171 jwxyz_assert_drawable (dpy->main_window, dpy->main_window);
176 jwxyz_set_matrices (Display *dpy, unsigned width, unsigned height,
179 Assert (width, "no width");
180 Assert (height, "no height");
182 /* TODO: Check registration pattern from Interference with rectangles instead of points. */
184 // The projection matrix is always set as follows. The modelview matrix is
185 // usually identity, but for points and thin lines, it's translated by 0.5.
186 glMatrixMode(GL_PROJECTION);
189 # if defined(USE_IPHONE) || defined(HAVE_ANDROID)
191 if (window_p && ignore_rotation_p(dpy)) {
192 int o = (int) current_device_rotation();
193 glRotatef (-o, 0, 0, 1);
196 // glPointSize(1); // This is the default.
199 glOrthof /* TODO: Is glOrthox worth it? Signs point to no. */
203 (0, width, height, 0, -1, 1);
205 glMatrixMode(GL_MODELVIEW);
206 # endif // HAVE_MOBILE
213 // iOS always uses OpenGL ES 1.1.
219 gl_check_ver (const struct gl_version *caps,
223 return caps->major > gl_major ||
224 (caps->major == gl_major && caps->minor >= gl_minor);
231 tex_parameters (Display *d, GLuint texture)
233 // TODO: Check for (ARB|EXT|NV)_texture_rectangle. (All three are alike.)
234 // Rectangle textures should be present on OS X with the following exceptions:
235 // - Generic renderer on PowerPC OS X 10.4 and earlier
237 glBindTexture (d->gl_texture_target, texture);
238 // TODO: This is all somewhere else. Refactor.
239 glTexParameteri (d->gl_texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
240 glTexParameteri (d->gl_texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
242 // This might be redundant for rectangular textures.
243 # ifndef HAVE_JWZGLES
244 const GLint wrap = GL_CLAMP;
245 # else // HAVE_JWZGLES
246 const GLint wrap = GL_CLAMP_TO_EDGE;
247 # endif // HAVE_JWZGLES
249 // In OpenGL, CLAMP_TO_EDGE is OpenGL 1.2 or GL_SGIS_texture_edge_clamp.
250 // This is always present with OpenGL ES.
251 glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_S, wrap);
252 glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_T, wrap);
256 tex_size (Display *dpy, unsigned *tex_w, unsigned *tex_h)
258 if (!dpy->gl_texture_npot_p) {
259 *tex_w = to_pow2(*tex_w);
260 *tex_h = to_pow2(*tex_h);
265 tex_image (Display *dpy, GLenum internalformat,
266 unsigned *tex_w, unsigned *tex_h, GLenum format, GLenum type,
269 unsigned w = *tex_w, h = *tex_h;
270 tex_size (dpy, tex_w, tex_h);
272 // TODO: Would using glTexSubImage2D exclusively be faster?
273 if (*tex_w == w && *tex_h == h) {
274 glTexImage2D (dpy->gl_texture_target, 0, internalformat, *tex_w, *tex_h,
275 0, format, type, data);
277 // TODO: Sampling the last row might be a problem if src_x != 0.
278 glTexImage2D (dpy->gl_texture_target, 0, internalformat, *tex_w, *tex_h,
279 0, format, type, NULL);
280 glTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, w, h,
286 extern const struct jwxyz_vtbl gl_vtbl;
289 jwxyz_gl_make_display (Window w)
291 Display *d = (Display *) calloc (1, sizeof(*d));
294 # ifndef HAVE_JWZGLES
295 struct gl_version version;
298 const GLubyte *version_str = glGetString (GL_VERSION);
300 /* iPhone is always OpenGL ES 1.1. */
301 if (sscanf ((const char *) version_str, "%u.%u",
302 &version.major, &version.minor) < 2)
308 # endif // !HAVE_JWZGLES
310 const GLubyte *extensions = glGetString (GL_EXTENSIONS);
313 - Apple TN2080: Understanding and Detecting OpenGL Functionality.
314 - OpenGL Programming Guide for the Mac - Best Practices for Working with
315 Texture Data - Optimal Data Formats and Types
318 // If a video adapter suports BGRA textures, then that's probably as fast as
319 // you're gonna get for getting a texture onto the screen.
321 /* TODO: Make BGRA work on iOS. As it is, it breaks XPutImage. (glTexImage2D, AFAIK) */
322 d->pixel_format = GL_RGBA; /*
323 gluCheckExtension ((const GLubyte *) "GL_APPLE_texture_format_BGRA8888",
324 extensions) ? GL_BGRA_EXT : GL_RGBA; */
325 d->pixel_type = GL_UNSIGNED_BYTE;
326 // See also OES_read_format.
327 # else // !HAVE_JWZGLES
328 if (gl_check_ver (&version, 1, 2) ||
329 (gluCheckExtension ((const GLubyte *) "GL_EXT_bgra", extensions) &&
330 gluCheckExtension ((const GLubyte *) "GL_APPLE_packed_pixels",
332 // APPLE_packed_pixels is only ever available on iOS, never Android.
333 d->pixel_format = GL_BGRA_EXT;
334 // Both Intel and PowerPC-era docs say to use GL_UNSIGNED_INT_8_8_8_8_REV.
335 d->pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
337 d->pixel_format = GL_RGBA;
338 d->pixel_type = GL_UNSIGNED_BYTE;
340 // GL_ABGR_EXT/GL_UNSIGNED_BYTE is another possibilty that may have made more
342 # endif // !HAVE_JWZGLES
344 // On really old systems, it would make sense to split textures
345 // into subsections, to work around the maximum texture size.
346 # ifndef HAVE_JWZGLES
347 d->gl_texture_npot_p = gluCheckExtension ((const GLubyte *)
348 "GL_ARB_texture_rectangle",
350 d->gl_texture_target = d->gl_texture_npot_p ?
351 GL_TEXTURE_RECTANGLE_EXT :
354 d->gl_texture_npot_p = jwzgles_gluCheckExtension
355 ((const GLubyte *) "GL_APPLE_texture_2D_limited_npot", extensions) ||
356 jwzgles_gluCheckExtension
357 ((const GLubyte *) "GL_OES_texture_npot", extensions) ||
358 jwzgles_gluCheckExtension // From PixelFlinger 1.4
359 ((const GLubyte *) "GL_ARB_texture_non_power_of_two", extensions);
361 d->gl_texture_target = GL_TEXTURE_2D;
364 Visual *v = &d->visual;
365 v->class = TrueColor;
366 if (d->pixel_format == GL_BGRA_EXT) {
367 v->red_mask = 0x00ff0000;
368 v->green_mask = 0x0000ff00;
369 v->blue_mask = 0x000000ff;
370 v->alpha_mask = 0xff000000;
372 Assert(d->pixel_format == GL_RGBA,
373 "jwxyz_gl_make_display: Unknown pixel_format");
374 unsigned long masks[4];
375 for (unsigned i = 0; i != 4; ++i) {
376 union color_bytes color;
378 color.bytes[i] = 0xff;
379 masks[i] = color.pixel;
381 v->red_mask = masks[0];
382 v->green_mask = masks[1];
383 v->blue_mask = masks[2];
384 v->alpha_mask = masks[3];
387 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
389 d->window_background = BlackPixel(d,0);
394 GLint max_texture_size, max_texture_units;
395 glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size);
396 glGetIntegerv (GL_MAX_TEXTURE_UNITS, &max_texture_units);
397 Log ("GL_MAX_TEXTURE_SIZE: %d, GL_MAX_TEXTURE_UNITS: %d\n",
398 max_texture_size, max_texture_units);
400 // OpenGL ES 1.1 and OpenGL 1.3 both promise at least 2 texture units:
401 // OpenGL (R) ES Common/Common-Lite Profile Specification, Version 1.1.12 (Full Specification)
402 // https://www.khronos.org/registry/OpenGL/specs/es/1.1/es_full_spec_1.1.pdf
403 // * Table 6.22. Implementation Dependent Values
404 // * D.2 Enhanced Texture Processing
405 // (OpenGL 1.2 provides multitexturing as an ARB extension, and requires 1
406 // texture unit only.)
408 // ...but the glGet reference page contradicts this, and says there can be
410 // https://www.khronos.org/registry/OpenGL-Refpages/es1.1/xhtml/glGet.xml
413 glGenTextures (countof (d->textures), d->textures);
415 for (unsigned i = 0; i != countof (d->textures); i++) {
416 tex_parameters (d, d->textures[i]);
419 d->gc_function = GXcopy;
420 d->gc_alpha_allowed_p = False;
423 jwxyz_assert_display(d);
428 jwxyz_gl_free_display (Display *dpy)
430 Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable");
432 /* TODO: Go over everything. */
434 free (dpy->queue_vertex);
435 free (dpy->queue_color);
437 jwxyz_sources_free (dpy->timers_data);
443 /* Call this when the View changes size or position.
446 jwxyz_window_resized (Display *dpy)
448 Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable");
450 const XRectangle *new_frame = jwxyz_frame (dpy->main_window);
451 unsigned new_width = new_frame->width;
452 unsigned new_height = new_frame->height;
454 Assert (new_width, "jwxyz_window_resized: No width.");
455 Assert (new_height, "jwxyz_window_resized: No height.");
457 /*if (cgc) w->cgc = cgc;
458 Assert (w->cgc, "no CGContext"); */
460 Log("resize: %d, %d\n", new_width, new_height);
462 jwxyz_gl_flush (dpy);
463 jwxyz_bind_drawable (dpy, dpy->main_window, dpy->main_window);
465 // TODO: What does the iPhone need?
467 // iOS only: If the main_window is not the current_drawable, then set_matrices
468 // was already called in bind_drawable.
469 jwxyz_set_matrices (dpy, new_width, new_height, True);
473 glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
475 glDrawBuffer (GL_FRONT);
476 glClearColor (1, 0, 1, 0);
477 glClear (GL_COLOR_BUFFER_BIT);
478 glDrawBuffer (GL_BACK);
479 glClearColor (0, 1, 1, 0);
480 glClear (GL_COLOR_BUFFER_BIT);
482 glDrawBuffer (draw_buffer); */
484 // Stylish and attractive purple!
485 // glClearColor (1, 0, 1, 0.5);
486 // glClear (GL_COLOR_BUFFER_BIT);
490 static jwxyz_sources_data *
491 display_sources_data (Display *dpy)
493 return dpy->timers_data;
500 return dpy->main_window;
504 visual (Display *dpy)
510 /* GC attributes by usage and OpenGL implementation:
512 All drawing functions:
513 function | glLogicOp w/ GL_COLOR_LOGIC_OP
514 clip_x_origin, clip_y_origin, clip_mask | Multitexturing w/ GL_TEXTURE1
516 Shape drawing functions:
517 foreground, background | glColor*
519 XDrawLines, XDrawRectangles, XDrawSegments:
520 line_width, cap_style, join_style | Lotsa vertices
523 fill_rule | Multiple GL_TRIANGLE_FANs
526 font | Cocoa, then OpenGL display lists.
528 alpha_allowed_p | GL_BLEND
530 antialias_p | Well, there's options:
531 * Multisampling would work, but that's something that would need to be set
532 per-Pixmap, not per-GC.
533 * GL_POINT, LINE, and POLYGON_SMOOTH are the old-school way of doing
534 this, but POINT_SMOOTH is unnecessary, and POLYGON_SMOOTH is missing from
535 GLES 1. All three are missing from GLES 2. Word on the street is that
536 these are deprecated anyway.
537 * Tiny textures with bilinear filtering to get the same effect as LINE_ and
538 POLYGON_SMOOTH. A bit tricky.
539 * Do nothing. Android hardware is very often high-DPI enough that
540 anti-aliasing doesn't matter all that much.
547 enqueue (Display *dpy, Drawable d, GC gc, int mode, size_t count,
550 if (dpy->queue_size &&
551 (!gc || /* Could allow NULL GCs here... */
552 dpy->gc_function != gc->gcv.function ||
553 dpy->gc_alpha_allowed_p != gc->gcv.alpha_allowed_p ||
554 dpy->gc_clip_mask != gc->clip_mask ||
555 dpy->gc_clip_x_origin != gc->gcv.clip_x_origin ||
556 dpy->gc_clip_y_origin != gc->gcv.clip_y_origin ||
557 dpy->queue_mode != mode ||
558 dpy->queue_drawable != d)) {
559 jwxyz_gl_flush (dpy);
562 jwxyz_bind_drawable (dpy, dpy->main_window, d);
563 jwxyz_gl_set_gc (dpy, gc);
565 if (mode == GL_TRIANGLE_STRIP)
566 Assert (count, "empty triangle strip");
567 // Use degenerate triangles to cut down on draw calls.
568 Bool prepend2 = mode == GL_TRIANGLE_STRIP && dpy->queue_size;
570 // ####: Primitive restarts should be used here when (if) they're available.
574 // TODO: Use glColor when we can get away with it.
575 size_t old_size = dpy->queue_size;
576 dpy->queue_size += count;
577 if (dpy->queue_size > dpy->queue_capacity) {
578 dpy->queue_capacity = dpy->queue_size * 2;
580 uint32_t *new_color = realloc (
581 dpy->queue_color, sizeof(*dpy->queue_color) * dpy->queue_capacity);
582 /* Allocate vertices as if they were always GLfloats. Otherwise, if
583 queue_vertex is allocated to hold GLshorts, then things get switched
584 to GLfloats, queue_vertex would be too small for the given capacity.
586 GLshort *new_vertex = realloc (
587 dpy->queue_vertex, sizeof(GLfloat) * 2 * dpy->queue_capacity);
589 if (!new_color || !new_vertex)
592 dpy->queue_color = new_color;
593 dpy->queue_vertex = new_vertex;
596 dpy->queue_mode = mode;
597 dpy->queue_drawable = d;
599 union color_bytes color;
601 // Like query_color, but for bytes.
602 jwxyz_validate_pixel (dpy, pixel, jwxyz_drawable_depth (d),
603 gc ? gc->gcv.alpha_allowed_p : False);
605 if (jwxyz_drawable_depth (d) == 1) {
606 uint8_t b = pixel ? 0xff : 0;
610 color.bytes[3] = 0xff;
612 JWXYZ_QUERY_COLOR (dpy, pixel, 0xffull, color.bytes);
615 for (size_t i = 0; i != count; ++i) // TODO: wmemset when applicable.
616 dpy->queue_color[i + old_size] = color.pixel;
618 void *result = (char *)dpy->queue_vertex + old_size * 2 *
619 (mode == GL_TRIANGLE_STRIP ? sizeof(GLfloat) : sizeof(GLshort));
621 dpy->queue_color[old_size] = dpy->queue_color[old_size - 1];
622 result = (GLfloat *)result + 4;
629 finish_triangle_strip (Display *dpy, GLfloat *enqueue_result)
631 if (enqueue_result != dpy->queue_vertex) {
632 enqueue_result[-4] = enqueue_result[-6];
633 enqueue_result[-3] = enqueue_result[-5];
634 enqueue_result[-2] = enqueue_result[0];
635 enqueue_result[-1] = enqueue_result[1];
641 query_color (Display *dpy, unsigned long pixel, unsigned int depth,
642 Bool alpha_allowed_p, GLfloat *rgba)
644 jwxyz_validate_pixel (dpy, pixel, depth, alpha_allowed_p);
653 JWXYZ_QUERY_COLOR (dpy, pixel, 1.0f, rgba);
659 set_color (Display *dpy, unsigned long pixel, unsigned int depth,
660 Bool alpha_allowed_p)
663 query_color (dpy, pixel, depth, alpha_allowed_p, rgba);
664 glColor4f (rgba[0], rgba[1], rgba[2], rgba[3]);
667 /* Pushes a GC context; sets Function and ClipMask. */
669 jwxyz_gl_set_gc (Display *dpy, GC gc)
672 Bool alpha_allowed_p;
675 // GC is NULL for XClearArea and XClearWindow.
677 function = gc->gcv.function;
678 alpha_allowed_p = gc->gcv.alpha_allowed_p || gc->clip_mask;
679 clip_mask = gc->clip_mask;
682 alpha_allowed_p = False;
686 /* GL_COLOR_LOGIC_OP: OpenGL 1.1. */
687 if (function != dpy->gc_function) {
688 dpy->gc_function = function;
689 if (function != GXcopy) {
690 /* Fun fact: The glLogicOp opcode constants are the same as the X11 GX*
691 function constants | GL_CLEAR.
693 glEnable (GL_COLOR_LOGIC_OP);
694 glLogicOp (gc->gcv.function | GL_CLEAR);
696 glDisable (GL_COLOR_LOGIC_OP);
700 /* Cocoa uses add/subtract/difference blending in place of logical ops.
701 It looks nice, but implementing difference blending in OpenGL appears to
702 require GL_KHR_blend_equation_advanced, and support for this is not
706 dpy->gc_alpha_allowed_p = alpha_allowed_p;
707 if (alpha_allowed_p || clip_mask) {
708 // TODO: Maybe move glBlendFunc to XCreatePixmap?
709 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
712 glDisable (GL_BLEND);
716 GL_TEXTURE0: Texture for XPutImage/XCopyArea (if applicable)
717 GL_TEXTURE1: Texture for clip masks (if applicable)
719 dpy->gc_clip_mask = clip_mask;
721 glActiveTexture (GL_TEXTURE1);
723 glEnable (dpy->gl_texture_target);
724 glBindTexture (dpy->gl_texture_target, gc->clip_mask);
726 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
727 alpha_allowed_p ? GL_MODULATE : GL_REPLACE);
729 glMatrixMode (GL_TEXTURE);
733 tex_w = gc->clip_mask_width + 2, tex_h = gc->clip_mask_height + 2;
734 tex_size (dpy, &tex_w, &tex_h);
736 # ifndef HAVE_JWZGLES
737 if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT)
744 glScalef (1.0f / tex_w, -1.0f / tex_h, 1);
747 glTranslatef (1 - gc->gcv.clip_x_origin,
748 1 - gc->gcv.clip_y_origin - (int)gc->clip_mask_height - 2,
751 glDisable (dpy->gl_texture_target);
753 glActiveTexture (GL_TEXTURE0);
758 set_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color)
760 jwxyz_gl_flush (dpy);
761 jwxyz_bind_drawable (dpy, dpy->main_window, d);
762 jwxyz_gl_set_gc (dpy, gc);
769 switch (gc->gcv.function) {
770 case GXset: color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
771 case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
774 depth = visual_depth (NULL, NULL);
777 set_color (dpy, color, depth, gc ? gc->gcv.alpha_allowed_p : False);
780 /* Pushes a GC context; sets color to the foreground color.
783 set_fg_gc (Display *dpy, Drawable d, GC gc)
785 set_color_gc (dpy, d, gc, gc->gcv.foreground);
789 next_point(short *v, XPoint p, int mode)
792 case CoordModeOrigin:
796 case CoordModePrevious:
801 Assert (False, "next_point: bad mode");
807 DrawPoints (Display *dpy, Drawable d, GC gc,
808 XPoint *points, int count, int mode)
812 // TODO: XPoints can be fed directly to OpenGL.
813 GLshort *gl_points = enqueue (dpy, d, gc, GL_POINTS, count,
814 gc->gcv.foreground); // TODO: enqueue returns NULL.
815 for (unsigned i = 0; i < count; i++) {
816 next_point (v, points[i], mode);
817 gl_points[2 * i] = v[0];
818 gl_points[2 * i + 1] = v[1];
826 texture_internalformat (Display *dpy)
829 return dpy->pixel_format;
836 gl_pixel_type (const Display *dpy)
838 return dpy->pixel_type;
842 clear_texture (Display *dpy)
844 glTexImage2D (dpy->gl_texture_target, 0, texture_internalformat(dpy), 0, 0,
845 0, dpy->pixel_format, gl_pixel_type (dpy), NULL);
850 vertex_pointer (Display *dpy, GLenum type, GLsizei stride,
853 glVertexPointer(2, type, stride, pointer);
854 if (dpy->gc_clip_mask) {
855 glClientActiveTexture (GL_TEXTURE1);
856 glEnableClientState (GL_TEXTURE_COORD_ARRAY);
857 glTexCoordPointer (2, type, stride, pointer);
858 glClientActiveTexture (GL_TEXTURE0);
864 jwxyz_gl_flush (Display *dpy)
866 Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable");
868 if (!dpy->queue_size)
871 // jwxyz_bind_drawable() and jwxyz_gl_set_gc() is called in enqueue().
873 glEnableClientState (GL_COLOR_ARRAY);
874 glEnableClientState (GL_VERTEX_ARRAY);
875 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
877 // TODO: Use glColor instead of glColorPointer if there's just one color.
878 // TODO: Does OpenGL use both GL_COLOR_ARRAY and glColor at the same time?
880 glColor4f (1, 1, 1, 1);
882 Bool shifted = dpy->queue_mode == GL_POINTS || dpy->queue_mode == GL_LINES;
884 glMatrixMode (GL_MODELVIEW);
885 glTranslatef (0.5, 0.5, 0);
888 glColorPointer (4, GL_UNSIGNED_BYTE, 0, dpy->queue_color);
890 dpy->queue_mode == GL_TRIANGLE_STRIP ? GL_FLOAT : GL_SHORT,
891 0, dpy->queue_vertex);
892 glDrawArrays (dpy->queue_mode, 0, dpy->queue_size);
894 // TODO: This is right, right?
895 if (dpy->queue_mode == GL_LINES && dpy->queue_line_cap) {
896 Assert (!(dpy->queue_size % 2), "bad count for GL_LINES");
897 glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(GLubyte) * 8,
899 vertex_pointer (dpy, GL_SHORT, sizeof(GLshort) * 4,
900 (GLshort *)dpy->queue_vertex + 2);
901 glDrawArrays (GL_POINTS, 0, dpy->queue_size / 2);
907 glDisableClientState (GL_COLOR_ARRAY);
908 glDisableClientState (GL_VERTEX_ARRAY);
915 jwxyz_gl_copy_area_read_tex_image (Display *dpy, unsigned src_height,
916 int src_x, int src_y,
917 unsigned int width, unsigned int height,
918 int dst_x, int dst_y)
920 # if defined HAVE_COCOA && !defined USE_IPHONE
921 /* TODO: Does this help? */
925 /* TODO: Fix TestX11 + mode_preserve with this one. */
927 unsigned tex_w = width, tex_h = height;
928 if (!dpy->gl_texture_npot_p) {
929 tex_w = to_pow2(tex_w);
930 tex_h = to_pow2(tex_h);
933 GLint internalformat = texture_internalformat(dpy);
935 /* TODO: This probably shouldn't always be texture_rgba. */
936 glBindTexture (dpy->gl_texture_target, dpy->textures[texture_rgba]);
938 if (tex_w == width && tex_h == height) {
939 glCopyTexImage2D (dpy->gl_texture_target, 0, internalformat,
940 src_x, src_height - src_y - height, width, height, 0);
942 glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
943 0, dpy->pixel_format, gl_pixel_type(dpy), NULL);
944 glCopyTexSubImage2D (dpy->gl_texture_target, 0, 0, 0,
945 src_x, src_height - src_y - height, width, height);
950 jwxyz_gl_copy_area_write_tex_image (Display *dpy, GC gc, int src_x, int src_y,
951 unsigned int width, unsigned int height,
952 int dst_x, int dst_y)
954 jwxyz_gl_set_gc (dpy, gc);
956 /* TODO: Copy-pasted from read_tex_image. */
957 unsigned tex_w = width, tex_h = height;
958 if (!dpy->gl_texture_npot_p) {
959 tex_w = to_pow2(tex_w);
960 tex_h = to_pow2(tex_h);
963 /* Must match what's in jwxyz_gl_copy_area_read_tex_image. */
964 glBindTexture (dpy->gl_texture_target, dpy->textures[texture_rgba]);
966 jwxyz_gl_draw_image (dpy, gc, dpy->gl_texture_target, tex_w, tex_h,
967 0, 0, gc->depth, width, height, dst_x, dst_y, False);
974 jwxyz_gl_draw_image (Display *dpy, GC gc, GLenum gl_texture_target,
975 unsigned int tex_w, unsigned int tex_h,
976 int src_x, int src_y, int src_depth,
977 unsigned int width, unsigned int height,
978 int dst_x, int dst_y, Bool flip_y)
980 if (!gc || src_depth == gc->depth) {
981 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
983 Assert (src_depth == 1 && gc->depth == 32,
984 "jwxyz_gl_draw_image: bad depths");
986 set_color (dpy, gc->gcv.background, gc->depth, gc->gcv.alpha_allowed_p);
989 query_color (dpy, gc->gcv.foreground, gc->depth, gc->gcv.alpha_allowed_p,
991 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
992 glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba);
995 glEnable (gl_texture_target);
996 glEnableClientState (GL_TEXTURE_COORD_ARRAY);
997 glEnableClientState (GL_VERTEX_ARRAY);
999 Assert (!glIsEnabled (GL_COLOR_ARRAY), "glIsEnabled (GL_COLOR_ARRAY)");
1000 Assert (!glIsEnabled (GL_NORMAL_ARRAY), "glIsEnabled (GL_NORMAL_ARRAY)");
1002 /* TODO: EXT_draw_texture or whatever it's called. */
1003 GLfloat vertices[4][2] = {
1005 {dst_x, dst_y + height},
1006 {dst_x + width, dst_y + height},
1007 {dst_x + width, dst_y}
1011 tex_x0 = src_x, tex_y0 = src_y,
1012 tex_x1 = src_x + width, tex_y1 = src_y;
1019 # ifndef HAVE_JWZGLES
1020 if (gl_texture_target != GL_TEXTURE_RECTANGLE_EXT)
1023 GLfloat mx = 1.0f / tex_w, my = 1.0f / tex_h;
1030 GLfloat tex_coords[4][2] = {
1037 vertex_pointer (dpy, GL_FLOAT, 0, vertices);
1038 glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
1039 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1042 glDisable (gl_texture_target);
1047 jwxyz_gl_copy_area_read_pixels (Display *dpy, Drawable src, Drawable dst,
1048 GC gc, int src_x, int src_y,
1049 unsigned int width, unsigned int height,
1050 int dst_x, int dst_y)
1052 XImage *img = XGetImage (dpy, src, src_x, src_y, width, height, ~0, ZPixmap);
1053 XPutImage (dpy, dst, gc, img, 0, 0, dst_x, dst_y, width, height);
1054 XDestroyImage (img);
1060 DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1063 set_fg_gc (dpy, d, gc);
1065 /* TODO: Thick lines
1066 * Zero-length line segments
1067 * Paths with zero length total (Remember line width, cap style.)
1074 GLshort *vertices = malloc(2 * sizeof(GLshort) * count); // TODO malloc NULL sigh
1076 glMatrixMode (GL_MODELVIEW);
1077 glTranslatef (0.5f, 0.5f, 0);
1079 short p[2] = {0, 0};
1080 for (unsigned i = 0; i < count; i++) {
1081 next_point (p, points[i], mode);
1082 vertices[2 * i] = p[0];
1083 vertices[2 * i + 1] = p[1];
1086 glEnableClientState (GL_VERTEX_ARRAY);
1087 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1088 vertex_pointer (dpy, GL_SHORT, 0, vertices);
1089 glDrawArrays (GL_LINE_STRIP, 0, count);
1093 if (gc->gcv.cap_style != CapNotLast) {
1094 // TODO: How does this look with multisampling?
1095 // TODO: Disable me for closed loops.
1096 vertex_pointer (dpy, GL_SHORT, 0, p);
1097 glDrawArrays (GL_POINTS, 0, 1);
1106 // Turn line segment into parallelogram based on line_width
1108 // TODO: Fix epicycle hack with large thickness, and truchet line segment ends
1111 drawThickLine (Display *dpy, Drawable d, GC gc, int line_width,
1114 double dx, dy, di, m, angle;
1115 int sx1, sx2, sy1, sy2;
1124 di = sqrt(dx * dx + dy * dy);
1131 float sn = sin(angle);
1132 float cs = cos(angle);
1133 float line_width_f = (float) line_width;
1135 float wsn = line_width_f * (sn/2);
1136 float csn = line_width_f * (cs/2);
1138 float x3 = sx1 - wsn;
1139 float y3 = sy1 + csn;
1140 float x4 = sx1 + wsn;
1141 float y4 = sy1 - csn;
1143 float x5 = sx2 - wsn;
1144 float y5 = sy2 + csn;
1145 float x6 = sx2 + wsn;
1146 float y6 = sy2 - csn;
1148 GLfloat *coords = enqueue (dpy, d, gc, GL_TRIANGLE_STRIP, 4,
1149 gc->gcv.foreground);
1158 finish_triangle_strip (dpy, coords);
1163 DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1165 /* TODO: Caps on thick lines. */
1166 /* Thin lines <= 1px are offset by +0.5; thick lines are not. */
1168 if (count == 1 && gc->gcv.line_width > 1) {
1169 drawThickLine (dpy, d, gc, gc->gcv.line_width, segments);
1172 if (dpy->queue_line_cap != (gc->gcv.cap_style != CapNotLast))
1173 jwxyz_gl_flush (dpy);
1174 dpy->queue_line_cap = gc->gcv.cap_style != CapNotLast;
1176 // TODO: Static assert here.
1177 Assert (sizeof(XSegment) == sizeof(short) * 4 &&
1178 sizeof(GLshort) == sizeof(short) &&
1179 offsetof(XSegment, x1) == 0 &&
1180 offsetof(XSegment, x2) == 4,
1181 "XDrawSegments: Data alignment mix-up.");
1183 memcpy (enqueue(dpy, d, gc, GL_LINES, count * 2, gc->gcv.foreground),
1184 segments, count * sizeof(XSegment));
1192 ClearWindow (Display *dpy, Window win)
1194 Assert (win == dpy->main_window, "not a window");
1196 jwxyz_gl_flush (dpy);
1197 jwxyz_bind_drawable (dpy, win, win);
1200 JWXYZ_QUERY_COLOR (dpy, dpy->window_background, 1.0f, color);
1202 glClearColor (color[0], color[1], color[2], 1);
1203 glClear (GL_COLOR_BUFFER_BIT);
1207 static unsigned long *
1208 window_background (Display *dpy)
1210 return &dpy->window_background;
1214 fill_rects (Display *dpy, Drawable d, GC gc,
1215 const XRectangle *rectangles, unsigned long nrectangles,
1216 unsigned long pixel)
1218 for (unsigned long i = 0; i != nrectangles; ++i) {
1219 const XRectangle *r = &rectangles[i];
1220 GLfloat *coords = enqueue (dpy, d, gc, GL_TRIANGLE_STRIP, 4, pixel);
1224 coords[3] = r->y + r->height;
1225 coords[4] = r->x + r->width;
1227 coords[6] = r->x + r->width;
1228 coords[7] = r->y + r->height;
1229 finish_triangle_strip (dpy, coords);
1235 FillPolygon (Display *dpy, Drawable d, GC gc,
1236 XPoint *points, int npoints, int shape, int mode)
1238 set_fg_gc(dpy, d, gc);
1240 // TODO: Re-implement the GLU tesselation functions.
1242 /* Complex: Pedal, and for some reason Attraction, Mountain, Qix, SpeedMine, Starfish
1243 * Nonconvex: Goop, Pacman, Rocks, Speedmine
1245 * We currently do Nonconvex with the simple-to-implement ear clipping
1246 * algorithm, but in the future we can replace that with an algorithm
1247 * with slower big-O growth
1252 // TODO: Feed vertices straight to OpenGL for CoordModeOrigin.
1254 if (shape == Convex) {
1256 GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc.
1257 short v[2] = {0, 0};
1259 for (unsigned i = 0; i < npoints; i++) {
1260 next_point(v, points[i], mode);
1261 vertices[2 * i] = v[0];
1262 vertices[2 * i + 1] = v[1];
1265 glEnableClientState (GL_VERTEX_ARRAY);
1266 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1268 vertex_pointer (dpy, GL_SHORT, 0, vertices);
1269 glDrawArrays (GL_TRIANGLE_FAN, 0, npoints);
1273 } else if (shape == Nonconvex) {
1275 // TODO: assert that x,y of first and last point match, as that is assumed
1278 root = (linked_point *) malloc( sizeof(linked_point) );
1279 set_points_list(points,npoints,root);
1280 traverse_points_list(dpy, root);
1283 Assert((shape == Convex || shape == Nonconvex),
1284 "XFillPolygon: (TODO) Unimplemented shape");
1290 #define radians(DEG) ((DEG) * M_PI / 180.0)
1291 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1294 arc_xy(GLfloat *p, double cx, double cy, double w2, double h2, double theta)
1296 p[0] = cos(theta) * w2 + cx;
1297 p[1] = -sin(theta) * h2 + cy;
1301 mod_neg(int a, unsigned b)
1303 /* Normal modulus is implementation defined for negative numbers. This is
1304 * well-defined such that the repeating pattern for a >= 0 is maintained for
1306 return a < 0 ? (b - 1) - (-(a + 1) % b) : a % b;
1309 /* TODO: Fill in arcs with line width > 1 */
1311 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1312 unsigned int width, unsigned int height,
1313 int angle1, int angle2, Bool fill_p)
1315 int gglw = gc->gcv.line_width;
1317 if (fill_p || gglw <= 1) {
1318 draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p);
1321 int w1, w2, h1, h2, gglwh;
1331 //draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p);
1332 draw_arc_gl (dpy, d, gc, x1, y1, w1, h1, angle1, angle2, fill_p);
1333 draw_arc_gl (dpy, d, gc, x2, y2, w2, h2, angle1, angle2, fill_p);
1340 draw_arc_gl (Display *dpy, Drawable d, GC gc, int x, int y,
1341 unsigned int width, unsigned int height,
1342 int angle1, int angle2, Bool fill_p)
1344 set_fg_gc(dpy, d, gc);
1346 /* Let's say the number of line segments needed to make a convincing circle is
1347 4*sqrt(radius). (But these arcs aren't necessarily circular arcs...) */
1349 double w2 = width * 0.5f, h2 = height * 0.5f;
1350 double a, b; /* Semi-major/minor axes. */
1359 const double two_pi = 2 * M_PI;
1361 double amb = a - b, apb = a + b;
1362 double h = (amb * amb) / (apb * apb);
1363 // TODO: Math cleanup.
1364 double C_approx = M_PI * apb * (1 + 3 * h / (10 + sqrtf(4 - 3 * h)));
1365 double segments_f = 4 * sqrtf(C_approx / (2 * M_PI));
1367 // TODO: Explain how drawing works what with the points of overlapping arcs
1371 unsigned segments_360 = segments_f;
1373 /* TODO: angle2 == 0. This is a tilted square with CapSquare. */
1374 /* TODO: color, thick lines, CapNotLast for thin lines */
1375 /* TODO: Transformations. */
1377 double segment_angle = two_pi / segments_360;
1379 const unsigned deg64 = 360 * 64;
1380 const double rad_from_deg64 = two_pi / deg64;
1387 angle1 = mod_neg(angle1, deg64); // TODO: Is this OK? Consider negative numbers.
1390 angle2 = deg64; // TODO: Handle circles special.
1393 angle1_f = angle1 * rad_from_deg64,
1394 angle2_f = angle2 * rad_from_deg64;
1396 if (angle2_f > two_pi) // TODO: Move this up.
1399 double segment1_angle_part = fmodf(angle1_f, segment_angle);
1401 unsigned segment1 = ((angle1_f - segment1_angle_part) / segment_angle) + 1.5;
1403 double angle_2r = angle2_f - segment1_angle_part;
1404 unsigned segments = angle_2r / segment_angle;
1406 GLfloat cx = x + w2, cy = y + h2;
1408 GLfloat *data = malloc((segments + 3) * sizeof(GLfloat) * 2); // TODO: Check result.
1410 GLfloat *data_ptr = data;
1417 arc_xy (data_ptr, cx, cy, w2, h2, angle1_f);
1420 for (unsigned s = 0; s != segments; ++s) {
1421 // TODO: Make sure values of theta for the following arc_xy call are between
1422 // angle1_f and angle1_f + angle2_f.
1423 arc_xy (data_ptr, cx, cy, w2, h2, (segment1 + s) * segment_angle);
1427 arc_xy (data_ptr, cx, cy, w2, h2, angle1_f + angle2_f);
1430 glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1431 glEnableClientState (GL_VERTEX_ARRAY);
1433 vertex_pointer (dpy, GL_FLOAT, 0, data);
1434 glDrawArrays (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP,
1436 (GLsizei)((data_ptr - data) / 2));
1443 unsigned segments = segments_f * (fabs(angle2) / (360 * 64));
1445 glBegin (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP);
1447 if (fill_p /* && gc->gcv.arc_mode == ArcPieSlice */)
1448 glVertex2f (cx, cy);
1450 /* TODO: This should fix the middle points of the arc so that the starting and ending points are OK. */
1452 float to_radians = 2 * M_PI / (360 * 64);
1453 float theta = angle1 * to_radians, d_theta = angle2 * to_radians / segments;
1455 for (unsigned s = 0; s != segments + 1; ++s) /* TODO: This is the right number of segments, yes? */
1457 glVertex2f(cos(theta) * w2 + cx, -sin(theta) * h2 + cy);
1483 CreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1485 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1486 gc->depth = jwxyz_drawable_depth (d);
1488 jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
1489 XChangeGC (dpy, gc, mask, xgcv);
1495 free_clip_mask (Display *dpy, GC gc)
1497 if (gc->gcv.clip_mask) {
1498 if (dpy->gc_clip_mask == gc->clip_mask) {
1499 jwxyz_gl_flush (dpy);
1500 dpy->gc_clip_mask = 0;
1502 glDeleteTextures (1, &gc->clip_mask);
1508 FreeGC (Display *dpy, GC gc)
1511 XUnloadFont (dpy, gc->gcv.font);
1513 free_clip_mask (dpy, gc);
1520 PutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1521 int src_x, int src_y, int dest_x, int dest_y,
1522 unsigned int w, unsigned int h)
1524 jwxyz_assert_display (dpy);
1526 const XRectangle *wr = jwxyz_frame (d);
1528 Assert (gc, "no GC");
1529 Assert ((w < 65535), "improbably large width");
1530 Assert ((h < 65535), "improbably large height");
1531 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1532 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1533 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1534 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1536 // Clip width and height to the bounds of the Drawable
1538 if (dest_x + w > wr->width) {
1539 if (dest_x > wr->width)
1541 w = wr->width - dest_x;
1543 if (dest_y + h > wr->height) {
1544 if (dest_y > wr->height)
1546 h = wr->height - dest_y;
1548 if (w <= 0 || h <= 0)
1551 // Clip width and height to the bounds of the XImage
1553 if (src_x + w > ximage->width) {
1554 if (src_x > ximage->width)
1556 w = ximage->width - src_x;
1558 if (src_y + h > ximage->height) {
1559 if (src_y > ximage->height)
1561 h = ximage->height - src_y;
1563 if (w <= 0 || h <= 0)
1566 /* Assert (d->win */
1568 if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
1571 jwxyz_gl_flush (dpy);
1572 jwxyz_bind_drawable (dpy, dpy->main_window, d);
1573 jwxyz_gl_set_gc (dpy, gc);
1575 int bpl = ximage->bytes_per_line;
1576 int bpp = ximage->bits_per_pixel;
1580 GLint tex_internalformat;
1581 GLenum tex_format, tex_type;
1585 tex_data = ximage->data + src_y * bpl + (src_x * 4);
1587 jwxyz_assert_display(dpy);
1589 /* There probably won't be any hacks that do this, but... */
1590 Assert (!(bpl % 4), "XPutImage: bytes_per_line not divisible by four.");
1592 tex_internalformat = texture_internalformat(dpy);
1593 tex_format = dpy->pixel_format;
1594 tex_type = gl_pixel_type(dpy);
1595 tex_index = texture_rgba;
1597 /* GL_UNPACK_ROW_LENGTH is not allowed to be negative. (sigh) */
1598 # ifndef HAVE_JWZGLES
1600 glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
1605 // glPixelStorei (GL_UNPACK_ALIGNMENT, 4); // Probably unnecessary.
1607 Assert (bpp == 1, "expected 1 or 32 bpp");
1608 Assert ((src_x % 8) == 0,
1609 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1611 const char *src_data = ximage->data + src_y * bpl + (src_x / 8);
1612 unsigned w8 = (w + 7) / 8;
1616 tex_data = malloc(src_w * h);
1622 Log("#PI ---------- %d %d %08lx %08lx",
1623 jwxyz_drawable_depth(d), ximage->depth,
1624 (unsigned long)d, (unsigned long)ximage);
1625 for (y = 0; y < ximage->height; y++) {
1627 for (x = 0; x < ximage->width; x++) {
1628 unsigned long b = XGetPixel(ximage, x, y);
1629 s[o++] = ( (b & 0xFF000000)
1630 ? ((b & 0x00FFFFFF) ? '#' : '-')
1631 : ((b & 0x00FFFFFF) ? '+' : ' '));
1636 Log("# PI ----------");
1639 uint32_t *data_out = (uint32_t *)tex_data;
1640 for(unsigned y = h; y; --y) {
1641 for(unsigned x = 0; x != w8; ++x) {
1642 // TODO: Does big endian work here?
1643 uint8_t byte = src_data[x];
1644 uint32_t word = byte;
1645 word = (word & 0x3) | ((word & 0xc) << 14);
1646 word = (word & 0x00010001) | ((word & 0x00020002) << 7);
1647 data_out[x << 1] = (word << 8) - word;
1650 word = (word & 0x3) | ((word & 0xc) << 14);
1651 word = (word & 0x00010001) | ((word & 0x00020002) << 7);
1652 data_out[(x << 1) | 1] = (word << 8) - word;
1655 data_out += src_w / 4;
1661 Log("#P2 ----------");
1662 for (y = 0; y < ximage->height; y++) {
1664 for (x = 0; x < ximage->width; x++) {
1665 unsigned long b = ((uint8_t *)tex_data)[y * w + x];
1666 s[o++] = ( (b & 0xFF000000)
1667 ? ((b & 0x00FFFFFF) ? '#' : '-')
1668 : ((b & 0x00FFFFFF) ? '+' : ' '));
1673 Log("# P2 ----------");
1677 tex_internalformat = GL_LUMINANCE;
1678 tex_format = GL_LUMINANCE;
1679 tex_type = GL_UNSIGNED_BYTE;
1680 tex_index = texture_mono;
1682 // glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1685 # if 1 // defined HAVE_JWZGLES
1686 // Regular OpenGL uses GL_TEXTURE_RECTANGLE_EXT in place of GL_TEXTURE_2D.
1687 // TODO: Make use of OES_draw_texture.
1689 unsigned tex_w = src_w, tex_h = h;
1690 glBindTexture (dpy->gl_texture_target, dpy->textures[tex_index]);
1692 // A fun project: reimplement xshm.c by means of a PBO using
1693 // GL_MAP_UNSYNCHRONIZED_BIT.
1695 tex_image (dpy, tex_internalformat, &tex_w, &tex_h, tex_format, tex_type,
1701 jwxyz_gl_draw_image (dpy, gc, dpy->gl_texture_target, tex_w, tex_h,
1702 0, 0, bpp, w, h, dest_x, dest_y, True);
1704 glRasterPos2i (dest_x, dest_y);
1705 glPixelZoom (1, -1);
1706 jwxyz_assert_display (dpy);
1707 glDrawPixels (w, h, dpy->pixel_format, gl_pixel_type(dpy), data);
1715 /* At the moment only XGetImage and get_xshm_image use XGetSubImage. */
1716 /* #### Twang calls XGetImage on the window intending to get a
1717 buffer full of black. This is returning a buffer full of white
1718 instead of black for some reason. */
1720 GetSubImage (Display *dpy, Drawable d, int x, int y,
1721 unsigned int width, unsigned int height,
1722 unsigned long plane_mask, int format,
1723 XImage *dest_image, int dest_x, int dest_y)
1725 Assert ((width < 65535), "improbably large width");
1726 Assert ((height < 65535), "improbably large height");
1727 Assert ((x < 65535 && x > -65535), "improbably large x");
1728 Assert ((y < 65535 && y > -65535), "improbably large y");
1730 jwxyz_gl_flush (dpy);
1731 jwxyz_bind_drawable (dpy, dpy->main_window, d);
1733 // TODO: What if this reads off the edge? What is supposed to happen?
1736 // In case the caller tries to write off the edge.
1738 max_width = dest_image->width - dest_x,
1739 max_height = dest_image->height - dest_y;
1741 if (width > max_width) {
1745 if (height > max_height) {
1746 height = max_height;
1750 Assert (jwxyz_drawable_depth (d) == dest_image->depth, "XGetSubImage: depth mismatch");
1752 if (dest_image->depth == visual_depth (NULL, NULL)) {
1753 Assert (!(dest_image->bytes_per_line % 4), "XGetSubImage: bytes_per_line not divisible by 4");
1754 unsigned pixels_per_line = dest_image->bytes_per_line / 4;
1756 Assert (pixels_per_line == width, "XGetSubImage: (TODO) pixels_per_line != width");
1758 glPixelStorei (GL_PACK_ROW_LENGTH, pixels_per_line);
1760 glPixelStorei (GL_PACK_ALIGNMENT, 4);
1762 uint32_t *dest_data = (uint32_t *)dest_image->data + pixels_per_line * dest_y + dest_x;
1764 glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
1765 dpy->pixel_format, gl_pixel_type(dpy), dest_data);
1767 /* Flip this upside down. :( */
1768 uint32_t *top = dest_data;
1769 uint32_t *bottom = dest_data + pixels_per_line * (height - 1);
1770 for (unsigned y = height / 2; y; --y) {
1771 for (unsigned x = 0; x != width; ++x) {
1772 uint32_t px = top[x];
1776 top += pixels_per_line;
1777 bottom -= pixels_per_line;
1781 uint32_t *rgba_image = malloc(width * height * 4);
1782 Assert (rgba_image, "not enough memory");
1784 // Must be GL_RGBA; GL_RED isn't available.
1785 glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
1786 GL_RGBA, GL_UNSIGNED_BYTE, rgba_image);
1788 Assert (!(dest_x % 8), "XGetSubImage: dest_x not byte-aligned");
1790 (uint8_t *)dest_image->data + dest_image->bytes_per_line * dest_y
1795 Log("#GI ---------- %d %d %d x %d %08lx",
1796 jwxyz_drawable_depth(d), dest_image->depth,
1799 for (int y = 0; y < height; y++) {
1801 for (x = 0; x < width; x++) {
1802 unsigned long b = rgba_image[(height-y-1) * width + x];
1803 s[x] = ( (b & 0xFF000000)
1804 ? ((b & 0x00FFFFFF) ? '#' : '-')
1805 : ((b & 0x00FFFFFF) ? '+' : ' '));
1810 Log("#GI ----------");
1813 const uint32_t *bottom = rgba_image + width * (height - 1);
1814 for (unsigned y = height; y; --y) {
1815 memset (top, 0, width / 8);
1816 for (unsigned x = 0; x != width; ++x) {
1817 if (bottom[x] & 0x80)
1818 top[x >> 3] |= (1 << (x & 7));
1820 top += dest_image->bytes_per_line;
1832 SetClipMask (Display *dpy, GC gc, Pixmap m)
1834 Assert (m != dpy->main_window, "not a pixmap");
1836 free_clip_mask (dpy, gc);
1843 Assert (jwxyz_drawable_depth (m) == 1, "wrong depth for clip mask");
1845 const XRectangle *frame = jwxyz_frame (m);
1846 gc->clip_mask_width = frame->width;
1847 gc->clip_mask_height = frame->height;
1849 /* Apparently GL_ALPHA isn't a valid format for a texture used in an FBO:
1851 (86) Are any one- or two- component formats color-renderable?
1853 Presently none of the one- or two- component texture formats
1854 defined in unextended OpenGL is color-renderable. The R
1855 and RG float formats defined by the NV_float_buffer
1856 extension are color-renderable.
1858 Although an early draft of the FBO specification permitted
1859 rendering into alpha, luminance, and intensity formats, this
1860 this capability was pulled when it was realized that it was
1861 under-specified exactly how rendering into these formats
1862 would work. (specifically, how R/G/B/A map to I/L/A)
1864 <https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_framebuffer_object.txt>
1866 ...Therefore, 1-bit drawables get to have wasted color channels.
1867 Currently, R=G=B=grey, and the alpha channel is unused.
1870 /* There doesn't seem to be any way to move what's on one of the color
1871 channels to the alpha channel in GLES 1.1 without leaving the GPU.
1874 /* GLES 1.1 only supports GL_CLAMP_TO_EDGE or GL_REPEAT for
1875 GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T, so to prevent drawing outside
1876 the clip mask pixmap, there's two options:
1878 2. Add a black border.
1880 Option #2 is in use here.
1882 The following converts in-place an RGBA image to alpha-only image with a
1886 // Prefix/suffix: 1 row+1 pixel, rounded to nearest int32.
1887 size_t pad0 = frame->width + 3, pad = (pad0 + 3) & ~3;
1888 uint8_t *data = malloc(2 * pad + frame->width * frame->height * 4);
1890 uint8_t *rgba = data + pad;
1891 uint8_t *alpha = rgba;
1892 uint8_t *alpha0 = alpha - pad0;
1893 memset (alpha0, 0, pad0); // Top row.
1895 jwxyz_gl_flush (dpy);
1896 jwxyz_bind_drawable (dpy, dpy->main_window, m);
1897 glReadPixels (0, 0, frame->width, frame->height, GL_RGBA, GL_UNSIGNED_BYTE,
1900 for (unsigned y = 0; y != frame->height; ++y) {
1901 for (unsigned x = 0; x != frame->width; ++x)
1902 alpha[x] = rgba[x * 4];
1904 rgba += frame->width * 4;
1906 alpha += frame->width;
1913 memset (alpha, 0, pad0); // Bottom row.
1915 glGenTextures (1, &gc->clip_mask);
1916 tex_parameters (dpy, gc->clip_mask);
1918 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1920 unsigned tex_w = frame->width + 2, tex_h = frame->height + 2;
1921 tex_image (dpy, GL_ALPHA, &tex_w, &tex_h, GL_ALPHA, GL_UNSIGNED_BYTE,
1930 SetClipOrigin (Display *dpy, GC gc, int x, int y)
1932 gc->gcv.clip_x_origin = x;
1933 gc->gcv.clip_y_origin = y;
1937 void set_points_list(XPoint *points, int npoints, linked_point *root)
1939 linked_point *current;
1942 for (int i = 0; i < npoints - 2 ; i++) {
1943 current->x = points[i].x;
1944 current->y = points[i].y;
1945 current->next = (linked_point *) malloc(sizeof(linked_point));
1946 current = current->next;
1948 current->x = points[npoints-2].x;
1949 current->y = points[npoints-2].y;
1950 current->next = root;
1954 double compute_edge_length(linked_point * a, linked_point * b)
1957 int xdiff, ydiff, xsq, ysq, added;
1958 double xy_add, edge_length;
1960 xdiff = a->x - b->x;
1961 ydiff = a->y - b->y;
1962 xsq = xdiff * xdiff;
1963 ysq = ydiff * ydiff;
1965 xy_add = (double) added;
1966 edge_length = sqrt(xy_add);
1970 double get_angle(double a, double b, double c)
1972 double cos_a, i_cos_a;
1973 cos_a = (((b * b) + (c * c)) - (a * a)) / (double) (2.0 * b * c);
1974 i_cos_a = acos(cos_a);
1979 Bool is_same_slope(linked_point * a)
1982 int abx, bcx, aby, bcy, aa, bb;
1989 // test if slopes are indefinite for both line segments
1991 return b->x == c->x;
1992 } else if (b->x == c->x) {
1993 return False; // false, as ax/bx is not indefinite
2006 void draw_three_vertices(Display *dpy, linked_point * a, Bool triangle)
2016 GLfloat vertices[3][2] = {
2023 drawType = GL_TRIANGLES;
2025 drawType = GL_LINES;
2028 glEnableClientState(GL_VERTEX_ARRAY);
2029 vertex_pointer(dpy, GL_FLOAT, 0, vertices);
2030 glDrawArrays(drawType, 0, 3);
2032 free(b); // cut midpoint off from remaining polygon vertex list
2037 Bool is_an_ear(linked_point * a)
2039 double edge_ab, edge_bc, edge_ac;
2040 double angle_a, angle_b, angle_c;
2042 linked_point *b, *c;
2046 my_pi = (double) M_PI;
2048 edge_ab = compute_edge_length(a, b);
2049 edge_bc = compute_edge_length(b, c);
2050 edge_ac = compute_edge_length(a, c);
2051 angle_a = get_angle(edge_bc, edge_ab, edge_ac);
2052 angle_b = get_angle(edge_ac, edge_ab, edge_bc);
2053 angle_c = get_angle(edge_ab, edge_ac, edge_bc);
2055 return angle_a < my_pi && angle_b < my_pi && angle_c < my_pi;
2059 Bool is_three_point_loop(linked_point * head)
2061 return head->x == head->next->next->next->x
2062 && head->y == head->next->next->next->y;
2066 void traverse_points_list(Display *dpy, linked_point * root)
2071 while (!is_three_point_loop(head)) {
2072 if (is_an_ear(head)) {
2073 draw_three_vertices(dpy, head, True);
2074 } else if (is_same_slope(head)) {
2075 draw_three_vertices(dpy, head, False);
2081 // handle final three vertices in polygon
2082 if (is_an_ear(head)) {
2083 draw_three_vertices(dpy, head, True);
2084 } else if (is_same_slope(head)) {
2085 draw_three_vertices(dpy, head, False);
2087 free(head->next->next);
2090 Assert (False, "traverse_points_list: unknown configuration");
2098 const struct jwxyz_vtbl gl_vtbl = {
2101 display_sources_data,
2125 #endif /* JWXYZ_GL -- entire file */