51a1564e65ab923817ccc53104f7b62900b7e3b7
[swftools.git] / lib / devices / opengl.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <math.h>
5
6 #include "../gfxdevice.h"
7 #include "../gfxtools.h"
8
9 #include <time.h>
10 #include <GL/gl.h>
11 #include <GL/glut.h>
12
13 #include <stdarg.h>
14
15 #define ZSTEP (1/65536.0)
16
17 typedef struct _fontlist {
18     gfxfont_t*font;
19     struct _fontlist*next;
20 } fontlist_t;
21
22 typedef struct _internal {
23     gfxfont_t*font;
24     char*fontid;
25     fontlist_t* fontlist;
26     int width, height;
27     int currentz;
28    
29     GLUtesselator *tesselator;
30     GLUtesselator *tesselator_tex;
31 } internal_t;
32
33 static int verbose = 1;
34 static void dbg(char*format, ...)
35 {
36     char buf[1024];
37     int l;
38     va_list arglist;
39     if(!verbose)
40         return;
41     va_start(arglist, format);
42     vsprintf(buf, format, arglist);
43     va_end(arglist);
44     l = strlen(buf);
45     while(l && buf[l-1]=='\n') {
46         buf[l-1] = 0;
47         l--;
48     }
49     printf("(device-opengl) %s\n", buf);
50     fflush(stdout);
51 }
52
53 #ifndef CALLBACK 
54 #define CALLBACK
55 #endif
56
57 typedef void(*callbackfunction_t)();
58
59 void CALLBACK errorCallback(GLenum errorCode)
60 {
61    const GLubyte *estring;
62    estring = gluErrorString(errorCode);
63    dbg("Tessellation Error: %s\n", estring);
64    exit(0);
65 }
66 void CALLBACK beginCallback(GLenum which)
67 {
68    glBegin(which);
69 }
70 void CALLBACK endCallback(void)
71 {
72    glEnd();
73 }
74 void CALLBACK vertexCallback(GLvoid *vertex)
75 {
76    double*xyz = (GLdouble*)vertex;
77    glVertex3d(xyz[0],xyz[1],xyz[2]);
78 }
79 void CALLBACK combineCallback(GLdouble coords[3], GLdouble *data[4], GLfloat w[4], GLdouble **out)
80 {
81    GLdouble *vertex;
82    vertex = (GLdouble *) malloc(6 * sizeof(GLdouble));
83    vertex[0] = coords[0];
84    vertex[1] = coords[1];
85    vertex[2] = coords[2];
86    *out = vertex;
87 }
88 void CALLBACK vertexCallbackTex(GLvoid *vertex)
89 {
90    double*v = (GLdouble*)vertex;
91    glTexCoord2f(v[3],v[4]);
92    glVertex3d(v[0],v[1],v[2]);
93 }
94 void CALLBACK combineCallbackTex(GLdouble coords[3], GLdouble *data[4], GLfloat w[4], GLdouble **out)
95 {
96    GLdouble *vertex, *texCoord;
97    vertex = (GLdouble *) malloc(5 * sizeof(GLdouble));
98    vertex[0] = coords[0];
99    vertex[1] = coords[1];
100    vertex[2] = coords[2];
101    texCoord = &vertex[3];
102    vertex[3] = w[0]*data[0][3] + w[1]*data[1][3] + w[2]*data[2][3] + w[3]*data[3][3];
103    vertex[4] = w[0]*data[0][4] + w[1]*data[1][4] + w[2]*data[2][4] + w[3]*data[3][4];
104    *out = vertex;
105 }
106
107 int opengl_setparameter(struct _gfxdevice*dev, const char*key, const char*value)
108 {
109     dbg("setparameter %s=%s", key, value);
110     return 0;
111 }
112
113 void opengl_startpage(struct _gfxdevice*dev, int width, int height)
114 {
115     dbg("startpage %d %d", width, height);
116     internal_t*i = (internal_t*)dev->internal;
117     i->width = width;
118     i->height = height;
119     i->currentz = 0;
120 }
121
122 void opengl_startclip(struct _gfxdevice*dev, gfxline_t*line)
123 {
124     dbg("startclip");
125 }
126
127 void opengl_endclip(struct _gfxdevice*dev)
128 {
129     dbg("endclip");
130 }
131
132 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)
133 {
134     dbg("stroke");
135     internal_t*i = (internal_t*)dev->internal;
136     char running = 0;
137     gfxline_t*l=0;
138     dbg("stroke");
139     glColor4f(color->r/255.0, color->g/255.0, color->b/255.0, color->a/255.0);
140     glLineWidth(width);
141
142     l = line;
143     while(l) {
144         if(l->type == gfx_moveTo) {
145             if(running) {
146                 running = 0;
147                 glEnd();
148             }
149         }
150         if(!running) {
151             running = 1;
152             glBegin(GL_LINE_STRIP);
153         }
154         glVertex3d(l->x, l->y, (i->currentz*ZSTEP));
155         l=l->next;
156     }
157     if(running) {
158         running = 0;
159         glEnd();
160     }
161     i->currentz ++;
162 }
163
164 void opengl_fill(struct _gfxdevice*dev, gfxline_t*line, gfxcolor_t*color)
165 {
166     dbg("fill");
167     internal_t*i = (internal_t*)dev->internal;
168     char running = 0;
169     int len = 0;
170     double*xyz=0;
171     double lastx=0,lasty=0;
172     gfxline_t*l=0;
173     dbg("fill");
174     glColor4f(color->r/255.0, color->g/255.0, color->b/255.0, color->a/255.0);
175
176     gluTessBeginPolygon(i->tesselator, NULL);
177     l = line;
178     len = 0;
179     while(l) {
180         if(l->type == gfx_splineTo) {
181             double c = sqrt(abs(l->x-2*l->sx+lastx) + abs(l->y-2*l->sy+lasty))/2;
182             int steps = (int)c;
183             if(steps<1) steps = 1;
184             len += steps;
185         } else {
186             len++;
187         }
188         l = l->next;
189     }
190     //printf("full len:%d\n", len);
191     double z = (i->currentz*ZSTEP);
192     xyz = malloc(sizeof(double)*3*len);
193     l = line;
194     len = 0;
195     while(l) {
196         if(l->type == gfx_moveTo) {
197             if(running) {
198                 running = 0;
199                 gluTessEndContour(i->tesselator);
200             }
201         }
202         if(!running) {
203             running = 1;
204             gluTessBeginContour(i->tesselator);
205         }
206
207         if(l->type == gfx_splineTo) {
208             int j;
209             double c = sqrt(abs(l->x-2*l->sx+lastx) + abs(l->y-2*l->sy+lasty))/2;
210             int steps = (int)c;
211             if(steps<1) steps = 1;
212             //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);
213             //printf("%f %f %f\n", lastx, l->sx, l->x);
214             //printf("%f %f %f\n", lasty, l->sy, l->y);
215             for(j=1;j<=steps;j++) {
216                 //printf("%d\n", j);
217                 double t = (double)j / (double)steps;
218                 xyz[len*3+0] = lastx*(1-t)*(1-t) + 2*l->sx*(1-t)*t + l->x*t*t;
219                 xyz[len*3+1] = lasty*(1-t)*(1-t) + 2*l->sy*(1-t)*t + l->y*t*t;
220                 xyz[len*3+2] = z;
221                 gluTessVertex(i->tesselator, &xyz[len*3], &xyz[len*3]);
222                 len++;
223             }
224             //printf("%d\n", len);
225         } else {
226             xyz[len*3+0] = l->x;
227             xyz[len*3+1] = l->y;
228             xyz[len*3+2] = z;
229             gluTessVertex(i->tesselator, &xyz[len*3], &xyz[len*3]);
230             len++;
231         }
232         lastx = l->x;
233         lasty = l->y;
234
235         l=l->next;
236     }
237     if(running) {
238         running = 0;
239         gluTessEndContour(i->tesselator);
240     }
241     gluTessEndPolygon(i->tesselator);
242     i->currentz ++;
243     free(xyz);
244 }
245
246 void opengl_fillbitmap(struct _gfxdevice*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
247 {
248     dbg("fillbitmap");
249     internal_t*i = (internal_t*)dev->internal;
250     char running = 0;
251     int len = 0;
252     double*xyz=0;
253     gfxline_t*l=0;
254     glColor4f(1.0,0,0,1.0);
255     
256     GLuint texIDs[1];
257     glGenTextures(1, texIDs);
258
259     int width_bits = 0;
260     int height_bits = 0;
261     while(1<<width_bits < img->width)
262         width_bits++;
263     while(1<<height_bits < img->height)
264         height_bits++;
265     int newwidth = 1<<width_bits;
266     int newheight = 1<<height_bits;
267
268     unsigned char*data = malloc(newwidth*newheight*4);
269     int x,y;
270     for(y=0;y<img->height;y++) {
271         for(x=0;x<img->width;x++) {
272             data[(y*newwidth+x)*4+0] = img->data[y*img->width+x].r;
273             data[(y*newwidth+x)*4+1] = img->data[y*img->width+x].g;
274             data[(y*newwidth+x)*4+2] = img->data[y*img->width+x].b;
275             data[(y*newwidth+x)*4+3] = img->data[y*img->width+x].a;
276         }
277     }
278
279     gfxmatrix_t m2;
280     gfxmatrix_invert(matrix, &m2);
281     m2.m00 /= newwidth;
282     m2.m10 /= newwidth;
283     m2.tx /= newwidth;
284     m2.m01 /= newheight;
285     m2.m11 /= newheight;
286     m2.ty /= newheight;
287
288     glEnable(GL_TEXTURE_2D);
289     glBindTexture(GL_TEXTURE_2D, texIDs[0]);
290     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, newwidth, newheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
291     
292     glEnable(GL_TEXTURE_2D);
293     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
294     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
295     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
296     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
297     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
298     
299     
300     gluTessBeginPolygon(i->tesselator_tex, NULL);
301     l = line;
302     len = 0;
303     while(l) {
304         len++;
305         l = l->next;
306     }
307     xyz = malloc(sizeof(double)*5*len);
308     l = line;
309     len = 0;
310     while(l) {
311         if(l->type == gfx_moveTo) {
312             if(running) {
313                 running = 0;
314                 gluTessEndContour(i->tesselator_tex);
315             }
316         }
317         if(!running) {
318             running = 1;
319             gluTessBeginContour(i->tesselator_tex);
320         }
321
322         xyz[len*5+0] = l->x;
323         xyz[len*5+1] = l->y;
324         xyz[len*5+2] = (i->currentz*ZSTEP);
325         xyz[len*5+3] = 0;
326         xyz[len*5+4] = 0;
327         gfxmatrix_transform(&m2, /*src*/&xyz[len*5+0], /*dest*/&xyz[len*5+3]);
328
329         gluTessVertex(i->tesselator_tex, &xyz[len*5], &xyz[len*5]);
330         len++;
331
332         l=l->next;
333     }
334
335     if(running) {
336         running = 0;
337         gluTessEndContour(i->tesselator_tex);
338     }
339     gluTessEndPolygon(i->tesselator_tex);
340     i->currentz ++;
341     free(xyz);
342     
343     glDisable(GL_TEXTURE_2D);
344 }
345
346 void opengl_fillgradient(struct _gfxdevice*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
347 {
348     dbg("fillgradient");
349 }
350
351 void opengl_addfont(gfxdevice_t*dev, gfxfont_t*font)
352 {
353     internal_t*i = (internal_t*)dev->internal;
354     
355     fontlist_t*last=0,*l = i->fontlist;
356     while(l) {
357         last = l;
358         if(!strcmp((char*)l->font->id, font->id)) {
359             return; // we already know this font
360         }
361         l = l->next;
362     }
363     l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
364     l->font = font;
365     l->next = 0;
366     if(last) {
367         last->next = l;
368     } else {
369         i->fontlist = l;
370     }
371 }
372
373 void opengl_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
374 {
375     internal_t*i = (internal_t*)dev->internal;
376
377     if(i->font && i->font->id && !strcmp(font->id, i->font->id)) {
378         // current font is correct
379     } else {
380         fontlist_t*l = i->fontlist;
381         i->font = 0;
382         while(l) {
383             if(!strcmp((char*)l->font->id, font->id)) {
384                 i->font = l->font;
385                 break;
386             }
387             l = l->next;
388         }
389         if(i->font == 0) {
390             fprintf(stderr, "Unknown font id: %s", font->id);
391             return;
392         }
393     }
394
395     gfxglyph_t*glyph = &i->font->glyphs[glyphnr];
396     
397     gfxline_t*line2 = gfxline_clone(glyph->line);
398     gfxline_transform(line2, matrix);
399     opengl_fill(dev, line2, color);
400     gfxline_free(line2);
401     
402     return;
403 }
404
405
406
407 void opengl_drawlink(struct _gfxdevice*dev, gfxline_t*line, char*action)
408 {
409     dbg("link");
410 }
411
412 void opengl_endpage(struct _gfxdevice*dev)
413 {
414     dbg("endpage");
415 }
416
417 int opengl_result_save(struct _gfxresult*gfx, char*filename)
418 {
419     dbg("result:save");
420     return 0;
421 }
422 void* opengl_result_get(struct _gfxresult*gfx, char*name)
423 {
424     dbg("result:get");
425     return 0;
426 }
427 void opengl_result_destroy(struct _gfxresult*gfx)
428 {
429     dbg("result:destroy");
430     free(gfx);
431 }
432
433 gfxresult_t*opengl_finish(struct _gfxdevice*dev)
434 {
435     dbg("finish");
436     internal_t*i = (internal_t*)dev->internal;
437     gluDeleteTess(i->tesselator);i->tesselator=0;
438     gluDeleteTess(i->tesselator_tex);i->tesselator_tex=0;
439     gfxresult_t*result = (gfxresult_t*)malloc(sizeof(gfxresult_t));
440     memset(result, 0, sizeof(gfxresult_t));
441     result->save = opengl_result_save;
442     result->get = opengl_result_get;
443     result->destroy = opengl_result_destroy;
444     return result;
445 }
446
447 void gfxdevice_opengl_init(gfxdevice_t*dev)
448 {
449     dbg("init");
450     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
451     memset(dev, 0, sizeof(gfxdevice_t));
452     
453     dev->name = "opengl";
454
455     dev->internal = i;
456     
457     dev->setparameter = opengl_setparameter;
458     dev->startpage = opengl_startpage;
459     dev->startclip = opengl_startclip;
460     dev->endclip = opengl_endclip;
461     dev->stroke = opengl_stroke;
462     dev->fill = opengl_fill;
463     dev->fillbitmap = opengl_fillbitmap;
464     dev->fillgradient = opengl_fillgradient;
465     dev->addfont = opengl_addfont;
466     dev->drawchar = opengl_drawchar;
467     dev->drawlink = opengl_drawlink;
468     dev->endpage = opengl_endpage;
469     dev->finish = opengl_finish;
470
471     i->tesselator = gluNewTess();
472     gluTessCallback(i->tesselator, GLU_TESS_ERROR, (callbackfunction_t)errorCallback);
473     gluTessCallback(i->tesselator, GLU_TESS_VERTEX, (callbackfunction_t)vertexCallback);
474     gluTessCallback(i->tesselator, GLU_TESS_BEGIN, (callbackfunction_t)beginCallback);
475     gluTessCallback(i->tesselator, GLU_TESS_END, (callbackfunction_t)endCallback);
476     //gluTessCallback(i->tesselator, GLU_TESS_END, (callbackfunction_t)combineCallback);
477     
478     i->tesselator_tex = gluNewTess();
479     gluTessCallback(i->tesselator_tex, GLU_TESS_ERROR, (callbackfunction_t)errorCallback);
480     gluTessCallback(i->tesselator_tex, GLU_TESS_VERTEX, (callbackfunction_t)vertexCallbackTex);
481     gluTessCallback(i->tesselator_tex, GLU_TESS_BEGIN, (callbackfunction_t)beginCallback);
482     gluTessCallback(i->tesselator_tex, GLU_TESS_END, (callbackfunction_t)endCallback);
483     gluTessCallback(i->tesselator_tex, GLU_TESS_COMBINE, (callbackfunction_t)combineCallbackTex);
484     
485     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
486 }