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