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