b897120f28e0b68de4ca8368963a50c06afa7a86
[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 vertexCallbackTex(GLvoid *vertex)
79 {
80    double*v = (GLdouble*)vertex;
81    glTexCoord2f(v[3],v[4]);
82    glVertex3d(v[0],v[1],v[2]);
83 }
84 void CALLBACK combineCallbackTex(GLdouble coords[3], GLdouble *data[4], GLfloat w[4], GLdouble **out)
85 {
86    GLdouble *vertex, *texCoord;
87    vertex = (GLdouble *) malloc(5 * sizeof(GLdouble));
88    vertex[0] = coords[0];
89    vertex[1] = coords[1];
90    vertex[2] = coords[2];
91    texCoord = &vertex[3];
92    vertex[3] = w[0]*data[0][3] + w[1]*data[1][3] + w[2]*data[2][3] + w[3]*data[3][3];
93    vertex[4] = w[0]*data[0][4] + w[1]*data[1][4] + w[2]*data[2][4] + w[3]*data[3][4];
94    *out = vertex;
95 }
96
97 int opengl_setparameter(struct _gfxdevice*dev, const char*key, const char*value)
98 {
99     dbg("setparameter %s=%s", key, value);
100     return 0;
101 }
102
103 void opengl_startpage(struct _gfxdevice*dev, int width, int height)
104 {
105     dbg("startpage %d %d", width, height);
106     internal_t*i = (internal_t*)dev->internal;
107     i->width = width;
108     i->height = height;
109     i->currentz = 0;
110 }
111
112 void opengl_startclip(struct _gfxdevice*dev, gfxline_t*line)
113 {
114     dbg("startclip");
115 }
116
117 void opengl_endclip(struct _gfxdevice*dev)
118 {
119     dbg("endclip");
120 }
121
122 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)
123 {
124     dbg("stroke");
125     internal_t*i = (internal_t*)dev->internal;
126     char running = 0;
127     gfxline_t*l=0;
128     dbg("stroke");
129     glColor4f(color->r/255.0, color->g/255.0, color->b/255.0, color->a/255.0);
130     glLineWidth(width);
131
132     l = line;
133     while(l) {
134         if(l->type == gfx_moveTo) {
135             if(running) {
136                 running = 0;
137                 glEnd();
138             }
139         }
140         if(!running) {
141             running = 1;
142             glBegin(GL_LINE_STRIP);
143         }
144         glVertex3d(l->x, l->y, (i->currentz*ZSTEP));
145         l=l->next;
146     }
147     if(running) {
148         running = 0;
149         glEnd();
150     }
151     i->currentz ++;
152 }
153
154 void opengl_fill(struct _gfxdevice*dev, gfxline_t*line, gfxcolor_t*color)
155 {
156     dbg("fill");
157     internal_t*i = (internal_t*)dev->internal;
158     char running = 0;
159     int len = 0;
160     double*xyz=0;
161     gfxline_t*l=0;
162     dbg("fill");
163     glColor4f(color->r/255.0, color->g/255.0, color->b/255.0, color->a/255.0);
164
165     gluTessBeginPolygon(i->tesselator, NULL);
166     l = line;
167     len = 0;
168     while(l) {
169         len++;
170         l = l->next;
171     }
172     xyz = malloc(sizeof(double)*3*len);
173     l = line;
174     len = 0;
175     while(l) {
176         if(l->type == gfx_moveTo) {
177             if(running) {
178                 running = 0;
179                 gluTessEndContour(i->tesselator);
180             }
181         }
182         if(!running) {
183             running = 1;
184             gluTessBeginContour(i->tesselator);
185         }
186
187         xyz[len*3+0] = l->x;
188         xyz[len*3+1] = l->y;
189         xyz[len*3+2] = (i->currentz*ZSTEP);
190         gluTessVertex(i->tesselator, &xyz[len*3], &xyz[len*3]);
191         len++;
192
193         l=l->next;
194     }
195     if(running) {
196         running = 0;
197         gluTessEndContour(i->tesselator);
198     }
199     gluTessEndPolygon(i->tesselator);
200     i->currentz ++;
201     free(xyz);
202 }
203
204 void opengl_fillbitmap(struct _gfxdevice*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
205 {
206     dbg("fillbitmap");
207     internal_t*i = (internal_t*)dev->internal;
208     char running = 0;
209     int len = 0;
210     double*xyz=0;
211     gfxline_t*l=0;
212     glColor4f(1.0,0,0,1.0);
213     
214     GLuint texIDs[1];
215     glGenTextures(1, texIDs);
216
217     int width_bits = 0;
218     int height_bits = 0;
219     while(1<<width_bits < img->width)
220         width_bits++;
221     while(1<<height_bits < img->height)
222         height_bits++;
223     int newwidth = 1<<width_bits;
224     int newheight = 1<<height_bits;
225
226     unsigned char*data = malloc(newwidth*newheight*4);
227     int x,y;
228     for(y=0;y<img->height;y++) {
229         for(x=0;x<img->width;x++) {
230             data[(y*newwidth+x)*4+0] = img->data[y*img->width+x].r;
231             data[(y*newwidth+x)*4+1] = img->data[y*img->width+x].g;
232             data[(y*newwidth+x)*4+2] = img->data[y*img->width+x].b;
233             data[(y*newwidth+x)*4+3] = img->data[y*img->width+x].a;
234         }
235     }
236
237     gfxmatrix_t m2;
238     gfxmatrix_invert(matrix, &m2);
239     m2.m00 /= newwidth;
240     m2.m10 /= newwidth;
241     m2.tx /= newwidth;
242     m2.m01 /= newheight;
243     m2.m11 /= newheight;
244     m2.ty /= newheight;
245
246     glEnable(GL_TEXTURE_2D);
247     glBindTexture(GL_TEXTURE_2D, texIDs[0]);
248     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, newwidth, newheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
249     
250     glEnable(GL_TEXTURE_2D);
251     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
252     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
253     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
254     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
255     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
256     
257     
258     gluTessBeginPolygon(i->tesselator_tex, NULL);
259     l = line;
260     len = 0;
261     while(l) {
262         len++;
263         l = l->next;
264     }
265     xyz = malloc(sizeof(double)*5*len);
266     l = line;
267     len = 0;
268     while(l) {
269         if(l->type == gfx_moveTo) {
270             if(running) {
271                 running = 0;
272                 gluTessEndContour(i->tesselator_tex);
273             }
274         }
275         if(!running) {
276             running = 1;
277             gluTessBeginContour(i->tesselator_tex);
278         }
279
280         xyz[len*5+0] = l->x;
281         xyz[len*5+1] = l->y;
282         xyz[len*5+2] = (i->currentz*ZSTEP);
283         xyz[len*5+3] = 0;
284         xyz[len*5+4] = 0;
285         gfxmatrix_transform(&m2, /*src*/&xyz[len*5+0], /*dest*/&xyz[len*5+3]);
286
287         gluTessVertex(i->tesselator_tex, &xyz[len*5], &xyz[len*5]);
288         len++;
289
290         l=l->next;
291     }
292
293     if(running) {
294         running = 0;
295         gluTessEndContour(i->tesselator_tex);
296     }
297     gluTessEndPolygon(i->tesselator_tex);
298     i->currentz ++;
299     free(xyz);
300     
301     glDisable(GL_TEXTURE_2D);
302 }
303
304 void opengl_fillgradient(struct _gfxdevice*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
305 {
306     dbg("fillgradient");
307 }
308
309 void opengl_addfont(gfxdevice_t*dev, gfxfont_t*font)
310 {
311     internal_t*i = (internal_t*)dev->internal;
312     
313     fontlist_t*last=0,*l = i->fontlist;
314     while(l) {
315         last = l;
316         if(!strcmp((char*)l->font->id, font->id)) {
317             return; // we already know this font
318         }
319         l = l->next;
320     }
321     l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
322     l->font = font;
323     l->next = 0;
324     if(last) {
325         last->next = l;
326     } else {
327         i->fontlist = l;
328     }
329 }
330
331 void opengl_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
332 {
333     internal_t*i = (internal_t*)dev->internal;
334
335     if(i->font && i->font->id && !strcmp(font->id, i->font->id)) {
336         // current font is correct
337     } else {
338         fontlist_t*l = i->fontlist;
339         i->font = 0;
340         while(l) {
341             if(!strcmp((char*)l->font->id, font->id)) {
342                 i->font = l->font;
343                 break;
344             }
345             l = l->next;
346         }
347         if(i->font == 0) {
348             fprintf(stderr, "Unknown font id: %s", font->id);
349             return;
350         }
351     }
352
353     gfxglyph_t*glyph = &i->font->glyphs[glyphnr];
354     
355     gfxline_t*line2 = gfxline_clone(glyph->line);
356     gfxline_transform(line2, matrix);
357     opengl_fill(dev, line2, color);
358     gfxline_free(line2);
359     
360     return;
361 }
362
363
364
365 void opengl_drawlink(struct _gfxdevice*dev, gfxline_t*line, char*action)
366 {
367     dbg("link");
368 }
369
370 void opengl_endpage(struct _gfxdevice*dev)
371 {
372     dbg("endpage");
373 }
374
375 int opengl_result_save(struct _gfxresult*gfx, char*filename)
376 {
377     dbg("result:save");
378     return 0;
379 }
380 void* opengl_result_get(struct _gfxresult*gfx, char*name)
381 {
382     dbg("result:get");
383     return 0;
384 }
385 void opengl_result_destroy(struct _gfxresult*gfx)
386 {
387     dbg("result:destroy");
388     free(gfx);
389 }
390
391 gfxresult_t*opengl_finish(struct _gfxdevice*dev)
392 {
393     dbg("finish");
394     internal_t*i = (internal_t*)dev->internal;
395     gluDeleteTess(i->tesselator);i->tesselator=0;
396     gluDeleteTess(i->tesselator_tex);i->tesselator_tex=0;
397     gfxresult_t*result = (gfxresult_t*)malloc(sizeof(gfxresult_t));
398     memset(result, 0, sizeof(gfxresult_t));
399     result->save = opengl_result_save;
400     result->get = opengl_result_get;
401     result->destroy = opengl_result_destroy;
402     return result;
403 }
404
405 void gfxdevice_opengl_init(gfxdevice_t*dev)
406 {
407     dbg("init");
408     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
409     memset(dev, 0, sizeof(gfxdevice_t));
410
411     dev->internal = i;
412     
413     dev->setparameter = opengl_setparameter;
414     dev->startpage = opengl_startpage;
415     dev->startclip = opengl_startclip;
416     dev->endclip = opengl_endclip;
417     dev->stroke = opengl_stroke;
418     dev->fill = opengl_fill;
419     dev->fillbitmap = opengl_fillbitmap;
420     dev->fillgradient = opengl_fillgradient;
421     dev->addfont = opengl_addfont;
422     dev->drawchar = opengl_drawchar;
423     dev->drawlink = opengl_drawlink;
424     dev->endpage = opengl_endpage;
425     dev->finish = opengl_finish;
426
427     i->tesselator = gluNewTess();
428     gluTessCallback(i->tesselator, GLU_TESS_ERROR, (callbackfunction_t)errorCallback);
429     gluTessCallback(i->tesselator, GLU_TESS_VERTEX, (callbackfunction_t)vertexCallback);
430     gluTessCallback(i->tesselator, GLU_TESS_BEGIN, (callbackfunction_t)beginCallback);
431     gluTessCallback(i->tesselator, GLU_TESS_END, (callbackfunction_t)endCallback);
432     
433     i->tesselator_tex = gluNewTess();
434     gluTessCallback(i->tesselator_tex, GLU_TESS_ERROR, (callbackfunction_t)errorCallback);
435     gluTessCallback(i->tesselator_tex, GLU_TESS_VERTEX, (callbackfunction_t)vertexCallbackTex);
436     gluTessCallback(i->tesselator_tex, GLU_TESS_BEGIN, (callbackfunction_t)beginCallback);
437     gluTessCallback(i->tesselator_tex, GLU_TESS_END, (callbackfunction_t)endCallback);
438     gluTessCallback(i->tesselator_tex, GLU_TESS_COMBINE, (callbackfunction_t)combineCallbackTex);
439     
440     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
441 }