temp
[llpp.git] / link.c
blob161d6ffb1b86d5127f309699ec7863982fd1940d
1 #define _GNU_SOURCE
2 #define GL_GLEXT_PROTOTYPES
3 #include <err.h>
4 #include <errno.h>
5 #include <stdio.h>
6 #include <stdarg.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <pthread.h>
11 #include <sys/mman.h>
12 #include <sys/poll.h>
13 #include <byteswap.h>
15 #include <GL/gl.h>
16 #include <GL/glext.h>
17 #include <GL/glx.h>
19 #include <caml/fail.h>
20 #include <caml/alloc.h>
21 #include <caml/memory.h>
22 #include <caml/unixsupport.h>
24 #include "fitz/fitz.h"
25 #include "mupdf/mupdf.h"
27 #include <sys/time.h>
29 static long pagesize;
31 struct page {
32 int pagenum;
33 fz_pixmap *pixmap;
34 GLuint texid;
35 struct page2 *page2;
36 struct page *prev;
39 struct page2 {
40 fz_bbox bbox;
41 fz_matrix ctm;
42 fz_pixmap pixmap;
43 int pagenum;
46 struct {
47 int sock;
48 int texid;
49 pthread_t thread;
50 pthread_cond_t cond;
51 pthread_mutex_t mutex;
52 struct page *pages;
53 struct page2 *pages2;
54 int page;
55 int pagecount;
56 pdf_xref *xref;
57 /* pdf_page *drawpage; */
58 fz_glyphcache *cache;
59 size_t mapsize;
60 void *map;
61 int w, h;
62 Display *dpy;
63 GLXContext ctx;
64 GLXDrawable drawable;
65 } state = {
66 .cond = PTHREAD_COND_INITIALIZER,
67 .mutex = PTHREAD_MUTEX_INITIALIZER
70 static double now (void)
72 struct timeval tv;
74 if (gettimeofday (&tv, NULL)) {
75 err (1, "gettimeofday");
77 return tv.tv_sec + tv.tv_usec*1e-6;
80 static void readdata (int fd, char *p, int size)
82 ssize_t n;
84 n = read (fd, p, size);
85 if (n - size) {
86 err (1, "read (req %d, ret %zd)", size, n);
90 static void writedata (int fd, char *p, int size)
92 char buf[4];
93 ssize_t n;
95 buf[0] = (size >> 24) & 0xff;
96 buf[1] = (size >> 16) & 0xff;
97 buf[2] = (size >> 8) & 0xff;
98 buf[3] = (size >> 0) & 0xff;
100 n = write (fd, buf, 4);
101 if (n != 4) {
102 err (1, "write %zd", n);
105 n = write (fd, p, size);
106 if (n - size) {
107 err (1, "write (req %d, ret %zd)", size, n);
111 static void __attribute__ ((format (printf, 2, 3)))
112 printd (int fd, const char *fmt, ...)
114 int len;
115 va_list ap;
116 char buf[200];
118 va_start (ap, fmt);
119 len = vsnprintf (buf, sizeof (buf), fmt, ap);
120 va_end (ap);
121 writedata (fd, buf, len);
124 static void closexref (void);
126 static void createmmap (struct page *page)
128 int fd, ret;
129 size_t size;
131 size = page->pixmap->w * page->pixmap->h * 4;
133 fd = open ("pdfmap", O_CREAT|O_TRUNC|O_RDWR);
134 if (fd == -1) {
135 err (1, "open");
137 ret = unlink ("pdfmap");
138 if (ret) {
139 err (1, "unlink");
141 size = (size + pagesize - 1) & ~(pagesize - 1);
142 ret = ftruncate (fd, size);
143 if (ret) {
144 err (1, "ftruncate");
146 page->pixmap->samples = mmap (NULL, size, PROT_READ|PROT_WRITE,
147 MAP_PRIVATE, fd, 0);
148 if (page->pixmap->samples == MAP_FAILED) {
149 err (1, "mmap");
153 static void lock (void)
155 int ret;
157 ret = pthread_mutex_lock (&state.mutex);
158 if (ret) {
159 errx (1, "pthread_mutex_lock: %s\n", strerror (ret));
162 static void unlock (void)
164 int ret;
166 ret = pthread_mutex_unlock (&state.mutex);
167 if (ret) {
168 errx (1, "pthread_mutex_unlock: %s\n", strerror (ret));
172 static void condsignal (void)
174 int ret;
176 ret = pthread_cond_signal (&state.cond);
177 if (ret) {
178 errx (1, "pthread_cond_signal: %s\n", strerror (ret));
182 static void condwait (void)
184 int ret;
186 ret = pthread_cond_wait (&state.cond, &state.mutex);
187 if (ret) {
188 errx (1, "pthread_cond_wait: %s\n", strerror (ret));
193 static void die(fz_error error)
195 fz_catch(error, "aborting");
196 closexref();
197 exit(1);
200 void openxref(char *filename, char *password, int dieonbadpass)
202 fz_stream *file;
203 int okay;
204 int fd;
205 char *basename;
207 basename = strrchr(filename, '/');
208 if (!basename)
209 basename = filename;
210 else
211 basename++;
213 fd = open(filename, O_BINARY | O_RDONLY, 0666);
214 if (fd < 0)
215 die(fz_throw("cannot open file '%s'", filename));
217 file = fz_openfile(fd);
218 state.xref = pdf_openxref(file);
219 if (!state.xref)
220 die(fz_throw("cannot open PDF file '%s'", basename));
221 fz_dropstream(file);
223 if (pdf_needspassword(state.xref))
225 okay = pdf_authenticatepassword(state.xref, password);
226 if (!okay && !dieonbadpass)
227 fz_warn("invalid password, attempting to continue.");
228 else if (!okay && dieonbadpass)
229 die(fz_throw("invalid password"));
232 state.pagecount = pdf_getpagecount(state.xref);
233 printd (state.sock, "C %d", state.pagecount);
236 static void flushxref(void)
238 if (state.xref)
240 pdf_flushxref(state.xref, 0);
244 static void closexref(void)
246 if (state.xref)
248 pdf_closexref(state.xref);
249 state.xref = nil;
253 static int readlen (int fd)
255 ssize_t n;
256 char p[4];
258 n = read (fd, p, 4);
259 if (n != 4) {
260 err (1, "read %zd", n);
263 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
266 static void freepage (struct page *page)
268 struct page *p;
270 fz_droppixmap (page->pixmap);
271 for (p = state.pages; p; p = p->prev) {
272 if (p->prev == page) {
273 p->prev = page->prev;
274 break;
277 free (page);
280 static void *render (int pagenum, int pindex)
282 fz_error error;
283 fz_obj *pageobj;
284 int w, h;
285 float zoom;
286 struct page *page;
287 struct page2 *page2;
288 fz_device *idev, *tdev, *mdev;
289 fz_displaylist *list;
290 pdf_page *drawpage;
292 printf ("render %d %d\n", pagenum, pindex);
293 pdf_flushxref (state.xref, 0);
294 page = calloc (sizeof (*page), 1);
295 if (!page) {
296 err (1, "malloc page %d\n", pagenum);
298 page->prev = state.pages;
299 state.pages = page;
301 page2 = &state.pages2[pindex];
303 pageobj = pdf_getpageobject(state.xref, pagenum);
304 if (!pageobj)
305 die (fz_throw ("cannot retrieve info from page %d", pagenum));
307 error = pdf_loadpage(&drawpage, state.xref, pageobj);
308 if (error)
309 die(error);
311 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, page2->bbox);
312 if (error)
313 die (error);
314 #if 0
315 fz_free (page->pixmap->samples);
316 createmmap (page);
317 #endif
318 fz_clearpixmap(page->pixmap, 0xFF);
320 list = fz_newdisplaylist ();
321 if (!list)
322 die (fz_throw ("fz_newdisplaylist failed"));
324 mdev = fz_newlistdevice(list);
325 error = pdf_runcontentstream(mdev, fz_identity(), state.xref,
326 drawpage->resources,
327 drawpage->contents);
328 if (error)
329 die (error);
331 fz_freedevice(mdev);
333 idev = fz_newdrawdevice (state.cache, page->pixmap);
334 if (!idev)
335 die (fz_throw ("fz_newdrawdevice failed"));
337 fz_executedisplaylist(list, idev, page2->ctm);
338 fz_freedevice(idev);
339 fz_freedisplaylist(list);
341 /* fz_debugpixmap (page->pixmap, "haha"); */
342 pdf_droppage (drawpage);
343 page->page2 = page2;
344 page->pagenum = pagenum;
345 return page;
348 static void layout1 (void)
350 int pagenum;
351 double a, b;
352 int prevrotate;
353 fz_rect prevbox;
354 int i, pindex, h;
355 asize_t size;
356 int64 mapsize;
357 struct page2 *p;
359 size = 0;
360 h = 0;
361 pindex = 0;
362 mapsize = 0;
363 a = now ();
364 for (pagenum = 1; pagenum <= state.pagecount; ++pagenum) {
365 float w;
366 float zoom;
367 int rotate;
368 fz_obj *obj;
369 fz_rect box;
370 fz_rect box2;
371 fz_matrix ctm;
372 fz_bbox bbox;
373 fz_error error;
374 fz_obj *pageobj;
376 pageobj = pdf_getpageobject (state.xref, pagenum);
377 if (!pageobj)
378 die (fz_throw ("cannot retrieve info from page %d", pagenum));
380 obj = fz_dictgets (pageobj, "CropBox");
381 if (!fz_isarray(obj)) {
382 obj = fz_dictgets (pageobj, "MediaBox");
383 if (!fz_isarray (obj))
384 die (fz_throw ("cannot find page bounds %d (%d R)",
385 fz_tonum (obj), fz_togen (obj)));
387 box = pdf_torect (obj);
389 obj = fz_dictgets (pageobj, "Rotate");
390 if (fz_isint (obj))
391 rotate = fz_toint (obj);
392 else
393 rotate = 0;
395 if (pagenum != 1
396 && (prevrotate == rotate
397 && !memcmp (&prevbox, &box, sizeof (box)))) {
398 h += p->pixmap.h;
399 continue;
402 memcpy (&prevbox, &box, sizeof (box));
403 prevrotate = rotate;
405 box.x0 = MIN (prevbox.x0, prevbox.x1);
406 box.y0 = MIN (prevbox.y0, prevbox.y1);
407 box.x1 = MAX (prevbox.x0, prevbox.x1);
408 box.y1 = MAX (prevbox.y0, prevbox.y1);
410 ctm = fz_identity ();
411 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
412 ctm = fz_concat (ctm, fz_rotate (rotate));
413 box2 = fz_transformrect (ctm, box);
414 w = box2.x1 - box2.x0;
416 zoom = state.w / w;
417 ctm = fz_identity ();
418 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
419 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
420 ctm = fz_concat (ctm, fz_rotate (rotate));
421 bbox = fz_roundrect (fz_transformrect (ctm, box));
423 size += sizeof (*state.pages2);
424 state.pages2 = caml_stat_resize (state.pages2, size);
426 p = &state.pages2[pindex++];
427 memcpy (&p->bbox, &bbox, sizeof (bbox));
428 memcpy (&p->ctm, &ctm, sizeof (ctm));
430 p->pagenum = pagenum - 1;
431 p->pixmap.x = bbox.x0;
432 p->pixmap.y = bbox.y0;
433 p->pixmap.w = bbox.x1 - bbox.x0;
434 p->pixmap.h = bbox.y1 - bbox.y0;
435 p->pixmap.n = 4;
436 h += p->pixmap.h;
439 for (i = pindex - 1; i >= 0; --i) {
440 p = &state.pages2[i];
441 printd (state.sock, "l %d %d %d",
442 p->pagenum, p->pixmap.w, p->pixmap.h);
445 state.mapsize = mapsize;
446 b = now ();
447 printf ("layout1 took %f sec\n", b - a);
448 printd (state.sock, "C %d", state.pagecount);
449 printd (state.sock, "m %d", h);
452 static void *mainloop (void *unused)
454 char *p = NULL;
455 int len, ret, oldlen = 0;
457 for (;;) {
458 len = readlen (state.sock);
459 if (len == 0) {
460 errx (1, "readlen returned 0");
463 if (oldlen < len + 1) {
464 p = realloc (p, len + 1);
465 if (!p) {
466 err (1, "realloc %d failed", len + 1);
468 oldlen = len + 1;
470 readdata (state.sock, p, len);
471 p[len] = 0;
473 if (!strncmp ("open", p, 4)) {
474 char *filename = p + 5;
476 openxref (filename, NULL, 1);
478 else if (!strncmp ("free", p, 4)) {
479 void *ptr;
481 ret = sscanf(p + 4, " %p", &ptr);
482 freepage (ptr);
484 else if (!strncmp ("layout", p, 6)) {
485 int y;
487 ret = sscanf (p + 6, " %d", &y);
488 if (ret != 1) {
489 errx (1, "malformed layout `%.*s' ret=%d", len, p, ret);
492 else if (!strncmp ("geometry", p, 8)) {
493 int w, h;
494 struct page *page;
496 ret = sscanf (p + 8, " %d %d", &w, &h);
497 if (ret != 2) {
498 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
500 state.h = h;
501 if (w != state.w) {
502 state.w = w;
503 for (page = state.pages; page; page = page->prev) {
504 page->texid = 0;
507 layout1 ();
509 else if (!strncmp ("render", p, 6)) {
510 int pagenum, pindex, w, h, ret;
511 struct page *page;
512 unsigned char *pix;
514 ret = sscanf (p + 6, " %d %d %d %d", &pagenum, &pindex, &w, &h);
516 if (ret != 4) {
517 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
520 page = render (pagenum, pindex);
521 printd (state.sock, "r %d %d %d %p\n",
522 pagenum,
523 state.w,
524 state.h,
525 page);
527 else {
528 errx (1, "unknown command %.*s", len, p);
531 return NULL;
534 static void upload (struct page *page, const char *cap)
536 int w, h, subimage = 0;
537 double start, end;
539 w = page->page2->bbox.x1 - page->page2->bbox.x0;
541 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
542 glPixelStorei(GL_UNPACK_ROW_LENGTH, w);
544 if (page->texid) {
545 GLboolean v = 0;
547 glAreTexturesResident (1, &page->texid, &v);
548 printf ("resident %d %d\n", page->pagenum, v);
549 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, page->texid);
551 else {
552 struct page *p;
553 int texid = (state.texid++ % 10) + 1;
555 h = page->page2->bbox.y1 - page->page2->bbox.y0;
557 for (p = state.pages; p; p = p->prev) {
558 if (p->texid == texid) {
559 int w1, h1;
560 p->texid = 0;
561 w1 = page->page2->bbox.x1 - page->page2->bbox.x0;
562 h1 = page->page2->bbox.y1 - page->page2->bbox.y0;
563 if (w == w1 && h == h1) {
564 subimage = 0;
566 break;
569 page->texid = texid;
571 /* glGenTextures (1, &page->texid); */
572 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, page->texid);
574 start = now ();
575 if (subimage)
576 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
582 GL_ABGR_EXT,
583 GL_UNSIGNED_BYTE,
584 page->pixmap->samples
586 else
587 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
589 GL_RGBA8,
593 #ifndef _ARCH_PPC
594 GL_BGRA_EXT,
595 GL_UNSIGNED_INT_8_8_8_8,
596 #else
597 GL_ABGR_EXT,
598 GL_UNSIGNED_BYTE, /* INT_8_8_8_8, */
599 #endif
600 page->pixmap->samples
603 end = now ();
604 printf ("%s(%s) %d took %f sec\n", cap,
605 subimage ? "sub" : "img",
606 page->pagenum, end - start);
610 CAMLprim value ml_preload (value ptr_v)
612 int ret;
613 void *ptr;
614 CAMLparam1 (ptr_v);
615 char *s = String_val (ptr_v);
617 ret = sscanf (s, "%p", &ptr);
618 if (ret != 1) {
619 errx (1, "cannot parse pointer `%s'", s);
621 upload (ptr, "preload");
622 CAMLreturn (Val_unit);
625 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
626 value py_v, value ptr_v)
628 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
629 int dispy = Int_val (dispy_v);
630 int w = Int_val (w_v);
631 int h = Int_val (h_v);
632 int py = Int_val (py_v);
633 char *s = String_val (ptr_v);
634 int ret;
635 const char *r;
636 void *ptr;
637 struct page *page;
639 ret = sscanf (s, "%p", &ptr);
640 if (ret != 1) {
641 errx (1, "cannot parse pointer `%s'", s);
643 page = ptr;
645 if (0)
646 printf ("draw[%d=%dx%d] dispy=%d w=%d h=%d py=%d ptr=%p\n",
647 page->pagenum,
648 page->page2->bbox.x1 - page->page2->bbox.x0,
649 page->page2->bbox.y1 - page->page2->bbox.y0,
650 dispy,
654 ptr);
656 upload (page, "upload");
657 glEnable (GL_TEXTURE_RECTANGLE_ARB);
658 #ifdef _ARCH_PPC
659 glEnable (GL_FRAGMENT_SHADER_ATI);
660 #endif
661 glBegin (GL_QUADS);
663 glTexCoord2i (0, py);
664 glVertex2i (0, dispy);
666 glTexCoord2i (w, py);
667 glVertex2i (w, dispy);
669 glTexCoord2i (w, py+h);
670 glVertex2i (w, dispy + h);
672 glTexCoord2i (0, py+h);
673 glVertex2i (0, dispy + h);
675 glEnd ();
676 glDisable (GL_TEXTURE_RECTANGLE_ARB);
677 #ifdef _ARCH_PPC
678 glDisable (GL_FRAGMENT_SHADER_ATI);
679 #endif
680 CAMLreturn (Val_unit);
683 static void initgl (void)
685 state.dpy = glXGetCurrentDisplay ();
686 if (!state.dpy) {
687 die (fz_throw ("glXGetCurrentDisplay"));
689 state.ctx = glXGetCurrentContext ();
690 if (!state.ctx) {
691 die (fz_throw ("glXGetCurrentContext"));
693 state.drawable = glXGetCurrentDrawable ();
694 if (!state.drawable) {
695 die (fz_throw ("glXGetCurrentDrawable"));
698 #ifdef _ARCH_PPC
699 glBindFragmentShaderATI (1);
700 glBeginFragmentShaderATI ();
702 glSampleMapATI (GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI);
704 glColorFragmentOp1ATI (GL_MOV_ATI,
705 GL_REG_1_ATI, GL_RED_BIT_ATI, GL_NONE,
706 GL_REG_0_ATI, GL_BLUE, GL_NONE);
707 glColorFragmentOp1ATI (GL_MOV_ATI,
708 GL_REG_1_ATI, GL_BLUE_BIT_ATI, GL_NONE,
709 GL_REG_0_ATI, GL_RED, GL_NONE);
710 glColorFragmentOp1ATI (
711 GL_MOV_ATI,
712 GL_REG_0_ATI, GL_RED_BIT_ATI | GL_BLUE_BIT_ATI, GL_NONE,
713 GL_REG_1_ATI, GL_NONE, GL_NONE
716 glEndFragmentShaderATI ();
717 #endif
720 CAMLprim value ml_init (value sock_v)
722 int ret;
723 fz_error error;
724 CAMLparam1 (sock_v);
726 pagesize = sysconf (_SC_PAGESIZE);
727 if (pagesize == -1) {
728 err (1, "sysconf");
731 state.sock = Int_val (sock_v);
732 initgl ();
734 state.cache = fz_newglyphcache ();
736 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
737 if (ret) {
738 unix_error (ret, "pthread_create", Nothing);
741 CAMLreturn (Val_unit);