2 #define GL_GLEXT_PROTOTYPES
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"
51 pthread_mutex_t mutex
;
57 /* pdf_page *drawpage; */
66 .cond
= PTHREAD_COND_INITIALIZER
,
67 .mutex
= PTHREAD_MUTEX_INITIALIZER
70 static double now (void)
74 if (gettimeofday (&tv
, NULL
)) {
75 err (1, "gettimeofday");
77 return tv
.tv_sec
+ tv
.tv_usec
*1e-6;
80 static void readdata (int fd
, char *p
, int size
)
84 n
= read (fd
, p
, size
);
86 err (1, "read (req %d, ret %zd)", size
, n
);
90 static void writedata (int fd
, char *p
, int size
)
95 buf
[0] = (size
>> 24) & 0xff;
96 buf
[1] = (size
>> 16) & 0xff;
97 buf
[2] = (size
>> 8) & 0xff;
98 buf
[3] = (size
>> 0) & 0xff;
100 n
= write (fd
, buf
, 4);
102 err (1, "write %zd", n
);
105 n
= write (fd
, p
, size
);
107 err (1, "write (req %d, ret %zd)", size
, n
);
111 static void __attribute__ ((format (printf
, 2, 3)))
112 printd (int fd
, const char *fmt
, ...)
119 len
= vsnprintf (buf
, sizeof (buf
), fmt
, ap
);
121 writedata (fd
, buf
, len
);
124 static void closexref (void);
126 static void createmmap (struct page
*page
)
131 size
= page
->pixmap
->w
* page
->pixmap
->h
* 4;
133 fd
= open ("pdfmap", O_CREAT
|O_TRUNC
|O_RDWR
);
137 ret
= unlink ("pdfmap");
141 size
= (size
+ pagesize
- 1) & ~(pagesize
- 1);
142 ret
= ftruncate (fd
, size
);
144 err (1, "ftruncate");
146 page
->pixmap
->samples
= mmap (NULL
, size
, PROT_READ
|PROT_WRITE
,
148 if (page
->pixmap
->samples
== MAP_FAILED
) {
153 static void lock (void)
157 ret
= pthread_mutex_lock (&state
.mutex
);
159 errx (1, "pthread_mutex_lock: %s\n", strerror (ret
));
162 static void unlock (void)
166 ret
= pthread_mutex_unlock (&state
.mutex
);
168 errx (1, "pthread_mutex_unlock: %s\n", strerror (ret
));
172 static void condsignal (void)
176 ret
= pthread_cond_signal (&state
.cond
);
178 errx (1, "pthread_cond_signal: %s\n", strerror (ret
));
182 static void condwait (void)
186 ret
= pthread_cond_wait (&state
.cond
, &state
.mutex
);
188 errx (1, "pthread_cond_wait: %s\n", strerror (ret
));
193 static void die(fz_error error
)
195 fz_catch(error
, "aborting");
200 void openxref(char *filename
, char *password
, int dieonbadpass
)
207 basename
= strrchr(filename
, '/');
213 fd
= open(filename
, O_BINARY
| O_RDONLY
, 0666);
215 die(fz_throw("cannot open file '%s'", filename
));
217 file
= fz_openfile(fd
);
218 state
.xref
= pdf_openxref(file
);
220 die(fz_throw("cannot open PDF file '%s'", basename
));
223 if (pdf_needspassword(state
.xref
))
225 okay
= pdf_authenticatepassword(state
.xref
, password
);
226 if (!okay
&& !dieonbadpass
)
227 fz_warn("invalid password, attempting to continue.");
228 else if (!okay
&& dieonbadpass
)
229 die(fz_throw("invalid password"));
232 state
.pagecount
= pdf_getpagecount(state
.xref
);
233 printd (state
.sock
, "C %d", state
.pagecount
);
236 static void flushxref(void)
240 pdf_flushxref(state
.xref
, 0);
244 static void closexref(void)
248 pdf_closexref(state
.xref
);
253 static int readlen (int fd
)
260 err (1, "read %zd", n
);
263 return (p
[0] << 24) | (p
[1] << 16) | (p
[2] << 8) | p
[3];
266 static void freepage (struct page
*page
)
270 fz_droppixmap (page
->pixmap
);
271 for (p
= state
.pages
; p
; p
= p
->prev
) {
272 if (p
->prev
== page
) {
273 p
->prev
= page
->prev
;
280 static void *render (int pagenum
, int pindex
)
288 fz_device
*idev
, *tdev
, *mdev
;
289 fz_displaylist
*list
;
292 printf ("render %d %d\n", pagenum
, pindex
);
293 pdf_flushxref (state
.xref
, 0);
294 page
= calloc (sizeof (*page
), 1);
296 err (1, "malloc page %d\n", pagenum
);
298 page
->prev
= state
.pages
;
301 page2
= &state
.pages2
[pindex
];
303 pageobj
= pdf_getpageobject(state
.xref
, pagenum
);
305 die (fz_throw ("cannot retrieve info from page %d", pagenum
));
307 error
= pdf_loadpage(&drawpage
, state
.xref
, pageobj
);
311 page
->pixmap
= fz_newpixmapwithrect (pdf_devicergb
, page2
->bbox
);
315 fz_free (page
->pixmap
->samples
);
318 fz_clearpixmap(page
->pixmap
, 0xFF);
320 list
= fz_newdisplaylist ();
322 die (fz_throw ("fz_newdisplaylist failed"));
324 mdev
= fz_newlistdevice(list
);
325 error
= pdf_runcontentstream(mdev
, fz_identity(), state
.xref
,
333 idev
= fz_newdrawdevice (state
.cache
, page
->pixmap
);
335 die (fz_throw ("fz_newdrawdevice failed"));
337 fz_executedisplaylist(list
, idev
, page2
->ctm
);
339 fz_freedisplaylist(list
);
341 /* fz_debugpixmap (page->pixmap, "haha"); */
342 pdf_droppage (drawpage
);
344 page
->pagenum
= pagenum
;
348 static void layout1 (void)
364 for (pagenum
= 1; pagenum
<= state
.pagecount
; ++pagenum
) {
376 pageobj
= pdf_getpageobject (state
.xref
, pagenum
);
378 die (fz_throw ("cannot retrieve info from page %d", pagenum
));
380 obj
= fz_dictgets (pageobj
, "CropBox");
381 if (!fz_isarray(obj
)) {
382 obj
= fz_dictgets (pageobj
, "MediaBox");
383 if (!fz_isarray (obj
))
384 die (fz_throw ("cannot find page bounds %d (%d R)",
385 fz_tonum (obj
), fz_togen (obj
)));
387 box
= pdf_torect (obj
);
389 obj
= fz_dictgets (pageobj
, "Rotate");
391 rotate
= fz_toint (obj
);
396 && (prevrotate
== rotate
397 && !memcmp (&prevbox
, &box
, sizeof (box
)))) {
402 memcpy (&prevbox
, &box
, sizeof (box
));
405 box
.x0
= MIN (prevbox
.x0
, prevbox
.x1
);
406 box
.y0
= MIN (prevbox
.y0
, prevbox
.y1
);
407 box
.x1
= MAX (prevbox
.x0
, prevbox
.x1
);
408 box
.y1
= MAX (prevbox
.y0
, prevbox
.y1
);
410 ctm
= fz_identity ();
411 ctm
= fz_concat (ctm
, fz_translate (0, -box
.y1
));
412 ctm
= fz_concat (ctm
, fz_rotate (rotate
));
413 box2
= fz_transformrect (ctm
, box
);
414 w
= box2
.x1
- box2
.x0
;
417 ctm
= fz_identity ();
418 ctm
= fz_concat (ctm
, fz_translate (0, -box
.y1
));
419 ctm
= fz_concat (ctm
, fz_scale (zoom
, -zoom
));
420 ctm
= fz_concat (ctm
, fz_rotate (rotate
));
421 bbox
= fz_roundrect (fz_transformrect (ctm
, box
));
423 size
+= sizeof (*state
.pages2
);
424 state
.pages2
= caml_stat_resize (state
.pages2
, size
);
426 p
= &state
.pages2
[pindex
++];
427 memcpy (&p
->bbox
, &bbox
, sizeof (bbox
));
428 memcpy (&p
->ctm
, &ctm
, sizeof (ctm
));
430 p
->pagenum
= pagenum
- 1;
431 p
->pixmap
.x
= bbox
.x0
;
432 p
->pixmap
.y
= bbox
.y0
;
433 p
->pixmap
.w
= bbox
.x1
- bbox
.x0
;
434 p
->pixmap
.h
= bbox
.y1
- bbox
.y0
;
439 for (i
= pindex
- 1; i
>= 0; --i
) {
440 p
= &state
.pages2
[i
];
441 printd (state
.sock
, "l %d %d %d",
442 p
->pagenum
, p
->pixmap
.w
, p
->pixmap
.h
);
445 state
.mapsize
= mapsize
;
447 printf ("layout1 took %f sec\n", b
- a
);
448 printd (state
.sock
, "C %d", state
.pagecount
);
449 printd (state
.sock
, "m %d", h
);
452 static void *mainloop (void *unused
)
455 int len
, ret
, oldlen
= 0;
458 len
= readlen (state
.sock
);
460 errx (1, "readlen returned 0");
463 if (oldlen
< len
+ 1) {
464 p
= realloc (p
, len
+ 1);
466 err (1, "realloc %d failed", len
+ 1);
470 readdata (state
.sock
, p
, len
);
473 if (!strncmp ("open", p
, 4)) {
474 char *filename
= p
+ 5;
476 openxref (filename
, NULL
, 1);
478 else if (!strncmp ("free", p
, 4)) {
481 ret
= sscanf(p
+ 4, " %p", &ptr
);
484 else if (!strncmp ("layout", p
, 6)) {
487 ret
= sscanf (p
+ 6, " %d", &y
);
489 errx (1, "malformed layout `%.*s' ret=%d", len
, p
, ret
);
492 else if (!strncmp ("geometry", p
, 8)) {
496 ret
= sscanf (p
+ 8, " %d %d", &w
, &h
);
498 errx (1, "malformed geometry `%.*s' ret=%d", len
, p
, ret
);
503 for (page
= state
.pages
; page
; page
= page
->prev
) {
509 else if (!strncmp ("render", p
, 6)) {
510 int pagenum
, pindex
, w
, h
, ret
;
514 ret
= sscanf (p
+ 6, " %d %d %d %d", &pagenum
, &pindex
, &w
, &h
);
517 errx (1, "bad render line `%.*s' ret=%d", len
, p
, ret
);
520 page
= render (pagenum
, pindex
);
521 printd (state
.sock
, "r %d %d %d %p\n",
528 errx (1, "unknown command %.*s", len
, p
);
534 static void upload (struct page
*page
, const char *cap
)
536 int w
, h
, subimage
= 0;
539 w
= page
->page2
->bbox
.x1
- page
->page2
->bbox
.x0
;
541 glPixelStorei(GL_UNPACK_ALIGNMENT
, 4);
542 glPixelStorei(GL_UNPACK_ROW_LENGTH
, w
);
547 glAreTexturesResident (1, &page
->texid
, &v
);
548 printf ("resident %d %d\n", page
->pagenum
, v
);
549 glBindTexture (GL_TEXTURE_RECTANGLE_ARB
, page
->texid
);
553 int texid
= (state
.texid
++ % 10) + 1;
555 h
= page
->page2
->bbox
.y1
- page
->page2
->bbox
.y0
;
557 for (p
= state
.pages
; p
; p
= p
->prev
) {
558 if (p
->texid
== texid
) {
561 w1
= page
->page2
->bbox
.x1
- page
->page2
->bbox
.x0
;
562 h1
= page
->page2
->bbox
.y1
- page
->page2
->bbox
.y0
;
563 if (w
== w1
&& h
== h1
) {
571 /* glGenTextures (1, &page->texid); */
572 glBindTexture (GL_TEXTURE_RECTANGLE_ARB
, page
->texid
);
576 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB
,
584 page
->pixmap
->samples
587 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB
,
595 GL_UNSIGNED_INT_8_8_8_8
,
598 GL_UNSIGNED_BYTE
, /* INT_8_8_8_8, */
600 page
->pixmap
->samples
604 printf ("%s(%s) %d took %f sec\n", cap
,
605 subimage
? "sub" : "img",
606 page
->pagenum
, end
- start
);
610 CAMLprim value
ml_preload (value ptr_v
)
615 char *s
= String_val (ptr_v
);
617 ret
= sscanf (s
, "%p", &ptr
);
619 errx (1, "cannot parse pointer `%s'", s
);
621 upload (ptr
, "preload");
622 CAMLreturn (Val_unit
);
625 CAMLprim value
ml_draw (value dispy_v
, value w_v
, value h_v
,
626 value py_v
, value ptr_v
)
628 CAMLparam5 (dispy_v
, w_v
, h_v
, py_v
, ptr_v
);
629 int dispy
= Int_val (dispy_v
);
630 int w
= Int_val (w_v
);
631 int h
= Int_val (h_v
);
632 int py
= Int_val (py_v
);
633 char *s
= String_val (ptr_v
);
639 ret
= sscanf (s
, "%p", &ptr
);
641 errx (1, "cannot parse pointer `%s'", s
);
646 printf ("draw[%d=%dx%d] dispy=%d w=%d h=%d py=%d ptr=%p\n",
648 page
->page2
->bbox
.x1
- page
->page2
->bbox
.x0
,
649 page
->page2
->bbox
.y1
- page
->page2
->bbox
.y0
,
656 upload (page
, "upload");
657 glEnable (GL_TEXTURE_RECTANGLE_ARB
);
659 glEnable (GL_FRAGMENT_SHADER_ATI
);
663 glTexCoord2i (0, py
);
664 glVertex2i (0, dispy
);
666 glTexCoord2i (w
, py
);
667 glVertex2i (w
, dispy
);
669 glTexCoord2i (w
, py
+h
);
670 glVertex2i (w
, dispy
+ h
);
672 glTexCoord2i (0, py
+h
);
673 glVertex2i (0, dispy
+ h
);
676 glDisable (GL_TEXTURE_RECTANGLE_ARB
);
678 glDisable (GL_FRAGMENT_SHADER_ATI
);
680 CAMLreturn (Val_unit
);
683 static void initgl (void)
685 state
.dpy
= glXGetCurrentDisplay ();
687 die (fz_throw ("glXGetCurrentDisplay"));
689 state
.ctx
= glXGetCurrentContext ();
691 die (fz_throw ("glXGetCurrentContext"));
693 state
.drawable
= glXGetCurrentDrawable ();
694 if (!state
.drawable
) {
695 die (fz_throw ("glXGetCurrentDrawable"));
699 glBindFragmentShaderATI (1);
700 glBeginFragmentShaderATI ();
702 glSampleMapATI (GL_REG_0_ATI
, GL_TEXTURE0_ARB
, GL_SWIZZLE_STR_ATI
);
704 glColorFragmentOp1ATI (GL_MOV_ATI
,
705 GL_REG_1_ATI
, GL_RED_BIT_ATI
, GL_NONE
,
706 GL_REG_0_ATI
, GL_BLUE
, GL_NONE
);
707 glColorFragmentOp1ATI (GL_MOV_ATI
,
708 GL_REG_1_ATI
, GL_BLUE_BIT_ATI
, GL_NONE
,
709 GL_REG_0_ATI
, GL_RED
, GL_NONE
);
710 glColorFragmentOp1ATI (
712 GL_REG_0_ATI
, GL_RED_BIT_ATI
| GL_BLUE_BIT_ATI
, GL_NONE
,
713 GL_REG_1_ATI
, GL_NONE
, GL_NONE
716 glEndFragmentShaderATI ();
720 CAMLprim value
ml_init (value sock_v
)
726 pagesize
= sysconf (_SC_PAGESIZE
);
727 if (pagesize
== -1) {
731 state
.sock
= Int_val (sock_v
);
734 state
.cache
= fz_newglyphcache ();
736 ret
= pthread_create (&state
.thread
, NULL
, mainloop
, NULL
);
738 unix_error (ret
, "pthread_create", Nothing
);
741 CAMLreturn (Val_unit
);