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