Decrease select timeout value (makes things less jerky here)
[llpp.git] / link.c
blob6d25c7ad8a4891a5bb10571ef255796c82b77f50
1 /* lot's of code c&p-ed directly from mupdf */
3 #define _GNU_SOURCE
4 #include <err.h>
5 #include <regex.h>
6 #include <errno.h>
7 #include <ctype.h>
8 #include <stdio.h>
9 #include <stdarg.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <pthread.h>
13 #include <sys/poll.h>
14 #include <sys/time.h>
16 /* fugly as hell and GCC specific but... */
17 #ifdef _BIG_ENDIAN
18 #define GL_GLEXT_PROTOTYPES
19 #endif
21 #include <GL/gl.h>
22 #include <GL/glext.h>
24 #include <caml/fail.h>
25 #include <caml/alloc.h>
26 #include <caml/memory.h>
27 #include <caml/unixsupport.h>
29 #include <fitz.h>
30 #include <mupdf.h>
32 #if 0
33 #define lprintf printf
34 #else
35 #define lprintf(...)
36 #endif
38 #define ARSERT(cond) for (;;) { \
39 if (!(cond)) { \
40 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
41 } \
42 break; \
45 struct slice {
46 int texindex;
47 int w, h;
50 struct page {
51 int pageno;
52 int slicecount;
53 fz_textspan *text;
54 fz_pixmap *pixmap;
55 pdf_page *drawpage;
56 struct pagedim *pagedim;
57 struct page *prev;
58 struct slice slices[];
61 struct pagedim {
62 int pageno;
63 int rotate;
64 fz_rect box;
65 fz_bbox bbox;
66 fz_matrix ctm;
69 struct {
70 int sock;
71 int sliceheight;
72 pthread_t thread;
73 struct page *pages;
74 struct pagedim *pagedims;
75 int pagecount;
76 int pagedimcount;
77 pdf_xref *xref;
78 fz_glyphcache *cache;
79 int w, h;
81 int useatifs;
83 int texindex;
84 int texcount;
85 GLuint *texids;
87 GLenum texform;
88 GLenum texty;
90 int lotsamemory;
92 int *pagetbl;
93 struct {
94 int w, h;
95 struct slice *slice;
96 } *texowners;
97 } state;
99 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
101 static void lock (const char *cap)
103 int ret = pthread_mutex_lock (&mutex);
104 if (ret) {
105 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
109 static void unlock (const char *cap)
111 int ret = pthread_mutex_unlock (&mutex);
112 if (ret) {
113 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
117 static int trylock (const char *cap)
119 int ret = pthread_mutex_trylock (&mutex);
121 if (ret && ret != EBUSY) {
122 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
124 return ret == EBUSY;
127 static void *parse_pointer (const char *cap, const char *s)
129 int ret;
130 void *ptr;
132 ret = sscanf (s, "%p", &ptr);
133 if (ret != 1) {
134 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
136 return ptr;
139 static int hasdata (int sock)
141 int ret;
142 struct pollfd pfd;
144 pfd.fd = sock;
145 pfd.events = POLLIN;
146 ret = poll (&pfd, 1, 0);
147 if (ret == 0) {
148 return 0;
150 if (ret != 1) {
151 err (1, "poll");
153 return pfd.revents & POLLIN;
156 static double now (void)
158 struct timeval tv;
160 if (gettimeofday (&tv, NULL)) {
161 err (1, "gettimeofday");
163 return tv.tv_sec + tv.tv_usec*1e-6;
166 static void readdata (int fd, char *p, int size)
168 ssize_t n;
170 n = read (fd, p, size);
171 if (n - size) {
172 err (1, "read (req %d, ret %zd)", size, n);
176 static void writedata (int fd, char *p, int size)
178 char buf[4];
179 ssize_t n;
181 buf[0] = (size >> 24) & 0xff;
182 buf[1] = (size >> 16) & 0xff;
183 buf[2] = (size >> 8) & 0xff;
184 buf[3] = (size >> 0) & 0xff;
186 n = write (fd, buf, 4);
187 if (n != 4) {
188 err (1, "write %zd", n);
191 n = write (fd, p, size);
192 if (n - size) {
193 err (1, "write (req %d, ret %zd)", size, n);
197 static void __attribute__ ((format (printf, 2, 3)))
198 printd (int fd, const char *fmt, ...)
200 int len;
201 va_list ap;
202 char buf[200];
204 va_start (ap, fmt);
205 len = vsnprintf (buf, sizeof (buf), fmt, ap);
206 va_end (ap);
207 writedata (fd, buf, len);
210 static void die (fz_error error)
212 fz_catch (error, "aborting");
213 if (state.xref)
214 pdf_closexref (state.xref);
215 exit (1);
218 static void openxref (char *filename)
220 int fd;
221 fz_error error;
222 fz_stream *file;
224 fd = open (filename, O_BINARY | O_RDONLY, 0666);
225 if (fd < 0)
226 die (fz_throw ("cannot open file '%s'", filename));
228 file = fz_openfile (fd);
229 state.xref = pdf_openxref (file);
230 if (!state.xref)
231 die (fz_throw ("cannot open PDF file '%s'", filename));
232 fz_dropstream (file);
234 if (pdf_needspassword (state.xref)) {
235 die (fz_throw ("password protected"));
238 error = pdf_loadpagetree (state.xref);
239 if (error) {
240 die (fz_throw ("cannot load page tree"));
243 state.pagecount = pdf_getpagecount (state.xref);
244 state.pagetbl = stat_alloc (state.pagecount * sizeof (*state.pagetbl));
247 static int readlen (int fd)
249 ssize_t n;
250 char p[4];
252 n = read (fd, p, 4);
253 if (n != 4) {
254 err (1, "read %zd", n);
257 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
260 static void freepage (struct page *page)
262 int i;
263 struct page *p;
265 fz_droppixmap (page->pixmap);
266 for (p = state.pages; p; p = p->prev) {
267 if (p->prev == page) {
268 p->prev = page->prev;
269 break;
272 for (i = 0; i < page->slicecount; ++i) {
273 struct slice *s = &page->slices[i];
274 if (s->texindex != -1) {
275 if (state.texowners[s->texindex].slice == s) {
276 state.texowners[s->texindex].slice = NULL;
277 ARSERT (state.texowners[s->texindex].w == s->w);
278 ARSERT (state.texowners[s->texindex].h >= s->h);
282 if (page->text) {
283 fz_freetextspan (page->text);
285 if (page->drawpage) {
286 pdf_freepage (page->drawpage);
289 free (page);
292 static void subdivide (struct page *p)
294 int i;
295 int h = p->pixmap->h;
296 int th = MIN (h, state.sliceheight);
298 for (i = 0; i < p->slicecount; ++i) {
299 struct slice *s = &p->slices[i];
300 s->texindex = -1;
301 s->h = MIN (th, h);
302 s->w = p->pixmap->w;
303 h -= th;
307 static void *render (int pageno, int pindex)
309 fz_error error;
310 int slicecount;
311 fz_obj *pageobj;
312 struct page *page;
313 double start, end;
314 pdf_page *drawpage;
315 fz_displaylist *list;
316 fz_device *idev, *mdev;
317 struct pagedim *pagedim;
319 start = now ();
320 /* printd (state.sock, "T rendering %d", pageno); */
321 pdf_flushxref (state.xref, 0);
323 pagedim = &state.pagedims[pindex];
324 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
325 + state.sliceheight - 1) / state.sliceheight;
326 slicecount += slicecount == 0;
328 page = calloc (sizeof (*page)
329 + (slicecount * sizeof (struct slice)), 1);
330 if (!page) {
331 err (1, "calloc page %d\n", pageno);
333 page->slicecount = slicecount;
334 page->prev = state.pages;
335 state.pages = page;
337 pageobj = pdf_getpageobject (state.xref, pageno);
338 if (!pageobj)
339 die (fz_throw ("cannot retrieve info from page %d", pageno));
341 error = pdf_loadpage (&drawpage, state.xref, pageobj);
342 if (error)
343 die (error);
345 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, pagedim->bbox);
346 if (error)
347 die (error);
348 fz_clearpixmap (page->pixmap, 0xFF);
350 list = fz_newdisplaylist ();
351 if (!list)
352 die (fz_throw ("fz_newdisplaylist failed"));
354 mdev = fz_newlistdevice (list);
355 error = pdf_runcontentstream (mdev, fz_identity (), state.xref,
356 drawpage->resources,
357 drawpage->contents);
358 if (error)
359 die (error);
360 fz_freedevice (mdev);
362 idev = fz_newdrawdevice (state.cache, page->pixmap);
363 if (!idev)
364 die (fz_throw ("fz_newdrawdevice failed"));
365 fz_executedisplaylist (list, idev, pagedim->ctm);
366 fz_freedevice (idev);
368 fz_freedisplaylist (list);
370 page->drawpage = drawpage;
371 page->pagedim = pagedim;
372 page->pageno = pageno;
373 subdivide (page);
374 end = now ();
376 if (!state.lotsamemory) {
377 pdf_agestoreditems (state.xref->store);
378 pdf_evictageditems (state.xref->store);
381 /* printd (state.sock, "T rendering %d took %f sec", pageno, end - start); */
382 return page;
385 static void initpdims (void)
387 int pageno;
388 double start, end;
390 start = now ();
391 for (pageno = 0; pageno < state.pagecount; ++pageno) {
392 int rotate;
393 fz_rect box;
394 struct pagedim *p;
395 fz_obj *obj, *pageobj;
397 pageobj = pdf_getpageobject (state.xref, pageno + 1);
399 obj = fz_dictgets (pageobj, "CropBox");
400 if (!fz_isarray (obj)) {
401 obj = fz_dictgets (pageobj, "MediaBox");
402 if (!fz_isarray (obj)) {
403 die (fz_throw ("cannot find page bounds %d (%d Rd)",
404 fz_tonum (pageobj), fz_togen (pageobj)));
407 box = pdf_torect (obj);
409 obj = fz_dictgets (pageobj, "Rotate");
410 if (fz_isint (obj)) {
411 rotate = fz_toint (obj);
413 else {
414 rotate = 0;
417 state.pagetbl[pageno] = fz_tonum (state.xref->pagerefs[pageno]);
418 p = &state.pagedims[state.pagedimcount - 1];
419 if ((state.pagedimcount == 0)
420 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
421 size_t size;
423 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
424 state.pagedims = realloc (state.pagedims, size);
425 if (!state.pagedims) {
426 err (1, "realloc pagedims to %zu (%d elems)",
427 size, state.pagedimcount + 1);
429 p = &state.pagedims[state.pagedimcount++];
430 p->rotate = rotate;
431 p->box = box;
432 p->pageno = pageno;
435 end = now ();
436 printd (state.sock, "T Processed %d pages in %f seconds",
437 state.pagecount, end - start);
440 static void layout (void)
442 int pindex;
443 fz_matrix ctm;
444 fz_rect box, box2;
445 double zoom, w;
446 struct pagedim *p = state.pagedims;
448 pindex = 0;
449 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
450 box.x0 = MIN (p->box.x0, p->box.x1);
451 box.y0 = MIN (p->box.y0, p->box.y1);
452 box.x1 = MAX (p->box.x0, p->box.x1);
453 box.y1 = MAX (p->box.y0, p->box.y1);
455 ctm = fz_identity ();
456 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
457 ctm = fz_concat (ctm, fz_rotate (p->rotate));
458 box2 = fz_transformrect (ctm, box);
459 w = box2.x1 - box2.x0;
461 zoom = (state.w / w);
462 ctm = fz_identity ();
463 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
464 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
465 ctm = fz_concat (ctm, fz_rotate (p->rotate));
466 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
467 memcpy (&p->ctm, &ctm, sizeof (ctm));
470 while (p-- != state.pagedims) {
471 printd (state.sock, "l %d %d %d",
472 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
476 static void recurse_outline (pdf_outline *outline, int level)
478 while (outline) {
479 int i, num;
480 fz_obj *obj;
481 int top = 0;
482 int pageno = -1;
484 if (!outline->link) goto next;
486 obj = outline->link->dest;
487 if (fz_isindirect (obj)) {
488 num = fz_tonum (obj);
490 for (i = 0; i < state.pagecount; ++i) {
491 if (state.pagetbl[i] == num) {
492 pageno = i;
493 break;
497 else if (fz_isarray (obj)) {
498 fz_obj *obj2;
500 obj2 = fz_arrayget (obj, 0);
501 if (fz_isint (obj2)) {
502 pageno = fz_toint (obj2);
504 else {
505 num = fz_tonum (obj2);
506 for (i = 0; i < state.pagecount; ++i) {
507 if (state.pagetbl[i] == num) {
508 pageno = i;
509 break;
514 if (fz_arraylen (obj) > 3) {
515 fz_point p;
516 struct pagedim *pagedim = state.pagedims;
518 for (i = 0; i < state.pagedimcount; ++i) {
519 if (state.pagedims[i].pageno > pageno)
520 break;
521 pagedim = &state.pagedims[i];
524 p.x = fz_toint (fz_arrayget (obj, 2));
525 p.y = fz_toint (fz_arrayget (obj, 3));
526 p = fz_transformpoint (pagedim->ctm, p);
527 top = p.y;
531 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
532 printd (state.sock, "o %d %d %d %s",
533 level, pageno, top, outline->title);
534 next:
535 if (outline->child) {
536 recurse_outline (outline->child, level + 1);
538 outline = outline->next;
542 static void process_outline (void)
544 pdf_outline *outline;
546 outline = pdf_loadoutline (state.xref);
547 if (outline) {
548 recurse_outline (outline, 0);
549 pdf_freeoutline (outline);
553 static int comparespans (const void *l, const void *r)
555 fz_textspan *const*ls = l;
556 fz_textspan *const*rs = r;
558 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
561 /* wishful thinking function */
562 static void search (regex_t *re, int pageno, int y, int forward)
564 int i, j;
565 int ret;
566 char *p;
567 char buf[256];
568 fz_error error;
569 fz_obj *pageobj;
570 fz_device *tdev;
571 pdf_page *drawpage;
572 fz_textspan *text, *span, **pspan;
573 struct pagedim *pdim, *pdimprev;
574 int stop = 0;
575 int niters = 0;
576 int nspans;
577 double start, end;
579 start = now ();
580 while (pageno >= 0 && pageno < state.pagecount && !stop) {
581 if (niters++ == 5) {
582 if (!state.lotsamemory) {
583 pdf_agestoreditems (state.xref->store);
584 pdf_evictageditems (state.xref->store);
586 niters = 0;
587 if (hasdata (state.sock)) {
588 printd (state.sock, "T attention requested aborting search at %d",
589 pageno);
590 stop = 1;
592 else {
593 printd (state.sock, "T searching in page %d", pageno);
596 pdimprev = NULL;
597 for (i = 0; i < state.pagedimcount; ++i) {
598 pdim = &state.pagedims[i];
599 if (pdim->pageno == pageno) {
600 goto found;
602 if (pdim->pageno > pageno) {
603 pdim = pdimprev;
604 goto found;
606 pdimprev = pdim;
608 pdim = pdimprev;
609 found:
611 pageobj = pdf_getpageobject (state.xref, pageno + 1);
612 if (!pageobj)
613 die (fz_throw ("cannot retrieve info from page %d", pageno));
615 error = pdf_loadpage (&drawpage, state.xref, pageobj);
616 if (error)
617 die (error);
619 text = fz_newtextspan ();
620 tdev = fz_newtextdevice (text);
621 error = pdf_runcontentstream (tdev, pdim->ctm, state.xref,
622 drawpage->resources,
623 drawpage->contents);
624 if (error) die (error);
625 fz_freedevice (tdev);
627 nspans = 0;
628 for (span = text; span; span = span->next) {
629 nspans++;
631 pspan = malloc (sizeof (void *) * nspans);
632 if (!pspan) {
633 err (1, "malloc span pointers %zu", sizeof (void *) * nspans);
635 for (i = 0, span = text; span; span = span->next, ++i) {
636 pspan[i] = span;
638 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
640 j = forward ? 0 : nspans - 1;
641 while (nspans--) {
642 regmatch_t rm;
644 span = pspan[j];
645 j += forward ? 1 : -1;
646 p = buf;
647 /* XXX: spans are not sorted "visually" */
648 for (i = 0; i < MIN (span->len, sizeof (buf) - 1); ++i) {
649 if (forward) {
650 if (span->text[i].bbox.y0 < y + 1) {
651 continue;
654 else {
655 if (span->text[i].bbox.y0 > y - 1) {
656 continue;
659 if (span->text[i].c < 256) {
660 *p++ = span->text[i].c;
662 else {
663 *p++ = '?';
666 if (p == buf) {
667 continue;
669 *p++ = 0;
671 ret = regexec (re, buf, 1, &rm, 0);
672 if (ret) {
673 if (ret != REG_NOMATCH) {
674 size_t size;
675 char errbuf[80];
676 size = regerror (ret, re, errbuf, sizeof (errbuf));
677 printd (state.sock,
678 "T regexec error `%.*s'",
679 (int) size, errbuf);
680 fz_freetextspan (text);
681 pdf_freepage (drawpage);
682 free (pspan);
683 return;
686 else {
687 fz_rect r;
689 r.x0 = span->text[rm.rm_so].bbox.x0 - pdim->bbox.x0;
690 r.y0 = span->text[rm.rm_so].bbox.y0;
691 r.x1 = span->text[rm.rm_eo - 1].bbox.x1 - pdim->bbox.x0;
692 r.y1 = span->text[rm.rm_eo - 1].bbox.y1;
694 if (!stop) {
695 printd (state.sock, "F %d %d %f %f %f %f",
696 pageno, 1,
697 r.x0, r.y0,
698 r.x1, r.y1);
700 else {
701 printd (state.sock, "R %d %d %f %f %f %f",
702 pageno, 2,
703 r.x0, r.y0,
704 r.x1, r.y1);
706 printd (state.sock, "T found at %d `%.*s' %f in %f sec",
707 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
708 span->text[0].bbox.y0 - drawpage->mediabox.y0,
709 now () - start);
710 stop = 1;
713 if (forward) {
714 pageno += 1;
715 y = 0;
717 else {
718 pageno -= 1;
719 y = INT_MAX;
721 fz_freetextspan (text);
722 pdf_freepage (drawpage);
723 free (pspan);
725 end = now ();
726 if (!stop) {
727 printd (state.sock, "T no matches %f sec", end - start);
729 printd (state.sock, "D");
732 static void *mainloop (void *unused)
734 char *p = NULL;
735 int len, ret, oldlen = 0;
737 for (;;) {
738 len = readlen (state.sock);
739 if (len == 0) {
740 errx (1, "readlen returned 0");
743 if (oldlen < len + 1) {
744 p = realloc (p, len + 1);
745 if (!p) {
746 err (1, "realloc %d failed", len + 1);
748 oldlen = len + 1;
750 readdata (state.sock, p, len);
751 p[len] = 0;
753 if (!strncmp ("open", p, 4)) {
754 fz_obj *obj;
755 char *filename = p + 5;
757 openxref (filename);
758 initpdims ();
760 obj = fz_dictgets (state.xref->trailer, "Info");
761 if (obj) {
762 obj = fz_dictgets (obj, "Title");
763 if (obj) {
764 printd (state.sock, "t %s", pdf_toutf8 (obj));
768 else if (!strncmp ("free", p, 4)) {
769 void *ptr;
771 ret = sscanf (p + 4, " %p", &ptr);
772 if (ret != 1) {
773 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
775 lock ("free");
776 freepage (ptr);
777 unlock ("free");
778 printd (state.sock, "d");
780 else if (!strncmp ("search", p, 6)) {
781 int icase, pageno, y, ret, len2, forward;
782 char *pattern;
783 regex_t re;
785 ret = sscanf (p + 6, " %d %d %d %d,%n",
786 &icase, &pageno, &y, &forward, &len2);
787 if (ret != 4) {
788 errx (1, "malformed search `%s' ret=%d", p, ret);
791 pattern = p + 6 + len2;
792 ret = regcomp (&re, pattern,
793 REG_EXTENDED | (icase ? REG_ICASE : 0));
794 if (ret) {
795 char errbuf[80];
796 size_t size;
798 size = regerror (ret, &re, errbuf, sizeof (errbuf));
799 printd (state.sock, "T regcomp failed `%.*s'",
800 (int) size, errbuf);
802 else {
803 search (&re, pageno, y, forward);
804 regfree (&re);
807 else if (!strncmp ("geometry", p, 8)) {
808 int w, h;
810 printd (state.sock, "c");
811 ret = sscanf (p + 8, " %d %d", &w, &h);
812 if (ret != 2) {
813 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
815 state.h = h;
816 if (w != state.w) {
817 int i;
818 state.w = w;
819 for (i = 0; i < state.texcount; ++i) {
820 state.texowners[i].slice = NULL;
823 lock ("geometry");
824 layout ();
825 process_outline ();
826 unlock ("geometry");
827 printd (state.sock, "C %d", state.pagecount);
829 else if (!strncmp ("render", p, 6)) {
830 int pageno, pindex, w, h, ret;
831 struct page *page;
833 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
834 if (ret != 4) {
835 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
838 page = render (pageno, pindex);
839 printd (state.sock, "r %d %d %d %p\n",
840 pageno,
841 state.w,
842 state.h,
843 page);
845 else {
846 errx (1, "unknown command %.*s", len, p);
849 return NULL;
852 static void upload2 (struct page *page, int slicenum, const char *cap)
854 int i;
855 int w, h;
856 double start, end;
857 struct slice *slice = &page->slices[slicenum];
859 w = page->pixmap->w;
860 h = page->pixmap->h;
862 ARSERT (w == slice->w);
863 if (slice->texindex != -1
864 && state.texowners[slice->texindex].slice == slice) {
865 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
867 else {
868 int subimage = 0;
869 int index = (state.texindex++ % state.texcount);
870 size_t offset = 0;
872 for (i = 0; i < slicenum; ++i) {
873 offset += w * page->slices[i].h * 4;
876 if (state.texowners[index].w == slice->w) {
877 if (state.texowners[index].h >= slice->h ) {
878 subimage = 1;
880 else {
881 state.texowners[index].h = slice->h;
884 else {
885 state.texowners[index].h = slice->h;
888 state.texowners[index].slice = slice;
889 state.texowners[index].w = slice->w;
890 slice->texindex = index;
892 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
893 start = now ();
894 if (subimage) {
896 GLenum err = glGetError ();
897 if (err != GL_NO_ERROR) {
898 printf ("\e[0;31mERROR1 %d %d %#x\e[0m\n", w, slice->h, err);
899 abort ();
902 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
907 slice->h,
908 state.texform,
909 state.texty,
910 page->pixmap->samples + offset
913 GLenum err = glGetError ();
914 if (err != GL_NO_ERROR) {
915 printf ("\e[0;31mERROR %d %d %#x\e[0m\n", w, slice->h, err);
916 abort ();
920 else {
921 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
923 GL_RGBA8,
925 slice->h,
927 state.texform,
928 state.texty,
929 page->pixmap->samples + offset
933 end = now ();
934 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
935 subimage ? "sub" : "img",
936 page->pageno, slicenum,
937 slice->w, slice->h,
938 state.texids[slice->texindex],
939 end - start);
943 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
944 value py_v, value ptr_v)
946 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
947 int dispy = Int_val (dispy_v);
948 int w = Int_val (w_v);
949 int h = Int_val (h_v);
950 int py = Int_val (py_v);
951 char *s = String_val (ptr_v);
952 int ret;
953 void *ptr;
954 struct page *page;
955 int slicenum = 0;
957 if (trylock ("ml_draw")) {
958 goto done;
961 ret = sscanf (s, "%p", &ptr);
962 if (ret != 1) {
963 errx (1, "cannot parse pointer `%s'", s);
965 page = ptr;
967 w = page->pixmap->w;
969 ARSERT (h >= 0 && "ml_draw wrong h");
971 glEnable (GL_TEXTURE_RECTANGLE_ARB);
972 if (state.useatifs) {
973 glEnable (GL_FRAGMENT_SHADER_ATI);
976 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
977 struct slice *slice = &page->slices[slicenum];
978 if (slice->h > py) {
979 break;
981 py -= slice->h;
984 h = MIN (state.h, h);
985 while (h) {
986 int th;
987 struct slice *slice = &page->slices[slicenum];
989 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
991 th = MIN (h, slice->h - py);
992 upload2 (page, slicenum, "upload");
994 glBegin (GL_QUADS);
996 glTexCoord2i (0, py);
997 glVertex2i (0, dispy);
999 glTexCoord2i (w, py);
1000 glVertex2i (w, dispy);
1002 glTexCoord2i (w, py+th);
1003 glVertex2i (w, dispy + th);
1005 glTexCoord2i (0, py+th);
1006 glVertex2i (0, dispy + th);
1008 glEnd ();
1010 h -= th;
1011 py = 0;
1012 dispy += th;
1013 slicenum += 1;
1016 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1017 if (state.useatifs) {
1018 glDisable (GL_FRAGMENT_SHADER_ATI);
1021 unlock ("ml_draw");
1022 done:
1023 CAMLreturn (Val_unit);
1026 static pdf_link *getlink (struct page *page, int x, int y)
1028 fz_point p;
1029 fz_matrix ctm;
1030 pdf_link *link;
1032 p.x = x;
1033 p.y = y;
1035 ctm = fz_invertmatrix (page->pagedim->ctm);
1036 p = fz_transformpoint (ctm, p);
1038 for (link = page->drawpage->links; link; link = link->next) {
1039 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1040 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1041 if (link->kind == PDF_LGOTO) {
1042 return link;
1047 return NULL;
1050 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
1052 CAMLparam3 (ptr_v, x_v, y_v);
1053 char *s = String_val (ptr_v);
1054 int ret;
1056 if (trylock ("ml_checklink")) {
1057 ret = 0;
1059 else {
1060 ret = NULL != getlink (parse_pointer ("ml_checklink", s),
1061 Int_val (x_v), Int_val (y_v));
1062 unlock ("ml_checklink");
1064 CAMLreturn (Val_bool (ret));
1067 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
1069 CAMLparam3 (ptr_v, x_v, y_v);
1070 CAMLlocal2 (ret_v, tup_v);
1071 pdf_link *link;
1072 struct page *page;
1073 char *s = String_val (ptr_v);
1075 if (trylock ("ml_getlink")) {
1076 ret_v = Val_int (0);
1077 goto done;
1080 page = parse_pointer ("ml_getlink", s);
1082 link = getlink (page, Int_val (x_v), Int_val (y_v));
1083 if (link) {
1084 int pageno;
1085 fz_point p;
1086 fz_obj *obj;
1088 pageno = -1;
1089 obj = fz_arrayget (link->dest, 0);
1090 if (fz_isindirect (obj)) {
1091 pageno = pdf_findpageobject (state.xref, obj) - 1;
1093 else if (fz_isint (obj)) {
1094 pageno = fz_toint (obj);
1097 if (fz_arraylen (link->dest) > 3) {
1098 p.x = fz_toint (fz_arrayget (link->dest, 2));
1099 p.y = fz_toint (fz_arrayget (link->dest, 3));
1100 p = fz_transformpoint (page->pagedim->ctm, p);
1102 else {
1103 p.x = 0.0;
1104 p.y = 0.0;
1107 tup_v = caml_alloc_tuple (2);
1108 ret_v = caml_alloc_small (1, 1);
1109 Field (tup_v, 0) = Val_int (pageno);
1110 Field (tup_v, 1) = Val_int (p.y);
1111 Field (ret_v, 0) = tup_v;
1113 else {
1114 ret_v = Val_int (0);
1116 unlock ("ml_getlink");
1118 done:
1119 CAMLreturn (ret_v);
1122 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1124 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1125 fz_matrix ctm;
1126 fz_point p1, p2;
1127 struct page *page;
1128 fz_textspan *span;
1129 char *s = String_val (ptr_v);
1130 int rectsel = Bool_val (rectsel_v);
1131 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1133 /* stop GCC from complaining about uninitialized variables */
1134 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1136 if (trylock ("ml_gettext")) {
1137 goto done;
1140 page = parse_pointer ("ml_gettext", s);
1142 oy = Int_val (oy_v);
1143 p1.x = Int_val (Field (rect_v, 0));
1144 p1.y = Int_val (Field (rect_v, 1));
1145 p2.x = Int_val (Field (rect_v, 2));
1146 p2.y = Int_val (Field (rect_v, 3));
1148 if (0) {
1149 glEnable (GL_BLEND);
1150 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1151 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1152 glColor4f (0, 0, 0, 0.2);
1153 glRecti (p1.x, p1.y, p2.x, p2.y);
1154 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1155 glDisable (GL_BLEND);
1158 ctm = page->pagedim->ctm;
1159 if (!page->text) {
1160 fz_error error;
1161 fz_device *tdev;
1163 page->text = fz_newtextspan ();
1164 tdev = fz_newtextdevice (page->text);
1165 error = pdf_runcontentstream (tdev, page->pagedim->ctm, state.xref,
1166 page->drawpage->resources,
1167 page->drawpage->contents);
1168 if (error) die (error);
1169 fz_freedevice (tdev);
1172 printf ("\ec");
1174 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1175 p1.x += page->pixmap->x;
1176 p1.y += page->pixmap->y;
1177 p2.x += page->pixmap->x;
1178 p2.y += page->pixmap->y;
1179 x0 = p1.x;
1180 y0 = p1.y;
1181 x1 = p2.x;
1182 y1 = p2.y;
1183 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1185 for (span = page->text; span; span = span->next) {
1186 int seen = 0;
1188 /* fz_debugtextspanxml (span); */
1189 for (i = 0; i < span->len; ++i) {
1190 long c;
1192 bx0 = span->text[i].bbox.x0;
1193 bx1 = span->text[i].bbox.x1;
1194 by0 = span->text[i].bbox.y0 + oy;
1195 by1 = span->text[i].bbox.y1 + oy;
1197 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1198 if (!seen) {
1199 rx0 = bx0 - page->pixmap->x;
1200 rx1 = bx1 - page->pixmap->x;
1201 ry0 = by0;
1202 ry1 = by1;
1205 seen = 1;
1206 c = span->text[i].c;
1207 if (c < 256) {
1208 if ((isprint (c) && !isspace (c))) {
1209 if (!rectsel) {
1210 bx0 -= page->pixmap->x;
1211 bx1 -= page->pixmap->x;
1212 glEnable (GL_BLEND);
1213 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1214 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1215 glColor4f (0.5, 0.5, 0.0, 0.6);
1216 glRecti (bx0, by0, bx1, by1);
1217 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1218 glDisable (GL_BLEND);
1220 if (isprint (c) || c ==' ') {
1221 rx1 = bx1;
1222 ry1 = by1;
1225 putc (c, stdout);
1227 else {
1228 putc ('?', stdout);
1233 if (rectsel) {
1234 if (seen) {
1235 glEnable (GL_BLEND);
1236 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1237 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1238 glColor4f (0.5, 0.5, 0.0, 0.6);
1239 glRecti (rx0, ry0, rx1, ry1);
1240 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1241 glDisable (GL_BLEND);
1245 if (seen && span->eol) {
1246 x0 = page->pixmap->x;
1247 putc ('\n', stdout);
1250 unlock ("ml_gettext");
1252 done:
1253 CAMLreturn (Val_unit);
1256 CAMLprim value ml_getpagewh (value pagedimno_v)
1258 CAMLparam1 (pagedimno_v);
1259 CAMLlocal1 (ret_v);
1260 int pagedimno = Int_val (pagedimno_v);
1262 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1263 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1264 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1265 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1266 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1267 CAMLreturn (ret_v);
1270 static void initgl (void)
1272 #ifdef _BIG_ENDIAN
1273 if (strstr ((char *) glGetString (GL_EXTENSIONS),
1274 "GL_ATI_fragment_shader")) {
1275 /* Here, with MESA, rv280, powerpc32: BGRA(rev) is slow while
1276 ABGR is fast, so fix things in the shader */
1277 state.texform = GL_ABGR_EXT;
1278 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1280 glBindFragmentShaderATI (1);
1281 glBeginFragmentShaderATI ();
1283 glSampleMapATI (GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI);
1285 glColorFragmentOp1ATI (GL_MOV_ATI,
1286 GL_REG_1_ATI, GL_RED_BIT_ATI, GL_NONE,
1287 GL_REG_0_ATI, GL_BLUE, GL_NONE);
1288 glColorFragmentOp1ATI (GL_MOV_ATI,
1289 GL_REG_1_ATI, GL_BLUE_BIT_ATI, GL_NONE,
1290 GL_REG_0_ATI, GL_RED, GL_NONE);
1291 glColorFragmentOp1ATI (
1292 GL_MOV_ATI,
1293 GL_REG_0_ATI, GL_RED_BIT_ATI | GL_BLUE_BIT_ATI, GL_NONE,
1294 GL_REG_1_ATI, GL_NONE, GL_NONE
1297 glEndFragmentShaderATI ();
1298 state.useatifs = 1;
1300 else {
1301 state.texform = GL_BGRA_EXT;
1302 state.texty = GL_UNSIGNED_INT_8_8_8_8_REV;
1304 #else
1305 state.texform = GL_BGRA_EXT;
1306 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1307 #endif
1310 CAMLprim value ml_init (value sock_v)
1312 int ret;
1313 CAMLparam1 (sock_v);
1315 state.texcount = 256;
1316 state.sliceheight = 24;
1318 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1319 if (!state.texids) {
1320 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1323 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1324 if (!state.texowners) {
1325 err (1, "calloc texowners %zu",
1326 state.texcount * sizeof (*state.texowners));
1329 glGenTextures (state.texcount, state.texids);
1331 state.sock = Int_val (sock_v);
1332 initgl ();
1334 state.cache = fz_newglyphcache ();
1335 if (!state.cache) {
1336 errx (1, "fz_newglyphcache failed");
1339 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1340 if (ret) {
1341 errx (1, "pthread_create: %s", strerror (errno));
1344 CAMLreturn (Val_unit);