36ffcc76af79b3948de9622dddc6dcad3eb8d4ef
[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/4.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    dbg("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     glColor4f(color->r/255.0, color->g/255.0, color->b/255.0, color->a/255.0);
268     
269     i->currentz ++;
270     z = (i->currentz*ZSTEP);
271     tesselatePolygon(i->tesselator, z, line);
272
273     //tesselatePolygon(i->tesselator_line, z, line);
274 }
275
276 typedef struct _gfxhash
277 {
278     unsigned char d[16];
279 } gfxhash_t;
280
281 char gfxhash_compare(gfxhash_t*h1, gfxhash_t*h2)
282 {
283     return !memcmp(h1->d, h2->d, 16);
284 }
285
286 typedef struct _imgopengl
287 {
288     gfxhash_t hash;
289     GLuint texID;
290     int width, height;
291     struct _imgopengl*next;
292 } imgopengl_t;
293
294 imgopengl_t*img2texid = 0;
295
296 gfxhash_t gfximage_hash(gfximage_t*img)
297 {
298     int t;
299     int size = img->width*img->height*4;
300     U8*data = (U8*)img->data;
301     gfxhash_t hash;
302     hash_md5(data, size, hash.d);
303     return hash;
304 }
305
306 imgopengl_t*addTexture(gfximage_t*img)
307 {
308     gfxhash_t hash = gfximage_hash(img);
309     imgopengl_t*i = img2texid;
310     
311     int width_bits = 0;
312     int height_bits = 0;
313     while(1<<width_bits < img->width)
314         width_bits++;
315     while(1<<height_bits < img->height)
316         height_bits++;
317     int newwidth = 1<<width_bits;
318     int newheight = 1<<height_bits;
319
320     while(i) {
321         if(gfxhash_compare(&hash, &i->hash) && newwidth==i->width && newheight==i->height) {
322             return i;
323         }
324         i = i->next;
325     }
326     
327     GLuint texIDs[1];
328     glGenTextures(1, texIDs);
329
330     i = malloc(sizeof(imgopengl_t));
331     i->hash = hash;
332     i->texID = texIDs[0];
333     i->next = img2texid;
334     img2texid = i;
335
336     i->width = newwidth;
337     i->height = newheight;
338
339     unsigned char*data = malloc(newwidth*newheight*4);
340     int x,y;
341     for(y=0;y<img->height;y++) {
342         for(x=0;x<img->width;x++) {
343             data[(y*newwidth+x)*4+0] = img->data[y*img->width+x].r;
344             data[(y*newwidth+x)*4+1] = img->data[y*img->width+x].g;
345             data[(y*newwidth+x)*4+2] = img->data[y*img->width+x].b;
346             data[(y*newwidth+x)*4+3] = img->data[y*img->width+x].a;
347         }
348         int lastx = img->width - 1;
349         for(;x<newwidth;x++) {
350             data[(y*newwidth+x)*4+0] = img->data[y*img->width+lastx].r;
351             data[(y*newwidth+x)*4+1] = img->data[y*img->width+lastx].g;
352             data[(y*newwidth+x)*4+2] = img->data[y*img->width+lastx].b;
353             data[(y*newwidth+x)*4+3] = img->data[y*img->width+lastx].a;
354         }
355     }
356     int lasty = img->height - 1;
357     for(;y<newheight;y++) {
358         for(x=0;x<newwidth;x++) {
359             data[(y*newwidth+x)*4+0] = img->data[lasty*img->width+x].r;
360             data[(y*newwidth+x)*4+1] = img->data[lasty*img->width+x].g;
361             data[(y*newwidth+x)*4+2] = img->data[lasty*img->width+x].b;
362             data[(y*newwidth+x)*4+3] = img->data[lasty*img->width+x].a;
363         }
364     }
365     
366     glEnable(GL_TEXTURE_2D);
367     glBindTexture(GL_TEXTURE_2D, i->texID);
368     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, i->width, i->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
369     
370     return i;
371 };
372
373 void opengl_fillbitmap(struct _gfxdevice*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
374 {
375     dbg("fillbitmap");
376     internal_t*i = (internal_t*)dev->internal;
377     char running = 0;
378     int len = 0;
379     double*xyz=0;
380     gfxline_t*l=0;
381     glColor4f(1.0,0,0,1.0);
382     
383     i->currentz ++;
384   
385     imgopengl_t* txt = addTexture(img);
386
387     gfxmatrix_t m2;
388     gfxmatrix_invert(matrix, &m2);
389     m2.m00 /= txt->width;
390     m2.m10 /= txt->width;
391     m2.tx /= txt->width;
392     m2.m01 /= txt->height;
393     m2.m11 /= txt->height;
394     m2.ty /= txt->height;
395
396     glEnable(GL_TEXTURE_2D);
397     glBindTexture(GL_TEXTURE_2D, txt->texID);
398     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
399     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
400     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
401     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
402     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
403     
404     gluTessBeginPolygon(i->tesselator_tex, NULL);
405     l = line;
406     len = 0;
407     while(l) {
408         len++;
409         l = l->next;
410     }
411     xyz = malloc(sizeof(double)*5*len);
412     l = line;
413     len = 0;
414     while(l) {
415         if(l->type == gfx_moveTo) {
416             if(running) {
417                 running = 0;
418                 gluTessEndContour(i->tesselator_tex);
419             }
420         }
421         if(!running) {
422             running = 1;
423             gluTessBeginContour(i->tesselator_tex);
424         }
425
426         xyz[len*5+0] = l->x;
427         xyz[len*5+1] = l->y;
428         xyz[len*5+2] = (i->currentz*ZSTEP);
429         xyz[len*5+3] = 0;
430         xyz[len*5+4] = 0;
431         gfxmatrix_transform(&m2, /*src*/&xyz[len*5+0], /*dest*/&xyz[len*5+3]);
432
433         gluTessVertex(i->tesselator_tex, &xyz[len*5], &xyz[len*5]);
434         len++;
435
436         l=l->next;
437     }
438
439     if(running) {
440         running = 0;
441         gluTessEndContour(i->tesselator_tex);
442     }
443     gluTessEndPolygon(i->tesselator_tex);
444     free(xyz);
445     
446     glDisable(GL_TEXTURE_2D);
447 }
448
449 void opengl_fillgradient(struct _gfxdevice*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
450 {
451     dbg("fillgradient");
452 }
453
454 void opengl_addfont(gfxdevice_t*dev, gfxfont_t*font)
455 {
456     internal_t*i = (internal_t*)dev->internal;
457     
458     fontlist_t*last=0,*l = i->fontlist;
459     while(l) {
460         last = l;
461         if(!strcmp((char*)l->font->id, font->id)) {
462             return; // we already know this font
463         }
464         l = l->next;
465     }
466     l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
467     l->font = font;
468     l->next = 0;
469     if(last) {
470         last->next = l;
471     } else {
472         i->fontlist = l;
473     }
474 }
475
476 void opengl_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
477 {
478     internal_t*i = (internal_t*)dev->internal;
479
480     if(i->font && i->font->id && !strcmp(font->id, i->font->id)) {
481         // current font is correct
482     } else {
483         fontlist_t*l = i->fontlist;
484         i->font = 0;
485         while(l) {
486             if(!strcmp((char*)l->font->id, font->id)) {
487                 i->font = l->font;
488                 break;
489             }
490             l = l->next;
491         }
492         if(i->font == 0) {
493             fprintf(stderr, "Unknown font id: %s", font->id);
494             return;
495         }
496     }
497
498     gfxglyph_t*glyph = &i->font->glyphs[glyphnr];
499     
500     gfxline_t*line2 = gfxline_clone(glyph->line);
501     gfxline_transform(line2, matrix);
502     opengl_fill(dev, line2, color);
503     gfxline_free(line2);
504     
505     i->currentz --;
506     
507     return;
508 }
509
510
511
512 void opengl_drawlink(struct _gfxdevice*dev, gfxline_t*line, char*action)
513 {
514     dbg("link");
515 }
516
517 void opengl_endpage(struct _gfxdevice*dev)
518 {
519     dbg("endpage");
520 }
521
522 int opengl_result_save(struct _gfxresult*gfx, char*filename)
523 {
524     dbg("result:save");
525     return 0;
526 }
527 void* opengl_result_get(struct _gfxresult*gfx, char*name)
528 {
529     dbg("result:get");
530     return 0;
531 }
532 void opengl_result_destroy(struct _gfxresult*gfx)
533 {
534     dbg("result:destroy");
535     free(gfx);
536 }
537
538 gfxresult_t*opengl_finish(struct _gfxdevice*dev)
539 {
540     dbg("finish");
541     internal_t*i = (internal_t*)dev->internal;
542     gluDeleteTess(i->tesselator);i->tesselator=0;
543     gluDeleteTess(i->tesselator_tex);i->tesselator_tex=0;
544     gfxresult_t*result = (gfxresult_t*)malloc(sizeof(gfxresult_t));
545     memset(result, 0, sizeof(gfxresult_t));
546     result->save = opengl_result_save;
547     result->get = opengl_result_get;
548     result->destroy = opengl_result_destroy;
549     return result;
550 }
551
552 void gfxdevice_opengl_init(gfxdevice_t*dev)
553 {
554     dbg("init");
555     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
556     memset(dev, 0, sizeof(gfxdevice_t));
557     
558     dev->name = "opengl";
559
560     dev->internal = i;
561     
562     dev->setparameter = opengl_setparameter;
563     dev->startpage = opengl_startpage;
564     dev->startclip = opengl_startclip;
565     dev->endclip = opengl_endclip;
566     dev->stroke = opengl_stroke;
567     dev->fill = opengl_fill;
568     dev->fillbitmap = opengl_fillbitmap;
569     dev->fillgradient = opengl_fillgradient;
570     dev->addfont = opengl_addfont;
571     dev->drawchar = opengl_drawchar;
572     dev->drawlink = opengl_drawlink;
573     dev->endpage = opengl_endpage;
574     dev->finish = opengl_finish;
575
576     i->tesselator = gluNewTess();
577     gluTessCallback(i->tesselator, GLU_TESS_ERROR, (callbackfunction_t)errorCallback);
578     gluTessCallback(i->tesselator, GLU_TESS_VERTEX, (callbackfunction_t)vertexCallback);
579     gluTessCallback(i->tesselator, GLU_TESS_BEGIN, (callbackfunction_t)beginCallback);
580     gluTessCallback(i->tesselator, GLU_TESS_END, (callbackfunction_t)endCallback);
581     //gluTessCallback(i->tesselator, GLU_TESS_COMBINE, (callbackfunction_t)combineCallback);
582     
583     i->tesselator_line = gluNewTess();
584     gluTessCallback(i->tesselator_line, GLU_TESS_ERROR, (callbackfunction_t)errorCallback);
585     gluTessCallback(i->tesselator_line, GLU_TESS_VERTEX, (callbackfunction_t)vertexCallback);
586     gluTessCallback(i->tesselator_line, GLU_TESS_BEGIN, (callbackfunction_t)beginCallback);
587     gluTessCallback(i->tesselator_line, GLU_TESS_END, (callbackfunction_t)endCallback);
588     gluTessProperty(i->tesselator_line, GLU_TESS_BOUNDARY_ONLY, 1.0);
589     
590     i->tesselator_tex = gluNewTess();
591     gluTessCallback(i->tesselator_tex, GLU_TESS_ERROR, (callbackfunction_t)errorCallback);
592     gluTessCallback(i->tesselator_tex, GLU_TESS_VERTEX, (callbackfunction_t)vertexCallbackTex);
593     gluTessCallback(i->tesselator_tex, GLU_TESS_BEGIN, (callbackfunction_t)beginCallback);
594     gluTessCallback(i->tesselator_tex, GLU_TESS_END, (callbackfunction_t)endCallback);
595     gluTessCallback(i->tesselator_tex, GLU_TESS_COMBINE, (callbackfunction_t)combineCallbackTex);
596     
597     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
598 }