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