OOM mitigation
[llpp.git] / link.c
blobf7636e99b50b808bdfb70586ee7bd8b951f38757
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 fz_bbox bbox;
64 fz_matrix ctm;
67 struct {
68 int sock;
69 int sliceheight;
70 pthread_t thread;
71 struct page *pages;
72 struct pagedim *pagedims;
73 int pagecount;
74 int pagedimcount;
75 pdf_xref *xref;
76 fz_glyphcache *cache;
77 int w, h;
79 int useatifs;
81 int texindex;
82 int texcount;
83 GLuint *texids;
85 GLenum texform;
86 GLenum texty;
88 int lotsamemory;
90 struct {
91 int w, h;
92 struct slice *slice;
93 } *texowners;
94 } state;
96 static void *parse_pointer (const char *cap, const char *s)
98 int ret;
99 void *ptr;
101 ret = sscanf (s, "%p", &ptr);
102 if (ret != 1) {
103 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
105 return ptr;
108 static int hasdata (int sock)
110 int ret;
111 struct pollfd pfd;
113 pfd.fd = sock;
114 pfd.events = POLLIN;
115 ret = poll (&pfd, 1, 0);
116 if (ret == 0) {
117 return 0;
119 if (ret != 1) {
120 err (1, "poll");
122 return pfd.revents & POLLIN;
125 static double now (void)
127 struct timeval tv;
129 if (gettimeofday (&tv, NULL)) {
130 err (1, "gettimeofday");
132 return tv.tv_sec + tv.tv_usec*1e-6;
135 static void readdata (int fd, char *p, int size)
137 ssize_t n;
139 n = read (fd, p, size);
140 if (n - size) {
141 err (1, "read (req %d, ret %zd)", size, n);
145 static void writedata (int fd, char *p, int size)
147 char buf[4];
148 ssize_t n;
150 buf[0] = (size >> 24) & 0xff;
151 buf[1] = (size >> 16) & 0xff;
152 buf[2] = (size >> 8) & 0xff;
153 buf[3] = (size >> 0) & 0xff;
155 n = write (fd, buf, 4);
156 if (n != 4) {
157 err (1, "write %zd", n);
160 n = write (fd, p, size);
161 if (n - size) {
162 err (1, "write (req %d, ret %zd)", size, n);
166 static void __attribute__ ((format (printf, 2, 3)))
167 printd (int fd, const char *fmt, ...)
169 int len;
170 va_list ap;
171 char buf[200];
173 va_start (ap, fmt);
174 len = vsnprintf (buf, sizeof (buf), fmt, ap);
175 va_end (ap);
176 writedata (fd, buf, len);
179 static void die (fz_error error)
181 fz_catch (error, "aborting");
182 if (state.xref)
183 pdf_closexref (state.xref);
184 exit (1);
187 void openxref (char *filename)
189 int fd;
190 fz_stream *file;
192 fd = open (filename, O_BINARY | O_RDONLY, 0666);
193 if (fd < 0)
194 die (fz_throw ("cannot open file '%s'", filename));
196 file = fz_openfile (fd);
197 state.xref = pdf_openxref (file);
198 if (!state.xref)
199 die (fz_throw ("cannot open PDF file '%s'", filename));
200 fz_dropstream (file);
202 if (pdf_needspassword (state.xref)) {
203 die (fz_throw ("password protected"));
205 state.pagecount = pdf_getpagecount (state.xref);
208 static int readlen (int fd)
210 ssize_t n;
211 char p[4];
213 n = read (fd, p, 4);
214 if (n != 4) {
215 err (1, "read %zd", n);
218 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
221 static void freepage (struct page *page)
223 int i;
224 struct page *p;
226 fz_droppixmap (page->pixmap);
227 for (p = state.pages; p; p = p->prev) {
228 if (p->prev == page) {
229 p->prev = page->prev;
230 break;
233 for (i = 0; i < page->slicecount; ++i) {
234 struct slice *s = &page->slices[i];
235 if (s->texindex != -1) {
236 if (state.texowners[s->texindex].slice == s) {
237 state.texowners[s->texindex].slice = NULL;
238 ARSERT (state.texowners[s->texindex].w == s->w);
239 ARSERT (state.texowners[s->texindex].h >= s->h);
243 if (page->text) {
244 fz_freetextspan (page->text);
246 if (page->drawpage) {
247 pdf_droppage (page->drawpage);
250 free (page);
253 static void subdivide (struct page *p)
255 int i;
256 int h = p->pixmap->h;
257 int th = MIN (h, state.sliceheight);
259 for (i = 0; i < p->slicecount; ++i) {
260 struct slice *s = &p->slices[i];
261 s->texindex = -1;
262 s->h = MIN (th, h);
263 s->w = p->pixmap->w;
264 h -= th;
268 static void *render (int pageno, int pindex)
270 fz_error error;
271 int slicecount;
272 fz_obj *pageobj;
273 struct page *page;
274 double start, end;
275 pdf_page *drawpage;
276 fz_displaylist *list;
277 fz_device *idev, *mdev;
278 struct pagedim *pagedim;
280 start = now ();
281 /* printd (state.sock, "T \"rendering %d\"", pageno); */
282 pdf_flushxref (state.xref, 0);
284 pagedim = &state.pagedims[pindex];
285 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
286 + state.sliceheight - 1) / state.sliceheight;
287 slicecount += slicecount == 0;
289 page = calloc (sizeof (*page)
290 + (slicecount * sizeof (struct slice)), 1);
291 if (!page) {
292 err (1, "calloc page %d\n", pageno);
294 page->slicecount = slicecount;
295 page->prev = state.pages;
296 state.pages = page;
298 pageobj = pdf_getpageobject (state.xref, pageno);
299 if (!pageobj)
300 die (fz_throw ("cannot retrieve info from page %d", pageno));
302 error = pdf_loadpage (&drawpage, state.xref, pageobj);
303 if (error)
304 die (error);
306 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, pagedim->bbox);
307 if (error)
308 die (error);
309 fz_clearpixmap (page->pixmap, 0xFF);
311 list = fz_newdisplaylist ();
312 if (!list)
313 die (fz_throw ("fz_newdisplaylist failed"));
315 mdev = fz_newlistdevice (list);
316 error = pdf_runcontentstream (mdev, fz_identity (), state.xref,
317 drawpage->resources,
318 drawpage->contents);
319 if (error)
320 die (error);
321 fz_freedevice (mdev);
323 idev = fz_newdrawdevice (state.cache, page->pixmap);
324 if (!idev)
325 die (fz_throw ("fz_newdrawdevice failed"));
326 fz_executedisplaylist (list, idev, pagedim->ctm);
327 fz_freedevice (idev);
329 fz_freedisplaylist (list);
331 page->drawpage = drawpage;
332 page->pagedim = pagedim;
333 page->pageno = pageno;
334 subdivide (page);
335 end = now ();
337 if (!state.lotsamemory) {
338 pdf_agestoreditems (state.xref->store);
339 pdf_evictageditems (state.xref->store);
342 /* printd (state.sock, "T \"rendering %d took %f sec\"", pageno, end - start); */
343 return page;
346 static void layout (void)
348 int pageno;
349 double a, b, c, d;
350 int prevrotate;
351 fz_rect prevbox;
352 int i, pindex;
353 asize_t size;
354 struct pagedim *p;
356 size = 0;
357 pindex = 0;
358 a = now ();
359 c = 0.0;
360 printd (state.sock, "c");
361 for (pageno = 1; pageno <= state.pagecount; ++pageno) {
362 float w;
363 float zoom;
364 int rotate;
365 fz_obj *obj;
366 fz_rect box;
367 fz_rect box2;
368 fz_matrix ctm;
369 fz_bbox bbox;
370 fz_obj *pageobj;
372 if (!(pageno & 31)) {
373 if (!c) {
374 c = a;
376 d = now ();
377 printd (state.sock, "T \"processing page %d %f\"", pageno, d - c);
378 c = d;
380 pageobj = pdf_getpageobject (state.xref, pageno);
381 if (!pageobj)
382 die (fz_throw ("cannot retrieve info from page %d", pageno));
384 obj = fz_dictgets (pageobj, "CropBox");
385 if (!fz_isarray (obj)) {
386 obj = fz_dictgets (pageobj, "MediaBox");
387 if (!fz_isarray (obj))
388 die (fz_throw ("cannot find page bounds %d (%d R)",
389 fz_tonum (obj), fz_togen (obj)));
391 box = pdf_torect (obj);
392 obj = fz_dictgets (pageobj, "Rotate");
393 if (fz_isint (obj))
394 rotate = fz_toint (obj);
395 else
396 rotate = 0;
398 if (pageno != 1
399 && (prevrotate == rotate
400 && !memcmp (&prevbox, &box, sizeof (box)))) {
401 continue;
404 memcpy (&prevbox, &box, sizeof (box));
405 prevrotate = rotate;
407 box.x0 = MIN (prevbox.x0, prevbox.x1);
408 box.y0 = MIN (prevbox.y0, prevbox.y1);
409 box.x1 = MAX (prevbox.x0, prevbox.x1);
410 box.y1 = MAX (prevbox.y0, prevbox.y1);
412 ctm = fz_identity ();
413 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
414 ctm = fz_concat (ctm, fz_rotate (rotate));
415 box2 = fz_transformrect (ctm, box);
416 w = box2.x1 - box2.x0;
418 zoom = (state.w / w);
419 ctm = fz_identity ();
420 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
421 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
422 ctm = fz_concat (ctm, fz_rotate (rotate));
423 bbox = fz_roundrect (fz_transformrect (ctm, box));
425 size += sizeof (*state.pagedims);
426 state.pagedims = caml_stat_resize (state.pagedims, size);
428 p = &state.pagedims[pindex++];
429 memcpy (&p->bbox, &bbox, sizeof (bbox));
430 memcpy (&p->ctm, &ctm, sizeof (ctm));
432 p->pageno = pageno - 1;
435 state.pagedimcount = pindex;
436 for (i = pindex - 1; i >= 0; --i) {
437 p = &state.pagedims[i];
438 printd (state.sock, "l %d %d %d",
439 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
442 b = now ();
443 printd (state.sock, "T \"Processed %d pages in %f seconds\"",
444 state.pagecount, b - a);
445 printd (state.sock, "C %d", state.pagecount);
448 /* wishful thinking function */
449 static void search (regex_t *re, int pageno, int y, int forward)
451 int i;
452 int ret;
453 char *p;
454 char buf[256];
455 fz_error error;
456 fz_obj *pageobj;
457 fz_device *tdev;
458 pdf_page *drawpage;
459 fz_textspan *text, *span;
460 struct pagedim *pdim, *pdimprev;
461 int stop = 0;
462 int niters = 0;
463 double start, end;
465 start = now ();
466 while (pageno >= 0 && pageno < state.pagecount && !stop) {
467 if (niters++ == 5) {
468 niters = 0;
469 if (hasdata (state.sock)) {
470 printd (state.sock, "T \"attention requested aborting search at %d\"",
471 pageno);
472 stop = 1;
474 else {
475 printd (state.sock, "T \"searching in page %d\"", pageno);
478 pdimprev = NULL;
479 for (i = 0; i < state.pagedimcount; ++i) {
480 pdim = &state.pagedims[i];
481 if (pdim->pageno == pageno) {
482 goto found;
484 if (pdim->pageno > pageno) {
485 pdim = pdimprev;
486 goto found;
488 pdimprev = pdim;
490 pdim = pdimprev;
491 found:
493 pageobj = pdf_getpageobject (state.xref, pageno + 1);
494 if (!pageobj)
495 die (fz_throw ("cannot retrieve info from page %d", pageno));
497 error = pdf_loadpage (&drawpage, state.xref, pageobj);
498 if (error)
499 die (error);
501 text = fz_newtextspan ();
502 tdev = fz_newtextdevice (text);
503 error = pdf_runcontentstream (tdev, pdim->ctm, state.xref,
504 drawpage->resources,
505 drawpage->contents);
506 if (error) die (error);
507 fz_freedevice (tdev);
509 for (span = text; span; span = span->next) {
510 regmatch_t rm;
512 p = buf;
513 /* XXX: spans are not sorted "visually" */
514 for (i = 0; i < MIN (span->len, sizeof (buf) - 1); ++i) {
515 if (forward) {
516 if (span->text[i].bbox.y0 < y + 1) {
517 continue;
520 else {
521 if (span->text[i].bbox.y0 > y - 1) {
522 continue;
525 if (span->text[i].c < 256) {
526 *p++ = span->text[i].c;
528 else {
529 *p++ = '?';
532 if (p == buf) {
533 continue;
535 *p++ = 0;
537 ret = regexec (re, buf, 1, &rm, 0);
538 if (ret) {
539 if (ret != REG_NOMATCH) {
540 size_t size;
541 char errbuf[80];
542 size = regerror (ret, re, errbuf, sizeof (errbuf));
543 printd (state.sock,
544 "T \"regexec error `%.*s'\"",
545 (int) size, errbuf);
548 else {
549 fz_rect r;
551 r.x0 = span->text[rm.rm_so].bbox.x0 - pdim->bbox.x0;
552 r.y0 = span->text[rm.rm_so].bbox.y0;
553 r.x1 = span->text[rm.rm_eo - 1].bbox.x1 - pdim->bbox.x0;
554 r.y1 = span->text[rm.rm_eo - 1].bbox.y1;
556 if (!stop) {
557 printd (state.sock, "F %d %d %f %f %f %f",
558 pageno, 1,
559 r.x0, r.y0,
560 r.x1, r.y1);
562 else {
563 printd (state.sock, "R %d %d %f %f %f %f",
564 pageno, 2,
565 r.x0, r.y0,
566 r.x1, r.y1);
568 printd (state.sock, "T \"found at %d `%.*s' %f in %f sec\"",
569 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
570 span->text[0].bbox.y0 - drawpage->mediabox.y0,
571 now () - start);
572 stop = 1;
575 if (forward) {
576 pageno += 1;
577 y = 0;
579 else {
580 pageno -= 1;
581 y = INT_MAX;
583 fz_freetextspan (text);
584 pdf_droppage (drawpage);
586 end = now ();
587 if (!stop) {
588 printd (state.sock, "T \"no matches %f sec\"", end - start);
590 printd (state.sock, "d");
593 static void *mainloop (void *unused)
595 char *p = NULL;
596 int len, ret, oldlen = 0;
598 for (;;) {
599 len = readlen (state.sock);
600 if (len == 0) {
601 errx (1, "readlen returned 0");
604 if (oldlen < len + 1) {
605 p = realloc (p, len + 1);
606 if (!p) {
607 err (1, "realloc %d failed", len + 1);
609 oldlen = len + 1;
611 readdata (state.sock, p, len);
612 p[len] = 0;
614 if (!strncmp ("open", p, 4)) {
615 char *filename = p + 5;
617 openxref (filename);
619 else if (!strncmp ("free", p, 4)) {
620 void *ptr;
622 ret = sscanf (p + 4, " %p", &ptr);
623 if (ret != 1) {
624 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
626 freepage (ptr);
627 printd (state.sock, "f");
629 else if (!strncmp ("search", p, 6)) {
630 int icase, pageno, y, ret, len2, forward;
631 char *pattern;
632 regex_t re;
634 ret = sscanf (p + 6, " %d %d %d %d %n",
635 &icase, &pageno, &y, &forward, &len2);
636 if (ret != 4) {
637 errx (1, "malformed search `%s' ret=%d", p, ret);
640 pattern = p + 6 + len2;
641 ret = regcomp (&re, pattern,
642 REG_EXTENDED | (icase ? REG_ICASE : 0));
643 if (ret) {
644 char errbuf[80];
645 size_t size;
647 size = regerror (ret, &re, errbuf, sizeof (errbuf));
648 printd (state.sock, "T \"regcomp failed `%.*s'\"", (int) size, errbuf);
650 else {
651 search (&re, pageno, y, forward);
652 regfree (&re);
655 else if (!strncmp ("geometry", p, 8)) {
656 int w, h;
658 ret = sscanf (p + 8, " %d %d", &w, &h);
659 if (ret != 2) {
660 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
662 state.h = h;
663 if (w != state.w) {
664 int i;
665 state.w = w;
666 for (i = 0; i < state.texcount; ++i) {
667 state.texowners[i].slice = NULL;
670 layout ();
672 else if (!strncmp ("render", p, 6)) {
673 int pageno, pindex, w, h, ret;
674 struct page *page;
676 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
677 if (ret != 4) {
678 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
681 page = render (pageno, pindex);
682 printd (state.sock, "r %d %d %d %p\n",
683 pageno,
684 state.w,
685 state.h,
686 page);
688 else {
689 errx (1, "unknown command %.*s", len, p);
692 return NULL;
695 static void upload2 (struct page *page, int slicenum, const char *cap)
697 int i;
698 int w, h;
699 double start, end;
700 struct slice *slice = &page->slices[slicenum];
702 w = page->pixmap->w;
703 h = page->pixmap->h;
705 ARSERT (w == slice->w);
706 if (slice->texindex != -1
707 && state.texowners[slice->texindex].slice == slice) {
708 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
710 else {
711 int subimage = 0;
712 int index = (state.texindex++ % state.texcount);
713 size_t offset = 0;
715 for (i = 0; i < slicenum; ++i) {
716 offset += w * page->slices[i].h * 4;
719 if (state.texowners[index].w == slice->w) {
720 if (state.texowners[index].h >= slice->h ) {
721 subimage = 1;
723 else {
724 state.texowners[index].h = slice->h;
727 else {
728 state.texowners[index].h = slice->h;
731 state.texowners[index].slice = slice;
732 state.texowners[index].w = slice->w;
733 slice->texindex = index;
735 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
736 start = now ();
737 if (subimage) {
739 GLenum err = glGetError ();
740 if (err != GL_NO_ERROR) {
741 printf ("\e[0;31mERROR1 %d %d %#x\e[0m\n", w, slice->h, err);
742 abort ();
745 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
750 slice->h,
751 state.texform,
752 state.texty,
753 page->pixmap->samples + offset
756 GLenum err = glGetError ();
757 if (err != GL_NO_ERROR) {
758 printf ("\e[0;31mERROR %d %d %#x\e[0m\n", w, slice->h, err);
759 abort ();
763 else {
764 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
766 GL_RGBA8,
768 slice->h,
770 state.texform,
771 state.texty,
772 page->pixmap->samples + offset
776 end = now ();
777 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
778 subimage ? "sub" : "img",
779 page->pageno, slicenum,
780 slice->w, slice->h,
781 state.texids[slice->texindex],
782 end - start);
786 CAMLprim value ml_preload (value ptr_v)
788 int i;
789 int ret;
790 void *ptr;
791 CAMLparam1 (ptr_v);
792 char *s = String_val (ptr_v);
793 struct page *page;
795 ret = sscanf (s, "%p", &ptr);
796 if (ret != 1) {
797 errx (1, "cannot parse pointer `%s'", s);
800 page = ptr;
801 for (i = 0; i < page->slicecount; ++i) {
802 upload2 (ptr, i, "preload");
805 CAMLreturn (Val_unit);
808 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
809 value py_v, value ptr_v)
811 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
812 int dispy = Int_val (dispy_v);
813 int w = Int_val (w_v);
814 int h = Int_val (h_v);
815 int py = Int_val (py_v);
816 char *s = String_val (ptr_v);
817 int ret;
818 void *ptr;
819 struct page *page;
820 int slicenum = 0;
822 ret = sscanf (s, "%p", &ptr);
823 if (ret != 1) {
824 errx (1, "cannot parse pointer `%s'", s);
826 page = ptr;
828 w = page->pixmap->w;
830 ARSERT (h >= 0 && "ml_draw wrong h");
832 glEnable (GL_TEXTURE_RECTANGLE_ARB);
833 if (state.useatifs) {
834 glEnable (GL_FRAGMENT_SHADER_ATI);
837 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
838 struct slice *slice = &page->slices[slicenum];
839 if (slice->h > py) {
840 break;
842 py -= slice->h;
845 h = MIN (state.h, h);
846 while (h) {
847 int th;
848 struct slice *slice = &page->slices[slicenum];
850 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
852 th = MIN (h, slice->h - py);
853 upload2 (page, slicenum, "upload");
855 glBegin (GL_QUADS);
857 glTexCoord2i (0, py);
858 glVertex2i (0, dispy);
860 glTexCoord2i (w, py);
861 glVertex2i (w, dispy);
863 glTexCoord2i (w, py+th);
864 glVertex2i (w, dispy + th);
866 glTexCoord2i (0, py+th);
867 glVertex2i (0, dispy + th);
869 glEnd ();
871 h -= th;
872 py = 0;
873 dispy += th;
874 slicenum += 1;
877 glDisable (GL_TEXTURE_RECTANGLE_ARB);
878 if (state.useatifs) {
879 glDisable (GL_FRAGMENT_SHADER_ATI);
881 CAMLreturn (Val_unit);
884 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
886 CAMLparam3 (ptr_v, x_v, y_v);
887 fz_point p;
888 fz_matrix ctm;
889 pdf_link *link;
890 int pageno = -1;
891 struct page *page;
892 char *s = String_val (ptr_v);
894 p.x = Int_val (x_v);
895 p.y = Int_val (y_v);
897 page = parse_pointer ("ml_checklink", s);
899 ctm = fz_invertmatrix (page->pagedim->ctm);
900 p = fz_transformpoint (ctm, p);
902 for (link = page->drawpage->links; link; link = link->next) {
903 if (p.x >= link->rect.x0 && p.x <= link->rect.x1)
904 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
905 if (link->kind == PDF_LGOTO) {
906 pageno = pdf_findpageobject (state.xref,
907 fz_arrayget (link->dest, 0)) - 1;
908 /* link->dest); */
910 break;
914 CAMLreturn (Val_int (pageno));
917 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
919 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
920 fz_matrix ctm;
921 fz_point p1, p2;
922 struct page *page;
923 fz_textspan *span;
924 char *s = String_val (ptr_v);
925 int rectsel = Bool_val (rectsel_v);
926 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
928 /* stop GCC from complaining about uninitialized variables */
929 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
931 page = parse_pointer ("ml_gettext", s);
933 oy = Int_val (oy_v);
934 p1.x = Int_val (Field (rect_v, 0));
935 p1.y = Int_val (Field (rect_v, 1));
936 p2.x = Int_val (Field (rect_v, 2));
937 p2.y = Int_val (Field (rect_v, 3));
939 if (0) {
940 glEnable (GL_BLEND);
941 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
942 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
943 glColor4f (0, 0, 0, 0.2);
944 glRecti (p1.x, p1.y, p2.x, p2.y);
945 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
946 glDisable (GL_BLEND);
949 ctm = page->pagedim->ctm;
950 if (!page->text) {
951 fz_error error;
952 fz_device *tdev;
954 page->text = fz_newtextspan ();
955 tdev = fz_newtextdevice (page->text);
956 error = pdf_runcontentstream (tdev, page->pagedim->ctm, state.xref,
957 page->drawpage->resources,
958 page->drawpage->contents);
959 if (error) die (error);
960 fz_freedevice (tdev);
963 printf ("\ec");
965 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
966 p1.x += page->pixmap->x;
967 p1.y += page->pixmap->y;
968 p2.x += page->pixmap->x;
969 p2.y += page->pixmap->y;
970 x0 = p1.x;
971 y0 = p1.y;
972 x1 = p2.x;
973 y1 = p2.y;
974 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
976 for (span = page->text; span; span = span->next) {
977 int seen = 0;
979 /* fz_debugtextspanxml (span); */
980 for (i = 0; i < span->len; ++i) {
981 long c;
983 bx0 = span->text[i].bbox.x0;
984 bx1 = span->text[i].bbox.x1;
985 by0 = span->text[i].bbox.y0 + oy;
986 by1 = span->text[i].bbox.y1 + oy;
988 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
989 if (!seen) {
990 rx0 = bx0 - page->pixmap->x;
991 rx1 = bx1 - page->pixmap->x;
992 ry0 = by0;
993 ry1 = by1;
996 seen = 1;
997 c = span->text[i].c;
998 if (c < 256) {
999 if ((isprint (c) && !isspace (c))) {
1000 if (!rectsel) {
1001 bx0 -= page->pixmap->x;
1002 bx1 -= page->pixmap->x;
1003 glEnable (GL_BLEND);
1004 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1005 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1006 glColor4f (0.5, 0.5, 0.0, 0.6);
1007 glRecti (bx0, by0, bx1, by1);
1008 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1009 glDisable (GL_BLEND);
1011 if (isprint (c) || c ==' ') {
1012 rx1 = bx1;
1013 ry1 = by1;
1016 putc (c, stdout);
1018 else {
1019 putc ('?', stdout);
1024 if (rectsel) {
1025 if (seen) {
1026 glEnable (GL_BLEND);
1027 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1028 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1029 glColor4f (0.5, 0.5, 0.0, 0.6);
1030 glRecti (rx0, ry0, rx1, ry1);
1031 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1032 glDisable (GL_BLEND);
1036 if (seen && span->eol) {
1037 x0 = page->pixmap->x;
1038 putc ('\n', stdout);
1042 CAMLreturn (Val_unit);
1045 static void initgl (void)
1047 #ifdef _BIG_ENDIAN
1048 if (strstr ((char *) glGetString (GL_EXTENSIONS),
1049 "GL_ATI_fragment_shader")) {
1050 /* Here, with MESA, rv280, powerpc32: BGRA(rev) is slow while
1051 ABGR is fast, so fix things in the shader */
1052 state.texform = GL_ABGR_EXT;
1053 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1055 glBindFragmentShaderATI (1);
1056 glBeginFragmentShaderATI ();
1058 glSampleMapATI (GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI);
1060 glColorFragmentOp1ATI (GL_MOV_ATI,
1061 GL_REG_1_ATI, GL_RED_BIT_ATI, GL_NONE,
1062 GL_REG_0_ATI, GL_BLUE, GL_NONE);
1063 glColorFragmentOp1ATI (GL_MOV_ATI,
1064 GL_REG_1_ATI, GL_BLUE_BIT_ATI, GL_NONE,
1065 GL_REG_0_ATI, GL_RED, GL_NONE);
1066 glColorFragmentOp1ATI (
1067 GL_MOV_ATI,
1068 GL_REG_0_ATI, GL_RED_BIT_ATI | GL_BLUE_BIT_ATI, GL_NONE,
1069 GL_REG_1_ATI, GL_NONE, GL_NONE
1072 glEndFragmentShaderATI ();
1073 state.useatifs = 1;
1075 else {
1076 state.texform = GL_BGRA_EXT;
1077 state.texty = GL_UNSIGNED_INT_8_8_8_8_REV;
1079 #else
1080 state.texform = GL_BGRA_EXT;
1081 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1082 #endif
1085 CAMLprim value ml_init (value sock_v)
1087 int ret;
1088 CAMLparam1 (sock_v);
1090 state.texcount = 128;
1091 state.sliceheight = 64;
1093 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1094 if (!state.texids) {
1095 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1098 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1099 if (!state.texowners) {
1100 err (1, "calloc texowners %zu",
1101 state.texcount * sizeof (*state.texowners));
1104 glGenTextures (state.texcount, state.texids);
1106 state.sock = Int_val (sock_v);
1107 initgl ();
1109 state.cache = fz_newglyphcache ();
1110 if (!state.cache) {
1111 errx (1, "fz_newglyphcache failed");
1114 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1115 if (ret) {
1116 unix_error (ret, "pthread_create", Nothing);
1119 CAMLreturn (Val_unit);