Another temp
[llpp.git] / link.c
blobb591c6617111dae33ff2ecfddd862925738f97d5
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 tex {
32 int index;
33 int h;
36 struct page {
37 int pagenum;
38 fz_pixmap *pixmap;
39 GLuint texid;
40 struct page2 *page2;
41 struct page *prev;
42 struct tex texs[];
45 struct page2 {
46 fz_bbox bbox;
47 fz_matrix ctm;
48 fz_pixmap pixmap;
49 int pagenum;
52 struct {
53 int sock;
54 pthread_t thread;
55 struct page *pages;
56 struct page2 *pages2;
57 int page;
58 int pagecount;
59 pdf_xref *xref;
60 fz_glyphcache *cache;
61 int w, h;
62 Display *dpy;
63 GLXContext ctx;
64 GLXDrawable drawable;
66 int texid;
67 int texcount;
68 int slicecount;
69 GLuint *texids;
70 struct tex **texowners;
71 } state;
73 static double now (void)
75 struct timeval tv;
77 if (gettimeofday (&tv, NULL)) {
78 err (1, "gettimeofday");
80 return tv.tv_sec + tv.tv_usec*1e-6;
83 static void readdata (int fd, char *p, int size)
85 ssize_t n;
87 n = read (fd, p, size);
88 if (n - size) {
89 err (1, "read (req %d, ret %zd)", size, n);
93 static void writedata (int fd, char *p, int size)
95 char buf[4];
96 ssize_t n;
98 buf[0] = (size >> 24) & 0xff;
99 buf[1] = (size >> 16) & 0xff;
100 buf[2] = (size >> 8) & 0xff;
101 buf[3] = (size >> 0) & 0xff;
103 n = write (fd, buf, 4);
104 if (n != 4) {
105 err (1, "write %zd", n);
108 n = write (fd, p, size);
109 if (n - size) {
110 err (1, "write (req %d, ret %zd)", size, n);
114 static void __attribute__ ((format (printf, 2, 3)))
115 printd (int fd, const char *fmt, ...)
117 int len;
118 va_list ap;
119 char buf[200];
121 va_start (ap, fmt);
122 len = vsnprintf (buf, sizeof (buf), fmt, ap);
123 va_end (ap);
124 writedata (fd, buf, len);
127 static void closexref (void);
129 static void createmmap (struct page *page)
131 int fd, ret;
132 size_t size;
134 size = page->pixmap->w * page->pixmap->h * 4;
136 fd = open ("pdfmap", O_CREAT|O_TRUNC|O_RDWR);
137 if (fd == -1) {
138 err (1, "open");
140 ret = unlink ("pdfmap");
141 if (ret) {
142 err (1, "unlink");
144 size = (size + pagesize - 1) & ~(pagesize - 1);
145 ret = ftruncate (fd, size);
146 if (ret) {
147 err (1, "ftruncate");
149 page->pixmap->samples = mmap (NULL, size, PROT_READ|PROT_WRITE,
150 MAP_PRIVATE, fd, 0);
151 if (page->pixmap->samples == MAP_FAILED) {
152 err (1, "mmap");
156 static void die(fz_error error)
158 fz_catch(error, "aborting");
159 closexref();
160 exit(1);
163 void openxref(char *filename, char *password, int dieonbadpass)
165 fz_stream *file;
166 int okay;
167 int fd;
168 char *basename;
170 basename = strrchr(filename, '/');
171 if (!basename)
172 basename = filename;
173 else
174 basename++;
176 fd = open(filename, O_BINARY | O_RDONLY, 0666);
177 if (fd < 0)
178 die(fz_throw("cannot open file '%s'", filename));
180 file = fz_openfile(fd);
181 state.xref = pdf_openxref(file);
182 if (!state.xref)
183 die(fz_throw("cannot open PDF file '%s'", basename));
184 fz_dropstream(file);
186 if (pdf_needspassword(state.xref))
188 okay = pdf_authenticatepassword(state.xref, password);
189 if (!okay && !dieonbadpass)
190 fz_warn("invalid password, attempting to continue.");
191 else if (!okay && dieonbadpass)
192 die(fz_throw("invalid password"));
195 state.pagecount = pdf_getpagecount(state.xref);
196 printd (state.sock, "C %d", state.pagecount);
199 static void flushxref(void)
201 if (state.xref)
203 pdf_flushxref(state.xref, 0);
207 static void closexref(void)
209 if (state.xref)
211 pdf_closexref(state.xref);
212 state.xref = nil;
216 static int readlen (int fd)
218 ssize_t n;
219 char p[4];
221 n = read (fd, p, 4);
222 if (n != 4) {
223 err (1, "read %zd", n);
226 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
229 static void freepage (struct page *page)
231 int i;
232 struct page *p;
234 fz_droppixmap (page->pixmap);
235 for (p = state.pages; p; p = p->prev) {
236 if (p->prev == page) {
237 p->prev = page->prev;
238 break;
241 for (i = 0; i < state.slicecount; ++i) {
242 struct tex *t = &p->texs[i];
243 state.texowners[t->index] = NULL;
245 free (page);
248 static void subdivide (struct page *p)
250 int i;
251 int h = p->pixmap->h;
253 if (state.slicecount == 1) {
254 struct tex *t = &p->texs[0];
255 t->index = -1;
256 t->h = h;
258 else {
259 int th = h / (state.slicecount - 1);
261 for (i = 0; i < state.slicecount; ++i) {
262 struct tex *t = &p->texs[i];
263 t->index = -1;
264 t->h = MIN (th, h);
265 h -= th;
270 static void *render (int pagenum, int pindex)
272 fz_error error;
273 fz_obj *pageobj;
274 int w, h;
275 float zoom;
276 struct page *page;
277 struct page2 *page2;
278 fz_device *idev, *tdev, *mdev;
279 fz_displaylist *list;
280 pdf_page *drawpage;
282 printf ("render %d %d\n", pagenum, pindex);
283 pdf_flushxref (state.xref, 0);
284 page = calloc (sizeof (*page) + (state.slicecount * sizeof (struct tex)), 1);
285 if (!page) {
286 err (1, "malloc page %d\n", pagenum);
288 page->prev = state.pages;
289 state.pages = page;
291 page2 = &state.pages2[pindex];
293 pageobj = pdf_getpageobject(state.xref, pagenum);
294 if (!pageobj)
295 die (fz_throw ("cannot retrieve info from page %d", pagenum));
297 error = pdf_loadpage(&drawpage, state.xref, pageobj);
298 if (error)
299 die(error);
301 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, page2->bbox);
302 if (error)
303 die (error);
304 #if 0
305 fz_free (page->pixmap->samples);
306 createmmap (page);
307 #endif
308 fz_clearpixmap(page->pixmap, 0xFF);
310 list = fz_newdisplaylist ();
311 if (!list)
312 die (fz_throw ("fz_newdisplaylist failed"));
314 mdev = fz_newlistdevice(list);
315 error = pdf_runcontentstream(mdev, fz_identity(), state.xref,
316 drawpage->resources,
317 drawpage->contents);
318 if (error)
319 die (error);
321 fz_freedevice(mdev);
323 idev = fz_newdrawdevice (state.cache, page->pixmap);
324 if (!idev)
325 die (fz_throw ("fz_newdrawdevice failed"));
327 fz_executedisplaylist(list, idev, page2->ctm);
328 fz_freedevice(idev);
329 fz_freedisplaylist(list);
331 /* fz_debugpixmap (page->pixmap, "haha"); */
332 pdf_droppage (drawpage);
333 page->page2 = page2;
334 page->pagenum = pagenum;
335 subdivide (page);
336 return page;
339 static void layout1 (void)
341 int pagenum;
342 double a, b;
343 int prevrotate;
344 fz_rect prevbox;
345 int i, pindex;
346 asize_t size;
347 int64 mapsize;
348 struct page2 *p;
350 size = 0;
351 pindex = 0;
352 mapsize = 0;
353 a = now ();
354 for (pagenum = 1; pagenum <= state.pagecount; ++pagenum) {
355 float w;
356 float zoom;
357 int rotate;
358 fz_obj *obj;
359 fz_rect box;
360 fz_rect box2;
361 fz_matrix ctm;
362 fz_bbox bbox;
363 fz_error error;
364 fz_obj *pageobj;
366 pageobj = pdf_getpageobject (state.xref, pagenum);
367 if (!pageobj)
368 die (fz_throw ("cannot retrieve info from page %d", pagenum));
370 obj = fz_dictgets (pageobj, "CropBox");
371 if (!fz_isarray(obj)) {
372 obj = fz_dictgets (pageobj, "MediaBox");
373 if (!fz_isarray (obj))
374 die (fz_throw ("cannot find page bounds %d (%d R)",
375 fz_tonum (obj), fz_togen (obj)));
377 box = pdf_torect (obj);
379 obj = fz_dictgets (pageobj, "Rotate");
380 if (fz_isint (obj))
381 rotate = fz_toint (obj);
382 else
383 rotate = 0;
385 if (pagenum != 1
386 && (prevrotate == rotate
387 && !memcmp (&prevbox, &box, sizeof (box)))) {
388 continue;
391 memcpy (&prevbox, &box, sizeof (box));
392 prevrotate = rotate;
394 box.x0 = MIN (prevbox.x0, prevbox.x1);
395 box.y0 = MIN (prevbox.y0, prevbox.y1);
396 box.x1 = MAX (prevbox.x0, prevbox.x1);
397 box.y1 = MAX (prevbox.y0, prevbox.y1);
399 ctm = fz_identity ();
400 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
401 ctm = fz_concat (ctm, fz_rotate (rotate));
402 box2 = fz_transformrect (ctm, box);
403 w = box2.x1 - box2.x0;
405 zoom = state.w / w;
406 ctm = fz_identity ();
407 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
408 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
409 ctm = fz_concat (ctm, fz_rotate (rotate));
410 bbox = fz_roundrect (fz_transformrect (ctm, box));
412 size += sizeof (*state.pages2);
413 state.pages2 = caml_stat_resize (state.pages2, size);
415 p = &state.pages2[pindex++];
416 memcpy (&p->bbox, &bbox, sizeof (bbox));
417 memcpy (&p->ctm, &ctm, sizeof (ctm));
419 p->pagenum = pagenum - 1;
420 p->pixmap.x = bbox.x0;
421 p->pixmap.y = bbox.y0;
422 p->pixmap.w = bbox.x1 - bbox.x0;
423 p->pixmap.h = bbox.y1 - bbox.y0;
424 p->pixmap.n = 4;
427 for (i = pindex - 1; i >= 0; --i) {
428 p = &state.pages2[i];
429 printd (state.sock, "l %d %d %d",
430 p->pagenum, p->pixmap.w, p->pixmap.h);
433 b = now ();
434 printf ("layout1 took %f sec\n", b - a);
435 printd (state.sock, "C %d", state.pagecount);
438 static void *mainloop (void *unused)
440 char *p = NULL;
441 int len, ret, oldlen = 0;
443 for (;;) {
444 len = readlen (state.sock);
445 if (len == 0) {
446 errx (1, "readlen returned 0");
449 if (oldlen < len + 1) {
450 p = realloc (p, len + 1);
451 if (!p) {
452 err (1, "realloc %d failed", len + 1);
454 oldlen = len + 1;
456 readdata (state.sock, p, len);
457 p[len] = 0;
459 if (!strncmp ("open", p, 4)) {
460 char *filename = p + 5;
462 openxref (filename, NULL, 1);
464 else if (!strncmp ("free", p, 4)) {
465 void *ptr;
467 ret = sscanf(p + 4, " %p", &ptr);
468 freepage (ptr);
470 else if (!strncmp ("layout", p, 6)) {
471 int y;
473 ret = sscanf (p + 6, " %d", &y);
474 if (ret != 1) {
475 errx (1, "malformed layout `%.*s' ret=%d", len, p, ret);
478 else if (!strncmp ("geometry", p, 8)) {
479 int w, h;
480 struct page *page;
482 ret = sscanf (p + 8, " %d %d", &w, &h);
483 if (ret != 2) {
484 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
486 state.h = h;
487 if (w != state.w) {
488 state.w = w;
489 for (page = state.pages; page; page = page->prev) {
490 page->texid = 0;
493 layout1 ();
495 else if (!strncmp ("render", p, 6)) {
496 int pagenum, pindex, w, h, ret;
497 struct page *page;
498 unsigned char *pix;
500 ret = sscanf (p + 6, " %d %d %d %d", &pagenum, &pindex, &w, &h);
502 if (ret != 4) {
503 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
506 page = render (pagenum, pindex);
507 printd (state.sock, "r %d %d %d %p\n",
508 pagenum,
509 state.w,
510 state.h,
511 page);
513 else {
514 errx (1, "unknown command %.*s", len, p);
517 return NULL;
520 static void upload2 (struct page *page, int texnum, const char *cap)
522 int i;
523 int w, h;
524 double start, end;
525 struct tex *tex = &page->texs[texnum];
527 w = page->pixmap->w;
528 h = page->pixmap->h;
530 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
531 glPixelStorei(GL_UNPACK_ROW_LENGTH, w);
533 if (tex->index != -1 && state.texowners[tex->index] == tex) {
534 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[tex->index]);
536 else {
537 int subimage;
538 int index = (state.texid++ % state.texcount);
539 size_t offset = 0;
541 for (i = 0; i < texnum; ++i) {
542 offset += w * page->texs[i].h * 4;
544 subimage = 0 || state.texowners[index]
545 ? state.texowners[index]->h == tex->h
549 state.texowners[index] = tex;
550 tex->index = index;
552 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[tex->index]);
554 start = now ();
555 if (subimage) {
556 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
561 tex->h,
562 #ifndef _ARCH_PPC
563 GL_BGRA_EXT,
564 GL_UNSIGNED_INT_8_8_8_8,
565 #else
566 GL_ABGR_EXT,
567 GL_UNSIGNED_BYTE, /* INT_8_8_8_8, */
568 #endif
569 page->pixmap->samples + offset
572 else {
573 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
575 GL_RGBA8,
577 tex->h,
579 #ifndef _ARCH_PPC
580 GL_BGRA_EXT,
581 GL_UNSIGNED_INT_8_8_8_8,
582 #else
583 GL_ABGR_EXT,
584 GL_UNSIGNED_BYTE, /* INT_8_8_8_8, */
585 #endif
586 page->pixmap->samples + offset
590 end = now ();
591 printf ("%s(%s) %d(%d) took %f sec\n", cap,
592 subimage ? "sub" : "img",
593 page->pagenum, texnum, end - start);
597 CAMLprim value ml_preload (value ptr_v)
599 int i;
600 int ret;
601 void *ptr;
602 CAMLparam1 (ptr_v);
603 char *s = String_val (ptr_v);
605 ret = sscanf (s, "%p", &ptr);
606 if (ret != 1) {
607 errx (1, "cannot parse pointer `%s'", s);
609 for (i = 0; i < state.slicecount; ++i)
610 upload2 (ptr, i, "preload");
611 CAMLreturn (Val_unit);
614 #if 0
615 #define lprintf printf
616 #else
617 #define lprintf(...)
618 #endif
620 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
621 value py_v, value ptr_v)
623 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
624 int dispy = Int_val (dispy_v);
625 int w = Int_val (w_v);
626 int h = Int_val (h_v);
627 int py = Int_val (py_v);
628 char *s = String_val (ptr_v);
629 int ret;
630 const char *r;
631 void *ptr;
632 struct page *page;
633 int texnum = 0;
635 ret = sscanf (s, "%p", &ptr);
636 if (ret != 1) {
637 errx (1, "cannot parse pointer `%s'", s);
639 page = ptr;
641 w = page->pixmap->w;
642 printf ("draw[%d] %dx%d %dx%d\n",
643 page->pagenum,
644 page->pixmap->w,
645 page->pixmap->h,
649 glEnable (GL_TEXTURE_RECTANGLE_ARB);
650 #ifdef _ARCH_PPC
651 glEnable (GL_FRAGMENT_SHADER_ATI);
652 #endif
654 for (texnum = 0; texnum != state.slicecount; ++texnum) {
655 struct tex *tex = &page->texs[texnum];
656 if (tex->h > py) {
657 break;
659 py -= tex->h;
662 while (h) {
663 int th;
664 struct tex *tex = &page->texs[texnum];
666 if (texnum >= state.slicecount) {
667 abort ();
669 th = MIN (h, tex->h - py);
670 lprintf ("draw[%d, %d], h=%d th=%d py=%d dispy=%d\n",
671 page->pagenum, texnum, h, th, py, dispy);
673 upload2 (page, texnum, "upload");
675 glBegin (GL_QUADS);
677 glTexCoord2i (0, py);
678 glVertex2i (0, dispy);
680 glTexCoord2i (w, py);
681 glVertex2i (w, dispy);
683 glTexCoord2i (w, py+th);
684 glVertex2i (w, dispy + th);
686 glTexCoord2i (0, py+th);
687 glVertex2i (0, dispy + th);
689 glEnd ();
691 h -= th;
692 py = 0;
693 dispy += th;
694 texnum += 1;
697 glDisable (GL_TEXTURE_RECTANGLE_ARB);
698 #ifdef _ARCH_PPC
699 glDisable (GL_FRAGMENT_SHADER_ATI);
700 #endif
701 CAMLreturn (Val_unit);
704 static void initgl (void)
706 state.dpy = glXGetCurrentDisplay ();
707 if (!state.dpy) {
708 die (fz_throw ("glXGetCurrentDisplay"));
710 state.ctx = glXGetCurrentContext ();
711 if (!state.ctx) {
712 die (fz_throw ("glXGetCurrentContext"));
714 state.drawable = glXGetCurrentDrawable ();
715 if (!state.drawable) {
716 die (fz_throw ("glXGetCurrentDrawable"));
719 #ifdef _ARCH_PPC
720 glBindFragmentShaderATI (1);
721 glBeginFragmentShaderATI ();
723 glSampleMapATI (GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI);
725 glColorFragmentOp1ATI (GL_MOV_ATI,
726 GL_REG_1_ATI, GL_RED_BIT_ATI, GL_NONE,
727 GL_REG_0_ATI, GL_BLUE, GL_NONE);
728 glColorFragmentOp1ATI (GL_MOV_ATI,
729 GL_REG_1_ATI, GL_BLUE_BIT_ATI, GL_NONE,
730 GL_REG_0_ATI, GL_RED, GL_NONE);
731 glColorFragmentOp1ATI (
732 GL_MOV_ATI,
733 GL_REG_0_ATI, GL_RED_BIT_ATI | GL_BLUE_BIT_ATI, GL_NONE,
734 GL_REG_1_ATI, GL_NONE, GL_NONE
737 glEndFragmentShaderATI ();
738 #endif
741 CAMLprim value ml_init (value sock_v)
743 int ret;
744 fz_error error;
745 CAMLparam1 (sock_v);
747 pagesize = sysconf (_SC_PAGESIZE);
748 if (pagesize == -1) {
749 err (1, "sysconf");
752 state.texcount = 32;
753 state.slicecount = 16;
755 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
756 if (!state.texids) {
757 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
760 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
761 if (!state.texowners) {
762 err (1, "calloc texowners %zu",
763 state.texcount * sizeof (*state.texowners));
766 glGenTextures (state.texcount, state.texids);
768 state.sock = Int_val (sock_v);
769 initgl ();
771 state.cache = fz_newglyphcache ();
773 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
774 if (ret) {
775 unix_error (ret, "pthread_create", Nothing);
778 CAMLreturn (Val_unit);