-/* xscreensaver, Copyright (c) 1991-2016 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2018 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
Xlib and that do OpenGL-ish things that bear some resemblance to the
things that Xlib might have done.
- This is the version of jwxyz for Android. The version used by MacOS
+ This is the version of jwxyz for Android. The version used by macOS
and iOS is in jwxyz.m.
*/
-/* Be advised, this is all very much a work in progress.
-
- TODO: The following should be implemented before OpenGL can be considered
- practical here:
- - Above all, pick the smallest not-yet working hack that utilizes the
- needed functionality.
- - Half-ass the drawing functions.
- - [OK] What Interference needs
- - Fast Pixmaps
- - Whatever clipping is used in XScreenSaver (shape and/or bitmap clipping)
- - Delayed context creation to support anti-aliasing/multisampling
- - Everything these hacks need:
- - FuzzyFlakes (needs wide lines)
- - Greynetic
- - [OK] Deluxe
- - [OK] Get DangerBall going.
- - [OK] iOS.
- - [Interference, so far...] And fast, too.
- - And text really needs to work for the FPS display. */
-
-/* Also, Take note that OS X can actually run with 256 colors. */
+/* Be advised, this is all very much a work in progress. */
+
+/* There is probably no reason to ever implement indexed-color rendering here,
+ even if many screenhacks still work with PseudoColor.
+ - OpenGL ES 1.1 (Android, iOS) doesn't support indexed color.
+ - macOS only provides indexed color via AGL (now deprecated), not
+ NSOpenGLPixelFormat.
+ */
/* TODO:
- malloc error checking
- iOS: OpenGL ES 2.0 isn't always available. Use OpenGL ES 1.1.
- OS X: Drivers can go back to OpenGL 1.1 (GeForce 2 MX on 10.5.8).
- Use stencil buffers (OpenGL 1.0+) for bitmap clipping masks.
- - glLogicOp is an actual thing that should work for GCs.
- Pixmaps can be any of the following, depending on GL implementation.
- This requires offscreen rendering. Fortunately, this is always
available.
*/
/* OpenGL hacks call a number of X11 functions, including
- * XCopyArea, XDrawString, XGetImage
- * XCreatePixmap, XCreateGC, XCreateImage
- * XPutPixel
- * Check these, of course. */
+ XCopyArea, XDrawString, XGetImage
+ XCreatePixmap, XCreateGC, XCreateImage
+ XPutPixel
+ Check these, of course.
+ */
#ifdef JWXYZ_GL /* entire file */
# import <QuartzCore/QuartzCore.h>
# include <OpenGLES/ES1/gl.h>
# include <OpenGLES/ES1/glext.h>
-
-# define NSView UIView
-# define NSRect CGRect
-# define NSPoint CGPoint
-# define NSSize CGSize
-# define NSColor UIColor
-# define NSImage UIImage
-# define NSEvent UIEvent
-# define NSFont UIFont
-# define NSGlyph CGGlyph
-# define NSWindow UIWindow
-# define NSMakeSize CGSizeMake
-# define NSBezierPath UIBezierPath
-# define colorWithDeviceRed colorWithRed
-
-# define NSFontTraitMask UIFontDescriptorSymbolicTraits
-// The values for the flags for NSFontTraitMask and
-// UIFontDescriptorSymbolicTraits match up, not that it really matters here.
-# define NSBoldFontMask UIFontDescriptorTraitBold
-# define NSFixedPitchFontMask UIFontDescriptorTraitMonoSpace
-# define NSItalicFontMask UIFontDescriptorTraitItalic
-
-# define NSOpenGLContext EAGLContext
-
# else
# include <OpenGL/glu.h>
# endif
# include "jwzglesI.h"
#endif
-#ifdef HAVE_ANDROID
-# include <android/log.h>
-#endif
-
#include "jwxyzI.h"
#include "jwxyz-timers.h"
#include "yarandom.h"
#include "utf8wc.h"
#include "xft.h"
+#include "pow2.h"
-#if defined HAVE_COCOA
-# include <CoreGraphics/CGGeometry.h>
-#else
-
-struct CGPoint {
- float x;
- float y;
-};
-typedef struct CGPoint CGPoint;
-
-struct CGSize {
- float width;
- float height;
-};
-typedef struct CGSize CGSize;
+#define countof(x) (sizeof((x))/sizeof((*x)))
-struct CGRect {
- CGPoint origin;
- CGSize size;
+union color_bytes {
+ uint32_t pixel;
+ uint8_t bytes[4];
};
-typedef struct CGRect CGRect;
-
-#endif
-
-# undef MAX
-# undef MIN
-# define MAX(a,b) ((a)>(b)?(a):(b))
-# define MIN(a,b) ((a)<(b)?(a):(b))
-union color_bytes
-{
- /* On 64-bit systems, high bits of the 32-bit pixel are available as scratch
- space. I doubt if any screen savers need it, but just in case... */
- unsigned long pixel;
- uint8_t bytes[4];
+// Use two textures: one for RGBA, one for luminance. Older Android doesn't
+// seem to like it when textures change format.
+enum {
+ texture_rgba,
+ texture_mono
};
struct jwxyz_Display {
+ const struct jwxyz_vtbl *vtbl; // Must come first.
+
Window main_window;
- Screen *screen;
- int screen_count;
+ GLenum pixel_format, pixel_type;
+ Visual visual;
struct jwxyz_sources_data *timers_data;
Bool gl_texture_npot_p;
/* Bool opengl_core_p */;
GLenum gl_texture_target;
-// #if defined USE_IPHONE
- GLuint rect_texture; // Also can work on the desktop.
-// #endif
+ GLuint textures[2]; // Also can work on the desktop.
unsigned long window_background;
-};
-struct jwxyz_Screen {
- Display *dpy;
- GLenum pixel_format, pixel_type;
- unsigned long black, white;
- Visual *visual;
- int screen_number;
+ int gc_function;
+ Bool gc_alpha_allowed_p;
+ int gc_clip_x_origin, gc_clip_y_origin;
+ GLuint gc_clip_mask;
+
+ // Alternately, there could be one queue per pixmap.
+ size_t queue_size, queue_capacity;
+ Drawable queue_drawable;
+ GLint queue_mode;
+ void *queue_vertex;
+ uint32_t *queue_color;
+ Bool queue_line_cap;
};
struct jwxyz_GC {
XGCValues gcv;
unsigned int depth;
- // CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
-};
-
-struct jwxyz_Font {
- Display *dpy;
- char *ps_name;
- void *native_font;
- int refcount; // for deciding when to release the native font
- float size; // points
- int ascent, descent;
- char *xa_font;
-
- // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
- // But we need the metrics on both of them, so they go here.
- XFontStruct metrics;
+ GLuint clip_mask;
+ unsigned clip_mask_width, clip_mask_height;
};
-struct jwxyz_XFontSet {
- XFontStruct *font;
+struct jwxyz_linked_point {
+ short x, y;
+ linked_point *next;
};
-/* XGetImage in CoreGraphics JWXYZ has to deal with funky pixel formats
- necessitating fast & flexible pixel conversion. OpenGL does image format
- conversion itself, so alloc_color and query_color are mercifully simple.
- */
-uint32_t
-jwxyz_alloc_color (Display *dpy,
- uint16_t r, uint16_t g, uint16_t b, uint16_t a)
-{
- union color_bytes color;
-
- /* Instead of (int)(c / 256.0), another possibility is
- (int)(c * 255.0 / 65535.0 + 0.5). This can be calculated using only
- uint8_t integer_math(uint16_t c) {
- unsigned c0 = c + 128;
- return (c0 - (c0 >> 8)) >> 8;
- }
- */
-
- color.bytes[0] = r >> 8;
- color.bytes[1] = g >> 8;
- color.bytes[2] = b >> 8;
- color.bytes[3] = a >> 8;
-
- if (dpy->screen->pixel_format == GL_BGRA_EXT) {
- color.pixel = color.bytes[2] |
- (color.bytes[1] << 8) |
- (color.bytes[0] << 16) |
- (color.bytes[3] << 24);
- } else {
- Assert(dpy->screen->pixel_format == GL_RGBA,
- "jwxyz_alloc_color: Unknown pixel_format");
- }
-
- return (uint32_t)color.pixel;
-}
-
-// Converts an array of pixels ('src') from one format to another, placing the
-// result in 'dest', according to the pixel conversion mode 'mode'.
-void
-jwxyz_query_color (Display *dpy, unsigned long pixel, uint8_t *rgba)
-{
- union color_bytes color;
-
- if(dpy->screen->pixel_format == GL_RGBA)
- {
- color.pixel = pixel;
- for (unsigned i = 0; i != 4; ++i)
- rgba[i] = color.bytes[i];
- return;
- }
-
- Assert (dpy->screen->pixel_format == GL_BGRA_EXT,
- "jwxyz_query_color: Unknown pixel format");
- /* TODO: Cross-check with XAllocColor. */
- rgba[0] = (pixel >> 16) & 0xFF;
- rgba[1] = (pixel >> 8) & 0xFF;
- rgba[2] = (pixel >> 0) & 0xFF;
- rgba[3] = (pixel >> 24) & 0xFF;
-}
-
-
void
jwxyz_assert_display(Display *dpy)
{
jwxyz_set_matrices (Display *dpy, unsigned width, unsigned height,
Bool window_p)
{
+ Assert (width, "no width");
+ Assert (height, "no height");
+
/* TODO: Check registration pattern from Interference with rectangles instead of points. */
// The projection matrix is always set as follows. The modelview matrix is
(caps->major == gl_major && caps->minor >= gl_minor);
}
-/*
-static GLboolean gl_check_ext(const struct gl_caps *caps,
- unsigned gl_major,
- unsigned gl_minor,
- const char *extension)
+#endif
+
+
+static void
+tex_parameters (Display *d, GLuint texture)
{
- return
- gl_check_ver(caps, gl_major, gl_minor) ||
- gluCheckExtension(extension, caps->extensions);
-}
-*/
+ // TODO: Check for (ARB|EXT|NV)_texture_rectangle. (All three are alike.)
+ // Rectangle textures should be present on OS X with the following exceptions:
+ // - Generic renderer on PowerPC OS X 10.4 and earlier
+ // - ATI Rage 128
+ glBindTexture (d->gl_texture_target, texture);
+ // TODO: This is all somewhere else. Refactor.
+ glTexParameteri (d->gl_texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri (d->gl_texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-#endif
+ // This might be redundant for rectangular textures.
+# ifndef HAVE_JWZGLES
+ const GLint wrap = GL_CLAMP;
+# else // HAVE_JWZGLES
+ const GLint wrap = GL_CLAMP_TO_EDGE;
+# endif // HAVE_JWZGLES
+ // In OpenGL, CLAMP_TO_EDGE is OpenGL 1.2 or GL_SGIS_texture_edge_clamp.
+ // This is always present with OpenGL ES.
+ glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_S, wrap);
+ glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_T, wrap);
+}
-// NSOpenGLContext *jwxyz_debug_context;
+static void
+tex_size (Display *dpy, unsigned *tex_w, unsigned *tex_h)
+{
+ if (!dpy->gl_texture_npot_p) {
+ *tex_w = to_pow2(*tex_w);
+ *tex_h = to_pow2(*tex_h);
+ }
+}
-/* We keep a list of all of the Displays that have been created and not
- yet freed so that they can have sensible display numbers. If three
- displays are created (0, 1, 2) and then #1 is closed, then the fourth
- display will be given the now-unused display number 1. (Everything in
- here assumes a 1:1 Display/Screen mapping.)
+static void
+tex_image (Display *dpy, GLenum internalformat,
+ unsigned *tex_w, unsigned *tex_h, GLenum format, GLenum type,
+ const void *data)
+{
+ unsigned w = *tex_w, h = *tex_h;
+ tex_size (dpy, tex_w, tex_h);
- The size of this array is the most number of live displays at one time.
- So if it's 20, then we'll blow up if the system has 19 monitors and also
- has System Preferences open (the small preview window).
+ // TODO: Would using glTexSubImage2D exclusively be faster?
+ if (*tex_w == w && *tex_h == h) {
+ glTexImage2D (dpy->gl_texture_target, 0, internalformat, *tex_w, *tex_h,
+ 0, format, type, data);
+ } else {
+ // TODO: Sampling the last row might be a problem if src_x != 0.
+ glTexImage2D (dpy->gl_texture_target, 0, internalformat, *tex_w, *tex_h,
+ 0, format, type, NULL);
+ glTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, w, h,
+ format, type, data);
+ }
+}
- Note that xlockmore-style savers tend to allocate big structures, so
- setting this to 1000 will waste a few megabytes. Also some of them assume
- that the number of screens never changes, so dynamically expanding this
- array won't work.
- */
-# ifndef USE_IPHONE
-static Display *jwxyz_live_displays[20] = { 0, };
-# endif
+extern const struct jwxyz_vtbl gl_vtbl;
Display *
-jwxyz_make_display (Window w)
+jwxyz_gl_make_display (Window w)
{
Display *d = (Display *) calloc (1, sizeof(*d));
- d->screen = (Screen *) calloc (1, sizeof(Screen));
- d->screen->dpy = d;
-
- d->screen_count = 1;
- d->screen->screen_number = 0;
-# ifndef USE_IPHONE
- {
- // Find the first empty slot in live_displays and plug us in.
- int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
- int i;
- for (i = 0; i < size; i++) {
- if (! jwxyz_live_displays[i])
- break;
- }
- if (i >= size) abort();
- jwxyz_live_displays[i] = d;
- d->screen_count = size;
- d->screen->screen_number = i;
- }
-# endif // !USE_IPHONE
+ d->vtbl = &gl_vtbl;
# ifndef HAVE_JWZGLES
struct gl_version version;
// you're gonna get for getting a texture onto the screen.
# ifdef HAVE_JWZGLES
/* TODO: Make BGRA work on iOS. As it is, it breaks XPutImage. (glTexImage2D, AFAIK) */
- d->screen->pixel_format = GL_RGBA; /*
+ d->pixel_format = GL_RGBA; /*
gluCheckExtension ((const GLubyte *) "GL_APPLE_texture_format_BGRA8888",
extensions) ? GL_BGRA_EXT : GL_RGBA; */
- d->screen->pixel_type = GL_UNSIGNED_BYTE;
+ d->pixel_type = GL_UNSIGNED_BYTE;
// See also OES_read_format.
# else // !HAVE_JWZGLES
if (gl_check_ver (&version, 1, 2) ||
(gluCheckExtension ((const GLubyte *) "GL_EXT_bgra", extensions) &&
gluCheckExtension ((const GLubyte *) "GL_APPLE_packed_pixels",
extensions))) {
- d->screen->pixel_format = GL_BGRA_EXT;
+ // APPLE_packed_pixels is only ever available on iOS, never Android.
+ d->pixel_format = GL_BGRA_EXT;
// Both Intel and PowerPC-era docs say to use GL_UNSIGNED_INT_8_8_8_8_REV.
- d->screen->pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ d->pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
} else {
- d->screen->pixel_format = GL_RGBA;
- d->screen->pixel_type = GL_UNSIGNED_BYTE;
+ d->pixel_format = GL_RGBA;
+ d->pixel_type = GL_UNSIGNED_BYTE;
}
// GL_ABGR_EXT/GL_UNSIGNED_BYTE is another possibilty that may have made more
// sense on PowerPC.
# endif // !HAVE_JWZGLES
- // On really old systems, it would make sense to split the texture
- // into subsections.
+ // On really old systems, it would make sense to split textures
+ // into subsections, to work around the maximum texture size.
# ifndef HAVE_JWZGLES
d->gl_texture_npot_p = gluCheckExtension ((const GLubyte *)
"GL_ARB_texture_rectangle",
d->gl_texture_target = GL_TEXTURE_2D;
# endif
- d->screen->black = jwxyz_alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF);
- d->screen->white = jwxyz_alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
-
- Visual *v = (Visual *) calloc (1, sizeof(Visual));
+ Visual *v = &d->visual;
v->class = TrueColor;
- v->red_mask = jwxyz_alloc_color (d, 0xFFFF, 0x0000, 0x0000, 0x0000);
- v->green_mask = jwxyz_alloc_color (d, 0x0000, 0xFFFF, 0x0000, 0x0000);
- v->blue_mask = jwxyz_alloc_color (d, 0x0000, 0x0000, 0xFFFF, 0x0000);
- v->bits_per_rgb = 8;
- d->screen->visual = v;
+ if (d->pixel_format == GL_BGRA_EXT) {
+ v->red_mask = 0x00ff0000;
+ v->green_mask = 0x0000ff00;
+ v->blue_mask = 0x000000ff;
+ v->alpha_mask = 0xff000000;
+ } else {
+ Assert(d->pixel_format == GL_RGBA,
+ "jwxyz_gl_make_display: Unknown pixel_format");
+ unsigned long masks[4];
+ for (unsigned i = 0; i != 4; ++i) {
+ union color_bytes color;
+ color.pixel = 0;
+ color.bytes[i] = 0xff;
+ masks[i] = color.pixel;
+ }
+ v->red_mask = masks[0];
+ v->green_mask = masks[1];
+ v->blue_mask = masks[2];
+ v->alpha_mask = masks[3];
+ }
d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
d->window_background = BlackPixel(d,0);
d->main_window = w;
+
{
- fputs((char *)glGetString(GL_VENDOR), stderr);
- fputc(' ', stderr);
- fputs((char *)glGetString(GL_RENDERER), stderr);
- fputc(' ', stderr);
- fputs((char *)glGetString(GL_VERSION), stderr);
- fputc('\n', stderr);
-// puts(caps.extensions);
- GLint max_texture_size;
+ GLint max_texture_size, max_texture_units;
glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size);
- printf ("GL_MAX_TEXTURE_SIZE: %d\n", max_texture_size);
+ glGetIntegerv (GL_MAX_TEXTURE_UNITS, &max_texture_units);
+ Log ("GL_MAX_TEXTURE_SIZE: %d, GL_MAX_TEXTURE_UNITS: %d\n",
+ max_texture_size, max_texture_units);
+
+ // OpenGL ES 1.1 and OpenGL 1.3 both promise at least 2 texture units:
+ // OpenGL (R) ES Common/Common-Lite Profile Specification, Version 1.1.12 (Full Specification)
+ // https://www.khronos.org/registry/OpenGL/specs/es/1.1/es_full_spec_1.1.pdf
+ // * Table 6.22. Implementation Dependent Values
+ // * D.2 Enhanced Texture Processing
+ // (OpenGL 1.2 provides multitexturing as an ARB extension, and requires 1
+ // texture unit only.)
+
+ // ...but the glGet reference page contradicts this, and says there can be
+ // just one.
+ // https://www.khronos.org/registry/OpenGL-Refpages/es1.1/xhtml/glGet.xml
}
-
- // In case a GL hack wants to use X11 to draw offscreen, the rect_texture is available.
- Assert (d->main_window == w, "Uh-oh.");
- glGenTextures (1, &d->rect_texture);
- // TODO: Check for (ARB|EXT|NV)_texture_rectangle. (All three are alike.)
- // Rectangle textures should be present on OS X with the following exceptions:
- // - Generic renderer on PowerPC OS X 10.4 and earlier
- // - ATI Rage 128
- glBindTexture (d->gl_texture_target, d->rect_texture);
- // TODO: This is all somewhere else. Refactor.
- glTexParameteri (d->gl_texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri (d->gl_texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- // This might be redundant for rectangular textures.
-# ifndef HAVE_JWZGLES
- const GLint wrap = GL_CLAMP;
-# else // HAVE_JWZGLES
- const GLint wrap = GL_CLAMP_TO_EDGE;
-# endif // HAVE_JWZGLES
+ glGenTextures (countof (d->textures), d->textures);
- // In OpenGL, CLAMP_TO_EDGE is OpenGL 1.2 or GL_SGIS_texture_edge_clamp.
- // This is always present with OpenGL ES.
- glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_S, wrap);
- glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_T, wrap);
+ for (unsigned i = 0; i != countof (d->textures); i++) {
+ tex_parameters (d, d->textures[i]);
+ }
+
+ d->gc_function = GXcopy;
+ d->gc_alpha_allowed_p = False;
+ d->gc_clip_mask = 0;
jwxyz_assert_display(d);
return d;
}
void
-jwxyz_free_display (Display *dpy)
+jwxyz_gl_free_display (Display *dpy)
{
+ Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable");
+
/* TODO: Go over everything. */
- jwxyz_sources_free (dpy->timers_data);
+ free (dpy->queue_vertex);
+ free (dpy->queue_color);
-# ifndef USE_IPHONE
- {
- // Find us in live_displays and clear that slot.
- int size = ScreenCount(dpy);
- int i;
- for (i = 0; i < size; i++) {
- if (dpy == jwxyz_live_displays[i]) {
- jwxyz_live_displays[i] = 0;
- break;
- }
- }
- if (i >= size) abort();
- }
-# endif // !USE_IPHONE
+ jwxyz_sources_free (dpy->timers_data);
- free (dpy->screen->visual);
- free (dpy->screen);
free (dpy);
}
-/* Call this after any modification to the bits on a Pixmap or Window.
- Most Pixmaps are used frequently as sources and infrequently as
- destinations, so it pays to cache the data as a CGImage as needed.
- */
-static void
-invalidate_drawable_cache (Drawable d)
-{
- /* TODO: Kill this outright. jwxyz_bind_drawable handles any potential
- invalidation.
- */
-
- /*
- if (d && d->cgi) {
- abort();
- CGImageRelease (d->cgi);
- d->cgi = 0;
- }
- */
-}
-
-
/* Call this when the View changes size or position.
*/
void
jwxyz_window_resized (Display *dpy)
{
+ Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable");
+
const XRectangle *new_frame = jwxyz_frame (dpy->main_window);
unsigned new_width = new_frame->width;
unsigned new_height = new_frame->height;
Log("resize: %d, %d\n", new_width, new_height);
+ jwxyz_gl_flush (dpy);
jwxyz_bind_drawable (dpy, dpy->main_window, dpy->main_window);
// TODO: What does the iPhone need?
// Stylish and attractive purple!
// glClearColor (1, 0, 1, 0.5);
// glClear (GL_COLOR_BUFFER_BIT);
-
- invalidate_drawable_cache (dpy->main_window);
}
-jwxyz_sources_data *
+static jwxyz_sources_data *
display_sources_data (Display *dpy)
{
return dpy->timers_data;
}
-Window
-XRootWindow (Display *dpy, int screen)
+static Window
+root (Display *dpy)
{
- return dpy ? dpy->main_window : 0;
+ return dpy->main_window;
}
-Screen *
-XDefaultScreenOfDisplay (Display *dpy)
+static Visual *
+visual (Display *dpy)
{
- return dpy ? dpy->screen : 0;
+ return &dpy->visual;
}
-Visual *
-XDefaultVisualOfScreen (Screen *screen)
-{
- return screen ? screen->visual : 0;
-}
-Display *
-XDisplayOfScreen (Screen *s)
-{
- return s ? s->dpy : 0;
-}
+/* GC attributes by usage and OpenGL implementation:
-int
-XDisplayNumberOfScreen (Screen *s)
-{
- return 0;
-}
+ All drawing functions:
+ function | glLogicOp w/ GL_COLOR_LOGIC_OP
+ clip_x_origin, clip_y_origin, clip_mask | Multitexturing w/ GL_TEXTURE1
-int
-XScreenNumberOfScreen (Screen *s)
-{
- return s? s->screen_number : 0;
-}
+ Shape drawing functions:
+ foreground, background | glColor*
-int
-jwxyz_ScreenCount (Display *dpy)
-{
- return dpy ? dpy->screen_count : 0;
-}
+ XDrawLines, XDrawRectangles, XDrawSegments:
+ line_width, cap_style, join_style | Lotsa vertices
-unsigned long
-XBlackPixelOfScreen(Screen *screen)
-{
- return screen->black;
-}
+ XFillPolygon:
+ fill_rule | Multiple GL_TRIANGLE_FANs
-unsigned long
-XWhitePixelOfScreen(Screen *screen)
-{
- return screen->white;
-}
+ XDrawText:
+ font | Cocoa, then OpenGL display lists.
-unsigned long
-XCellsOfScreen(Screen *screen)
-{
- Visual *v = screen->visual;
- return v->red_mask | v->green_mask | v->blue_mask;
-}
+ alpha_allowed_p | GL_BLEND
+ antialias_p | Well, there's options:
+ * Multisampling would work, but that's something that would need to be set
+ per-Pixmap, not per-GC.
+ * GL_POINT, LINE, and POLYGON_SMOOTH are the old-school way of doing
+ this, but POINT_SMOOTH is unnecessary, and POLYGON_SMOOTH is missing from
+ GLES 1. All three are missing from GLES 2. Word on the street is that
+ these are deprecated anyway.
+ * Tiny textures with bilinear filtering to get the same effect as LINE_ and
+ POLYGON_SMOOTH. A bit tricky.
+ * Do nothing. Android hardware is very often high-DPI enough that
+ anti-aliasing doesn't matter all that much.
-/* GC attributes by usage and OpenGL implementation:
- *
- * All drawing functions:
- * function | glLogicOp w/ GL_COLOR_LOGIC_OP
- * clip_x_origin, clip_y_origin, clip_mask | Stencil mask
- *
- * Shape drawing functions:
- * foreground, background | glColor*
- *
- * XDrawLines, XDrawRectangles, XDrawSegments:
- * line_width, cap_style, join_style | Lotsa vertices
- *
- * XFillPolygon:
- * fill_rule | Multiple GL_TRIANGLE_FANs
- *
- * XDrawText:
- * font | Cocoa, then OpenGL display lists.
- *
- * alpha_allowed_p | TODO
- * antialias_p | TODO
- *
- * Nothing, really:
- * subwindow_mode
-*/
+ Nothing, really:
+ subwindow_mode
+ */
-static void
-set_clip_mask (GC gc)
+static void *
+enqueue (Display *dpy, Drawable d, GC gc, int mode, size_t count,
+ unsigned long pixel)
{
- Assert (!gc->gcv.clip_mask, "set_gc: TODO");
+ if (dpy->queue_size &&
+ (!gc || /* Could allow NULL GCs here... */
+ dpy->gc_function != gc->gcv.function ||
+ dpy->gc_alpha_allowed_p != gc->gcv.alpha_allowed_p ||
+ dpy->gc_clip_mask != gc->clip_mask ||
+ dpy->gc_clip_x_origin != gc->gcv.clip_x_origin ||
+ dpy->gc_clip_y_origin != gc->gcv.clip_y_origin ||
+ dpy->queue_mode != mode ||
+ dpy->queue_drawable != d)) {
+ jwxyz_gl_flush (dpy);
+ }
+
+ jwxyz_bind_drawable (dpy, dpy->main_window, d);
+ jwxyz_gl_set_gc (dpy, gc);
+
+ if (mode == GL_TRIANGLE_STRIP)
+ Assert (count, "empty triangle strip");
+ // Use degenerate triangles to cut down on draw calls.
+ Bool prepend2 = mode == GL_TRIANGLE_STRIP && dpy->queue_size;
+
+ // ####: Primitive restarts should be used here when (if) they're available.
+ if (prepend2)
+ count += 2;
+
+ // TODO: Use glColor when we can get away with it.
+ size_t old_size = dpy->queue_size;
+ dpy->queue_size += count;
+ if (dpy->queue_size > dpy->queue_capacity) {
+ dpy->queue_capacity = dpy->queue_size * 2;
+
+ uint32_t *new_color = realloc (
+ dpy->queue_color, sizeof(*dpy->queue_color) * dpy->queue_capacity);
+ /* Allocate vertices as if they were always GLfloats. Otherwise, if
+ queue_vertex is allocated to hold GLshorts, then things get switched
+ to GLfloats, queue_vertex would be too small for the given capacity.
+ */
+ GLshort *new_vertex = realloc (
+ dpy->queue_vertex, sizeof(GLfloat) * 2 * dpy->queue_capacity);
+
+ if (!new_color || !new_vertex)
+ return NULL;
+
+ dpy->queue_color = new_color;
+ dpy->queue_vertex = new_vertex;
+ }
+
+ dpy->queue_mode = mode;
+ dpy->queue_drawable = d;
+
+ union color_bytes color;
+
+ // Like query_color, but for bytes.
+ jwxyz_validate_pixel (dpy, pixel, jwxyz_drawable_depth (d),
+ gc ? gc->gcv.alpha_allowed_p : False);
+
+ if (jwxyz_drawable_depth (d) == 1) {
+ uint8_t b = pixel ? 0xff : 0;
+ color.bytes[0] = b;
+ color.bytes[1] = b;
+ color.bytes[2] = b;
+ color.bytes[3] = 0xff;
+ } else {
+ JWXYZ_QUERY_COLOR (dpy, pixel, 0xffull, color.bytes);
+ }
+
+ for (size_t i = 0; i != count; ++i) // TODO: wmemset when applicable.
+ dpy->queue_color[i + old_size] = color.pixel;
+
+ void *result = (char *)dpy->queue_vertex + old_size * 2 *
+ (mode == GL_TRIANGLE_STRIP ? sizeof(GLfloat) : sizeof(GLshort));
+ if (prepend2) {
+ dpy->queue_color[old_size] = dpy->queue_color[old_size - 1];
+ result = (GLfloat *)result + 4;
+ }
+ return result;
}
+
static void
-set_function (int function)
+finish_triangle_strip (Display *dpy, GLfloat *enqueue_result)
{
- Assert (function == GXcopy, "set_gc: (TODO) Stubbed gcv function");
-
- /* TODO: The GL_COLOR_LOGIC_OP extension is exactly what is needed here. (OpenGL 1.1)
- Fun fact: The glLogicOp opcode constants are the same as the X11 GX* function constants | GL_CLEAR.
- */
-
-#if 0
- switch (gc->gcv.function) {
- case GXset:
- case GXclear:
- case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
- case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
- case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
- case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
- default: Assert(0, "unknown gcv function"); break;
+ if (enqueue_result != dpy->queue_vertex) {
+ enqueue_result[-4] = enqueue_result[-6];
+ enqueue_result[-3] = enqueue_result[-5];
+ enqueue_result[-2] = enqueue_result[0];
+ enqueue_result[-1] = enqueue_result[1];
}
-#endif
}
static void
-set_color (Display *dpy, unsigned long pixel, unsigned int depth,
- Bool alpha_allowed_p)
+query_color (Display *dpy, unsigned long pixel, unsigned int depth,
+ Bool alpha_allowed_p, GLfloat *rgba)
{
jwxyz_validate_pixel (dpy, pixel, depth, alpha_allowed_p);
if (depth == 1) {
GLfloat f = pixel;
- glColor4f (f, f, f, 1);
+ rgba[0] = f;
+ rgba[1] = f;
+ rgba[2] = f;
+ rgba[3] = 1;
} else {
- /* TODO: alpha_allowed_p */
- uint8_t rgba[4];
- jwxyz_query_color (dpy, pixel, rgba);
-#ifdef HAVE_JWZGLES
- glColor4f (rgba[0] / 255.0f, rgba[1] / 255.0f,
- rgba[2] / 255.0f, rgba[3] / 255.0f);
-#else
- glColor4ubv (rgba);
-#endif
+ JWXYZ_QUERY_COLOR (dpy, pixel, 1.0f, rgba);
}
}
-/* Pushes a GC context; sets Function, ClipMask, and color.
- */
+
static void
-set_color_gc (Display *dpy, GC gc, unsigned long color)
+set_color (Display *dpy, unsigned long pixel, unsigned int depth,
+ Bool alpha_allowed_p)
+{
+ GLfloat rgba[4];
+ query_color (dpy, pixel, depth, alpha_allowed_p, rgba);
+ glColor4f (rgba[0], rgba[1], rgba[2], rgba[3]);
+}
+
+/* Pushes a GC context; sets Function and ClipMask. */
+void
+jwxyz_gl_set_gc (Display *dpy, GC gc)
{
- // GC is NULL for XClearArea and XClearWindow.
- unsigned int depth;
int function;
+ Bool alpha_allowed_p;
+ GLuint clip_mask;
+
+ // GC is NULL for XClearArea and XClearWindow.
if (gc) {
function = gc->gcv.function;
- depth = gc->depth;
- set_clip_mask (gc);
+ alpha_allowed_p = gc->gcv.alpha_allowed_p || gc->clip_mask;
+ clip_mask = gc->clip_mask;
} else {
function = GXcopy;
- depth = visual_depth (NULL, NULL);
- // TODO: Set null clip mask here.
+ alpha_allowed_p = False;
+ clip_mask = 0;
+ }
+
+ /* GL_COLOR_LOGIC_OP: OpenGL 1.1. */
+ if (function != dpy->gc_function) {
+ dpy->gc_function = function;
+ if (function != GXcopy) {
+ /* Fun fact: The glLogicOp opcode constants are the same as the X11 GX*
+ function constants | GL_CLEAR.
+ */
+ glEnable (GL_COLOR_LOGIC_OP);
+ glLogicOp (gc->gcv.function | GL_CLEAR);
+ } else {
+ glDisable (GL_COLOR_LOGIC_OP);
+ }
+ }
+
+ /* Cocoa uses add/subtract/difference blending in place of logical ops.
+ It looks nice, but implementing difference blending in OpenGL appears to
+ require GL_KHR_blend_equation_advanced, and support for this is not
+ widespread.
+ */
+
+ dpy->gc_alpha_allowed_p = alpha_allowed_p;
+ if (alpha_allowed_p || clip_mask) {
+ // TODO: Maybe move glBlendFunc to XCreatePixmap?
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+ } else {
+ glDisable (GL_BLEND);
+ }
+
+ /* Texture units:
+ GL_TEXTURE0: Texture for XPutImage/XCopyArea (if applicable)
+ GL_TEXTURE1: Texture for clip masks (if applicable)
+ */
+ dpy->gc_clip_mask = clip_mask;
+
+ glActiveTexture (GL_TEXTURE1);
+ if (clip_mask) {
+ glEnable (dpy->gl_texture_target);
+ glBindTexture (dpy->gl_texture_target, gc->clip_mask);
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
+ alpha_allowed_p ? GL_MODULATE : GL_REPLACE);
+
+ glMatrixMode (GL_TEXTURE);
+ glLoadIdentity ();
+
+ unsigned
+ tex_w = gc->clip_mask_width + 2, tex_h = gc->clip_mask_height + 2;
+ tex_size (dpy, &tex_w, &tex_h);
+
+# ifndef HAVE_JWZGLES
+ if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT)
+ {
+ glScalef (1, -1, 1);
+ }
+ else
+# endif
+ {
+ glScalef (1.0f / tex_w, -1.0f / tex_h, 1);
+ }
+
+ glTranslatef (1 - gc->gcv.clip_x_origin,
+ 1 - gc->gcv.clip_y_origin - (int)gc->clip_mask_height - 2,
+ 0);
+ } else {
+ glDisable (dpy->gl_texture_target);
}
+ glActiveTexture (GL_TEXTURE0);
+}
+
+
+static void
+set_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color)
+{
+ jwxyz_gl_flush (dpy);
+ jwxyz_bind_drawable (dpy, dpy->main_window, d);
+ jwxyz_gl_set_gc (dpy, gc);
+
+ unsigned int depth;
- set_function (function);
+ if (gc) {
+ depth = gc->depth;
- switch (function) {
- case GXset: color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
- case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
+ switch (gc->gcv.function) {
+ case GXset: color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
+ case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
+ }
+ } else {
+ depth = visual_depth (NULL, NULL);
}
set_color (dpy, color, depth, gc ? gc->gcv.alpha_allowed_p : False);
-
- /* TODO: Antialiasing. */
- /* CGContextSetShouldAntialias (cgc, antialias_p); */
}
/* Pushes a GC context; sets color to the foreground color.
*/
static void
-set_fg_gc (Display *dpy, GC gc)
+set_fg_gc (Display *dpy, Drawable d, GC gc)
{
- set_color_gc (dpy, gc, gc->gcv.foreground);
+ set_color_gc (dpy, d, gc, gc->gcv.foreground);
}
static void
}
}
-int
-XDrawPoints (Display *dpy, Drawable d, GC gc,
- XPoint *points, int count, int mode)
+static int
+DrawPoints (Display *dpy, Drawable d, GC gc,
+ XPoint *points, int count, int mode)
{
- jwxyz_bind_drawable (dpy, dpy->main_window, d);
- set_fg_gc (dpy, gc);
-
-/*
-
- glBegin(GL_POINTS);
- for (unsigned i = 0; i < count; i++) {
- next_point(v, points[i], mode);
- glVertex2f(v[0] + 0.5f, v[1] + 0.5f);
- }
- glEnd();
- */
-
short v[2] = {0, 0};
// TODO: XPoints can be fed directly to OpenGL.
- GLshort *gl_points = malloc (count * 2 * sizeof(GLshort)); // TODO: malloc returns NULL.
+ GLshort *gl_points = enqueue (dpy, d, gc, GL_POINTS, count,
+ gc->gcv.foreground); // TODO: enqueue returns NULL.
for (unsigned i = 0; i < count; i++) {
next_point (v, points[i], mode);
gl_points[2 * i] = v[0];
gl_points[2 * i + 1] = v[1];
}
-
- glMatrixMode (GL_MODELVIEW);
- glTranslatef (0.5, 0.5, 0);
-
- glEnableClientState (GL_VERTEX_ARRAY);
- glDisableClientState (GL_TEXTURE_COORD_ARRAY);
- glVertexPointer (2, GL_SHORT, 0, gl_points);
- glDrawArrays (GL_POINTS, 0, count);
-
- free (gl_points);
-
- glLoadIdentity ();
-
+
return 0;
}
static GLint
-texture_internalformat(Display *dpy)
+texture_internalformat (Display *dpy)
{
#ifdef HAVE_JWZGLES
- return dpy->screen->pixel_format;
+ return dpy->pixel_format;
#else
return GL_RGBA;
#endif
}
-static GLenum gl_pixel_type(const Display *dpy)
+static GLenum
+gl_pixel_type (const Display *dpy)
{
- return dpy->screen->pixel_type;
+ return dpy->pixel_type;
}
static void
clear_texture (Display *dpy)
{
glTexImage2D (dpy->gl_texture_target, 0, texture_internalformat(dpy), 0, 0,
- 0, dpy->screen->pixel_format, gl_pixel_type (dpy), NULL);
+ 0, dpy->pixel_format, gl_pixel_type (dpy), NULL);
}
-static void set_white (void)
+
+static void
+vertex_pointer (Display *dpy, GLenum type, GLsizei stride,
+ const void *pointer)
{
-#ifdef HAVE_JWZGLES
- glColor4f (1, 1, 1, 1);
-#else
- glColor3ub (0xff, 0xff, 0xff);
-#endif
+ glVertexPointer(2, type, stride, pointer);
+ if (dpy->gc_clip_mask) {
+ glClientActiveTexture (GL_TEXTURE1);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer (2, type, stride, pointer);
+ glClientActiveTexture (GL_TEXTURE0);
+ }
}
-static GLsizei to_pow2 (size_t x);
+void
+jwxyz_gl_flush (Display *dpy)
+{
+ Assert (dpy->vtbl == &gl_vtbl, "jwxyz-gl.c: bad vtable");
+
+ if (!dpy->queue_size)
+ return;
+
+ // jwxyz_bind_drawable() and jwxyz_gl_set_gc() is called in enqueue().
+
+ glEnableClientState (GL_COLOR_ARRAY);
+ glEnableClientState (GL_VERTEX_ARRAY);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ // TODO: Use glColor instead of glColorPointer if there's just one color.
+ // TODO: Does OpenGL use both GL_COLOR_ARRAY and glColor at the same time?
+ // (Probably not.)
+ glColor4f (1, 1, 1, 1);
+
+ Bool shifted = dpy->queue_mode == GL_POINTS || dpy->queue_mode == GL_LINES;
+ if (shifted) {
+ glMatrixMode (GL_MODELVIEW);
+ glTranslatef (0.5, 0.5, 0);
+ }
+
+ glColorPointer (4, GL_UNSIGNED_BYTE, 0, dpy->queue_color);
+ vertex_pointer (dpy,
+ dpy->queue_mode == GL_TRIANGLE_STRIP ? GL_FLOAT : GL_SHORT,
+ 0, dpy->queue_vertex);
+ glDrawArrays (dpy->queue_mode, 0, dpy->queue_size);
+
+ // TODO: This is right, right?
+ if (dpy->queue_mode == GL_LINES && dpy->queue_line_cap) {
+ Assert (!(dpy->queue_size % 2), "bad count for GL_LINES");
+ glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(GLubyte) * 8,
+ dpy->queue_color);
+ vertex_pointer (dpy, GL_SHORT, sizeof(GLshort) * 4,
+ (GLshort *)dpy->queue_vertex + 2);
+ glDrawArrays (GL_POINTS, 0, dpy->queue_size / 2);
+ }
+
+ if (shifted)
+ glLoadIdentity ();
+
+ glDisableClientState (GL_COLOR_ARRAY);
+ glDisableClientState (GL_VERTEX_ARRAY);
+
+ dpy->queue_size = 0;
+}
void
-jwxyz_gl_copy_area_copy_tex_image (Display *dpy, Drawable src, Drawable dst,
- GC gc, int src_x, int src_y,
+jwxyz_gl_copy_area_read_tex_image (Display *dpy, unsigned src_height,
+ int src_x, int src_y,
unsigned int width, unsigned int height,
int dst_x, int dst_y)
{
- const XRectangle *src_frame = jwxyz_frame (src);
-
- Assert(gc->gcv.function == GXcopy, "XCopyArea: Unknown function");
-
- jwxyz_bind_drawable (dpy, dpy->main_window, src);
-
# if defined HAVE_COCOA && !defined USE_IPHONE
/* TODO: Does this help? */
/* glFinish(); */
# endif
+ /* TODO: Fix TestX11 + mode_preserve with this one. */
+
unsigned tex_w = width, tex_h = height;
if (!dpy->gl_texture_npot_p) {
tex_w = to_pow2(tex_w);
GLint internalformat = texture_internalformat(dpy);
- glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
+ /* TODO: This probably shouldn't always be texture_rgba. */
+ glBindTexture (dpy->gl_texture_target, dpy->textures[texture_rgba]);
if (tex_w == width && tex_h == height) {
glCopyTexImage2D (dpy->gl_texture_target, 0, internalformat,
- src_x, src_frame->height - src_y - height,
- width, height, 0);
+ src_x, src_height - src_y - height, width, height, 0);
} else {
glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
- 0, dpy->screen->pixel_format, gl_pixel_type(dpy), NULL);
+ 0, dpy->pixel_format, gl_pixel_type(dpy), NULL);
glCopyTexSubImage2D (dpy->gl_texture_target, 0, 0, 0,
- src_x, src_frame->height - src_y - height,
- width, height);
+ src_x, src_height - src_y - height, width, height);
+ }
+}
+
+void
+jwxyz_gl_copy_area_write_tex_image (Display *dpy, GC gc, int src_x, int src_y,
+ unsigned int width, unsigned int height,
+ int dst_x, int dst_y)
+{
+ jwxyz_gl_set_gc (dpy, gc);
+
+ /* TODO: Copy-pasted from read_tex_image. */
+ unsigned tex_w = width, tex_h = height;
+ if (!dpy->gl_texture_npot_p) {
+ tex_w = to_pow2(tex_w);
+ tex_h = to_pow2(tex_h);
}
- jwxyz_bind_drawable (dpy, dpy->main_window, dst);
- set_white ();
- glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
- glEnable (dpy->gl_texture_target);
+ /* Must match what's in jwxyz_gl_copy_area_read_tex_image. */
+ glBindTexture (dpy->gl_texture_target, dpy->textures[texture_rgba]);
+
+ jwxyz_gl_draw_image (dpy, gc, dpy->gl_texture_target, tex_w, tex_h,
+ 0, 0, gc->depth, width, height, dst_x, dst_y, False);
+
+ clear_texture (dpy);
+}
+
+void
+jwxyz_gl_draw_image (Display *dpy, GC gc, GLenum gl_texture_target,
+ unsigned int tex_w, unsigned int tex_h,
+ int src_x, int src_y, int src_depth,
+ unsigned int width, unsigned int height,
+ int dst_x, int dst_y, Bool flip_y)
+{
+ if (!gc || src_depth == gc->depth) {
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ } else {
+ Assert (src_depth == 1 && gc->depth == 32,
+ "jwxyz_gl_draw_image: bad depths");
+
+ set_color (dpy, gc->gcv.background, gc->depth, gc->gcv.alpha_allowed_p);
+
+ GLfloat rgba[4];
+ query_color (dpy, gc->gcv.foreground, gc->depth, gc->gcv.alpha_allowed_p,
+ rgba);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
+ glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba);
+ }
+
+ glEnable (gl_texture_target);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
glEnableClientState (GL_VERTEX_ARRAY);
-
- /* TODO: Copied from XPutImage. Refactor. */
+
+ Assert (!glIsEnabled (GL_COLOR_ARRAY), "glIsEnabled (GL_COLOR_ARRAY)");
+ Assert (!glIsEnabled (GL_NORMAL_ARRAY), "glIsEnabled (GL_NORMAL_ARRAY)");
+
/* TODO: EXT_draw_texture or whatever it's called. */
- GLfloat vertices[4][2] =
- {
+ GLfloat vertices[4][2] = {
{dst_x, dst_y},
{dst_x, dst_y + height},
{dst_x + width, dst_y + height},
{dst_x + width, dst_y}
};
-
-#ifdef HAVE_JWZGLES
- static const GLshort tex_coords[4][2] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
-#else
- GLshort tex_coords[4][2] = {{0, height}, {0, 0}, {width, 0}, {width, height}};
-#endif
- glVertexPointer (2, GL_FLOAT, 0, vertices);
- glTexCoordPointer (2, GL_SHORT, 0, tex_coords);
+ GLfloat
+ tex_x0 = src_x, tex_y0 = src_y,
+ tex_x1 = src_x + width, tex_y1 = src_y;
+
+ if (flip_y)
+ tex_y1 += height;
+ else
+ tex_y0 += height;
+
+# ifndef HAVE_JWZGLES
+ if (gl_texture_target != GL_TEXTURE_RECTANGLE_EXT)
+# endif
+ {
+ GLfloat mx = 1.0f / tex_w, my = 1.0f / tex_h;
+ tex_x0 *= mx;
+ tex_y0 *= my;
+ tex_x1 *= mx;
+ tex_y1 *= my;
+ }
+
+ GLfloat tex_coords[4][2] = {
+ {tex_x0, tex_y0},
+ {tex_x0, tex_y1},
+ {tex_x1, tex_y1},
+ {tex_x1, tex_y0}
+ };
+
+ vertex_pointer (dpy, GL_FLOAT, 0, vertices);
+ glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
-
- clear_texture (dpy);
-
- glDisable (dpy->gl_texture_target);
+
+//clear_texture();
+ glDisable (gl_texture_target);
}
+#if 0
void
jwxyz_gl_copy_area_read_pixels (Display *dpy, Drawable src, Drawable dst,
GC gc, int src_x, int src_y,
unsigned int width, unsigned int height,
int dst_x, int dst_y)
{
-# if 1
XImage *img = XGetImage (dpy, src, src_x, src_y, width, height, ~0, ZPixmap);
XPutImage (dpy, dst, gc, img, 0, 0, dst_x, dst_y, width, height);
XDestroyImage (img);
-# endif
-
-# if 0
- /* Something may or may not be broken in here. (shrug) */
- bind_drawable(dpy, src);
-
- /* Error checking would be nice. */
- void *pixels = malloc (src_rect.size.width * 4 * src_rect.size.height);
-
- glPixelStorei (GL_PACK_ROW_LENGTH, 0);
- glPixelStorei (GL_PACK_ALIGNMENT, 4);
-
- glReadPixels (src_rect.origin.x, dst_frame.size.height - (src_rect.origin.y + src_rect.size.height),
- src_rect.size.width, src_rect.size.height,
- GL_RGBA, GL_UNSIGNED_BYTE, // TODO: Pick better formats.
- pixels);
-
- bind_drawable (dpy, dst);
-
- glPixelZoom (1.0f, 1.0f);
-
- glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
- glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
-
- glRasterPos2i (dst_rect.origin.x, dst_rect.origin.y + dst_rect.size.height);
- glDrawPixels (dst_rect.size.width, dst_rect.size.height,
- GL_RGBA, GL_UNSIGNED_BYTE, pixels);
-
- free(pixels);
-# endif
-}
-
-
-#if 0
-// TODO: Make sure offset works in super-sampled mode.
-static void
-adjust_point_for_line (GC gc, CGPoint *p)
-{
- // Here's the authoritative discussion on how X draws lines:
- // http://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:CreateGC:line-width
- if (gc->gcv.line_width <= 1) {
- /* Thin lines are "drawn using an unspecified, device-dependent
- algorithm", but seriously though, Bresenham's algorithm. Bresenham's
- algorithm runs to and from pixel centers.
-
- There's a few screenhacks (Maze, at the very least) that set line_width
- to 1 when it probably should be set to 0, so it's line_width <= 1
- instead of < 1.
- */
- p->x += 0.5;
- p->y -= 0.5;
- } else {
- /* Thick lines OTOH run from the upper-left corners of pixels. This means
- that a horizontal thick line of width 1 straddles two scan lines.
- Aliasing requires one of these scan lines be chosen; the following
- nudges the point so that the right choice is made. */
- p->y -= 1e-3;
- }
}
#endif
-int
-XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
-{
- // TODO: XDrawLine == XDrawSegments(nlines == 1), also in jwxyz.m
- XSegment segment;
- segment.x1 = x1;
- segment.y1 = y1;
- segment.x2 = x2;
- segment.y2 = y2;
- XDrawSegments (dpy, d, gc, &segment, 1);
-
- // when drawing a zero-length line, obey line-width and cap-style.
-/* if (x1 == x2 && y1 == y2) {
- int w = gc->gcv.line_width;
- x1 -= w/2;
- y1 -= w/2;
- if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
- return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
- else {
- if (!w)
- w = 1; // Actually show zero-length lines.
- return XFillRectangle (dpy, d, gc, x1, y1, w, w);
- }
- }
-
- CGPoint p = point_for_line (d, gc, x1, y1);
-
- push_fg_gc (dpy, d, gc, NO);
-
- CGContextRef cgc = d->cgc;
- set_line_mode (cgc, &gc->gcv);
- CGContextBeginPath (cgc);
- CGContextMoveToPoint (cgc, p.x, p.y);
- p = point_for_line(d, gc, x2, y2);
- CGContextAddLineToPoint (cgc, p.x, p.y);
- CGContextStrokePath (cgc);
- pop_gc (d, gc);
- invalidate_drawable_cache (d); */
- return 0;
-}
-
-int
-XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
- int mode)
+static int
+DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
+ int mode)
{
- jwxyz_bind_drawable (dpy, dpy->main_window, d);
- set_fg_gc (dpy, gc);
+ set_fg_gc (dpy, d, gc);
/* TODO: Thick lines
* Zero-length line segments
glEnableClientState (GL_VERTEX_ARRAY);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
- glVertexPointer (2, GL_SHORT, 0, vertices);
+ vertex_pointer (dpy, GL_SHORT, 0, vertices);
glDrawArrays (GL_LINE_STRIP, 0, count);
free (vertices);
if (gc->gcv.cap_style != CapNotLast) {
// TODO: How does this look with multisampling?
// TODO: Disable me for closed loops.
- glVertexPointer (2, GL_SHORT, 0, p);
+ vertex_pointer (dpy, GL_SHORT, 0, p);
glDrawArrays (GL_POINTS, 0, 1);
}
}
-int
-XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
+// Turn line segment into parallelogram based on line_width
+//
+// TODO: Fix epicycle hack with large thickness, and truchet line segment ends
+//
+static void
+drawThickLine (Display *dpy, Drawable d, GC gc, int line_width,
+ XSegment *segments)
{
- jwxyz_bind_drawable (dpy, dpy->main_window, d);
- set_fg_gc (dpy, gc);
-
- /* TODO: Thick lines. */
+ double dx, dy, di, m, angle;
+ int sx1, sx2, sy1, sy2;
+
+ sx1 = segments->x1;
+ sy1 = segments->y1;
+ sx2 = segments->x2;
+ sy2 = segments->y2;
+
+ dx = sx1 - sx2;
+ dy = sy1 - sy2;
+ di = sqrt(dx * dx + dy * dy);
+ dx = dx / di;
+ dy = dy / di;
+ m = dy / dx;
+
+ angle = atan(m);
+
+ float sn = sin(angle);
+ float cs = cos(angle);
+ float line_width_f = (float) line_width;
+
+ float wsn = line_width_f * (sn/2);
+ float csn = line_width_f * (cs/2);
+
+ float x3 = sx1 - wsn;
+ float y3 = sy1 + csn;
+ float x4 = sx1 + wsn;
+ float y4 = sy1 - csn;
+
+ float x5 = sx2 - wsn;
+ float y5 = sy2 + csn;
+ float x6 = sx2 + wsn;
+ float y6 = sy2 - csn;
+
+ GLfloat *coords = enqueue (dpy, d, gc, GL_TRIANGLE_STRIP, 4,
+ gc->gcv.foreground);
+ coords[0] = x3;
+ coords[1] = y3;
+ coords[2] = x4;
+ coords[3] = y4;
+ coords[4] = x5;
+ coords[5] = y5;
+ coords[6] = x6;
+ coords[7] = y6;
+ finish_triangle_strip (dpy, coords);
+}
+
+
+static int
+DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
+{
+ /* TODO: Caps on thick lines. */
/* Thin lines <= 1px are offset by +0.5; thick lines are not. */
-
- glMatrixMode (GL_MODELVIEW);
- glTranslatef (0.5, 0.5, 0);
- glEnableClientState (GL_VERTEX_ARRAY);
- glDisableClientState (GL_TEXTURE_COORD_ARRAY);
-
- Assert (sizeof(XSegment) == sizeof(short) * 4, "XDrawSegments: Data alignment mix-up."); // TODO: Static assert here.
- Assert (sizeof(GLshort) == sizeof(short), "XDrawSegments: Data alignment mix-up."); // TODO: Static assert here.
- Assert (offsetof(XSegment, x1) == 0, "XDrawSegments: Data alignment mix-up.");
- Assert (offsetof(XSegment, x2) == 4, "XDrawSegments: Data alignment mix-up.");
- glVertexPointer (2, GL_SHORT, 0, segments);
- glDrawArrays (GL_LINES, 0, count * 2);
-
- if (gc->gcv.cap_style != CapNotLast) {
- glVertexPointer (2, GL_SHORT, sizeof(GLshort) * 4, (const GLshort *)segments + 2);
- glDrawArrays (GL_POINTS, 0, count);
+ if (count == 1 && gc->gcv.line_width > 1) {
+ drawThickLine (dpy, d, gc, gc->gcv.line_width, segments);
}
-
- glLoadIdentity ();
-
-/* CGRect wr = d->frame;
- push_fg_gc (dpy, d, gc, NO);
- set_line_mode (cgc, &gc->gcv);
- CGContextBeginPath (cgc);
- for (i = 0; i < count; i++) {
- CGPoint p = point_for_line (d, gc, segments->x1, segments->y1);
- CGContextMoveToPoint (cgc, p.x, p.y);
- p = point_for_line (d, gc, segments->x2, segments->y2);
- CGContextAddLineToPoint (cgc, p.x, p.y);
- segments++;
+ else {
+ if (dpy->queue_line_cap != (gc->gcv.cap_style != CapNotLast))
+ jwxyz_gl_flush (dpy);
+ dpy->queue_line_cap = gc->gcv.cap_style != CapNotLast;
+
+ // TODO: Static assert here.
+ Assert (sizeof(XSegment) == sizeof(short) * 4 &&
+ sizeof(GLshort) == sizeof(short) &&
+ offsetof(XSegment, x1) == 0 &&
+ offsetof(XSegment, x2) == 4,
+ "XDrawSegments: Data alignment mix-up.");
+
+ memcpy (enqueue(dpy, d, gc, GL_LINES, count * 2, gc->gcv.foreground),
+ segments, count * sizeof(XSegment));
}
- CGContextStrokePath (cgc);
- pop_gc (d, gc);
- invalidate_drawable_cache (d); */
+
return 0;
}
-int
-XClearWindow (Display *dpy, Window win)
+static int
+ClearWindow (Display *dpy, Window win)
{
Assert (win == dpy->main_window, "not a window");
- const XRectangle *wr = jwxyz_frame (win);
- /* TODO: Use glClear if there's no background pixmap. */
- return XClearArea (dpy, win, 0, 0, wr->width, wr->height, 0);
-}
-unsigned long
-jwxyz_window_background (Display *dpy)
-{
- return dpy->window_background;
+ jwxyz_gl_flush (dpy);
+ jwxyz_bind_drawable (dpy, win, win);
+
+ GLfloat color[4];
+ JWXYZ_QUERY_COLOR (dpy, dpy->window_background, 1.0f, color);
+
+ glClearColor (color[0], color[1], color[2], 1);
+ glClear (GL_COLOR_BUFFER_BIT);
+ return True;
}
-int
-XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
+static unsigned long *
+window_background (Display *dpy)
{
- Assert (w == dpy->main_window, "not a window");
- jwxyz_validate_pixel (dpy, pixel, visual_depth (NULL, NULL), False);
- dpy->window_background = pixel;
- return 0;
+ return &dpy->window_background;
}
-void
-jwxyz_fill_rects (Display *dpy, Drawable d, GC gc,
- const XRectangle *rectangles, unsigned long nrectangles,
- unsigned long pixel)
+static void
+fill_rects (Display *dpy, Drawable d, GC gc,
+ const XRectangle *rectangles, unsigned long nrectangles,
+ unsigned long pixel)
{
- jwxyz_bind_drawable (dpy, dpy->main_window, d);
- set_color_gc (dpy, gc, pixel);
-/*
- glBegin(GL_QUADS);
- for (unsigned i = 0; i != nrectangles; ++i) {
- const XRectangle *r = &rectangles[i];
- glVertex2i(r->x, r->y);
- glVertex2i(r->x, r->y + r->height);
- glVertex2i(r->x + r->width, r->y + r->height);
- glVertex2i(r->x + r->width, r->y);
- }
- glEnd(); */
-
- glEnableClientState (GL_VERTEX_ARRAY);
- glDisableClientState (GL_TEXTURE_COORD_ARRAY);
-
- for (unsigned long i = 0; i != nrectangles; ++i)
- {
+ for (unsigned long i = 0; i != nrectangles; ++i) {
const XRectangle *r = &rectangles[i];
-
- GLfloat coords[4][2] =
- {
- {r->x, r->y},
- {r->x, r->y + r->height},
- {r->x + r->width, r->y + r->height},
- {r->x + r->width, r->y}
- };
-
- // TODO: Several rects at once. Maybe even tune for XScreenSaver workloads.
- glVertexPointer (2, GL_FLOAT, 0, coords);
- jwxyz_assert_gl ();
- glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
- jwxyz_assert_gl ();
+ GLfloat *coords = enqueue (dpy, d, gc, GL_TRIANGLE_STRIP, 4, pixel);
+ coords[0] = r->x;
+ coords[1] = r->y;
+ coords[2] = r->x;
+ coords[3] = r->y + r->height;
+ coords[4] = r->x + r->width;
+ coords[5] = r->y;
+ coords[6] = r->x + r->width;
+ coords[7] = r->y + r->height;
+ finish_triangle_strip (dpy, coords);
}
}
-int
-XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
-{
- Assert(win == dpy->main_window, "XClearArea: not a window");
- Assert(!exp, "XClearArea: exposures unsupported");
-
- jwxyz_fill_rect (dpy, win, 0, x, y, w, h, dpy->window_background);
- return 0;
-}
-
-
-int
-XFillPolygon (Display *dpy, Drawable d, GC gc,
- XPoint *points, int npoints, int shape, int mode)
+static int
+FillPolygon (Display *dpy, Drawable d, GC gc,
+ XPoint *points, int npoints, int shape, int mode)
{
- jwxyz_bind_drawable (dpy, dpy->main_window, d);
- set_fg_gc(dpy, gc);
+ set_fg_gc(dpy, d, gc);
// TODO: Re-implement the GLU tesselation functions.
/* Complex: Pedal, and for some reason Attraction, Mountain, Qix, SpeedMine, Starfish
* Nonconvex: Goop, Pacman, Rocks, Speedmine
+ *
+ * We currently do Nonconvex with the simple-to-implement ear clipping
+ * algorithm, but in the future we can replace that with an algorithm
+ * with slower big-O growth
+ *
*/
- Assert(shape == Convex, "XFillPolygon: (TODO) Unimplemented shape");
// TODO: Feed vertices straight to OpenGL for CoordModeOrigin.
- GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc.
- short v[2] = {0, 0};
-
- for (unsigned i = 0; i < npoints; i++) {
- next_point(v, points[i], mode);
- vertices[2 * i] = v[0];
- vertices[2 * i + 1] = v[1];
- }
- glEnableClientState (GL_VERTEX_ARRAY);
- glDisableClientState (GL_TEXTURE_COORD_ARRAY);
-
- glVertexPointer (2, GL_SHORT, 0, vertices);
- glDrawArrays (GL_TRIANGLE_FAN, 0, npoints);
+ if (shape == Convex) {
+
+ GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc.
+ short v[2] = {0, 0};
- free(vertices);
-
- /*
- CGRect wr = d->frame;
- int i;
- push_fg_gc (dpy, d, gc, YES);
- CGContextRef cgc = d->cgc;
- CGContextBeginPath (cgc);
- float x = 0, y = 0;
- for (i = 0; i < npoints; i++) {
- if (i > 0 && mode == CoordModePrevious) {
- x += points[i].x;
- y -= points[i].y;
- } else {
- x = wr.origin.x + points[i].x;
- y = wr.origin.y + wr.size.height - points[i].y;
+ for (unsigned i = 0; i < npoints; i++) {
+ next_point(v, points[i], mode);
+ vertices[2 * i] = v[0];
+ vertices[2 * i + 1] = v[1];
}
-
- if (i == 0)
- CGContextMoveToPoint (cgc, x, y);
- else
- CGContextAddLineToPoint (cgc, x, y);
+
+ glEnableClientState (GL_VERTEX_ARRAY);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ vertex_pointer (dpy, GL_SHORT, 0, vertices);
+ glDrawArrays (GL_TRIANGLE_FAN, 0, npoints);
+
+ free(vertices);
+
+ } else if (shape == Nonconvex) {
+
+ // TODO: assert that x,y of first and last point match, as that is assumed
+
+ linked_point *root;
+ root = (linked_point *) malloc( sizeof(linked_point) );
+ set_points_list(points,npoints,root);
+ traverse_points_list(dpy, root);
+
+ } else {
+ Assert((shape == Convex || shape == Nonconvex),
+ "XFillPolygon: (TODO) Unimplemented shape");
}
- CGContextClosePath (cgc);
- if (gc->gcv.fill_rule == EvenOddRule)
- CGContextEOFillPath (cgc);
- else
- CGContextFillPath (cgc);
- pop_gc (d, gc);
- invalidate_drawable_cache (d);
- */
+
return 0;
}
return a < 0 ? (b - 1) - (-(a + 1) % b) : a % b;
}
+/* TODO: Fill in arcs with line width > 1 */
+static int
+draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
+ unsigned int width, unsigned int height,
+ int angle1, int angle2, Bool fill_p)
+{
+ int gglw = gc->gcv.line_width;
+
+ if (fill_p || gglw <= 1) {
+ draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p);
+ }
+ else {
+ int w1, w2, h1, h2, gglwh;
+ w1 = width + gglw;
+ h1 = height + gglw;
+ h2 = height - gglw;
+ w2 = width - gglw;
+ gglwh = gglw / 2;
+ int x1 = x - gglwh;
+ int x2 = x + gglwh;
+ int y1 = y - gglwh;
+ int y2 = y + gglwh;
+ //draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p);
+ draw_arc_gl (dpy, d, gc, x1, y1, w1, h1, angle1, angle2, fill_p);
+ draw_arc_gl (dpy, d, gc, x2, y2, w2, h2, angle1, angle2, fill_p);
+ }
+ return 0;
+}
+
+
int
-jwxyz_draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
- unsigned int width, unsigned int height,
- int angle1, int angle2, Bool fill_p)
+draw_arc_gl (Display *dpy, Drawable d, GC gc, int x, int y,
+ unsigned int width, unsigned int height,
+ int angle1, int angle2, Bool fill_p)
{
- jwxyz_bind_drawable (dpy, dpy->main_window, d);
- set_fg_gc(dpy, gc);
+ set_fg_gc(dpy, d, gc);
/* Let's say the number of line segments needed to make a convincing circle is
4*sqrt(radius). (But these arcs aren't necessarily circular arcs...) */
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
glEnableClientState (GL_VERTEX_ARRAY);
- glVertexPointer (2, GL_FLOAT, 0, data);
+ vertex_pointer (dpy, GL_FLOAT, 0, data);
glDrawArrays (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP,
0,
(GLsizei)((data_ptr - data) / 2));
}
-XGCValues *
-jwxyz_gc_gcv (GC gc)
+static XGCValues *
+gc_gcv (GC gc)
{
return &gc->gcv;
}
-unsigned int
-jwxyz_gc_depth (GC gc)
+static unsigned int
+gc_depth (GC gc)
{
return gc->depth;
}
-GC
-XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
+static GC
+CreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
{
struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
gc->depth = jwxyz_drawable_depth (d);
}
-int
-XFreeGC (Display *dpy, GC gc)
+static void
+free_clip_mask (Display *dpy, GC gc)
{
- if (gc->gcv.font)
- XUnloadFont (dpy, gc->gcv.font);
-
- // TODO
-/*
- Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
-
if (gc->gcv.clip_mask) {
- XFreePixmap (dpy, gc->gcv.clip_mask);
- CGImageRelease (gc->clip_mask);
+ if (dpy->gc_clip_mask == gc->clip_mask) {
+ jwxyz_gl_flush (dpy);
+ dpy->gc_clip_mask = 0;
+ }
+ glDeleteTextures (1, &gc->clip_mask);
}
-*/
- free (gc);
- return 0;
}
-/*
-static void
-flipbits (unsigned const char *in, unsigned char *out, int length)
-{
- static const unsigned char table[256] = {
- 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
- 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
- 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
- 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
- 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
- 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
- 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
- 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
- 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
- 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
- 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
- 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
- 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
- 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
- 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
- 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
- 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
- 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
- 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
- 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
- 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
- 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
- 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
- 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
- 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
- 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
- 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
- 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
- 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
- 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
- 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
- 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
- };
- while (length-- > 0)
- *out++ = table[*in++];
-}
-*/
-
-// Copied and pasted from OSX/XScreenSaverView.m
-static GLsizei
-to_pow2 (size_t x)
+static int
+FreeGC (Display *dpy, GC gc)
{
- if (x <= 1)
- return 1;
-
- size_t mask = (size_t)-1;
- unsigned bits = sizeof(x) * CHAR_BIT;
- unsigned log2 = bits;
-
- --x;
- while (bits) {
- if (!(x & mask)) {
- log2 -= bits;
- x <<= bits;
- }
-
- bits >>= 1;
- mask <<= bits;
- }
+ if (gc->gcv.font)
+ XUnloadFont (dpy, gc->gcv.font);
- return 1 << log2;
+ free_clip_mask (dpy, gc);
+ free (gc);
+ return 0;
}
-int
-XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
- int src_x, int src_y, int dest_x, int dest_y,
- unsigned int w, unsigned int h)
+static int
+PutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
+ int src_x, int src_y, int dest_x, int dest_y,
+ unsigned int w, unsigned int h)
{
jwxyz_assert_display (dpy);
if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
return 0;
+ jwxyz_gl_flush (dpy);
jwxyz_bind_drawable (dpy, dpy->main_window, d);
+ jwxyz_gl_set_gc (dpy, gc);
+
int bpl = ximage->bytes_per_line;
int bpp = ximage->bits_per_pixel;
- /* int bsize = bpl * h; */
- char *data = ximage->data;
-/*
- CGRect r;
- r.origin.x = wr->x + dest_x;
- r.origin.y = wr->y + wr->height - dest_y - h;
- r.size.width = w;
- r.size.height = h;
-*/
+ char *tex_data;
+ unsigned src_w;
+ GLint tex_internalformat;
+ GLenum tex_format, tex_type;
+ unsigned tex_index;
- Assert (gc->gcv.function == GXcopy, "XPutImage: (TODO) GC function not supported");
- Assert (!gc->gcv.clip_mask, "XPutImage: (TODO) GC clip mask not supported");
-
if (bpp == 32) {
-
- /* Take advantage of the fact that it's ok for (bpl != w * bpp)
- to create a CGImage from a sub-rectagle of the XImage.
- */
- data += (src_y * bpl) + (src_x * 4);
+ tex_data = ximage->data + src_y * bpl + (src_x * 4);
jwxyz_assert_display(dpy);
/* There probably won't be any hacks that do this, but... */
Assert (!(bpl % 4), "XPutImage: bytes_per_line not divisible by four.");
- unsigned src_w = bpl / 4;
+ tex_internalformat = texture_internalformat(dpy);
+ tex_format = dpy->pixel_format;
+ tex_type = gl_pixel_type(dpy);
+ tex_index = texture_rgba;
/* GL_UNPACK_ROW_LENGTH is not allowed to be negative. (sigh) */
# ifndef HAVE_JWZGLES
- glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
src_w = w;
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
+# else
+ src_w = bpl / 4;
# endif
- glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
+ // glPixelStorei (GL_UNPACK_ALIGNMENT, 4); // Probably unnecessary.
+ } else {
+ Assert (bpp == 1, "expected 1 or 32 bpp");
+ Assert ((src_x % 8) == 0,
+ "XPutImage with non-byte-aligned 1bpp X offset not implemented");
-# if 1 // defined HAVE_JWZGLES
- // Regular OpenGL uses GL_TEXTURE_RECTANGLE_EXT in place of GL_TEXTURE_2D.
- // TODO: Make use of OES_draw_texture.
- // TODO: Coords might be wrong; things might be upside-down or backwards
- // or whatever.
-
- unsigned tex_w = src_w, tex_h = h;
- if (!dpy->gl_texture_npot_p) {
- tex_w = to_pow2(tex_w);
- tex_h = to_pow2(tex_h);
- }
+ const char *src_data = ximage->data + src_y * bpl + (src_x / 8);
+ unsigned w8 = (w + 7) / 8;
- GLint internalformat = texture_internalformat(dpy);
+ src_w = w8 * 8;
- glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
+ tex_data = malloc(src_w * h);
- if (tex_w == src_w && tex_h == h) {
- glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
- 0, dpy->screen->pixel_format, gl_pixel_type(dpy), data);
- } else {
- // TODO: Sampling the last row might be a problem if src_x != 0.
- glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
- 0, dpy->screen->pixel_format, gl_pixel_type(dpy), NULL);
- glTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, src_w, h,
- dpy->screen->pixel_format, gl_pixel_type(dpy), data);
+#if 0
+ {
+ char s[10240];
+ int x, y, o;
+ Log("#PI ---------- %d %d %08lx %08lx",
+ jwxyz_drawable_depth(d), ximage->depth,
+ (unsigned long)d, (unsigned long)ximage);
+ for (y = 0; y < ximage->height; y++) {
+ o = 0;
+ for (x = 0; x < ximage->width; x++) {
+ unsigned long b = XGetPixel(ximage, x, y);
+ s[o++] = ( (b & 0xFF000000)
+ ? ((b & 0x00FFFFFF) ? '#' : '-')
+ : ((b & 0x00FFFFFF) ? '+' : ' '));
+ }
+ s[o] = 0;
+ Log("#PI [%s]",s);
+ }
+ Log("# PI ----------");
}
-
- set_white ();
- // glEnable (dpy->gl_texture_target);
- // glColor4f (0.5, 0, 1, 1);
- glEnable (dpy->gl_texture_target);
- glEnableClientState (GL_VERTEX_ARRAY);
- glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+#endif
+ uint32_t *data_out = (uint32_t *)tex_data;
+ for(unsigned y = h; y; --y) {
+ for(unsigned x = 0; x != w8; ++x) {
+ // TODO: Does big endian work here?
+ uint8_t byte = src_data[x];
+ uint32_t word = byte;
+ word = (word & 0x3) | ((word & 0xc) << 14);
+ word = (word & 0x00010001) | ((word & 0x00020002) << 7);
+ data_out[x << 1] = (word << 8) - word;
+
+ word = byte >> 4;
+ word = (word & 0x3) | ((word & 0xc) << 14);
+ word = (word & 0x00010001) | ((word & 0x00020002) << 7);
+ data_out[(x << 1) | 1] = (word << 8) - word;
+ }
+ src_data += bpl;
+ data_out += src_w / 4;
+ }
+#if 0
+ {
+ char s[10240];
+ int x, y, o;
+ Log("#P2 ----------");
+ for (y = 0; y < ximage->height; y++) {
+ o = 0;
+ for (x = 0; x < ximage->width; x++) {
+ unsigned long b = ((uint8_t *)tex_data)[y * w + x];
+ s[o++] = ( (b & 0xFF000000)
+ ? ((b & 0x00FFFFFF) ? '#' : '-')
+ : ((b & 0x00FFFFFF) ? '+' : ' '));
+ }
+ s[o] = 0;
+ Log("#P2 [%s]",s);
+ }
+ Log("# P2 ----------");
+ }
+#endif
- // TODO: Why are these ever turned on in the first place?
- glDisableClientState (GL_COLOR_ARRAY);
- glDisableClientState (GL_NORMAL_ARRAY);
- // glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ tex_internalformat = GL_LUMINANCE;
+ tex_format = GL_LUMINANCE;
+ tex_type = GL_UNSIGNED_BYTE;
+ tex_index = texture_mono;
- GLfloat vertices[4][2] =
- {
- {dest_x, dest_y},
- {dest_x, dest_y + h},
- {dest_x + w, dest_y + h},
- {dest_x + w, dest_y}
- };
+ // glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+ }
- GLfloat texcoord_w, texcoord_h;
-# ifndef HAVE_JWZGLES
- if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT) {
- texcoord_w = w;
- texcoord_h = h;
- } else
-# endif /* HAVE_JWZGLES */
- {
- texcoord_w = (double)w / tex_w;
- texcoord_h = (double)h / tex_h;
- }
+# if 1 // defined HAVE_JWZGLES
+ // Regular OpenGL uses GL_TEXTURE_RECTANGLE_EXT in place of GL_TEXTURE_2D.
+ // TODO: Make use of OES_draw_texture.
- GLfloat tex_coords[4][2];
- tex_coords[0][0] = 0;
- tex_coords[0][1] = 0;
- tex_coords[1][0] = 0;
- tex_coords[1][1] = texcoord_h;
- tex_coords[2][0] = texcoord_w;
- tex_coords[2][1] = texcoord_h;
- tex_coords[3][0] = texcoord_w;
- tex_coords[3][1] = 0;
-
- glVertexPointer (2, GL_FLOAT, 0, vertices);
- glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
-
- // Respect the alpha channel in the XImage if we're using alpha.
- if (gc->gcv.alpha_allowed_p) {
- glEnable (GL_BLEND);
- glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
+ unsigned tex_w = src_w, tex_h = h;
+ glBindTexture (dpy->gl_texture_target, dpy->textures[tex_index]);
- glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
+ // A fun project: reimplement xshm.c by means of a PBO using
+ // GL_MAP_UNSYNCHRONIZED_BIT.
- if (gc->gcv.alpha_allowed_p)
- glDisable (GL_BLEND);
+ tex_image (dpy, tex_internalformat, &tex_w, &tex_h, tex_format, tex_type,
+ tex_data);
-// clear_texture();
- glDisable (dpy->gl_texture_target);
+ if (bpp == 1)
+ free(tex_data);
+
+ jwxyz_gl_draw_image (dpy, gc, dpy->gl_texture_target, tex_w, tex_h,
+ 0, 0, bpp, w, h, dest_x, dest_y, True);
# else
- glRasterPos2i (dest_x, dest_y);
- glPixelZoom (1, -1);
- jwxyz_assert_display (dpy);
- glDrawPixels (w, h, dpy->screen->pixel_format, gl_pixel_type(dpy), data);
+ glRasterPos2i (dest_x, dest_y);
+ glPixelZoom (1, -1);
+ jwxyz_assert_display (dpy);
+ glDrawPixels (w, h, dpy->pixel_format, gl_pixel_type(dpy), data);
# endif
- } else { // (bpp == 1)
-
- // Assert(FALSE, "XPutImage: TODO");
- // Check out ximage_(get|put)pixel_1
-
-#if 0
- /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
- #### However, the bit order within a byte in a 1bpp XImage is
- the wrong way around from what Quartz expects, so first we
- have to copy the data to reverse it. Shit! Maybe it
- would be worthwhile to go through the hacks and #ifdef
- each one that diddles 1bpp XImage->data directly...
- */
- Assert ((src_x % 8) == 0,
- "XPutImage with non-byte-aligned 1bpp X offset not implemented");
-
- data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
- unsigned char *flipped = (unsigned char *) malloc (bsize);
-
- flipbits ((unsigned char *) data, flipped, bsize);
-
- CGDataProviderRef prov =
- CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
- CGImageRef mask = CGImageMaskCreate (w, h,
- 1, bpp, bpl,
- prov,
- NULL, /* decode[] */
- GL_FALSE); /* interpolate */
- push_fg_gc (dpy, d, gc, GL_TRUE);
-
- CGContextFillRect (cgc, r); // foreground color
- CGContextClipToMask (cgc, r, mask);
- set_color (dpy, cgc, gc->gcv.background, gc->depth, GL_FALSE, GL_TRUE);
- CGContextFillRect (cgc, r); // background color
- pop_gc (d, gc);
-
- free (flipped);
- CGDataProviderRelease (prov);
- CGImageRelease (mask);
-#endif
- }
-
jwxyz_assert_gl ();
- invalidate_drawable_cache (d);
return 0;
}
-/* At the moment nothing actually uses XGetSubImage. */
-/* #### Actually lots of things call XGetImage, which calls XGetSubImage.
- E.g., Twang calls XGetImage on the window intending to get a
+/* At the moment only XGetImage and get_xshm_image use XGetSubImage. */
+/* #### Twang calls XGetImage on the window intending to get a
buffer full of black. This is returning a buffer full of white
instead of black for some reason. */
static XImage *
-XGetSubImage (Display *dpy, Drawable d, int x, int y,
- unsigned int width, unsigned int height,
- unsigned long plane_mask, int format,
- XImage *dest_image, int dest_x, int dest_y)
+GetSubImage (Display *dpy, Drawable d, int x, int y,
+ unsigned int width, unsigned int height,
+ unsigned long plane_mask, int format,
+ XImage *dest_image, int dest_x, int dest_y)
{
Assert ((width < 65535), "improbably large width");
Assert ((height < 65535), "improbably large height");
Assert ((x < 65535 && x > -65535), "improbably large x");
Assert ((y < 65535 && y > -65535), "improbably large y");
+ jwxyz_gl_flush (dpy);
jwxyz_bind_drawable (dpy, dpy->main_window, d);
// TODO: What if this reads off the edge? What is supposed to happen?
uint32_t *dest_data = (uint32_t *)dest_image->data + pixels_per_line * dest_y + dest_x;
glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
- dpy->screen->pixel_format, gl_pixel_type(dpy), dest_data);
+ dpy->pixel_format, gl_pixel_type(dpy), dest_data);
/* Flip this upside down. :( */
uint32_t *top = dest_data;
}
} else {
- /* TODO: Actually get pixels. */
+ uint32_t *rgba_image = malloc(width * height * 4);
+ Assert (rgba_image, "not enough memory");
+
+ // Must be GL_RGBA; GL_RED isn't available.
+ glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
+ GL_RGBA, GL_UNSIGNED_BYTE, rgba_image);
Assert (!(dest_x % 8), "XGetSubImage: dest_x not byte-aligned");
- uint8_t *dest_data =
+ uint8_t *top =
(uint8_t *)dest_image->data + dest_image->bytes_per_line * dest_y
+ dest_x / 8;
- for (unsigned y = height / 2; y; --y) {
- memset (dest_data, y & 1 ? 0x55 : 0xAA, width / 8);
- dest_data += dest_image->bytes_per_line;
- }
- }
-
- return dest_image;
-}
-
-XImage *
-XGetImage (Display *dpy, Drawable d, int x, int y,
- unsigned int width, unsigned int height,
- unsigned long plane_mask, int format)
-{
- unsigned depth = jwxyz_drawable_depth (d);
- XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
- 0, 0);
- image->data = (char *) malloc (height * image->bytes_per_line);
-
- return XGetSubImage (dpy, d, x, y, width, height, plane_mask, format,
- image, 0, 0);
-}
-
-/* Returns a transformation matrix to do rotation as per the provided
- EXIF "Orientation" value.
- */
-/*
-static CGAffineTransform
-exif_rotate (int rot, CGSize rect)
-{
- CGAffineTransform trans = CGAffineTransformIdentity;
- switch (rot) {
- case 2: // flip horizontal
- trans = CGAffineTransformMakeTranslation (rect.width, 0);
- trans = CGAffineTransformScale (trans, -1, 1);
- break;
-
- case 3: // rotate 180
- trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
- trans = CGAffineTransformRotate (trans, M_PI);
- break;
-
- case 4: // flip vertical
- trans = CGAffineTransformMakeTranslation (0, rect.height);
- trans = CGAffineTransformScale (trans, 1, -1);
- break;
-
- case 5: // transpose (UL-to-LR axis)
- trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
- trans = CGAffineTransformScale (trans, -1, 1);
- trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
- break;
-
- case 6: // rotate 90
- trans = CGAffineTransformMakeTranslation (0, rect.width);
- trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
- break;
-
- case 7: // transverse (UR-to-LL axis)
- trans = CGAffineTransformMakeScale (-1, 1);
- trans = CGAffineTransformRotate (trans, M_PI / 2);
- break;
-
- case 8: // rotate 270
- trans = CGAffineTransformMakeTranslation (rect.height, 0);
- trans = CGAffineTransformRotate (trans, M_PI / 2);
- break;
-
- default:
- break;
- }
-
- return trans;
-}
-*/
-
-void
-jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
- Bool nsimg_p, void *img_arg,
- XRectangle *geom_ret, int exif_rotation)
-{
- Assert (False, "jwxyz_draw_NSImage_or_CGImage: TODO stub");
-#if 0
- CGImageRef cgi;
-# ifndef USE_IPHONE
- CGImageSourceRef cgsrc;
-# endif // USE_IPHONE
- NSSize imgr;
-
- CGContextRef cgc = d->cgc;
-
- if (nsimg_p) {
-
- NSImage *nsimg = (NSImage *) img_arg;
- imgr = [nsimg size];
-
-# ifndef USE_IPHONE
- // convert the NSImage to a CGImage via the toll-free-bridging
- // of NSData and CFData...
- //
- NSData *nsdata = [NSBitmapImageRep
- TIFFRepresentationOfImageRepsInArray:
- [nsimg representations]];
- CFDataRef cfdata = (CFDataRef) nsdata;
- cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
- cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
-# else // USE_IPHONE
- cgi = nsimg.CGImage;
-# endif // USE_IPHONE
-
- } else {
- cgi = (CGImageRef) img_arg;
- imgr.width = CGImageGetWidth (cgi);
- imgr.height = CGImageGetHeight (cgi);
- }
-
- Bool rot_p = (exif_rotation >= 5);
-
- if (rot_p)
- imgr = NSMakeSize (imgr.height, imgr.width);
-
- CGRect winr = d->frame;
- float rw = winr.size.width / imgr.width;
- float rh = winr.size.height / imgr.height;
- float r = (rw < rh ? rw : rh);
-
- CGRect dst, dst2;
- dst.size.width = imgr.width * r;
- dst.size.height = imgr.height * r;
- dst.origin.x = (winr.size.width - dst.size.width) / 2;
- dst.origin.y = (winr.size.height - dst.size.height) / 2;
-
- dst2.origin.x = dst2.origin.y = 0;
- if (rot_p) {
- dst2.size.width = dst.size.height;
- dst2.size.height = dst.size.width;
- } else {
- dst2.size = dst.size;
- }
-
- // Clear the part not covered by the image to background or black.
- //
- if (d->type == WINDOW)
- XClearWindow (dpy, d);
- else {
- jwxyz_fill_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height,
- drawable_depth (d) == 1 ? 0 : BlackPixel(dpy,0));
- }
-
- CGAffineTransform trans =
- exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
-
- CGContextSaveGState (cgc);
- CGContextConcatCTM (cgc,
- CGAffineTransformMakeTranslation (dst.origin.x,
- dst.origin.y));
- CGContextConcatCTM (cgc, trans);
- //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
- CGContextDrawImage (cgc, dst2, cgi);
- CGContextRestoreGState (cgc);
-
-# ifndef USE_IPHONE
- if (nsimg_p) {
- CFRelease (cgsrc);
- CGImageRelease (cgi);
- }
-# endif // USE_IPHONE
-
- if (geom_ret) {
- geom_ret->x = dst.origin.x;
- geom_ret->y = dst.origin.y;
- geom_ret->width = dst.size.width;
- geom_ret->height = dst.size.height;
- }
-
- invalidate_drawable_cache (d);
-#endif
-}
-
-#ifndef HAVE_JWZGLES
-
-/*
-static void
-create_rectangle_texture (GLuint *texture)
-{
- glGenTextures(1, texture);
- glBindTexture(GL_TEXTURE_RECTANGLE_EXT, *texture);
- glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-}
-*/
-
-#endif
-
-
#if 0
-static Pixmap
-copy_pixmap (Display *dpy, Pixmap p)
-{
- if (!p) return 0;
- Assert (p->type == PIXMAP, "not a pixmap");
-
- Pixmap p2 = 0;
-
- Window root;
- int x, y;
- unsigned int width, height, border_width, depth;
- if (XGetGeometry (dpy, p, &root,
- &x, &y, &width, &height, &border_width, &depth)) {
- XGCValues gcv;
- gcv.function = GXcopy;
- GC gc = XCreateGC (dpy, p, GCFunction, &gcv);
- if (gc) {
- p2 = XCreatePixmap (dpy, p, width, height, depth);
- if (p2)
- XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0);
- XFreeGC (dpy, gc);
+ {
+ char s[10240];
+ Log("#GI ---------- %d %d %d x %d %08lx",
+ jwxyz_drawable_depth(d), dest_image->depth,
+ width, height,
+ (unsigned long) d);
+ for (int y = 0; y < height; y++) {
+ int x;
+ for (x = 0; x < width; x++) {
+ unsigned long b = rgba_image[(height-y-1) * width + x];
+ s[x] = ( (b & 0xFF000000)
+ ? ((b & 0x00FFFFFF) ? '#' : '-')
+ : ((b & 0x00FFFFFF) ? '+' : ' '));
+ }
+ s[x] = 0;
+ Log("#GI [%s]",s);
+ }
+ Log("#GI ----------");
}
- }
-
- Assert (p2, "could not copy pixmap");
-
- return p2;
-}
#endif
-
-
-// This is XQueryFont, but for the XFontStruct embedded in 'Font'
-//
-static void
-query_font (Font fid)
-{
- if (!fid || !fid->native_font) {
- Assert (0, "no native font in fid");
- return;
- }
-
- int first = 32;
- int last = 255;
-
- Display *dpy = fid->dpy;
- void *native_font = fid->native_font;
-
- XFontStruct *f = &fid->metrics;
- XCharStruct *min = &f->min_bounds;
- XCharStruct *max = &f->max_bounds;
-
- f->fid = fid;
- f->min_char_or_byte2 = first;
- f->max_char_or_byte2 = last;
- f->default_char = 'M';
- f->ascent = fid->ascent;
- f->descent = fid->descent;
-
- min->width = 32767; // set to smaller values in the loop
- min->ascent = 32767;
- min->descent = 32767;
- min->lbearing = 32767;
- min->rbearing = 32767;
-
- f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
-
- for (int i = first; i <= last; i++) {
- XCharStruct *cs = &f->per_char[i-first];
- char s = (char) i;
- jwxyz_render_text (dpy, native_font, &s, 1, GL_FALSE, cs, 0);
-
- max->width = MAX (max->width, cs->width);
- max->ascent = MAX (max->ascent, cs->ascent);
- max->descent = MAX (max->descent, cs->descent);
- max->lbearing = MAX (max->lbearing, cs->lbearing);
- max->rbearing = MAX (max->rbearing, cs->rbearing);
-
- min->width = MIN (min->width, cs->width);
- min->ascent = MIN (min->ascent, cs->ascent);
- min->descent = MIN (min->descent, cs->descent);
- min->lbearing = MIN (min->lbearing, cs->lbearing);
- min->rbearing = MIN (min->rbearing, cs->rbearing);
-/*
- Log (" %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d\n",
- i, i, cs->width, cs->lbearing, cs->rbearing,
- cs->ascent, cs->descent);
- */
- }
-}
-
-
-// Since 'Font' includes the metrics, this just makes a copy of that.
-//
-XFontStruct *
-XQueryFont (Display *dpy, Font fid)
-{
- // copy XFontStruct
- XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
- *f = fid->metrics;
- f->fid = fid;
-
- // build XFontProps
- f->n_properties = 1;
- f->properties = malloc (sizeof(*f->properties) * f->n_properties);
- f->properties[0].name = XA_FONT;
- Assert (sizeof (f->properties[0].card32) >= sizeof (char *),
- "atoms probably needs a real implementation");
- // If XInternAtom is ever implemented, use it here.
- f->properties[0].card32 = (unsigned long)(char *)fid->xa_font;
-
- // copy XCharStruct array
- int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
- f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
-
- memcpy (f->per_char, fid->metrics.per_char,
- size * sizeof (XCharStruct));
-
- return f;
-}
-
-
-static Font
-copy_font (Font fid)
-{
- fid->refcount++;
- return fid;
-}
-
-
-#if 0
-
-
-static NSArray *
-font_family_members (NSString *family_name)
-{
-# ifndef USE_IPHONE
- return [[NSFontManager sharedFontManager]
- availableMembersOfFontFamily:family_name];
-# else
- return [UIFont fontNamesForFamilyName:family_name];
-# endif
-}
-
-
-static NSString *
-default_font_family (NSFontTraitMask require)
-{
- return require & NSFixedPitchFontMask ? @"Courier" : @"Verdana";
-}
-
-
-static NSFont *
-try_font (NSFontTraitMask traits, NSFontTraitMask mask,
- NSString *family_name, float size,
- char **name_ret)
-{
- Assert (size > 0, "zero font size");
-
- NSArray *family_members = font_family_members (family_name);
- if (!family_members.count)
- family_members = font_family_members (default_font_family (traits));
-
-# ifndef USE_IPHONE
- for (unsigned k = 0; k != family_members.count; ++k) {
-
- NSArray *member = [family_members objectAtIndex:k];
- NSFontTraitMask font_mask =
- [(NSNumber *)[member objectAtIndex:3] unsignedIntValue];
-
- if ((font_mask & mask) == traits) {
-
- NSString *name = [member objectAtIndex:0];
- NSFont *f = [NSFont fontWithName:name size:size];
- if (!f)
- break;
-
- /* Don't use this font if it (probably) doesn't include ASCII characters.
- */
- NSStringEncoding enc = [f mostCompatibleStringEncoding];
- if (! (enc == NSUTF8StringEncoding ||
- enc == NSISOLatin1StringEncoding ||
- enc == NSNonLossyASCIIStringEncoding ||
- enc == NSISOLatin2StringEncoding ||
- enc == NSUnicodeStringEncoding ||
- enc == NSWindowsCP1250StringEncoding ||
- enc == NSWindowsCP1252StringEncoding ||
- enc == NSMacOSRomanStringEncoding)) {
- // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
- break;
+ const uint32_t *bottom = rgba_image + width * (height - 1);
+ for (unsigned y = height; y; --y) {
+ memset (top, 0, width / 8);
+ for (unsigned x = 0; x != width; ++x) {
+ if (bottom[x] & 0x80)
+ top[x >> 3] |= (1 << (x & 7));
}
- // NSLog(@"using \"%@\": %d", name, enc);
-
- // *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
- *name_ret = strdup (name.UTF8String);
- return f;
- }
- }
-# else // USE_IPHONE
-
- for (NSString *fn in family_members) {
-# define MATCH(X) \
- ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
- != NSNotFound)
-
- // The magic invocation for getting font names is
- // [[UIFontDescriptor
- // fontDescriptorWithFontAttributes:@{UIFontDescriptorNameAttribute: name}]
- // symbolicTraits]
- // ...but this only works on iOS 7 and later.
- NSFontTraitMask font_mask = 0;
- if (MATCH(@"Bold"))
- font_mask |= NSBoldFontMask;
- if (MATCH(@"Italic") || MATCH(@"Oblique"))
- font_mask |= NSItalicFontMask;
-
- if ((font_mask & mask) == traits) {
-
- /* Check if it can do ASCII. No good way to accomplish this!
- These are fonts present in iPhone Simulator as of June 2012
- that don't include ASCII.
- */
- if (MATCH(@"AppleGothic") || // Korean
- MATCH(@"Dingbats") || // Dingbats
- MATCH(@"Emoji") || // Emoticons
- MATCH(@"Geeza") || // Arabic
- MATCH(@"Hebrew") || // Hebrew
- MATCH(@"HiraKaku") || // Japanese
- MATCH(@"HiraMin") || // Japanese
- MATCH(@"Kailasa") || // Tibetan
- MATCH(@"Ornaments") || // Dingbats
- MATCH(@"STHeiti") // Chinese
- )
- break;
-
- *name_ret = strdup (fn.UTF8String);
- return [UIFont fontWithName:fn size:size];
- }
-# undef MATCH
- }
-
-# endif
-
- return NULL;
-}
-
-
-
-/* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead
- of XLFD strings; also they can be comma-separated strings with multiple
- font names. First one that exists wins.
- */
-static NSFont *
-try_native_font (const char *name, float scale,
- char **name_ret, float *size_ret, char **xa_font)
-{
- if (!name) return 0;
- const char *spc = strrchr (name, ' ');
- if (!spc) return 0;
-
- NSFont *f = 0;
- char *token = strdup (name);
- char *name2;
-
- while ((name2 = strtok (token, ","))) {
- token = 0;
-
- while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n')
- name2++;
-
- spc = strrchr (name2, ' ');
- if (!spc) continue;
-
- int dsize = 0;
- if (1 != sscanf (spc, " %d ", &dsize))
- continue;
- float size = dsize;
-
- if (size <= 4) continue;
-
- size *= scale;
-
- name2[strlen(name2) - strlen(spc)] = 0;
-
- NSString *nsname = [NSString stringWithCString:name2
- encoding:NSUTF8StringEncoding];
- f = [NSFont fontWithName:nsname size:size];
- if (f) {
- *name_ret = name2;
- *size_ret = size;
- *xa_font = strdup (name); // Maybe this should be an XLFD?
- break;
- } else {
- NSLog(@"No native font: \"%@\" %.0f", nsname, size);
- }
- }
-
- free (token);
- return f;
-}
-
-
-/* Returns a random font in the given size and face.
- */
-static NSFont *
-random_font (NSFontTraitMask traits, NSFontTraitMask mask,
- float size, NSString **family_ret, char **name_ret)
-{
-
-# ifndef USE_IPHONE
- // Providing Unbold or Unitalic in the mask for availableFontNamesWithTraits
- // returns an empty list, at least on a system with default fonts only.
- NSArray *families = [[NSFontManager sharedFontManager]
- availableFontFamilies];
- if (!families) return 0;
-# else
- NSArray *families = [UIFont familyNames];
-
- // There are many dups in the families array -- uniquify it.
- {
- NSArray *sorted_families =
- [families sortedArrayUsingSelector:@selector(compare:)];
- NSMutableArray *new_families =
- [NSMutableArray arrayWithCapacity:sorted_families.count];
-
- NSString *prev_family = nil;
- for (NSString *family in sorted_families) {
- if ([family compare:prev_family])
- [new_families addObject:family];
+ top += dest_image->bytes_per_line;
+ bottom -= width;
}
- families = new_families;
- }
-# endif // USE_IPHONE
-
- long n = [families count];
- if (n <= 0) return 0;
-
- int j;
- for (j = 0; j < n; j++) {
- int i = random() % n;
- NSString *family_name = [families objectAtIndex:i];
-
- NSFont *result = try_font (traits, mask, family_name, size, name_ret);
- if (result) {
- [*family_ret release];
- *family_ret = family_name;
- [*family_ret retain];
- return result;
- }
+ free (rgba_image);
}
- // None of the fonts support ASCII?
- return 0;
+ return dest_image;
}
-// Fonts need this. XDisplayHeightMM and friends should probably be consistent
-// with this as well if they're ever implemented.
-static const unsigned dpi = 75;
-
-
-static const char *
-xlfd_field_end (const char *s)
+static int
+SetClipMask (Display *dpy, GC gc, Pixmap m)
{
- const char *s2 = strchr(s, '-');
- if (!s2)
- s2 = s + strlen(s);
- return s2;
-}
+ Assert (m != dpy->main_window, "not a pixmap");
+ free_clip_mask (dpy, gc);
-static size_t
-xlfd_next (const char **s, const char **s2)
-{
- if (!**s2) {
- *s = *s2;
- } else {
- Assert (**s2 == '-', "xlfd parse error");
- *s = *s2 + 1;
- *s2 = xlfd_field_end (*s);
+ if (!m) {
+ gc->clip_mask = 0;
+ return 0;
}
- return *s2 - *s;
-}
-
-static NSFont *
-try_xlfd_font (const char *name, float scale,
- char **name_ret, float *size_ret, char **xa_font)
-{
- NSFont *nsfont = 0;
- NSString *family_name = nil;
- NSFontTraitMask require = 0, forbid = 0;
- GLboolean rand = GL_FALSE;
- float size = 0;
- char *ps_name = 0;
-
- const char *s = (name ? name : "");
-
- size_t L = strlen (s);
-# define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
-# define UNSPEC (L == 0 || L == 1 && *s == '*')
- if (CMP ("6x10")) size = 8, require |= NSFixedPitchFontMask;
- else if (CMP ("6x10bold")) size = 8, require |= NSFixedPitchFontMask | NSBoldFontMask;
- else if (CMP ("fixed")) size = 12, require |= NSFixedPitchFontMask;
- else if (CMP ("9x15")) size = 12, require |= NSFixedPitchFontMask;
- else if (CMP ("9x15bold")) size = 12, require |= NSFixedPitchFontMask | NSBoldFontMask;
- else if (CMP ("vga")) size = 12, require |= NSFixedPitchFontMask;
- else if (CMP ("console")) size = 12, require |= NSFixedPitchFontMask;
- else if (CMP ("gallant")) size = 12, require |= NSFixedPitchFontMask;
- else {
-
- // Incorrect fields are ignored.
-
- if (*s == '-')
- ++s;
- const char *s2 = xlfd_field_end(s);
-
- // Foundry (ignore)
-
- L = xlfd_next (&s, &s2); // Family name
- // This used to substitute Georgia for Times. Now it doesn't.
- if (CMP ("random")) {
- rand = GL_TRUE;
- } else if (CMP ("fixed")) {
- require |= NSFixedPitchFontMask;
- family_name = @"Courier";
- } else if (!UNSPEC) {
- family_name = [[[NSString alloc] initWithBytes:s
- length:L
- encoding:NSUTF8StringEncoding]
- autorelease];
- }
-
- L = xlfd_next (&s, &s2); // Weight name
- if (CMP ("bold") || CMP ("demibold"))
- require |= NSBoldFontMask;
- else if (CMP ("medium") || CMP ("regular"))
- forbid |= NSBoldFontMask;
-
- L = xlfd_next (&s, &s2); // Slant
- if (CMP ("i") || CMP ("o"))
- require |= NSItalicFontMask;
- else if (CMP ("r"))
- forbid |= NSItalicFontMask;
+ Assert (jwxyz_drawable_depth (m) == 1, "wrong depth for clip mask");
- xlfd_next (&s, &s2); // Set width name (ignore)
- xlfd_next (&s, &s2); // Add style name (ignore)
+ const XRectangle *frame = jwxyz_frame (m);
+ gc->clip_mask_width = frame->width;
+ gc->clip_mask_height = frame->height;
- xlfd_next (&s, &s2); // Pixel size (ignore)
+ /* Apparently GL_ALPHA isn't a valid format for a texture used in an FBO:
- xlfd_next (&s, &s2); // Point size
- char *s3;
- uintmax_t n = strtoumax(s, &s3, 10);
- if (s2 == s3)
- size = n / 10.0;
+ (86) Are any one- or two- component formats color-renderable?
- xlfd_next (&s, &s2); // Resolution X (ignore)
- xlfd_next (&s, &s2); // Resolution Y (ignore)
+ Presently none of the one- or two- component texture formats
+ defined in unextended OpenGL is color-renderable. The R
+ and RG float formats defined by the NV_float_buffer
+ extension are color-renderable.
- xlfd_next (&s, &s2); // Spacing
- if (CMP ("p"))
- forbid |= NSFixedPitchFontMask;
- else if (CMP ("m") || CMP ("c"))
- require |= NSFixedPitchFontMask;
+ Although an early draft of the FBO specification permitted
+ rendering into alpha, luminance, and intensity formats, this
+ this capability was pulled when it was realized that it was
+ under-specified exactly how rendering into these formats
+ would work. (specifically, how R/G/B/A map to I/L/A)
- // Don't care about average_width or charset registry.
- }
-# undef CMP
-# undef UNSPEC
+ <https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_framebuffer_object.txt>
- if (!family_name && !rand)
- family_name = default_font_family (require);
-
- if (size < 6 || size > 1000)
- size = 12;
-
- size *= scale;
-
- NSFontTraitMask mask = require | forbid;
+ ...Therefore, 1-bit drawables get to have wasted color channels.
+ Currently, R=G=B=grey, and the alpha channel is unused.
+ */
- if (rand) {
- nsfont = random_font (require, mask, size, &family_name, &ps_name);
- [family_name autorelease];
- }
+ /* There doesn't seem to be any way to move what's on one of the color
+ channels to the alpha channel in GLES 1.1 without leaving the GPU.
+ */
- if (!nsfont)
- nsfont = try_font (require, mask, family_name, size, &ps_name);
+ /* GLES 1.1 only supports GL_CLAMP_TO_EDGE or GL_REPEAT for
+ GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T, so to prevent drawing outside
+ the clip mask pixmap, there's two options:
+ 1. Use glScissor.
+ 2. Add a black border.
- // if that didn't work, turn off attibutes until it does
- // (e.g., there is no "Monaco-Bold".)
- //
- if (!nsfont && (mask & NSItalicFontMask)) {
- require &= ~NSItalicFontMask;
- mask &= ~NSItalicFontMask;
- nsfont = try_font (require, mask, family_name, size, &ps_name);
- }
- if (!nsfont && (mask & NSBoldFontMask)) {
- require &= ~NSBoldFontMask;
- mask &= ~NSBoldFontMask;
- nsfont = try_font (require, mask, family_name, size, &ps_name);
- }
- if (!nsfont && (mask & NSFixedPitchFontMask)) {
- require &= ~NSFixedPitchFontMask;
- mask &= ~NSFixedPitchFontMask;
- nsfont = try_font (require, mask, family_name, size, &ps_name);
- }
+ Option #2 is in use here.
- if (nsfont) {
- *name_ret = ps_name;
- *size_ret = size;
- float actual_size = size / scale;
- asprintf(xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
- family_name.UTF8String,
- (require & NSBoldFontMask) ? "bold" : "medium",
- (require & NSItalicFontMask) ? 'o' : 'r',
- (unsigned)(dpi * actual_size / 72.27 + 0.5),
- (unsigned)(actual_size * 10 + 0.5), dpi, dpi,
- (require & NSFixedPitchFontMask) ? 'm' : 'p');
- return nsfont;
- } else {
- return 0;
- }
-}
+ The following converts in-place an RGBA image to alpha-only image with a
+ 1px black border.
+ */
-#endif
+ // Prefix/suffix: 1 row+1 pixel, rounded to nearest int32.
+ size_t pad0 = frame->width + 3, pad = (pad0 + 3) & ~3;
+ uint8_t *data = malloc(2 * pad + frame->width * frame->height * 4);
-Font
-XLoadFont (Display *dpy, const char *name)
-{
- Font fid = (Font) calloc (1, sizeof(*fid));
- fid->refcount = 1;
- fid->dpy = dpy;
+ uint8_t *rgba = data + pad;
+ uint8_t *alpha = rgba;
+ uint8_t *alpha0 = alpha - pad0;
+ memset (alpha0, 0, pad0); // Top row.
- // (TODO) float scale = 1;
+ jwxyz_gl_flush (dpy);
+ jwxyz_bind_drawable (dpy, dpy->main_window, m);
+ glReadPixels (0, 0, frame->width, frame->height, GL_RGBA, GL_UNSIGNED_BYTE,
+ rgba);
-# ifdef USE_IPHONE
- /* Since iOS screens are physically smaller than desktop screens, scale up
- the fonts to make them more readable.
+ for (unsigned y = 0; y != frame->height; ++y) {
+ for (unsigned x = 0; x != frame->width; ++x)
+ alpha[x] = rgba[x * 4];
- Note that X11 apps on iOS also have the backbuffer sized in points
- instead of pixels, resulting in an effective X11 screen size of 768x1024
- or so, even if the display has significantly higher resolution. That is
- unrelated to this hack, which is really about DPI.
- */
- /* scale = 2; */
-# endif
+ rgba += frame->width * 4;
- fid->dpy = dpy;
- fid->native_font = jwxyz_load_native_font (dpy, name,
- &fid->ps_name, &fid->size,
- &fid->ascent, &fid->descent);
- if (!fid->native_font) {
- free (fid);
- return 0;
+ alpha += frame->width;
+ alpha[0] = 0;
+ alpha[1] = 0;
+ alpha += 2;
}
- query_font (fid);
- return fid;
-}
+ alpha -= 2;
+ memset (alpha, 0, pad0); // Bottom row.
+ glGenTextures (1, &gc->clip_mask);
+ tex_parameters (dpy, gc->clip_mask);
-XFontStruct *
-XLoadQueryFont (Display *dpy, const char *name)
-{
- Font fid = XLoadFont (dpy, name);
- if (!fid) return 0;
- return XQueryFont (dpy, fid);
-}
-
-int
-XUnloadFont (Display *dpy, Font fid)
-{
- if (--fid->refcount < 0) abort();
- if (fid->refcount > 0) return 0;
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
- if (fid->native_font)
- jwxyz_release_native_font (fid->dpy, fid->native_font);
+ unsigned tex_w = frame->width + 2, tex_h = frame->height + 2;
+ tex_image (dpy, GL_ALPHA, &tex_w, &tex_h, GL_ALPHA, GL_UNSIGNED_BYTE,
+ alpha0);
- if (fid->ps_name)
- free (fid->ps_name);
- if (fid->metrics.per_char)
- free (fid->metrics.per_char);
-
- // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
- // crashes in [NSFont ascender] <- query_font, and it seems to go away
- // if I never release the nsfont. So, fuck it, we'll just leak fonts.
- // They're probably not very big...
- //
- // [fid->nsfont release];
- // CFRelease (fid->nsfont);
-
- free (fid);
- return 0;
-}
+ free(data);
-int
-XFreeFontInfo (char **names, XFontStruct *info, int n)
-{
- int i;
- if (names) {
- for (i = 0; i < n; i++)
- if (names[i]) free (names[i]);
- free (names);
- }
- if (info) {
- for (i = 0; i < n; i++)
- if (info[i].per_char) {
- free (info[i].per_char);
- free (info[i].properties);
- }
- free (info);
- }
return 0;
}
-int
-XFreeFont (Display *dpy, XFontStruct *f)
+static int
+SetClipOrigin (Display *dpy, GC gc, int x, int y)
{
- Font fid = f->fid;
- XFreeFontInfo (0, f, 1);
- XUnloadFont (dpy, fid);
+ gc->gcv.clip_x_origin = x;
+ gc->gcv.clip_y_origin = y;
return 0;
}
-
-int
-XSetFont (Display *dpy, GC gc, Font fid)
+void set_points_list(XPoint *points, int npoints, linked_point *root)
{
- Font font2 = copy_font (fid);
- if (gc->gcv.font)
- XUnloadFont (dpy, gc->gcv.font);
- gc->gcv.font = font2;
- return 0;
-}
+ linked_point *current;
-
-XFontSet
-XCreateFontSet (Display *dpy, char *name,
- char ***missing_charset_list_return,
- int *missing_charset_count_return,
- char **def_string_return)
-{
- char *name2 = strdup (name);
- char *s = strchr (name, ',');
- if (s) *s = 0;
- XFontSet set = 0;
- XFontStruct *f = XLoadQueryFont (dpy, name2);
- if (f)
- {
- set = (XFontSet) calloc (1, sizeof(*set));
- set->font = f;
+ current = root;
+ for (int i = 0; i < npoints - 2 ; i++) {
+ current->x = points[i].x;
+ current->y = points[i].y;
+ current->next = (linked_point *) malloc(sizeof(linked_point));
+ current = current->next;
}
- free (name2);
- if (missing_charset_list_return) *missing_charset_list_return = 0;
- if (missing_charset_count_return) *missing_charset_count_return = 0;
- if (def_string_return) *def_string_return = 0;
- return set;
+ current->x = points[npoints-2].x;
+ current->y = points[npoints-2].y;
+ current->next = root;
}
-void
-XFreeFontSet (Display *dpy, XFontSet set)
+double compute_edge_length(linked_point * a, linked_point * b)
{
- XFreeFont (dpy, set->font);
- free (set);
-}
+ int xdiff, ydiff, xsq, ysq, added;
+ double xy_add, edge_length;
-const char *
-jwxyz_nativeFontName (Font f, float *size)
-{
- if (size) *size = f->size;
- return f->ps_name;
-}
-
-
-void
-XFreeStringList (char **list)
-{
- int i;
- if (!list) return;
- for (i = 0; list[i]; i++)
- XFree (list[i]);
- XFree (list);
+ xdiff = a->x - b->x;
+ ydiff = a->y - b->y;
+ xsq = xdiff * xdiff;
+ ysq = ydiff * ydiff;
+ added = xsq + ysq;
+ xy_add = (double) added;
+ edge_length = sqrt(xy_add);
+ return edge_length;
}
-
-// Given a UTF8 string, return an NSString. Bogus UTF8 characters are ignored.
-// We have to do this because stringWithCString returns NULL if there are
-// any invalid characters at all.
-//
-/* TODO
-static NSString *
-sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP)
+double get_angle(double a, double b, double c)
{
- int out_len = in_len * 4; // length of string might increase
- char *s2 = (char *) malloc (out_len);
- char *out = s2;
- const char *in_end = in + in_len;
- const char *out_end = out + out_len;
- Bool latin1_p = True;
-
- while (in < in_end)
- {
- unsigned long uc;
- long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc);
- long L2 = utf8_encode (uc, out, out_end - out);
- in += L1;
- out += L2;
- if (uc > 255) latin1_p = False;
- }
- *out = 0;
- NSString *nsstr =
- [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
- free (s2);
- if (latin1_pP) *latin1_pP = latin1_p;
- return (nsstr ? nsstr : @"");
+ double cos_a, i_cos_a;
+ cos_a = (((b * b) + (c * c)) - (a * a)) / (double) (2.0 * b * c);
+ i_cos_a = acos(cos_a);
+ return i_cos_a;
}
-*/
-int
-XTextExtents (XFontStruct *f, const char *s, int length,
- int *dir_ret, int *ascent_ret, int *descent_ret,
- XCharStruct *cs)
-{
- // Unfortunately, adding XCharStructs together to get the extents for a
- // string doesn't work: Cocoa uses non-integral character advancements, but
- // XCharStruct.width is an integer. Plus that doesn't take into account
- // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
- // Zapfino.
-
- Font ff = f->fid;
- Display *dpy = ff->dpy;
- jwxyz_render_text (dpy, ff->native_font, s, length, GL_FALSE, cs, 0);
- *dir_ret = 0;
- *ascent_ret = f->ascent;
- *descent_ret = f->descent;
- return 0;
-}
-int
-XTextWidth (XFontStruct *f, const char *s, int length)
+Bool is_same_slope(linked_point * a)
{
- int ascent, descent, dir;
- XCharStruct cs;
- XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
- return cs.width;
-}
+ int abx, bcx, aby, bcy, aa, bb;
+ linked_point *b;
+ linked_point *c;
-int
-XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
- int *dir_ret, int *ascent_ret, int *descent_ret,
- XCharStruct *cs)
-{
- // Bool latin1_p = True;
- int i, utf8_len = 0;
- char *utf8 = XChar2b_to_utf8 (s, &utf8_len); // already sanitized
+ b = a->next;
+ c = b->next;
- for (i = 0; i < length; i++)
- if (s[i].byte1 > 0) {
- // latin1_p = False;
- break;
+ // test if slopes are indefinite for both line segments
+ if (a->x == b->x) {
+ return b->x == c->x;
+ } else if (b->x == c->x) {
+ return False; // false, as ax/bx is not indefinite
}
- {
- Font ff = f->fid;
- Display *dpy = ff->dpy;
- jwxyz_render_text (dpy, ff->native_font, utf8, strlen(utf8),
- GL_TRUE, cs, 0);
- }
+ abx = a->x - b->x;
+ bcx = b->x - c->x;
+ aby = a->y - b->y;
+ bcy = b->y - c->y;
+ aa = abx * bcy;
+ bb = bcx * aby;
- *dir_ret = 0;
- *ascent_ret = f->ascent;
- *descent_ret = f->descent;
- free (utf8);
- return 0;
+ return aa == bb;
}
+void draw_three_vertices(Display *dpy, linked_point * a, Bool triangle)
+{
-/* "Returns the distance in pixels in the primary draw direction from
- the drawing origin to the origin of the next character to be drawn."
-
- "overall_ink_return is set to the bbox of the string's character ink."
-
- "The overall_ink_return for a nondescending, horizontally drawn Latin
- character is conventionally entirely above the baseline; that is,
- overall_ink_return.height <= -overall_ink_return.y."
-
- [So this means that y is the top of the ink, and height grows down:
- For above-the-baseline characters, y is negative.]
-
- "The overall_ink_return for a nonkerned character is entirely at, and to
- the right of, the origin; that is, overall_ink_return.x >= 0."
-
- [So this means that x is the left of the ink, and width grows right.
- For left-of-the-origin characters, x is negative.]
+ linked_point *b;
+ linked_point *c;
+ GLenum drawType;
- "A character consisting of a single pixel at the origin would set
- overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
- */
-int
-Xutf8TextExtents (XFontSet set, const char *str, int len,
- XRectangle *overall_ink_return,
- XRectangle *overall_logical_return)
-{
-#if 0
- Bool latin1_p;
- NSString *nsstr = sanitize_utf8 (str, len, &latin1_p);
- XCharStruct cs;
+ b = a->next;
+ c = b->next;
- utf8_metrics (set->font->fid, nsstr, &cs);
+ GLfloat vertices[3][2] = {
+ {a->x, a->y},
+ {b->x, b->y},
+ {c->x, c->y}
+ };
- /* "The overall_logical_return is the bounding box that provides minimum
- spacing to other graphical features for the string. Other graphical
- features, for example, a border surrounding the text, should not
- intersect this rectangle."
+ if (triangle) {
+ drawType = GL_TRIANGLES;
+ } else {
+ drawType = GL_LINES;
+ }
- So I think that means they're the same? Or maybe "ink" is the bounding
- box, and "logical" is the advancement? But then why is the return value
- the advancement?
- */
- if (overall_ink_return)
- XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
- if (overall_logical_return)
- XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ vertex_pointer(dpy, GL_FLOAT, 0, vertices);
+ glDrawArrays(drawType, 0, 3);
- return cs.width;
-#endif
- abort();
+ free(b); // cut midpoint off from remaining polygon vertex list
+ a->next = c;
}
-static int
-draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
- const char *str, size_t len, GLboolean utf8)
+Bool is_an_ear(linked_point * a)
{
- Font ff = gc->gcv.font;
- XCharStruct cs;
+ double edge_ab, edge_bc, edge_ac;
+ double angle_a, angle_b, angle_c;
+ double my_pi;
+ linked_point *b, *c;
- char *data = 0;
- jwxyz_render_text (dpy, ff->native_font, str, len, utf8, &cs, &data);
- int w = cs.rbearing - cs.lbearing;
- int h = cs.ascent + cs.descent;
+ b = a->next;
+ c = b->next;
+ my_pi = (double) M_PI;
- if (w < 0 || h < 0) abort();
- if (w == 0 || h == 0) {
- if (data) free(data);
- return 0;
- }
-
- XImage *img = XCreateImage (dpy, dpy->screen->visual, 32,
- ZPixmap, 0, data, w, h, 0, 0);
-
- /* The image of text is a 32-bit image, in white.
- Take the red channel for intensity and use that as alpha.
- replace RGB with the GC's foreground color.
- This expects that XPutImage respects alpha and only writes
- the bits that are not masked out.
- This also assumes that XPutImage expects ARGB.
- */
- {
- char *s = data;
- char *end = s + (w * h * 4);
- uint8_t rgba[4];
- jwxyz_query_color (dpy, gc->gcv.foreground, rgba);
- while (s < end) {
-
- s[3] = s[1];
- s[0] = rgba[0];
- s[1] = rgba[1];
- s[2] = rgba[2];
- s += 4;
- }
- }
-
- XPutImage (dpy, d, gc, img, 0, 0,
- x + cs.lbearing,
- y - cs.ascent,
- w, h);
- XDestroyImage (img);
-
- return 0;
-}
+ edge_ab = compute_edge_length(a, b);
+ edge_bc = compute_edge_length(b, c);
+ edge_ac = compute_edge_length(a, c);
+ angle_a = get_angle(edge_bc, edge_ab, edge_ac);
+ angle_b = get_angle(edge_ac, edge_ab, edge_bc);
+ angle_c = get_angle(edge_ab, edge_ac, edge_bc);
-int
-XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
- const char *str, int len)
-{
- return draw_string (dpy, d, gc, x, y, str, len, GL_FALSE);
+ return angle_a < my_pi && angle_b < my_pi && angle_c < my_pi;
}
-int
-XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
- const XChar2b *str, int len)
+Bool is_three_point_loop(linked_point * head)
{
- XChar2b *b2 = malloc ((len + 1) * sizeof(*b2));
- char *s2;
- int ret;
- memcpy (b2, str, len * sizeof(*b2));
- b2[len].byte1 = b2[len].byte2 = 0;
- s2 = XChar2b_to_utf8 (b2, 0);
- free (b2);
- ret = draw_string (dpy, d, gc, x, y, s2, strlen(s2), GL_TRUE);
- free (s2);
- return ret;
+ return head->x == head->next->next->next->x
+ && head->y == head->next->next->next->y;
}
-void
-Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
- int x, int y, const char *str, int len)
+void traverse_points_list(Display *dpy, linked_point * root)
{
- draw_string (dpy, d, gc, x, y, str, len, GL_TRUE);
-}
+ linked_point *head;
+ head = root;
+ while (!is_three_point_loop(head)) {
+ if (is_an_ear(head)) {
+ draw_three_vertices(dpy, head, True);
+ } else if (is_same_slope(head)) {
+ draw_three_vertices(dpy, head, False);
+ } else {
+ head = head->next;
+ }
+ }
-int
-XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
- const char *str, int len)
-{
- int ascent, descent, dir;
- XCharStruct cs;
- XTextExtents (&gc->gcv.font->metrics, str, len,
- &dir, &ascent, &descent, &cs);
- jwxyz_fill_rect (dpy, d, gc,
- x + MIN (0, cs.lbearing),
- y - MAX (0, ascent),
- MAX (MAX (0, cs.rbearing) -
- MIN (0, cs.lbearing),
- cs.width),
- MAX (0, ascent) + MAX (0, descent),
- gc->gcv.background);
- return XDrawString (dpy, d, gc, x, y, str, len);
-}
+ // handle final three vertices in polygon
+ if (is_an_ear(head)) {
+ draw_three_vertices(dpy, head, True);
+ } else if (is_same_slope(head)) {
+ draw_three_vertices(dpy, head, False);
+ } else {
+ free(head->next->next);
+ free(head->next);
+ free(head);
+ Assert (False, "traverse_points_list: unknown configuration");
+ }
+ free(head->next);
+ free(head);
+}
-int
-XSetClipMask (Display *dpy, GC gc, Pixmap m)
-{
-//#### abort();
-/*
- TODO
- Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
+const struct jwxyz_vtbl gl_vtbl = {
+ root,
+ visual,
+ display_sources_data,
- if (gc->gcv.clip_mask) {
- XFreePixmap (dpy, gc->gcv.clip_mask);
- CGImageRelease (gc->clip_mask);
- }
+ window_background,
+ draw_arc,
+ fill_rects,
+ gc_gcv,
+ gc_depth,
+ jwxyz_draw_string,
- gc->gcv.clip_mask = copy_pixmap (dpy, m);
- if (gc->gcv.clip_mask)
- gc->clip_mask =
- CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
- else
- gc->clip_mask = 0;
-*/
-
- return 0;
-}
+ jwxyz_gl_copy_area,
-int
-XSetClipOrigin (Display *dpy, GC gc, int x, int y)
-{
- gc->gcv.clip_x_origin = x;
- gc->gcv.clip_y_origin = y;
- return 0;
-}
+ DrawPoints,
+ DrawSegments,
+ CreateGC,
+ FreeGC,
+ ClearWindow,
+ SetClipMask,
+ SetClipOrigin,
+ FillPolygon,
+ DrawLines,
+ PutImage,
+ GetSubImage
+};
#endif /* JWXYZ_GL -- entire file */