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