Do not use display lists
[llpp.git] / link.c
blobf9c60df255f107ff557044db78b7df6afd68e26e
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_device *idev;
316 struct pagedim *pagedim;
318 start = now ();
319 /* printd (state.sock, "T rendering %d", pageno); */
320 pdf_flushxref (state.xref, 0);
322 pagedim = &state.pagedims[pindex];
323 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
324 + state.sliceheight - 1) / state.sliceheight;
325 slicecount += slicecount == 0;
327 page = calloc (sizeof (*page)
328 + (slicecount * sizeof (struct slice)), 1);
329 if (!page) {
330 err (1, "calloc page %d\n", pageno);
332 page->slicecount = slicecount;
333 page->prev = state.pages;
334 state.pages = page;
336 pageobj = pdf_getpageobject (state.xref, pageno);
337 if (!pageobj)
338 die (fz_throw ("cannot retrieve info from page %d", pageno));
340 error = pdf_loadpage (&drawpage, state.xref, pageobj);
341 if (error)
342 die (error);
344 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, pagedim->bbox);
345 if (error)
346 die (error);
347 fz_clearpixmap (page->pixmap, 0xFF);
349 idev = fz_newdrawdevice (state.cache, page->pixmap);
350 if (!idev)
351 die (fz_throw ("fz_newdrawdevice failed"));
352 error = pdf_runcontentstream (idev, pagedim->ctm, state.xref,
353 drawpage->resources,
354 drawpage->contents);
355 fz_freedevice (idev);
357 page->drawpage = drawpage;
358 page->pagedim = pagedim;
359 page->pageno = pageno;
360 subdivide (page);
361 end = now ();
363 if (!state.lotsamemory) {
364 pdf_agestoreditems (state.xref->store);
365 pdf_evictageditems (state.xref->store);
368 printd (state.sock, "T rendering %d took %f sec", pageno, end - start);
369 return page;
372 static void initpdims (void)
374 int pageno;
375 double start, end;
377 start = now ();
378 for (pageno = 0; pageno < state.pagecount; ++pageno) {
379 int rotate;
380 fz_rect box;
381 struct pagedim *p;
382 fz_obj *obj, *pageobj;
384 pageobj = pdf_getpageobject (state.xref, pageno + 1);
386 obj = fz_dictgets (pageobj, "CropBox");
387 if (!fz_isarray (obj)) {
388 obj = fz_dictgets (pageobj, "MediaBox");
389 if (!fz_isarray (obj)) {
390 die (fz_throw ("cannot find page bounds %d (%d Rd)",
391 fz_tonum (pageobj), fz_togen (pageobj)));
394 box = pdf_torect (obj);
396 obj = fz_dictgets (pageobj, "Rotate");
397 if (fz_isint (obj)) {
398 rotate = fz_toint (obj);
400 else {
401 rotate = 0;
404 state.pagetbl[pageno] = fz_tonum (state.xref->pagerefs[pageno]);
405 p = &state.pagedims[state.pagedimcount - 1];
406 if ((state.pagedimcount == 0)
407 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
408 size_t size;
410 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
411 state.pagedims = realloc (state.pagedims, size);
412 if (!state.pagedims) {
413 err (1, "realloc pagedims to %zu (%d elems)",
414 size, state.pagedimcount + 1);
416 p = &state.pagedims[state.pagedimcount++];
417 p->rotate = rotate;
418 p->box = box;
419 p->pageno = pageno;
422 end = now ();
423 printd (state.sock, "T Processed %d pages in %f seconds",
424 state.pagecount, end - start);
427 static void layout (void)
429 int pindex;
430 fz_matrix ctm;
431 fz_rect box, box2;
432 double zoom, w;
433 struct pagedim *p = state.pagedims;
435 pindex = 0;
436 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
437 box.x0 = MIN (p->box.x0, p->box.x1);
438 box.y0 = MIN (p->box.y0, p->box.y1);
439 box.x1 = MAX (p->box.x0, p->box.x1);
440 box.y1 = MAX (p->box.y0, p->box.y1);
442 ctm = fz_identity ();
443 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
444 ctm = fz_concat (ctm, fz_rotate (p->rotate));
445 box2 = fz_transformrect (ctm, box);
446 w = box2.x1 - box2.x0;
448 zoom = (state.w / w);
449 ctm = fz_identity ();
450 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
451 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
452 ctm = fz_concat (ctm, fz_rotate (p->rotate));
453 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
454 memcpy (&p->ctm, &ctm, sizeof (ctm));
457 while (p-- != state.pagedims) {
458 printd (state.sock, "l %d %d %d",
459 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
463 static void recurse_outline (pdf_outline *outline, int level)
465 while (outline) {
466 int i, num;
467 fz_obj *obj;
468 int top = 0;
469 int pageno = -1;
471 if (!outline->link) goto next;
473 obj = outline->link->dest;
474 if (fz_isindirect (obj)) {
475 num = fz_tonum (obj);
477 for (i = 0; i < state.pagecount; ++i) {
478 if (state.pagetbl[i] == num) {
479 pageno = i;
480 break;
484 else if (fz_isarray (obj)) {
485 fz_obj *obj2;
487 obj2 = fz_arrayget (obj, 0);
488 if (fz_isint (obj2)) {
489 pageno = fz_toint (obj2);
491 else {
492 num = fz_tonum (obj2);
493 for (i = 0; i < state.pagecount; ++i) {
494 if (state.pagetbl[i] == num) {
495 pageno = i;
496 break;
501 if (fz_arraylen (obj) > 3) {
502 fz_point p;
503 struct pagedim *pagedim = state.pagedims;
505 for (i = 0; i < state.pagedimcount; ++i) {
506 if (state.pagedims[i].pageno > pageno)
507 break;
508 pagedim = &state.pagedims[i];
511 p.x = fz_toint (fz_arrayget (obj, 2));
512 p.y = fz_toint (fz_arrayget (obj, 3));
513 p = fz_transformpoint (pagedim->ctm, p);
514 top = p.y;
518 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
519 printd (state.sock, "o %d %d %d %s",
520 level, pageno, top, outline->title);
521 next:
522 if (outline->child) {
523 recurse_outline (outline->child, level + 1);
525 outline = outline->next;
529 static void process_outline (void)
531 pdf_outline *outline;
533 outline = pdf_loadoutline (state.xref);
534 if (outline) {
535 recurse_outline (outline, 0);
536 pdf_freeoutline (outline);
540 static int comparespans (const void *l, const void *r)
542 fz_textspan *const*ls = l;
543 fz_textspan *const*rs = r;
545 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
548 /* wishful thinking function */
549 static void search (regex_t *re, int pageno, int y, int forward)
551 int i, j;
552 int ret;
553 char *p;
554 char buf[256];
555 fz_error error;
556 fz_obj *pageobj;
557 fz_device *tdev;
558 pdf_page *drawpage;
559 fz_textspan *text, *span, **pspan;
560 struct pagedim *pdim, *pdimprev;
561 int stop = 0;
562 int niters = 0;
563 int nspans;
564 double start, end;
566 start = now ();
567 while (pageno >= 0 && pageno < state.pagecount && !stop) {
568 if (niters++ == 5) {
569 if (!state.lotsamemory) {
570 pdf_agestoreditems (state.xref->store);
571 pdf_evictageditems (state.xref->store);
573 niters = 0;
574 if (hasdata (state.sock)) {
575 printd (state.sock, "T attention requested aborting search at %d",
576 pageno);
577 stop = 1;
579 else {
580 printd (state.sock, "T searching in page %d", pageno);
583 pdimprev = NULL;
584 for (i = 0; i < state.pagedimcount; ++i) {
585 pdim = &state.pagedims[i];
586 if (pdim->pageno == pageno) {
587 goto found;
589 if (pdim->pageno > pageno) {
590 pdim = pdimprev;
591 goto found;
593 pdimprev = pdim;
595 pdim = pdimprev;
596 found:
598 pageobj = pdf_getpageobject (state.xref, pageno + 1);
599 if (!pageobj)
600 die (fz_throw ("cannot retrieve info from page %d", pageno));
602 error = pdf_loadpage (&drawpage, state.xref, pageobj);
603 if (error)
604 die (error);
606 text = fz_newtextspan ();
607 tdev = fz_newtextdevice (text);
608 error = pdf_runcontentstream (tdev, pdim->ctm, state.xref,
609 drawpage->resources,
610 drawpage->contents);
611 if (error) die (error);
612 fz_freedevice (tdev);
614 nspans = 0;
615 for (span = text; span; span = span->next) {
616 nspans++;
618 pspan = malloc (sizeof (void *) * nspans);
619 if (!pspan) {
620 err (1, "malloc span pointers %zu", sizeof (void *) * nspans);
622 for (i = 0, span = text; span; span = span->next, ++i) {
623 pspan[i] = span;
625 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
627 j = forward ? 0 : nspans - 1;
628 while (nspans--) {
629 regmatch_t rm;
631 span = pspan[j];
632 j += forward ? 1 : -1;
633 p = buf;
634 /* XXX: spans are not sorted "visually" */
635 for (i = 0; i < MIN (span->len, sizeof (buf) - 1); ++i) {
636 if (forward) {
637 if (span->text[i].bbox.y0 < y + 1) {
638 continue;
641 else {
642 if (span->text[i].bbox.y0 > y - 1) {
643 continue;
646 if (span->text[i].c < 256) {
647 *p++ = span->text[i].c;
649 else {
650 *p++ = '?';
653 if (p == buf) {
654 continue;
656 *p++ = 0;
658 ret = regexec (re, buf, 1, &rm, 0);
659 if (ret) {
660 if (ret != REG_NOMATCH) {
661 size_t size;
662 char errbuf[80];
663 size = regerror (ret, re, errbuf, sizeof (errbuf));
664 printd (state.sock,
665 "T regexec error `%.*s'",
666 (int) size, errbuf);
667 fz_freetextspan (text);
668 pdf_freepage (drawpage);
669 free (pspan);
670 return;
673 else {
674 fz_rect r;
676 r.x0 = span->text[rm.rm_so].bbox.x0 - pdim->bbox.x0;
677 r.y0 = span->text[rm.rm_so].bbox.y0;
678 r.x1 = span->text[rm.rm_eo - 1].bbox.x1 - pdim->bbox.x0;
679 r.y1 = span->text[rm.rm_eo - 1].bbox.y1;
681 if (!stop) {
682 printd (state.sock, "F %d %d %f %f %f %f",
683 pageno, 1,
684 r.x0, r.y0,
685 r.x1, r.y1);
687 else {
688 printd (state.sock, "R %d %d %f %f %f %f",
689 pageno, 2,
690 r.x0, r.y0,
691 r.x1, r.y1);
693 printd (state.sock, "T found at %d `%.*s' %f in %f sec",
694 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
695 span->text[0].bbox.y0 - drawpage->mediabox.y0,
696 now () - start);
697 stop = 1;
700 if (forward) {
701 pageno += 1;
702 y = 0;
704 else {
705 pageno -= 1;
706 y = INT_MAX;
708 fz_freetextspan (text);
709 pdf_freepage (drawpage);
710 free (pspan);
712 end = now ();
713 if (!stop) {
714 printd (state.sock, "T no matches %f sec", end - start);
716 printd (state.sock, "D");
719 static void *mainloop (void *unused)
721 char *p = NULL;
722 int len, ret, oldlen = 0;
724 for (;;) {
725 len = readlen (state.sock);
726 if (len == 0) {
727 errx (1, "readlen returned 0");
730 if (oldlen < len + 1) {
731 p = realloc (p, len + 1);
732 if (!p) {
733 err (1, "realloc %d failed", len + 1);
735 oldlen = len + 1;
737 readdata (state.sock, p, len);
738 p[len] = 0;
740 if (!strncmp ("open", p, 4)) {
741 fz_obj *obj;
742 char *filename = p + 5;
744 openxref (filename);
745 initpdims ();
747 obj = fz_dictgets (state.xref->trailer, "Info");
748 if (obj) {
749 obj = fz_dictgets (obj, "Title");
750 if (obj) {
751 printd (state.sock, "t %s", pdf_toutf8 (obj));
755 else if (!strncmp ("free", p, 4)) {
756 void *ptr;
758 ret = sscanf (p + 4, " %p", &ptr);
759 if (ret != 1) {
760 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
762 lock ("free");
763 freepage (ptr);
764 unlock ("free");
765 printd (state.sock, "d");
767 else if (!strncmp ("search", p, 6)) {
768 int icase, pageno, y, ret, len2, forward;
769 char *pattern;
770 regex_t re;
772 ret = sscanf (p + 6, " %d %d %d %d,%n",
773 &icase, &pageno, &y, &forward, &len2);
774 if (ret != 4) {
775 errx (1, "malformed search `%s' ret=%d", p, ret);
778 pattern = p + 6 + len2;
779 ret = regcomp (&re, pattern,
780 REG_EXTENDED | (icase ? REG_ICASE : 0));
781 if (ret) {
782 char errbuf[80];
783 size_t size;
785 size = regerror (ret, &re, errbuf, sizeof (errbuf));
786 printd (state.sock, "T regcomp failed `%.*s'",
787 (int) size, errbuf);
789 else {
790 search (&re, pageno, y, forward);
791 regfree (&re);
794 else if (!strncmp ("geometry", p, 8)) {
795 int w, h;
797 printd (state.sock, "c");
798 ret = sscanf (p + 8, " %d %d", &w, &h);
799 if (ret != 2) {
800 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
802 state.h = h;
803 if (w != state.w) {
804 int i;
805 state.w = w;
806 for (i = 0; i < state.texcount; ++i) {
807 state.texowners[i].slice = NULL;
810 lock ("geometry");
811 layout ();
812 process_outline ();
813 unlock ("geometry");
814 printd (state.sock, "C %d", state.pagecount);
816 else if (!strncmp ("render", p, 6)) {
817 int pageno, pindex, w, h, ret;
818 struct page *page;
820 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
821 if (ret != 4) {
822 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
825 page = render (pageno, pindex);
826 printd (state.sock, "r %d %d %d %p\n",
827 pageno,
828 state.w,
829 state.h,
830 page);
832 else {
833 errx (1, "unknown command %.*s", len, p);
836 return NULL;
839 static void upload2 (struct page *page, int slicenum, const char *cap)
841 int i;
842 int w, h;
843 double start, end;
844 struct slice *slice = &page->slices[slicenum];
846 w = page->pixmap->w;
847 h = page->pixmap->h;
849 ARSERT (w == slice->w);
850 if (slice->texindex != -1
851 && state.texowners[slice->texindex].slice == slice) {
852 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
854 else {
855 int subimage = 0;
856 int index = (state.texindex++ % state.texcount);
857 size_t offset = 0;
859 for (i = 0; i < slicenum; ++i) {
860 offset += w * page->slices[i].h * 4;
863 if (state.texowners[index].w == slice->w) {
864 if (state.texowners[index].h >= slice->h ) {
865 subimage = 1;
867 else {
868 state.texowners[index].h = slice->h;
871 else {
872 state.texowners[index].h = slice->h;
875 state.texowners[index].slice = slice;
876 state.texowners[index].w = slice->w;
877 slice->texindex = index;
879 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
880 start = now ();
881 if (subimage) {
883 GLenum err = glGetError ();
884 if (err != GL_NO_ERROR) {
885 printf ("\e[0;31mERROR1 %d %d %#x\e[0m\n", w, slice->h, err);
886 abort ();
889 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
894 slice->h,
895 state.texform,
896 state.texty,
897 page->pixmap->samples + offset
900 GLenum err = glGetError ();
901 if (err != GL_NO_ERROR) {
902 printf ("\e[0;31mERROR %d %d %#x\e[0m\n", w, slice->h, err);
903 abort ();
907 else {
908 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
910 GL_RGBA8,
912 slice->h,
914 state.texform,
915 state.texty,
916 page->pixmap->samples + offset
920 end = now ();
921 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
922 subimage ? "sub" : "img",
923 page->pageno, slicenum,
924 slice->w, slice->h,
925 state.texids[slice->texindex],
926 end - start);
930 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
931 value py_v, value ptr_v)
933 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
934 int dispy = Int_val (dispy_v);
935 int w = Int_val (w_v);
936 int h = Int_val (h_v);
937 int py = Int_val (py_v);
938 char *s = String_val (ptr_v);
939 int ret;
940 void *ptr;
941 struct page *page;
942 int slicenum = 0;
944 if (trylock ("ml_draw")) {
945 goto done;
948 ret = sscanf (s, "%p", &ptr);
949 if (ret != 1) {
950 errx (1, "cannot parse pointer `%s'", s);
952 page = ptr;
954 w = page->pixmap->w;
956 ARSERT (h >= 0 && "ml_draw wrong h");
958 glEnable (GL_TEXTURE_RECTANGLE_ARB);
959 if (state.useatifs) {
960 glEnable (GL_FRAGMENT_SHADER_ATI);
963 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
964 struct slice *slice = &page->slices[slicenum];
965 if (slice->h > py) {
966 break;
968 py -= slice->h;
971 h = MIN (state.h, h);
972 while (h) {
973 int th;
974 struct slice *slice = &page->slices[slicenum];
976 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
978 th = MIN (h, slice->h - py);
979 upload2 (page, slicenum, "upload");
981 glBegin (GL_QUADS);
983 glTexCoord2i (0, py);
984 glVertex2i (0, dispy);
986 glTexCoord2i (w, py);
987 glVertex2i (w, dispy);
989 glTexCoord2i (w, py+th);
990 glVertex2i (w, dispy + th);
992 glTexCoord2i (0, py+th);
993 glVertex2i (0, dispy + th);
995 glEnd ();
997 h -= th;
998 py = 0;
999 dispy += th;
1000 slicenum += 1;
1003 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1004 if (state.useatifs) {
1005 glDisable (GL_FRAGMENT_SHADER_ATI);
1008 unlock ("ml_draw");
1009 done:
1010 CAMLreturn (Val_unit);
1013 static pdf_link *getlink (struct page *page, int x, int y)
1015 fz_point p;
1016 fz_matrix ctm;
1017 pdf_link *link;
1019 p.x = x;
1020 p.y = y;
1022 ctm = fz_invertmatrix (page->pagedim->ctm);
1023 p = fz_transformpoint (ctm, p);
1025 for (link = page->drawpage->links; link; link = link->next) {
1026 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1027 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1028 if (link->kind == PDF_LGOTO) {
1029 return link;
1034 return NULL;
1037 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
1039 CAMLparam3 (ptr_v, x_v, y_v);
1040 char *s = String_val (ptr_v);
1041 int ret;
1043 if (trylock ("ml_checklink")) {
1044 ret = 0;
1046 else {
1047 ret = NULL != getlink (parse_pointer ("ml_checklink", s),
1048 Int_val (x_v), Int_val (y_v));
1049 unlock ("ml_checklink");
1051 CAMLreturn (Val_bool (ret));
1054 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
1056 CAMLparam3 (ptr_v, x_v, y_v);
1057 CAMLlocal2 (ret_v, tup_v);
1058 pdf_link *link;
1059 struct page *page;
1060 char *s = String_val (ptr_v);
1062 if (trylock ("ml_getlink")) {
1063 ret_v = Val_int (0);
1064 goto done;
1067 page = parse_pointer ("ml_getlink", s);
1069 link = getlink (page, Int_val (x_v), Int_val (y_v));
1070 if (link) {
1071 int pageno;
1072 fz_point p;
1073 fz_obj *obj;
1075 pageno = -1;
1076 obj = fz_arrayget (link->dest, 0);
1077 if (fz_isindirect (obj)) {
1078 pageno = pdf_findpageobject (state.xref, obj) - 1;
1080 else if (fz_isint (obj)) {
1081 pageno = fz_toint (obj);
1084 if (fz_arraylen (link->dest) > 3) {
1085 p.x = fz_toint (fz_arrayget (link->dest, 2));
1086 p.y = fz_toint (fz_arrayget (link->dest, 3));
1087 p = fz_transformpoint (page->pagedim->ctm, p);
1089 else {
1090 p.x = 0.0;
1091 p.y = 0.0;
1094 tup_v = caml_alloc_tuple (2);
1095 ret_v = caml_alloc_small (1, 1);
1096 Field (tup_v, 0) = Val_int (pageno);
1097 Field (tup_v, 1) = Val_int (p.y);
1098 Field (ret_v, 0) = tup_v;
1100 else {
1101 ret_v = Val_int (0);
1103 unlock ("ml_getlink");
1105 done:
1106 CAMLreturn (ret_v);
1109 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1111 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1112 fz_matrix ctm;
1113 fz_point p1, p2;
1114 struct page *page;
1115 fz_textspan *span;
1116 char *s = String_val (ptr_v);
1117 int rectsel = Bool_val (rectsel_v);
1118 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1120 /* stop GCC from complaining about uninitialized variables */
1121 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1123 if (trylock ("ml_gettext")) {
1124 goto done;
1127 page = parse_pointer ("ml_gettext", s);
1129 oy = Int_val (oy_v);
1130 p1.x = Int_val (Field (rect_v, 0));
1131 p1.y = Int_val (Field (rect_v, 1));
1132 p2.x = Int_val (Field (rect_v, 2));
1133 p2.y = Int_val (Field (rect_v, 3));
1135 if (0) {
1136 glEnable (GL_BLEND);
1137 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1138 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1139 glColor4f (0, 0, 0, 0.2);
1140 glRecti (p1.x, p1.y, p2.x, p2.y);
1141 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1142 glDisable (GL_BLEND);
1145 ctm = page->pagedim->ctm;
1146 if (!page->text) {
1147 fz_error error;
1148 fz_device *tdev;
1150 page->text = fz_newtextspan ();
1151 tdev = fz_newtextdevice (page->text);
1152 error = pdf_runcontentstream (tdev, page->pagedim->ctm, state.xref,
1153 page->drawpage->resources,
1154 page->drawpage->contents);
1155 if (error) die (error);
1156 fz_freedevice (tdev);
1159 printf ("\ec");
1161 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1162 p1.x += page->pixmap->x;
1163 p1.y += page->pixmap->y;
1164 p2.x += page->pixmap->x;
1165 p2.y += page->pixmap->y;
1166 x0 = p1.x;
1167 y0 = p1.y;
1168 x1 = p2.x;
1169 y1 = p2.y;
1170 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1172 for (span = page->text; span; span = span->next) {
1173 int seen = 0;
1175 /* fz_debugtextspanxml (span); */
1176 for (i = 0; i < span->len; ++i) {
1177 long c;
1179 bx0 = span->text[i].bbox.x0;
1180 bx1 = span->text[i].bbox.x1;
1181 by0 = span->text[i].bbox.y0 + oy;
1182 by1 = span->text[i].bbox.y1 + oy;
1184 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1185 if (!seen) {
1186 rx0 = bx0 - page->pixmap->x;
1187 rx1 = bx1 - page->pixmap->x;
1188 ry0 = by0;
1189 ry1 = by1;
1192 seen = 1;
1193 c = span->text[i].c;
1194 if (c < 256) {
1195 if ((isprint (c) && !isspace (c))) {
1196 if (!rectsel) {
1197 bx0 -= page->pixmap->x;
1198 bx1 -= page->pixmap->x;
1199 glEnable (GL_BLEND);
1200 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1201 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1202 glColor4f (0.5, 0.5, 0.0, 0.6);
1203 glRecti (bx0, by0, bx1, by1);
1204 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1205 glDisable (GL_BLEND);
1207 if (isprint (c) || c ==' ') {
1208 rx1 = bx1;
1209 ry1 = by1;
1212 putc (c, stdout);
1214 else {
1215 putc ('?', stdout);
1220 if (rectsel) {
1221 if (seen) {
1222 glEnable (GL_BLEND);
1223 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1224 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1225 glColor4f (0.5, 0.5, 0.0, 0.6);
1226 glRecti (rx0, ry0, rx1, ry1);
1227 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1228 glDisable (GL_BLEND);
1232 if (seen && span->eol) {
1233 x0 = page->pixmap->x;
1234 putc ('\n', stdout);
1237 unlock ("ml_gettext");
1239 done:
1240 CAMLreturn (Val_unit);
1243 CAMLprim value ml_getpagewh (value pagedimno_v)
1245 CAMLparam1 (pagedimno_v);
1246 CAMLlocal1 (ret_v);
1247 int pagedimno = Int_val (pagedimno_v);
1249 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1250 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1251 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1252 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1253 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1254 CAMLreturn (ret_v);
1257 static void initgl (void)
1259 #ifdef _BIG_ENDIAN
1260 if (strstr ((char *) glGetString (GL_EXTENSIONS),
1261 "GL_ATI_fragment_shader")) {
1262 /* Here, with MESA, rv280, powerpc32: BGRA(rev) is slow while
1263 ABGR is fast, so fix things in the shader */
1264 state.texform = GL_ABGR_EXT;
1265 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1267 glBindFragmentShaderATI (1);
1268 glBeginFragmentShaderATI ();
1270 glSampleMapATI (GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI);
1272 glColorFragmentOp1ATI (GL_MOV_ATI,
1273 GL_REG_1_ATI, GL_RED_BIT_ATI, GL_NONE,
1274 GL_REG_0_ATI, GL_BLUE, GL_NONE);
1275 glColorFragmentOp1ATI (GL_MOV_ATI,
1276 GL_REG_1_ATI, GL_BLUE_BIT_ATI, GL_NONE,
1277 GL_REG_0_ATI, GL_RED, GL_NONE);
1278 glColorFragmentOp1ATI (
1279 GL_MOV_ATI,
1280 GL_REG_0_ATI, GL_RED_BIT_ATI | GL_BLUE_BIT_ATI, GL_NONE,
1281 GL_REG_1_ATI, GL_NONE, GL_NONE
1284 glEndFragmentShaderATI ();
1285 state.useatifs = 1;
1287 else {
1288 state.texform = GL_BGRA_EXT;
1289 state.texty = GL_UNSIGNED_INT_8_8_8_8_REV;
1291 #else
1292 state.texform = GL_BGRA_EXT;
1293 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1294 #endif
1297 CAMLprim value ml_init (value sock_v)
1299 int ret;
1300 CAMLparam1 (sock_v);
1302 state.texcount = 256;
1303 state.sliceheight = 24;
1305 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1306 if (!state.texids) {
1307 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1310 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1311 if (!state.texowners) {
1312 err (1, "calloc texowners %zu",
1313 state.texcount * sizeof (*state.texowners));
1316 glGenTextures (state.texcount, state.texids);
1318 state.sock = Int_val (sock_v);
1319 initgl ();
1321 state.cache = fz_newglyphcache ();
1322 if (!state.cache) {
1323 errx (1, "fz_newglyphcache failed");
1326 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1327 if (ret) {
1328 errx (1, "pthread_create: %s", strerror (errno));
1331 CAMLreturn (Val_unit);