summaryrefslogtreecommitdiffstats
path: root/common/glc.c.save
diff options
context:
space:
mode:
Diffstat (limited to 'common/glc.c.save')
-rw-r--r--common/glc.c.save1413
1 files changed, 1413 insertions, 0 deletions
diff --git a/common/glc.c.save b/common/glc.c.save
new file mode 100644
index 00000000..19581105
--- /dev/null
+++ b/common/glc.c.save
@@ -0,0 +1,1413 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#ifdef WIN32
+#include "glext.h"
+#include "wglext.h"
+#endif
+
+#include "glc.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define ASSERT(x) if (!(x)) {printf("%s: assert failed %s\n", __FUNCTION__, #x); for (;;);}
+
+#define GLC_ERROR_TETS { \
+ GLenum gl_err; glFlush(); \
+ if ((gl_err = glGetError()) != GL_NO_ERROR) { \
+ printf("%s[%d]: opengl error: %s\n", __FUNCTION__, __LINE__, gluErrorString(gl_err)); \
+ for(;;); \
+ } \
+}
+
+#define WARN_ONCE(x) { \
+ static int warn = TRUE; \
+ if (warn) { \
+ printf x; \
+ warn = FALSE; \
+ } \
+}
+
+#define TESS_VERTEX_ALLOC_BUNCH 20
+
+typedef struct InternaCtx InternaCtx;
+typedef struct InternalPat {
+ InternaCtx *owner;
+ int refs;
+ GLuint texture;
+ int x_orign;
+ int y_orign;
+ int width;
+ int height;
+} InternalPat;
+
+typedef struct Pathpath {
+ int start_point;
+ int num_segments;
+} Path;
+
+enum {
+ GLC_PATH_SEG_LINES,
+ GLC_PATH_SEG_BEIZER,
+};
+
+//todo: flatten cache
+typedef struct PathSegment {
+ int type;
+ int count;
+} PathSegment;
+
+typedef struct PathPoint {
+ double x;
+ double y;
+ double z;
+} PathPoint;
+
+typedef GLdouble Vertex[3];
+
+typedef struct InternalPath {
+ InternaCtx *owner;
+
+ Path *paths;
+ int paths_size;
+ int paths_pos;
+
+ PathSegment *segments;
+ int segments_size;
+ int segments_pos;
+
+ PathPoint *points;
+ int points_size;
+ int points_pos;
+
+ Path *current_path;
+ PathSegment *current_segment;
+
+} InternalPath;
+
+typedef struct TassVertex TassVertex;
+struct TassVertex {
+ PathPoint point;
+ TassVertex *list_link;
+ TassVertex *next;
+};
+
+typedef struct TassVertexBuf TassVertexBuf;
+struct TassVertexBuf {
+ TassVertexBuf *next;
+ TassVertex vertexs[0];
+};
+
+struct InternaCtx {
+ int draw_mode;
+ int stencil_refs;
+ int stencil_mask;
+ int width;
+ int height;
+ GLfloat line_width;
+ InternalPat *pat;
+ int max_texture_size;
+ GLUtesselator* tesselator;
+ TassVertex *free_tess_vertex;
+ TassVertex *used_tess_vertex;
+ TassVertexBuf *vertex_bufs;
+#ifdef WIN32
+ PFNGLBLENDEQUATIONPROC glBlendEquation;
+#endif
+};
+
+#define Y(y) -(y)
+#define VERTEX2(x, y) glVertex2d(x, Y(y))
+
+static void fill_rect(InternaCtx *ctx, void *rect);
+static void fill_path(InternaCtx *ctx, void *path);
+static void fill_mask(InternaCtx *ctx, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap);
+static void set_pat(InternaCtx *ctx, InternalPat *pat);
+
+static inline void *zmalloc(size_t size)
+{
+ return calloc(1, size);
+}
+
+static inline void set_raster_pos(InternaCtx *ctx, int x, int y)
+{
+ if (x >= 0 && y >= 0 && x < ctx->width && y < ctx->height) {
+ glRasterPos2i(x, Y(y));
+ return;
+ }
+ glRasterPos2i(0, 0);
+ glBitmap(0, 0, 0, 0, (GLfloat)x, (GLfloat)Y(y), NULL);
+}
+
+static TassVertex *alloc_tess_vertex(InternaCtx *ctx)
+{
+ TassVertex *vertex;
+
+ if (!ctx->free_tess_vertex) {
+ TassVertexBuf *buf;
+ int i;
+
+ if (!(buf = (TassVertexBuf *)malloc(sizeof(TassVertexBuf) +
+ sizeof(TassVertex) * TESS_VERTEX_ALLOC_BUNCH))) {
+ //warn
+ return NULL;
+ }
+ buf->next = ctx->vertex_bufs;
+ ctx->vertex_bufs = buf;
+ for (i = 0; i < TESS_VERTEX_ALLOC_BUNCH; i++) {
+ buf->vertexs[i].point.z = 0;
+ buf->vertexs[i].next = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = &buf->vertexs[i];
+ }
+ }
+
+ vertex = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = vertex->next;
+ vertex->next = ctx->used_tess_vertex;
+ ctx->used_tess_vertex = vertex;
+ return vertex;
+}
+
+static void reset_tass_vertex(InternaCtx *ctx)
+{
+ TassVertex *vertex;
+ while ((vertex = ctx->used_tess_vertex)) {
+ ctx->used_tess_vertex = vertex->next;
+ vertex->next = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = vertex;
+ }
+}
+
+static void free_tass_vertex_bufs(InternaCtx *ctx)
+{
+ TassVertexBuf *buf;
+
+ ctx->used_tess_vertex = NULL;
+ ctx->free_tess_vertex = NULL;
+ while ((buf = ctx->vertex_bufs)) {
+ ctx->vertex_bufs = buf->next;
+ free(buf);
+ }
+}
+
+//naiev bezier flattener
+static TassVertex *bezier_flattener(InternaCtx *ctx, PathPoint *points)
+{
+ double ax, bx, cx;
+ double ay, by, cy;
+ const int num_points = 30;
+ double dt;
+ int i;
+
+ TassVertex *vertex_list = NULL;
+ TassVertex *curr_vertex;
+
+ for (i = 0; i < num_points - 2; i++) {
+ TassVertex *vertex;
+
+ if (!(vertex = alloc_tess_vertex(ctx))) {
+ //warn
+ return NULL;
+ }
+ vertex->list_link = vertex_list;
+ vertex_list = vertex;
+ }
+
+ curr_vertex = vertex_list;
+
+ cx = 3.0 * (points[1].x - points[0].x);
+ bx = 3.0 * (points[2].x - points[1].x) - cx;
+ ax = points[3].x - points[0].x - cx - bx;
+
+ cy = 3.0 * (points[1].y - points[0].y);
+ by = 3.0 * (points[2].y - points[1].y) - cy;
+ ay = points[3].y - points[0].y - cy - by;
+
+ dt = 1.0 / ( num_points - 1 );
+
+ for( i = 1; i < num_points - 1; i++, curr_vertex = curr_vertex->list_link) {
+ double tSquared, tCubed;
+ double t;
+ t = i * dt;
+
+ tSquared = t * t;
+ tCubed = tSquared * t;
+
+ curr_vertex->point.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + points[0].x;
+ curr_vertex->point.y = (ay * tCubed) + (by * tSquared) + (cy * t) + points[0].y;
+ }
+
+ return vertex_list;
+}
+
+#define MORE_X(path, Type, name) {\
+ Type *name;\
+ \
+ if (!(name = (Type *)zmalloc(sizeof(*name) * path->name##_size * 2))) {\
+ return FALSE;\
+ }\
+ memcpy(name, path->name, sizeof(*name) * path->name##_size);\
+ free(path->name);\
+ path->name = name;\
+ path->name##_size *= 2;\
+ return TRUE;\
+}
+
+static int more_points(InternalPath *path)
+{
+ MORE_X(path, PathPoint, points);
+}
+
+static int more_segments(InternalPath *path)
+{
+ MORE_X(path, PathSegment, segments);
+}
+
+static int more_paths(InternalPath *path)
+{
+ MORE_X(path, Path, paths);
+}
+
+static inline void put_point(InternalPath *path, double x, double y)
+{
+ path->points[path->points_pos].x = x;
+ path->points[path->points_pos++].y = Y(y + 0.5);
+ path->points[path->points_pos].z = 0;
+}
+
+void glc_path_move_to(GLCPath path, double x, double y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (internal->current_segment) {
+ internal->current_segment = NULL;
+ internal->current_path = NULL;
+ if (internal->points_pos == internal->points_size && !more_points(internal)) {
+ //warn
+ return;
+ }
+ internal->points_pos++;
+ }
+ internal->points[internal->points_pos - 1].x = x;
+ internal->points[internal->points_pos - 1].y = Y(y + 0.5);
+ internal->points[internal->points_pos - 1].z = 0;
+}
+
+static int add_segment_common(InternalPath *internal, int type, int num_points)
+{
+ if (internal->points_size - internal->points_pos < num_points && !more_points(internal)) {
+ //warn
+ return FALSE;
+ }
+
+ if (internal->current_segment) {
+ if (internal->current_segment->type == type) {
+ internal->current_segment->count++;
+ return TRUE;
+ }
+ if (internal->segments_pos == internal->segments_size && !more_segments(internal)) {
+ //warn
+ return FALSE;
+ }
+ internal->current_segment = &internal->segments[internal->segments_pos++];
+ internal->current_segment->type = type;
+ internal->current_segment->count = 1;
+ internal->current_path->num_segments++;
+ return TRUE;
+ }
+
+ if (internal->paths_pos == internal->paths_size && !more_paths(internal)) {
+ //warn
+ return FALSE;
+ }
+
+ if (internal->segments_pos == internal->segments_size && !more_segments(internal)) {
+ //warn
+ return FALSE;
+ }
+
+ internal->current_path = &internal->paths[internal->paths_pos++];
+ internal->current_path->start_point = internal->points_pos - 1;
+ internal->current_path->num_segments = 1;
+ internal->current_segment = &internal->segments[internal->segments_pos++];
+ internal->current_segment->type = type;
+ internal->current_segment->count = 1;
+ return TRUE;
+}
+
+void glc_path_line_to(GLCPath path, double x, double y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (!add_segment_common(internal, GLC_PATH_SEG_LINES, 1)) {
+ return;
+ }
+ put_point(internal, x, y);
+}
+
+void glc_path_curve_to(GLCPath path, double p1_x, double p1_y, double p2_x, double p2_y,
+ double p3_x, double p3_y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (!add_segment_common(internal, GLC_PATH_SEG_BEIZER, 3)) {
+ return;
+ }
+ put_point(internal, p1_x, p1_y);
+ put_point(internal, p2_x, p2_y);
+ put_point(internal, p3_x, p3_y);
+}
+
+void glc_path_close(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+ if (!internal->current_path) {
+ return;
+ }
+ PathPoint *end_point = &internal->points[internal->current_path->start_point];
+ glc_path_line_to(path, end_point->x, Y(end_point->y));
+ glc_path_move_to(path, end_point->x, Y(end_point->y));
+}
+
+void glc_path_cleare(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+ internal->paths_pos = internal->segments_pos = 0;
+ internal->current_segment = NULL;
+ internal->current_path = NULL;
+
+ internal->points[0].x = 0;
+ internal->points[0].y = 0;
+ internal->points_pos = 1;
+}
+
+GLCPath glc_path_create(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPath *path;
+
+ ASSERT(ctx);
+ if (!(path = (InternalPath *)zmalloc(sizeof(*path)))) {
+ return NULL;
+ }
+
+ path->paths = (Path *)malloc(sizeof(*path->paths) * (path->paths_size = 2));
+ if (!path->paths) {
+ goto error_1;
+ }
+
+ path->segments = (PathSegment *)malloc(sizeof(*path->segments) * (path->segments_size = 4));
+ if (!path->segments) {
+ goto error_2;
+ }
+
+ path->points = (PathPoint *)malloc(sizeof(*path->points) * (path->points_size = 20));
+ if (!path->points) {
+ goto error_3;
+ }
+
+ path->owner = ctx;
+ path->points_pos = 1;
+ return path;
+
+error_3:
+ free(path->segments);
+
+error_2:
+ free(path->paths);
+
+error_1:
+ free(path);
+
+ return NULL;
+}
+
+void glc_path_destroy(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ if (!path) {
+ return;
+ }
+
+ free(internal->points);
+ free(internal->segments);
+ free(internal->paths);
+ free(internal);
+}
+
+static inline void unref_pat(InternalPat *pat)
+{
+ if (!pat) {
+ return;
+ }
+ ASSERT(pat->refs > 0);
+ if (--pat->refs == 0) {
+ glFinish();
+ glDeleteTextures(1, &pat->texture);
+ free(pat);
+ }
+ GLC_ERROR_TETS;
+}
+
+static inline InternalPat *ref_pat(InternalPat *pat)
+{
+ pat->refs++;
+ return pat;
+}
+
+#ifdef WIN32
+static inline int find_msb(uint32_t val)
+{
+ uint32_t r;
+ __asm {
+ bsr eax, val
+ jnz found
+ mov eax, -1
+
+ found:
+ mov r, eax
+ }
+ return r + 1;
+}
+#else
+static inline int find_msb(uint32_t val)
+{
+ int ret;
+
+ asm("bsrl %1,%0\n\t"
+ "jnz 1f\n\t"
+ "movl $-1,%0\n"
+ "1:"
+ : "=r"(ret) : "r"(val));
+ return ret + 1;
+}
+#endif
+
+static int to_pwoer_two(uint32_t val)
+{
+ if ((val & (val - 1)) == 0) {
+ return val;
+ }
+ return 1 << find_msb(val);
+}
+
+static void scale(uint32_t *dest, uint32_t dest_width, uint32_t dest_height,
+ uint32_t *src, uint32_t src_width, uint32_t src_height, int src_stride)
+{
+ double x_scale = (double)src_width / dest_width;
+ double y_scale = (double)src_height / dest_height;
+ uint32_t i;
+ uint32_t j;
+ int prev_row = -1;
+
+ for (i = 0; i < dest_height; i++) {
+ int row = (int)(y_scale * i);
+ if (row == prev_row) {
+ memcpy(dest, dest - dest_width, dest_width * sizeof(uint32_t));
+ dest += dest_width;
+ continue;
+ }
+ for (j = 0; j < dest_width; j++) {
+ int col = (int)(x_scale * j);
+ *(dest++) = *(src + col);
+ }
+ prev_row = row;
+ src = (uint32_t *)((uint8_t *)src + src_stride);
+ }
+}
+
+static inline void init_pattern(InternalPat *pat, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternaCtx *ctx = pat->owner;
+ uint32_t *tmp_pixmap = NULL;
+ int width;
+ int height;
+ int width2;
+ int height2;
+
+ const int pix_bytes = 4;
+
+ ASSERT(image->format == GLC_IMAGE_RGB32); //for now
+
+ width = image->width;
+ height = image->height;
+ width2 = to_pwoer_two(width);
+ height2 = to_pwoer_two(height);
+
+ ASSERT(width > 0 && height > 0);
+ ASSERT(width > 0 && width <= pat->owner->max_texture_size);
+ ASSERT(height > 0 && height <= pat->owner->max_texture_size);
+
+ if (width2 != width || height2 != height) {
+ if (!(tmp_pixmap = (uint32_t *)malloc(width2 * height2 * sizeof(uint32_t)))) {
+ //warn
+ return;
+ }
+ scale(tmp_pixmap, width2, height2, (uint32_t *)image->pixels, width, height, image->stride);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, pat->texture);
+
+ //glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ if (tmp_pixmap) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, width2);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, width2, height2, 0, GL_BGRA, GL_UNSIGNED_BYTE,
+ tmp_pixmap);
+ free(tmp_pixmap);
+ } else {
+ ASSERT(image->stride % pix_bytes == 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, image->stride / pix_bytes);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE,
+ image->pixels);
+ }
+
+ GLC_ERROR_TETS;
+ pat->x_orign = x_orign % width;
+ pat->y_orign = y_orign % height;
+ pat->width = width;
+ pat->height = height;
+
+ if (ctx->pat == pat) {
+ set_pat(pat->owner, pat);
+ } else if (ctx->pat) {
+ glBindTexture(GL_TEXTURE_2D, ctx->pat->texture);
+ }
+}
+
+GLCPattern glc_pattern_create(GLCCtx glc, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPat *pat;
+
+ ASSERT(ctx && image);
+
+ if (!(pat = (InternalPat *)zmalloc(sizeof(*pat)))) {
+ return NULL;
+ }
+ pat->refs = 1;
+ pat->owner = ctx;
+ glGenTextures(1, &pat->texture);
+ init_pattern(pat, x_orign, y_orign, image);
+ return pat;
+}
+
+void glc_pattern_set(GLCPattern pattern, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternalPat *pat = (InternalPat *)pattern;
+ ASSERT(pat && pat->owner);
+
+ glFinish();
+ init_pattern(pat, x_orign, y_orign, image);
+}
+
+void glc_pattern_destroy(GLCPattern pat)
+{
+ unref_pat((InternalPat *)pat);
+ GLC_ERROR_TETS;
+}
+
+static void set_pat(InternaCtx *ctx, InternalPat *pat)
+{
+ pat = ref_pat(pat);
+ unref_pat(ctx->pat);
+ ctx->pat = pat;
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, pat->texture);
+
+ GLfloat s_gen_params[] = { (GLfloat)1.0 / pat->width, 0, 0, 0 };
+ GLfloat t_gen_params[] = { 0, (GLfloat)1.0 / (GLfloat)pat->height, 0, 0 };
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen_params);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen_params);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef((float)pat->x_orign / pat->width, (float)Y(pat->y_orign) / pat->height, 0);
+ GLC_ERROR_TETS;
+}
+
+void glc_set_pattern(GLCCtx glc, GLCPattern pattern)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPat *pat = (InternalPat *)pattern;
+
+ ASSERT(ctx && pat && pat->owner == ctx);
+ set_pat(ctx, pat);
+}
+
+void glc_set_rgb(GLCCtx glc, double red, double green, double blue)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+
+ glDisable(GL_TEXTURE_2D);
+ unref_pat(ctx->pat);
+ ctx->pat = NULL;
+ glColor4d(red, green, blue, 1);
+ GLC_ERROR_TETS;
+}
+
+void glc_set_op(GLCCtx glc, GLCOp op)
+{
+ if (op == GL_COPY) {
+ glDisable(GL_COLOR_LOGIC_OP);
+ return;
+ }
+ glLogicOp(op);
+ glEnable(GL_COLOR_LOGIC_OP);
+}
+
+void glc_set_line_width(GLCCtx glc, double width)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ ctx->line_width = (GLfloat)width;
+ if (ctx->line_width > 0) {
+ glLineWidth(ctx->line_width);
+ } else {
+ ctx->line_width = 0;
+ }
+ GLC_ERROR_TETS;
+}
+
+void glc_set_fill_mode(GLCCtx glc, GLCFillMode fill_mode)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ int mode;
+ switch (fill_mode) {
+ case GLC_FILL_MODE_WINDING_ODD:
+ mode = GLU_TESS_WINDING_ODD;
+ break;
+ case GLC_FILL_MODE_WINDING_NONZERO:
+ mode = GLU_TESS_WINDING_NONZERO;
+ break;
+ default:
+ //warn
+ return;
+ }
+ gluTessProperty(ctx->tesselator, GLU_TESS_WINDING_RULE, mode);
+}
+
+static inline void add_stencil_client(InternaCtx *ctx)
+{
+ if (!ctx->stencil_refs) {
+ glEnable(GL_STENCIL_TEST);
+ }
+ ctx->stencil_refs++;
+}
+
+static inline void remove_stencil_client(InternaCtx *ctx)
+{
+ ctx->stencil_refs--;
+ if (!ctx->stencil_refs) {
+ glDisable(GL_STENCIL_TEST);
+ }
+}
+
+void glc_set_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ ASSERT(ctx && bitmap);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glDisable(GL_BLEND);
+
+ if (!(ctx->stencil_mask & mask)) {
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= mask;
+ }
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+ glStencilMask(mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glStencilFunc(GL_ALWAYS, mask, mask);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ fill_mask(ctx, x_dest, y_dest, width, height, stride, bitmap);
+}
+
+
+void glc_mask_rects(GLCCtx glc, int num_rect, GLCRect *rects, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ GLCRect *end;
+ ASSERT(ctx && rects);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glDisable(GL_BLEND);
+
+ if (!(ctx->stencil_mask & mask)) {
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= mask;
+ }
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+ glStencilMask(mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glStencilFunc(GL_ALWAYS, mask, mask);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ end = rects + num_rect;
+ for (; rects < end; rects++) {
+ fill_rect(ctx, rects);
+ }
+}
+
+void glc_clear_mask(GLCCtx glc, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ ASSERT(ctx);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if ((ctx->stencil_mask & mask)) {
+ ctx->stencil_mask &= ~mask;
+ remove_stencil_client(ctx);
+ }
+}
+
+void glc_clip_reset(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ if (!(ctx->stencil_mask & 0x03)) {
+ return;
+ }
+ remove_stencil_client(ctx);
+ ctx->stencil_mask &= ~0x03;
+ glStencilMask(0x03);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ GLC_ERROR_TETS;
+}
+
+static void clip_common(InternaCtx *ctx, GLCClipOp op, void (*fill_func)(InternaCtx *, void *),
+ void *data)
+{
+ int stencil_val;
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ glDisable(GL_BLEND);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+
+ if (op == GLC_CLIP_OP_SET) {
+ glc_clip_reset(ctx);
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= 0x01;
+ } else if (!(ctx->stencil_mask & 0x03)) {
+ GLCRect area;
+ if (op == GLC_CLIP_OP_OR) {
+ return;
+ }
+ area.x = area.y = 0;
+ area.width= ctx->width;
+ area.height = ctx->height;
+ clip_common(ctx, GLC_CLIP_OP_SET, fill_rect, &area);
+ }
+ glStencilMask(0x03);
+ switch (op) {
+ case GLC_CLIP_OP_SET:
+ case GLC_CLIP_OP_OR:
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_ALWAYS, stencil_val, stencil_val);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ fill_func(ctx, data);
+ break;
+ case GLC_CLIP_OP_AND: {
+ int clear_mask;
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_EQUAL, stencil_val, stencil_val);
+ if (stencil_val == 0x01) {
+ glStencilOp(GL_ZERO, GL_INCR, GL_INCR);
+ stencil_val = 0x02;
+ clear_mask = 0x01;
+ } else {
+ glStencilOp(GL_ZERO, GL_DECR, GL_DECR);
+ stencil_val = 0x01;
+ clear_mask = 0x02;
+ }
+ fill_func(ctx, data);
+
+ glStencilMask(clear_mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ ctx->stencil_mask = (ctx->stencil_mask & ~clear_mask) |stencil_val;
+ break;
+ }
+ case GLC_CLIP_OP_EXCLUDE:
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_EQUAL, stencil_val, stencil_val);
+ glStencilOp(GL_KEEP, GL_ZERO, GL_ZERO);
+ fill_func(ctx, data);
+ break;
+ }
+ GLC_ERROR_TETS;
+}
+
+void glc_clip_rect(GLCCtx glc, const GLCRect *rect, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && rect);
+ clip_common(ctx, op, fill_rect, (void *)rect);
+}
+
+void glc_clip_path(GLCCtx glc, GLCPath path, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && path);
+ clip_common(ctx, op, fill_path, path);
+}
+
+typedef struct FillMaskInfo {
+ int x_dest;
+ int y_dest;
+ int width;
+ int height;
+ int stride;
+ const uint8_t *bitmap;
+} FillMaskInfo;
+
+static void __fill_mask(InternaCtx *ctx, void *data)
+{
+ FillMaskInfo *info = (FillMaskInfo *)data;
+ fill_mask(ctx, info->x_dest, info->y_dest, info->width, info->height, info->stride,
+ info->bitmap);
+}
+
+void glc_clip_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ FillMaskInfo mask_info;
+
+ ASSERT(ctx && bitmap);
+ mask_info.x_dest = x_dest;
+ mask_info.y_dest = y_dest;
+ mask_info.width = width;
+ mask_info.height = height;
+ mask_info.stride = stride;
+ mask_info.bitmap = bitmap;
+ clip_common(ctx, op, __fill_mask, &mask_info);
+}
+
+static inline void start_draw(InternaCtx *ctx)
+{
+ if (ctx->draw_mode) {
+ return;
+ }
+ ctx->draw_mode = TRUE;
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glStencilFunc(GL_EQUAL, ctx->stencil_mask, ctx->stencil_mask);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ } else {
+ glDisable(GL_TEXTURE_2D);
+ }
+ GLC_ERROR_TETS;
+}
+
+static void fill_rect(InternaCtx *ctx, void *r)
+{
+ GLCRect *rect = (GLCRect *)r;
+ glRectd(rect->x, Y(rect->y), rect->x + rect->width, Y(rect->y + rect->height));
+ /*glBegin(GL_POLYGON);
+ VERTEX2(rect->x, rect->y);
+ VERTEX2 (rect->x + rect->width, rect->y);
+ VERTEX2 (rect->x + rect->width, rect->y + rect->height);
+ VERTEX2 (rect->x , rect->y + rect->height);
+ glEnd();*/
+ GLC_ERROR_TETS;
+}
+
+void glc_fill_rect(GLCCtx glc, const GLCRect *rect)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ start_draw(ctx);
+ fill_rect(ctx, (void *)rect);
+ GLC_ERROR_TETS;
+}
+
+static void fill_path(InternaCtx *ctx, void *p)
+{
+ InternalPath *path = (InternalPath *)p;
+
+ PathPoint *current_point = path->points;
+ PathSegment *current_segment = path->segments;
+ Path *current_path = path->paths;
+ Path *end_path = current_path + path->paths_pos;
+ reset_tass_vertex(ctx);
+ gluTessBeginPolygon(ctx->tesselator, ctx);
+ for (; current_path < end_path; current_path++) {
+ gluTessBeginContour(ctx->tesselator);
+ PathSegment *end_segment = current_segment + current_path->num_segments;
+ gluTessVertex(ctx->tesselator, (GLdouble *)current_point, current_point);
+ current_point++;
+ for (; current_segment < end_segment; current_segment++) {
+ PathPoint *end_point;
+ if (current_segment->type == GLC_PATH_SEG_BEIZER) {
+ end_point = current_point + current_segment->count * 3;
+ for (; current_point < end_point; current_point += 3) {
+ TassVertex *vertex = bezier_flattener(ctx, current_point - 1);
+ while (vertex) {
+ gluTessVertex(ctx->tesselator, (GLdouble *)&vertex->point,
+ (GLdouble *)&vertex->point);
+ vertex = vertex->list_link;
+ }
+ gluTessVertex(ctx->tesselator, (GLdouble *)&current_point[2],
+ (GLdouble *)&current_point[2]);
+ }
+ } else {
+ ASSERT(current_segment->type == GLC_PATH_SEG_LINES);
+ end_point = current_point + current_segment->count;
+ for (; current_point < end_point; current_point++) {
+ gluTessVertex(ctx->tesselator, (GLdouble *)current_point ,
+ (GLdouble *)current_point);
+ }
+ }
+ }
+ gluTessEndContour(ctx->tesselator);
+ }
+ gluTessEndPolygon(ctx->tesselator);
+}
+
+void glc_fill_path(GLCCtx glc, GLCPath path_ref)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && path_ref);
+ start_draw(ctx);
+ fill_path(ctx, path_ref);
+}
+
+static void fill_mask(InternaCtx *ctx, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap)
+{
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride * 8);
+ glBitmap(width, height, 0, 0, 0, 0, bitmap);
+}
+
+void _glc_fill_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && bitmap);
+ start_draw(ctx);
+ if (ctx->pat) {
+ WARN_ONCE(("%s: unimplemented fill mask with pattern\n", __FUNCTION__));
+ }
+ fill_mask(ctx, x_dest, y_dest, width, height, stride, bitmap);
+}
+
+void glc_fill_alpha(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *alpha_mask)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ GLCRect r;
+
+ ASSERT(ctx);
+ start_draw(ctx);
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+ glPixelZoom(1, 1);
+ glDrawPixels(width, height, GL_ALPHA, GL_UNSIGNED_BYTE, alpha_mask);
+
+ r.x = x_dest;
+ r.y = y_dest;
+ r.width = width;
+ r.height = height;
+
+ //todo: support color/texture alpah vals (GL_MODULATE)
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ fill_rect(ctx, &r);
+ glDisable(GL_BLEND);
+}
+
+void glc_stroke_rect(GLCCtx glc, const GLCRect *rect)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ if (ctx->line_width == 0) {
+ return;
+ }
+
+ start_draw(ctx);
+
+ glBegin(GL_LINES);
+ VERTEX2 (rect->x , rect->y + 0.5);
+ VERTEX2 (rect->x + rect->width, rect->y + 0.5);
+ glEnd();
+
+ glBegin(GL_LINES);
+ VERTEX2 (rect->x + rect->width - 0.5, rect->y);
+ VERTEX2 (rect->x + rect->width - 0.5, rect->y + rect->height);
+ glEnd();
+
+ glBegin(GL_LINES);
+ VERTEX2 (rect->x + rect->width, rect->y + rect->height - 0.5);
+ VERTEX2 (rect->x, rect->y + rect->height - 0.5);
+ glEnd();
+
+ glBegin(GL_LINES);
+ VERTEX2(rect->x + 0.5, rect->y + rect->height);
+ VERTEX2(rect->x + 0.5 , rect->y);
+ glEnd();
+ GLC_ERROR_TETS;
+}
+
+void glc_stroke_path(GLCCtx glc, GLCPath path_ref)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPath *path = (InternalPath *)path_ref;
+
+ ASSERT(ctx && path);
+ if (ctx->line_width == 0) {
+ return;
+ }
+ start_draw(ctx);
+
+ reset_tass_vertex(ctx);
+ PathPoint *current_point = path->points;
+ PathSegment *current_segment = path->segments;
+ Path *current_path = path->paths;
+ Path *end_path = current_path + path->paths_pos;
+ for (; current_path < end_path; current_path++) {
+ glBegin(GL_LINE_STRIP);
+ PathSegment *end_segment = current_segment + current_path->num_segments;
+ glVertex2d(current_point->x , current_point->y);
+ current_point++;
+ for (; current_segment < end_segment; current_segment++) {
+ PathPoint *end_point;
+ if (current_segment->type == GLC_PATH_SEG_BEIZER) {
+ end_point = current_point + current_segment->count * 3;
+ for (; current_point < end_point; current_point += 3) {
+ TassVertex *vertex = bezier_flattener(ctx, current_point - 1);
+ while (vertex) {
+ glVertex2d(vertex->point.x, vertex->point.y);
+ vertex = vertex->list_link;
+ }
+ glVertex2d(current_point[2].x , current_point[2].y);
+ }
+ } else {
+ ASSERT(current_segment->type == GLC_PATH_SEG_LINES);
+ end_point = current_point + current_segment->count;
+ for (; current_point < end_point; current_point++) {
+ glVertex2d(current_point->x , current_point->y);
+ }
+ }
+ }
+ glEnd();
+ }
+}
+
+void glc_draw_image(GLCCtx glc, const GLCRecti *dest, const GLCRecti *src, const GLCImage *image,
+ int scale_mode, double alpha)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint8_t *pixels;
+ const int pix_bytes = 4;
+
+ ASSERT(ctx && image);
+ ASSERT(src->width > 0 && src->height > 0);
+
+ ASSERT(image->format == GLC_IMAGE_RGB32 || image->format == GLC_IMAGE_ARGB32); //for now
+ start_draw(ctx);
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ set_raster_pos(ctx, dest->x, dest->y + dest->height);
+
+ if (dest->width == src->width && src->height == dest->height) {
+ glPixelZoom(1, 1);
+ } else {
+ glPixelZoom((float)dest->width / src->width, (float)dest->height / src->height);
+ }
+
+ pixels = image->pixels + src->x * 4 + (image->height - (src->y + src->height)) * image->stride;
+ if (image->format == GLC_IMAGE_ARGB32 || alpha != 1) {
+ glPixelTransferf(GL_ALPHA_SCALE, (GLfloat)alpha);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ }
+ ASSERT(image->stride % pix_bytes == 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, image->stride / pix_bytes);
+ glDrawPixels(src->width, src->height, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
+
+ if (image->format == GLC_IMAGE_ARGB32 || alpha != 1) {
+ glDisable(GL_BLEND);
+ }
+
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ }
+ GLC_ERROR_TETS;
+}
+
+void glc_copy_pixels(GLCCtx glc, int x_dest, int y_dest, int x_src, int y_src, int width,
+ int height)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ ASSERT(ctx);
+#ifdef USE_COPY_PIXELS
+ start_draw(ctx);
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelZoom(1, 1);
+ glCopyPixels(x_src, ctx->height - (y_src + height), width, height, GL_COLOR);
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ }
+#else
+ GLuint texture;
+ int width2 = to_pwoer_two(width);
+ int height2 = to_pwoer_two(height);
+
+ start_draw(ctx);
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x_src, ctx->height - (y_src + height),
+ width2, height2, 0);
+
+ GLfloat s_gen_params[] = { (GLfloat)1.0 / width2, 0, 0, 0 };
+ GLfloat t_gen_params[] = { 0, (GLfloat)1.0 / height2, 0, 0 };
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen_params);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen_params);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef((float)-x_dest / width2, (float)-Y(y_dest + height) / height2, 0);
+
+ glRecti(x_dest, Y(y_dest), x_dest + width, Y(y_dest + height));
+ glFinish();
+ glDeleteTextures(1, &texture);
+ if (!ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ } else {
+ set_pat(ctx, ctx->pat);
+ }
+#endif
+ GLC_ERROR_TETS;
+}
+
+void glc_read_pixels(GLCCtx glc, int x, int y, GLCImage *image)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && image);
+ ASSERT(image->format == GLC_IMAGE_RGB32); //for now
+ ASSERT((image->stride % 4) == 0); //for now
+ glPixelStorei(GL_PACK_ROW_LENGTH, image->stride / 4);
+ glReadPixels(x, ctx->height - (y + image->height), image->width, image->height,
+ GL_BGRA, GL_UNSIGNED_BYTE, image->pixels);
+}
+
+void glc_clear(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void glc_flush(GLCCtx glc)
+{
+ glFlush();
+
+ GLC_ERROR_TETS;
+}
+
+static void tessellation_combine(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4],
+ GLdouble **data_out, void *usr_data)
+{
+ TassVertex *vertex;
+
+ if (!(vertex = alloc_tess_vertex((InternaCtx *)usr_data))) {
+ *data_out = NULL;
+ return;
+ }
+ vertex->point.x = coords[0];
+ vertex->point.y = coords[1];
+ //vertex->point.z = coords[2];
+ *data_out = (GLdouble *)&vertex->point;
+}
+
+static void tessellation_error(GLenum errorCode)
+{
+ printf ("%s: %s\n", __FUNCTION__, gluErrorString(errorCode));
+}
+
+#ifdef WIN32
+#define TESS_CALL_BACK_TYPE void (CALLBACK *)()
+#else
+#define TESS_CALL_BACK_TYPE void (*)()
+#endif
+
+static int init(InternaCtx *ctx, int width, int height)
+{
+#ifdef WIN32
+ if (!(ctx->glBlendEquation = (PFNGLBLENDEQUATIONPROC)wglGetProcAddress("glBlendEquation"))) {
+ return FALSE;
+ }
+#endif
+ ctx->width = width;
+ ctx->height = height;
+ ctx->line_width = 1;
+
+ glClearColor(0, 0, 0, 0);
+ glClearStencil(0);
+
+ if (!(ctx->tesselator = gluNewTess())) {
+ return FALSE;
+ }
+
+ glViewport(0, 0, width, height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, width, 0, height, -1, 1);
+
+ gluTessProperty(ctx->tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
+ gluTessCallback(ctx->tesselator, GLU_BEGIN, (TESS_CALL_BACK_TYPE)glBegin);
+ gluTessCallback(ctx->tesselator, GLU_VERTEX, (TESS_CALL_BACK_TYPE)glVertex3dv);
+ gluTessCallback(ctx->tesselator, GLU_END, (TESS_CALL_BACK_TYPE)glEnd);
+ gluTessCallback(ctx->tesselator, GLU_TESS_COMBINE_DATA, (TESS_CALL_BACK_TYPE)tessellation_combine);
+ gluTessCallback(ctx->tesselator, GLU_TESS_ERROR, (TESS_CALL_BACK_TYPE)tessellation_error);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(0, (GLfloat)height, 0);
+
+ glGetIntegerv( GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
+
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
+ glPixelTransferf(GL_ALPHA_BIAS, 0);
+#ifdef WIN32
+ ctx->glBlendEquation(GL_FUNC_ADD);
+#else
+ glBlendEquation(GL_FUNC_ADD);
+#endif
+
+ glStencilMask(0xff);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ return TRUE;
+}
+
+GLCCtx glc_create(int width, int height)
+{
+ InternaCtx *ctx;
+
+ ASSERT(sizeof(PathPoint) == sizeof(Vertex));
+
+ if (!(ctx = (InternaCtx *)zmalloc(sizeof(*ctx)))) {
+ return NULL;
+ }
+
+ if (!init(ctx, width, height)) {
+ free(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+void glc_destroy(GLCCtx glc)
+{
+ InternaCtx *ctx;
+
+ if (!(ctx = (InternaCtx *)glc)) {
+ return;
+ }
+
+ unref_pat(ctx->pat);
+ free_tass_vertex_bufs(ctx);
+ free(ctx);
+}
+
+/*
+ todo:
+ 1. test double vs float in gl calls
+ 2. int vs flat raster position
+ 3. pixels stride vs bytes stride
+ 4. improve non power of two.
+ glGetString(GL_EXTENSIONS);
+ ARB_texture_non_power_of_two
+ ARB_texture_rectangle
+ GL_TEXTURE_RECTANGLE_ARB
+ 5. scale
+ 6. origin
+ 7. fonts
+ 8. support more image formats
+ 9. use GLCImage in mask ops?
+*/
+