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