#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <math.h>
#include "../gfxdevice.h"
#include "../gfxtools.h"
+#include "../MD5.h"
+#include "../types.h"
+#include <math.h>
#include <time.h>
#include <GL/gl.h>
#include <GL/glut.h>
-#include <setjmp.h>
+#include <stdarg.h>
+
+//#define ZSTEP (1/65536.0)
+#define ZSTEP (1/32.0)
+//#define ZSTEP (1/4.0)
typedef struct _fontlist {
gfxfont_t*font;
- char*id;
struct _fontlist*next;
} fontlist_t;
gfxfont_t*font;
char*fontid;
fontlist_t* fontlist;
+ int width, height;
+ int currentz;
+
+ int config_polygonoutlines;
+
+ GLUtesselator *tesselator;
+ GLUtesselator *tesselator_line;
+ GLUtesselator *tesselator_tex;
} internal_t;
+static int verbose = 0;
+static void dbg(char*format, ...)
+{
+ char buf[1024];
+ int l;
+ va_list arglist;
+ if(!verbose)
+ return;
+ va_start(arglist, format);
+ vsnprintf(buf, sizeof(buf)-1, format, arglist);
+ va_end(arglist);
+ l = strlen(buf);
+ while(l && buf[l-1]=='\n') {
+ buf[l-1] = 0;
+ l--;
+ }
+ printf("(device-opengl) %s\n", buf);
+ fflush(stdout);
+}
+
+#ifndef CALLBACK
+#define CALLBACK
+#endif
+
+typedef void(*callbackfunction_t)();
+
+void CALLBACK errorCallback(GLenum errorCode)
+{
+ const GLubyte *estring;
+ estring = gluErrorString(errorCode);
+ printf("Tessellation Error: %s\n", estring);
+ exit(0);
+}
+void CALLBACK beginCallback(GLenum which)
+{
+ glBegin(which);
+}
+void CALLBACK endCallback(void)
+{
+ glEnd();
+}
+void CALLBACK vertexCallback(GLvoid *vertex)
+{
+ double*xyz = (GLdouble*)vertex;
+ glVertex3d(xyz[0],xyz[1],xyz[2]);
+}
+void CALLBACK combineCallback(GLdouble coords[3], GLdouble *data[4], GLfloat w[4], GLdouble **out)
+{
+ GLdouble *vertex;
+ vertex = (GLdouble *) malloc(6 * sizeof(GLdouble));
+ vertex[0] = coords[0];
+ vertex[1] = coords[1];
+ vertex[2] = coords[2];
+ *out = vertex;
+}
+void CALLBACK vertexCallbackTex(GLvoid *vertex)
+{
+ double*v = (GLdouble*)vertex;
+ glTexCoord2f(v[3],v[4]);
+ glVertex3d(v[0],v[1],v[2]);
+}
+void CALLBACK combineCallbackTex(GLdouble coords[3], GLdouble *data[4], GLfloat w[4], GLdouble **out)
+{
+ GLdouble *vertex;
+ vertex = (GLdouble *) malloc(5 * sizeof(GLdouble));
+ vertex[0] = coords[0];
+ vertex[1] = coords[1];
+ vertex[2] = coords[2];
+ if(data[2] && data[3]) {
+ vertex[3] = w[0]*data[0][3] + w[1]*data[1][3] + w[2]*data[2][3] + w[3]*data[3][3];
+ vertex[4] = w[0]*data[0][4] + w[1]*data[1][4] + w[2]*data[2][4] + w[3]*data[3][4];
+ } else {
+ vertex[3] = w[0]*data[0][3] + w[1]*data[1][3];
+ vertex[4] = w[0]*data[0][4] + w[1]*data[1][4];
+ }
+ *out = vertex;
+}
+
int opengl_setparameter(struct _gfxdevice*dev, const char*key, const char*value)
{
+ internal_t*i = (internal_t*)dev->internal;
+ dbg("setparameter %s=%s", key, value);
+ if(!strcmp(key, "polygonoutlines")) {
+ i->config_polygonoutlines = atoi(value);
+ }
return 0;
}
-jmp_buf env;
-//jmp_buf env2;
+void opengl_startpage(struct _gfxdevice*dev, int width, int height)
+{
+ dbg("startpage %d %d", width, height);
+ internal_t*i = (internal_t*)dev->internal;
+ i->width = width;
+ i->height = height;
+ i->currentz = 0;
+}
-void display()
+void opengl_startclip(struct _gfxdevice*dev, gfxline_t*line)
{
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- /*if(!setjmp(env2))
- longjmp(env, 1);*/
- longjmp(env, 1);
+ dbg("startclip");
}
-void opengl_startpage(struct _gfxdevice*dev, int width, int height)
+void opengl_endclip(struct _gfxdevice*dev)
{
- glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
- glutInitWindowSize(width,height);
- glutInitWindowPosition(100,100);
- glutCreateWindow("gfxdevice_opengl");
- glutDisplayFunc(display);
+ dbg("endclip");
+}
- glClearColor(1.0,0.0,0.0,0.0);
- glShadeModel(GL_FLAT);
- glEnable(GL_DEPTH_TEST);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+void opengl_stroke(struct _gfxdevice*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
+{
+ dbg("stroke");
+ internal_t*i = (internal_t*)dev->internal;
+ i->currentz++;
+ char running = 0;
+ gfxline_t*l=0;
+
+ glColor4f(color->r/255.0, color->g/255.0, color->b/255.0, color->a/255.0);
- glViewport(0,0, width, height);
+ //glLineWidth(width*64);
+ if(width <= 0) {
+ width = 1.0;
+ }
+ glLineWidth(width);
+ double z = i->currentz*ZSTEP;
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- //gluPerspective(/*angle*/60.0, (GLfloat)width/(GLfloat)height, /*nearclip*/1.0, /*farclip*/30.0);
+ glPolygonOffset(0.0, 500.0);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glScalef(1.0/width,1.0/height,1.0);
-
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- int ret=0;
- ret = setjmp(env);
- if(!ret) {
- glutMainLoop();
+ l = line;
+ while(l) {
+ if(l->type == gfx_moveTo) {
+ if(running) {
+ running = 0;
+ glEnd();
+ }
+ }
+ if(!running) {
+ running = 1;
+ glBegin(GL_LINE_STRIP);
+ }
+ glVertex3d(l->x, l->y, z);
+ l=l->next;
}
+ if(running) {
+ running = 0;
+ glEnd();
+ }
+ glLineWidth(1.0);
}
-void opengl_startclip(struct _gfxdevice*dev, gfxline_t*line)
+#define SPLINE_SUBDIVISION 2
+
+void tesselatePolygon(GLUtesselator*tesselator, double z, gfxline_t*line)
{
- int t;
- for(t=1;t<100;t++) {
- glColor3f(1.0,1.0,1.0);
- glBegin(GL_POLYGON);
- glVertex3f(-t*10,-t*10,0);
- glVertex3f(-t*10,t*10,0);
- glVertex3f(t*10,t*10,0);
- glVertex3f(t*10,-t*10,0);
- glEnd();
+ int len = 0;
+ gfxline_t*l=0;
+ double lastx=0,lasty=0;
+ double*xyz=0;
+ char running = 0;
+ gluTessBeginPolygon(tesselator, NULL);
+ l = line;
+ len = 0;
+ while(l) {
+ if(l->type == gfx_splineTo) {
+ double c = sqrt(abs(l->x-2*l->sx+lastx) + abs(l->y-2*l->sy+lasty))*SPLINE_SUBDIVISION;
+ int steps = (int)c;
+ if(steps<1) steps = 1;
+ len += steps;
+ } else {
+ len++;
+ }
+ l = l->next;
+ }
+ //printf("full len:%d\n", len);
+ xyz = malloc(sizeof(double)*3*len);
+ l = line;
+ len = 0;
+ while(l) {
+ if(l->type == gfx_moveTo) {
+ if(running) {
+ running = 0;
+ gluTessEndContour(tesselator);
+ }
+ }
+ if(!running) {
+ running = 1;
+ gluTessBeginContour(tesselator);
+ }
+
+ if(l->type == gfx_splineTo) {
+ int j;
+ double c = sqrt(abs(l->x-2*l->sx+lastx) + abs(l->y-2*l->sy+lasty))*SPLINE_SUBDIVISION;
+ int steps = (int)c;
+ if(steps<1) steps = 1;
+ //printf("c=%f d1=%f (%f/%f) d2=%f (%f/%f)\n", c,d1,l->x-l->sx,l->y-l->sy,d2,lastx-l->sx,lasty-l->sy);
+ //printf("%f %f %f\n", lastx, l->sx, l->x);
+ //printf("%f %f %f\n", lasty, l->sy, l->y);
+ for(j=1;j<=steps;j++) {
+ //printf("%d\n", j);
+ double t = (double)j / (double)steps;
+ xyz[len*3+0] = lastx*(1-t)*(1-t) + 2*l->sx*(1-t)*t + l->x*t*t;
+ xyz[len*3+1] = lasty*(1-t)*(1-t) + 2*l->sy*(1-t)*t + l->y*t*t;
+ xyz[len*3+2] = z;
+ gluTessVertex(tesselator, &xyz[len*3], &xyz[len*3]);
+ len++;
+ }
+ //printf("%d\n", len);
+ } else {
+ xyz[len*3+0] = l->x;
+ xyz[len*3+1] = l->y;
+ xyz[len*3+2] = z;
+ gluTessVertex(tesselator, &xyz[len*3], &xyz[len*3]);
+ len++;
+ }
+ lastx = l->x;
+ lasty = l->y;
+
+ l=l->next;
+ }
+ if(running) {
+ running = 0;
+ gluTessEndContour(tesselator);
}
- //glRectf(-50,-50,0.0,0.0);
+ gluTessEndPolygon(tesselator);
+ free(xyz);
}
-void opengl_endclip(struct _gfxdevice*dev)
+void opengl_fill(struct _gfxdevice*dev, gfxline_t*line, gfxcolor_t*color)
{
+ double z;
+ dbg("fill %02x%02x%02x%02x", color->a, color->r, color->g, color->b);
+ internal_t*i = (internal_t*)dev->internal;
+
+ glDisable(GL_TEXTURE_2D);
+ glColor4f(color->r/255.0, color->g/255.0, color->b/255.0, color->a/255.0);
+
+ i->currentz ++;
+ z = (i->currentz*ZSTEP);
+ tesselatePolygon(i->tesselator, z, line);
+
+ //tesselatePolygon(i->tesselator_line, z, line);
}
-void opengl_stroke(struct _gfxdevice*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
+typedef struct _gfxhash
{
+ unsigned char d[16];
+} gfxhash_t;
+
+char gfxhash_compare(gfxhash_t*h1, gfxhash_t*h2)
+{
+ return !memcmp(h1->d, h2->d, 16);
}
-void opengl_fill(struct _gfxdevice*dev, gfxline_t*line, gfxcolor_t*color)
+typedef struct _imgopengl
+{
+ gfxhash_t hash;
+ GLuint texID;
+ int width, height;
+ unsigned char*data;
+ struct _imgopengl*next;
+} imgopengl_t;
+
+static imgopengl_t*img2texid = 0;
+
+static gfxhash_t gfximage_hash(gfximage_t*img)
{
+ int t;
+ int size = img->width*img->height*4;
+ U8*data = (U8*)img->data;
+ gfxhash_t hash;
+ hash_md5(data, size, hash.d);
+ return hash;
}
+static void delTextures()
+{
+ imgopengl_t*i = img2texid;
+ while(i) {
+ imgopengl_t*next = i->next;
+ if(i->data) {
+ glDeleteTextures(1, &i->texID);
+ free(i->data);
+ }
+ memset(i, 0, sizeof(imgopengl_t));
+ free(i);
+ i = next;
+ }
+}
+
+static imgopengl_t*addTexture(gfximage_t*img)
+{
+ gfxhash_t hash = gfximage_hash(img);
+ imgopengl_t*i = img2texid;
+
+ int width_bits = 0;
+ int height_bits = 0;
+ while(1<<width_bits < img->width)
+ width_bits++;
+ while(1<<height_bits < img->height)
+ height_bits++;
+ int newwidth = 1<<width_bits;
+ int newheight = 1<<height_bits;
+
+ while(i) {
+ if(gfxhash_compare(&hash, &i->hash) && newwidth==i->width && newheight==i->height) {
+ return i;
+ }
+ i = i->next;
+ }
+
+ GLuint texIDs[1];
+ glGenTextures(1, texIDs);
+
+ i = malloc(sizeof(imgopengl_t));
+ i->hash = hash;
+ i->texID = texIDs[0];
+ i->next = img2texid;
+ img2texid = i;
+
+ i->width = newwidth;
+ i->height = newheight;
+
+ unsigned char*data = malloc(newwidth*newheight*4);
+ i->data = data;
+ int x,y;
+ for(y=0;y<img->height;y++) {
+ for(x=0;x<img->width;x++) {
+ data[(y*newwidth+x)*4+0] = img->data[y*img->width+x].r;
+ data[(y*newwidth+x)*4+1] = img->data[y*img->width+x].g;
+ data[(y*newwidth+x)*4+2] = img->data[y*img->width+x].b;
+ data[(y*newwidth+x)*4+3] = img->data[y*img->width+x].a;
+ }
+ int lastx = img->width - 1;
+ for(;x<newwidth;x++) {
+ data[(y*newwidth+x)*4+0] = img->data[y*img->width+lastx].r;
+ data[(y*newwidth+x)*4+1] = img->data[y*img->width+lastx].g;
+ data[(y*newwidth+x)*4+2] = img->data[y*img->width+lastx].b;
+ data[(y*newwidth+x)*4+3] = img->data[y*img->width+lastx].a;
+ }
+ }
+ int lasty = img->height - 1;
+ for(;y<newheight;y++) {
+ for(x=0;x<newwidth;x++) {
+ data[(y*newwidth+x)*4+0] = img->data[lasty*img->width+x].r;
+ data[(y*newwidth+x)*4+1] = img->data[lasty*img->width+x].g;
+ data[(y*newwidth+x)*4+2] = img->data[lasty*img->width+x].b;
+ data[(y*newwidth+x)*4+3] = img->data[lasty*img->width+x].a;
+ }
+ }
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, i->texID);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, i->width, i->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+ return i;
+};
+
void opengl_fillbitmap(struct _gfxdevice*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
{
+ dbg("fillbitmap");
+ internal_t*i = (internal_t*)dev->internal;
+ char running = 0;
+ int len = 0;
+ double*xyz=0;
+ gfxline_t*l=0;
+ glColor4f(1.0,0,0.7,1.0);
+
+ i->currentz ++;
+
+ imgopengl_t* txt = addTexture(img);
+
+ gfxmatrix_t m2;
+ gfxmatrix_invert(matrix, &m2);
+ m2.m00 /= txt->width;
+ m2.m10 /= txt->width;
+ m2.tx /= txt->width;
+ m2.m01 /= txt->height;
+ m2.m11 /= txt->height;
+ m2.ty /= txt->height;
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, txt->texID);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ gluTessBeginPolygon(i->tesselator_tex, NULL);
+ l = line;
+ len = 0;
+ while(l) {
+ len++;
+ l = l->next;
+ }
+ xyz = malloc(sizeof(double)*5*len);
+ l = line;
+ len = 0;
+ while(l) {
+ if(l->type == gfx_moveTo) {
+ if(running) {
+ running = 0;
+ gluTessEndContour(i->tesselator_tex);
+ }
+ }
+ if(!running) {
+ running = 1;
+ gluTessBeginContour(i->tesselator_tex);
+ }
+
+ xyz[len*5+0] = l->x;
+ xyz[len*5+1] = l->y;
+ xyz[len*5+2] = (i->currentz*ZSTEP);
+ xyz[len*5+3] = 0;
+ xyz[len*5+4] = 0;
+ gfxmatrix_transform(&m2, /*src*/&xyz[len*5+0], /*dest*/&xyz[len*5+3]);
+
+ gluTessVertex(i->tesselator_tex, &xyz[len*5], &xyz[len*5]);
+ len++;
+
+ l=l->next;
+ }
+
+ if(running) {
+ running = 0;
+ gluTessEndContour(i->tesselator_tex);
+ }
+ gluTessEndPolygon(i->tesselator_tex);
+ free(xyz);
+
+ glDisable(GL_TEXTURE_2D);
}
void opengl_fillgradient(struct _gfxdevice*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
{
+ dbg("fillgradient");
}
-void opengl_addfont(struct _gfxdevice*dev, char*fontid, gfxfont_t*font)
+void opengl_addfont(gfxdevice_t*dev, gfxfont_t*font)
{
+ internal_t*i = (internal_t*)dev->internal;
+
+ fontlist_t*last=0,*l = i->fontlist;
+ while(l) {
+ last = l;
+ if(!strcmp((char*)l->font->id, font->id)) {
+ return; // we already know this font
+ }
+ l = l->next;
+ }
+ l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
+ l->font = font;
+ l->next = 0;
+ if(last) {
+ last->next = l;
+ } else {
+ i->fontlist = l;
+ }
}
-void opengl_drawchar(struct _gfxdevice*dev, char*fontid, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
+void opengl_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
{
+ internal_t*i = (internal_t*)dev->internal;
+ if(!font)
+ return;
+
+ if(i->font && i->font->id && !strcmp(font->id, i->font->id)) {
+ // current font is correct
+ } else {
+ fontlist_t*l = i->fontlist;
+ i->font = 0;
+ while(l) {
+ if(!strcmp((char*)l->font->id, font->id)) {
+ i->font = l->font;
+ break;
+ }
+ l = l->next;
+ }
+ if(i->font == 0) {
+ opengl_addfont(dev, font);
+ i->font = font;
+ //fprintf(stderr, "Unknown font id: %s", font->id);
+ //return;
+ }
+ }
+
+ gfxglyph_t*glyph = &i->font->glyphs[glyphnr];
+
+ gfxline_t*line2 = gfxline_clone(glyph->line);
+ gfxline_transform(line2, matrix);
+ opengl_fill(dev, line2, color);
+ gfxline_free(line2);
+
+ i->currentz --;
+
+ return;
}
-void opengl_drawlink(struct _gfxdevice*dev, gfxline_t*line, char*action)
+
+
+void opengl_drawlink(struct _gfxdevice*dev, gfxline_t*line, const char*action)
{
+ dbg("link");
}
void opengl_endpage(struct _gfxdevice*dev)
{
- glFlush();
- //longjmp(env2, 1);
- glutMainLoop();
+ dbg("endpage");
}
-
-int opengl_result_save(struct _gfxresult*gfx, char*filename)
+int opengl_result_save(struct _gfxresult*gfx, const char*filename)
{
+ dbg("result:save");
return 0;
}
-void* opengl_result_get(struct _gfxresult*gfx, char*name)
+void* opengl_result_get(struct _gfxresult*gfx, const char*name)
{
+ dbg("result:get");
return 0;
}
void opengl_result_destroy(struct _gfxresult*gfx)
{
+ dbg("result:destroy");
free(gfx);
+ delTextures();
}
gfxresult_t*opengl_finish(struct _gfxdevice*dev)
{
+ dbg("finish");
internal_t*i = (internal_t*)dev->internal;
+ gluDeleteTess(i->tesselator);i->tesselator=0;
+ gluDeleteTess(i->tesselator_tex);i->tesselator_tex=0;
gfxresult_t*result = (gfxresult_t*)malloc(sizeof(gfxresult_t));
memset(result, 0, sizeof(gfxresult_t));
result->save = opengl_result_save;
void gfxdevice_opengl_init(gfxdevice_t*dev)
{
+ dbg("init");
internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
- int argc=1;
- char*argv[]={"gfxdevice_opengl", 0};
- glutInit(&argc, argv);
-
memset(dev, 0, sizeof(gfxdevice_t));
+
+ dev->name = "opengl";
dev->internal = i;
dev->drawlink = opengl_drawlink;
dev->endpage = opengl_endpage;
dev->finish = opengl_finish;
+
+ i->tesselator = gluNewTess();
+ gluTessCallback(i->tesselator, GLU_TESS_ERROR, (callbackfunction_t)errorCallback);
+ gluTessCallback(i->tesselator, GLU_TESS_VERTEX, (callbackfunction_t)vertexCallback);
+ gluTessCallback(i->tesselator, GLU_TESS_BEGIN, (callbackfunction_t)beginCallback);
+ gluTessCallback(i->tesselator, GLU_TESS_END, (callbackfunction_t)endCallback);
+ gluTessCallback(i->tesselator, GLU_TESS_COMBINE, (callbackfunction_t)combineCallback);
+
+ i->tesselator_line = gluNewTess();
+ gluTessCallback(i->tesselator_line, GLU_TESS_ERROR, (callbackfunction_t)errorCallback);
+ gluTessCallback(i->tesselator_line, GLU_TESS_VERTEX, (callbackfunction_t)vertexCallback);
+ gluTessCallback(i->tesselator_line, GLU_TESS_BEGIN, (callbackfunction_t)beginCallback);
+ gluTessCallback(i->tesselator_line, GLU_TESS_END, (callbackfunction_t)endCallback);
+ gluTessProperty(i->tesselator_line, GLU_TESS_BOUNDARY_ONLY, 1.0);
+
+ i->tesselator_tex = gluNewTess();
+ gluTessCallback(i->tesselator_tex, GLU_TESS_ERROR, (callbackfunction_t)errorCallback);
+ gluTessCallback(i->tesselator_tex, GLU_TESS_VERTEX, (callbackfunction_t)vertexCallbackTex);
+ gluTessCallback(i->tesselator_tex, GLU_TESS_BEGIN, (callbackfunction_t)beginCallback);
+ gluTessCallback(i->tesselator_tex, GLU_TESS_END, (callbackfunction_t)endCallback);
+ gluTessCallback(i->tesselator_tex, GLU_TESS_COMBINE, (callbackfunction_t)combineCallbackTex);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}