#include #include #include #include #include #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 *)¤t_point[2], (GLdouble *)¤t_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? */