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"
70 struct tex
**texowners
;
73 static double now (void)
77 if (gettimeofday (&tv
, NULL
)) {
78 err (1, "gettimeofday");
80 return tv
.tv_sec
+ tv
.tv_usec
*1e-6;
83 static void readdata (int fd
, char *p
, int size
)
87 n
= read (fd
, p
, size
);
89 err (1, "read (req %d, ret %zd)", size
, n
);
93 static void writedata (int fd
, char *p
, int size
)
98 buf
[0] = (size
>> 24) & 0xff;
99 buf
[1] = (size
>> 16) & 0xff;
100 buf
[2] = (size
>> 8) & 0xff;
101 buf
[3] = (size
>> 0) & 0xff;
103 n
= write (fd
, buf
, 4);
105 err (1, "write %zd", n
);
108 n
= write (fd
, p
, size
);
110 err (1, "write (req %d, ret %zd)", size
, n
);
114 static void __attribute__ ((format (printf
, 2, 3)))
115 printd (int fd
, const char *fmt
, ...)
122 len
= vsnprintf (buf
, sizeof (buf
), fmt
, ap
);
124 writedata (fd
, buf
, len
);
127 static void closexref (void);
129 static void createmmap (struct page
*page
)
134 size
= page
->pixmap
->w
* page
->pixmap
->h
* 4;
136 fd
= open ("pdfmap", O_CREAT
|O_TRUNC
|O_RDWR
);
140 ret
= unlink ("pdfmap");
144 size
= (size
+ pagesize
- 1) & ~(pagesize
- 1);
145 ret
= ftruncate (fd
, size
);
147 err (1, "ftruncate");
149 page
->pixmap
->samples
= mmap (NULL
, size
, PROT_READ
|PROT_WRITE
,
151 if (page
->pixmap
->samples
== MAP_FAILED
) {
156 static void die(fz_error error
)
158 fz_catch(error
, "aborting");
163 void openxref(char *filename
, char *password
, int dieonbadpass
)
170 basename
= strrchr(filename
, '/');
176 fd
= open(filename
, O_BINARY
| O_RDONLY
, 0666);
178 die(fz_throw("cannot open file '%s'", filename
));
180 file
= fz_openfile(fd
);
181 state
.xref
= pdf_openxref(file
);
183 die(fz_throw("cannot open PDF file '%s'", basename
));
186 if (pdf_needspassword(state
.xref
))
188 okay
= pdf_authenticatepassword(state
.xref
, password
);
189 if (!okay
&& !dieonbadpass
)
190 fz_warn("invalid password, attempting to continue.");
191 else if (!okay
&& dieonbadpass
)
192 die(fz_throw("invalid password"));
195 state
.pagecount
= pdf_getpagecount(state
.xref
);
196 printd (state
.sock
, "C %d", state
.pagecount
);
199 static void flushxref(void)
203 pdf_flushxref(state
.xref
, 0);
207 static void closexref(void)
211 pdf_closexref(state
.xref
);
216 static int readlen (int fd
)
223 err (1, "read %zd", n
);
226 return (p
[0] << 24) | (p
[1] << 16) | (p
[2] << 8) | p
[3];
229 static void freepage (struct page
*page
)
234 fz_droppixmap (page
->pixmap
);
235 for (p
= state
.pages
; p
; p
= p
->prev
) {
236 if (p
->prev
== page
) {
237 p
->prev
= page
->prev
;
241 for (i
= 0; i
< state
.slicecount
; ++i
) {
242 struct tex
*t
= &p
->texs
[i
];
243 state
.texowners
[t
->index
] = NULL
;
248 static void subdivide (struct page
*p
)
251 int h
= p
->pixmap
->h
;
253 if (state
.slicecount
== 1) {
254 struct tex
*t
= &p
->texs
[0];
259 int th
= h
/ (state
.slicecount
- 1);
261 for (i
= 0; i
< state
.slicecount
; ++i
) {
262 struct tex
*t
= &p
->texs
[i
];
270 static void *render (int pagenum
, int pindex
)
278 fz_device
*idev
, *tdev
, *mdev
;
279 fz_displaylist
*list
;
282 printf ("render %d %d\n", pagenum
, pindex
);
283 pdf_flushxref (state
.xref
, 0);
284 page
= calloc (sizeof (*page
) + (state
.slicecount
* sizeof (struct tex
)), 1);
286 err (1, "malloc page %d\n", pagenum
);
288 page
->prev
= state
.pages
;
291 page2
= &state
.pages2
[pindex
];
293 pageobj
= pdf_getpageobject(state
.xref
, pagenum
);
295 die (fz_throw ("cannot retrieve info from page %d", pagenum
));
297 error
= pdf_loadpage(&drawpage
, state
.xref
, pageobj
);
301 page
->pixmap
= fz_newpixmapwithrect (pdf_devicergb
, page2
->bbox
);
305 fz_free (page
->pixmap
->samples
);
308 fz_clearpixmap(page
->pixmap
, 0xFF);
310 list
= fz_newdisplaylist ();
312 die (fz_throw ("fz_newdisplaylist failed"));
314 mdev
= fz_newlistdevice(list
);
315 error
= pdf_runcontentstream(mdev
, fz_identity(), state
.xref
,
323 idev
= fz_newdrawdevice (state
.cache
, page
->pixmap
);
325 die (fz_throw ("fz_newdrawdevice failed"));
327 fz_executedisplaylist(list
, idev
, page2
->ctm
);
329 fz_freedisplaylist(list
);
331 /* fz_debugpixmap (page->pixmap, "haha"); */
332 pdf_droppage (drawpage
);
334 page
->pagenum
= pagenum
;
339 static void layout1 (void)
354 for (pagenum
= 1; pagenum
<= state
.pagecount
; ++pagenum
) {
366 pageobj
= pdf_getpageobject (state
.xref
, pagenum
);
368 die (fz_throw ("cannot retrieve info from page %d", pagenum
));
370 obj
= fz_dictgets (pageobj
, "CropBox");
371 if (!fz_isarray(obj
)) {
372 obj
= fz_dictgets (pageobj
, "MediaBox");
373 if (!fz_isarray (obj
))
374 die (fz_throw ("cannot find page bounds %d (%d R)",
375 fz_tonum (obj
), fz_togen (obj
)));
377 box
= pdf_torect (obj
);
379 obj
= fz_dictgets (pageobj
, "Rotate");
381 rotate
= fz_toint (obj
);
386 && (prevrotate
== rotate
387 && !memcmp (&prevbox
, &box
, sizeof (box
)))) {
391 memcpy (&prevbox
, &box
, sizeof (box
));
394 box
.x0
= MIN (prevbox
.x0
, prevbox
.x1
);
395 box
.y0
= MIN (prevbox
.y0
, prevbox
.y1
);
396 box
.x1
= MAX (prevbox
.x0
, prevbox
.x1
);
397 box
.y1
= MAX (prevbox
.y0
, prevbox
.y1
);
399 ctm
= fz_identity ();
400 ctm
= fz_concat (ctm
, fz_translate (0, -box
.y1
));
401 ctm
= fz_concat (ctm
, fz_rotate (rotate
));
402 box2
= fz_transformrect (ctm
, box
);
403 w
= box2
.x1
- box2
.x0
;
406 ctm
= fz_identity ();
407 ctm
= fz_concat (ctm
, fz_translate (0, -box
.y1
));
408 ctm
= fz_concat (ctm
, fz_scale (zoom
, -zoom
));
409 ctm
= fz_concat (ctm
, fz_rotate (rotate
));
410 bbox
= fz_roundrect (fz_transformrect (ctm
, box
));
412 size
+= sizeof (*state
.pages2
);
413 state
.pages2
= caml_stat_resize (state
.pages2
, size
);
415 p
= &state
.pages2
[pindex
++];
416 memcpy (&p
->bbox
, &bbox
, sizeof (bbox
));
417 memcpy (&p
->ctm
, &ctm
, sizeof (ctm
));
419 p
->pagenum
= pagenum
- 1;
420 p
->pixmap
.x
= bbox
.x0
;
421 p
->pixmap
.y
= bbox
.y0
;
422 p
->pixmap
.w
= bbox
.x1
- bbox
.x0
;
423 p
->pixmap
.h
= bbox
.y1
- bbox
.y0
;
427 for (i
= pindex
- 1; i
>= 0; --i
) {
428 p
= &state
.pages2
[i
];
429 printd (state
.sock
, "l %d %d %d",
430 p
->pagenum
, p
->pixmap
.w
, p
->pixmap
.h
);
434 printf ("layout1 took %f sec\n", b
- a
);
435 printd (state
.sock
, "C %d", state
.pagecount
);
438 static void *mainloop (void *unused
)
441 int len
, ret
, oldlen
= 0;
444 len
= readlen (state
.sock
);
446 errx (1, "readlen returned 0");
449 if (oldlen
< len
+ 1) {
450 p
= realloc (p
, len
+ 1);
452 err (1, "realloc %d failed", len
+ 1);
456 readdata (state
.sock
, p
, len
);
459 if (!strncmp ("open", p
, 4)) {
460 char *filename
= p
+ 5;
462 openxref (filename
, NULL
, 1);
464 else if (!strncmp ("free", p
, 4)) {
467 ret
= sscanf(p
+ 4, " %p", &ptr
);
470 else if (!strncmp ("layout", p
, 6)) {
473 ret
= sscanf (p
+ 6, " %d", &y
);
475 errx (1, "malformed layout `%.*s' ret=%d", len
, p
, ret
);
478 else if (!strncmp ("geometry", p
, 8)) {
482 ret
= sscanf (p
+ 8, " %d %d", &w
, &h
);
484 errx (1, "malformed geometry `%.*s' ret=%d", len
, p
, ret
);
489 for (page
= state
.pages
; page
; page
= page
->prev
) {
495 else if (!strncmp ("render", p
, 6)) {
496 int pagenum
, pindex
, w
, h
, ret
;
500 ret
= sscanf (p
+ 6, " %d %d %d %d", &pagenum
, &pindex
, &w
, &h
);
503 errx (1, "bad render line `%.*s' ret=%d", len
, p
, ret
);
506 page
= render (pagenum
, pindex
);
507 printd (state
.sock
, "r %d %d %d %p\n",
514 errx (1, "unknown command %.*s", len
, p
);
520 static void upload2 (struct page
*page
, int texnum
, const char *cap
)
525 struct tex
*tex
= &page
->texs
[texnum
];
530 glPixelStorei(GL_UNPACK_ALIGNMENT
, 4);
531 glPixelStorei(GL_UNPACK_ROW_LENGTH
, w
);
533 if (tex
->index
!= -1 && state
.texowners
[tex
->index
] == tex
) {
534 glBindTexture (GL_TEXTURE_RECTANGLE_ARB
, state
.texids
[tex
->index
]);
538 int index
= (state
.texid
++ % state
.texcount
);
541 for (i
= 0; i
< texnum
; ++i
) {
542 offset
+= w
* page
->texs
[i
].h
* 4;
544 subimage
= 0 || state
.texowners
[index
]
545 ? state
.texowners
[index
]->h
== tex
->h
549 state
.texowners
[index
] = tex
;
552 glBindTexture (GL_TEXTURE_RECTANGLE_ARB
, state
.texids
[tex
->index
]);
556 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB
,
564 GL_UNSIGNED_INT_8_8_8_8
,
567 GL_UNSIGNED_BYTE
, /* INT_8_8_8_8, */
569 page
->pixmap
->samples
+ offset
573 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB
,
581 GL_UNSIGNED_INT_8_8_8_8
,
584 GL_UNSIGNED_BYTE
, /* INT_8_8_8_8, */
586 page
->pixmap
->samples
+ offset
591 printf ("%s(%s) %d(%d) took %f sec\n", cap
,
592 subimage
? "sub" : "img",
593 page
->pagenum
, texnum
, end
- start
);
597 CAMLprim value
ml_preload (value ptr_v
)
603 char *s
= String_val (ptr_v
);
605 ret
= sscanf (s
, "%p", &ptr
);
607 errx (1, "cannot parse pointer `%s'", s
);
609 for (i
= 0; i
< state
.slicecount
; ++i
)
610 upload2 (ptr
, i
, "preload");
611 CAMLreturn (Val_unit
);
615 #define lprintf printf
620 CAMLprim value
ml_draw (value dispy_v
, value w_v
, value h_v
,
621 value py_v
, value ptr_v
)
623 CAMLparam5 (dispy_v
, w_v
, h_v
, py_v
, ptr_v
);
624 int dispy
= Int_val (dispy_v
);
625 int w
= Int_val (w_v
);
626 int h
= Int_val (h_v
);
627 int py
= Int_val (py_v
);
628 char *s
= String_val (ptr_v
);
635 ret
= sscanf (s
, "%p", &ptr
);
637 errx (1, "cannot parse pointer `%s'", s
);
642 printf ("draw[%d] %dx%d %dx%d\n",
649 glEnable (GL_TEXTURE_RECTANGLE_ARB
);
651 glEnable (GL_FRAGMENT_SHADER_ATI
);
654 for (texnum
= 0; texnum
!= state
.slicecount
; ++texnum
) {
655 struct tex
*tex
= &page
->texs
[texnum
];
664 struct tex
*tex
= &page
->texs
[texnum
];
666 if (texnum
>= state
.slicecount
) {
669 th
= MIN (h
, tex
->h
- py
);
670 lprintf ("draw[%d, %d], h=%d th=%d py=%d dispy=%d\n",
671 page
->pagenum
, texnum
, h
, th
, py
, dispy
);
673 upload2 (page
, texnum
, "upload");
677 glTexCoord2i (0, py
);
678 glVertex2i (0, dispy
);
680 glTexCoord2i (w
, py
);
681 glVertex2i (w
, dispy
);
683 glTexCoord2i (w
, py
+th
);
684 glVertex2i (w
, dispy
+ th
);
686 glTexCoord2i (0, py
+th
);
687 glVertex2i (0, dispy
+ th
);
697 glDisable (GL_TEXTURE_RECTANGLE_ARB
);
699 glDisable (GL_FRAGMENT_SHADER_ATI
);
701 CAMLreturn (Val_unit
);
704 static void initgl (void)
706 state
.dpy
= glXGetCurrentDisplay ();
708 die (fz_throw ("glXGetCurrentDisplay"));
710 state
.ctx
= glXGetCurrentContext ();
712 die (fz_throw ("glXGetCurrentContext"));
714 state
.drawable
= glXGetCurrentDrawable ();
715 if (!state
.drawable
) {
716 die (fz_throw ("glXGetCurrentDrawable"));
720 glBindFragmentShaderATI (1);
721 glBeginFragmentShaderATI ();
723 glSampleMapATI (GL_REG_0_ATI
, GL_TEXTURE0_ARB
, GL_SWIZZLE_STR_ATI
);
725 glColorFragmentOp1ATI (GL_MOV_ATI
,
726 GL_REG_1_ATI
, GL_RED_BIT_ATI
, GL_NONE
,
727 GL_REG_0_ATI
, GL_BLUE
, GL_NONE
);
728 glColorFragmentOp1ATI (GL_MOV_ATI
,
729 GL_REG_1_ATI
, GL_BLUE_BIT_ATI
, GL_NONE
,
730 GL_REG_0_ATI
, GL_RED
, GL_NONE
);
731 glColorFragmentOp1ATI (
733 GL_REG_0_ATI
, GL_RED_BIT_ATI
| GL_BLUE_BIT_ATI
, GL_NONE
,
734 GL_REG_1_ATI
, GL_NONE
, GL_NONE
737 glEndFragmentShaderATI ();
741 CAMLprim value
ml_init (value sock_v
)
747 pagesize
= sysconf (_SC_PAGESIZE
);
748 if (pagesize
== -1) {
753 state
.slicecount
= 16;
755 state
.texids
= calloc (state
.texcount
* sizeof (*state
.texids
), 1);
757 err (1, "calloc texids %zu", state
.texcount
* sizeof (*state
.texids
));
760 state
.texowners
= calloc (state
.texcount
* sizeof (*state
.texowners
), 1);
761 if (!state
.texowners
) {
762 err (1, "calloc texowners %zu",
763 state
.texcount
* sizeof (*state
.texowners
));
766 glGenTextures (state
.texcount
, state
.texids
);
768 state
.sock
= Int_val (sock_v
);
771 state
.cache
= fz_newglyphcache ();
773 ret
= pthread_create (&state
.thread
, NULL
, mainloop
, NULL
);
775 unix_error (ret
, "pthread_create", Nothing
);
778 CAMLreturn (Val_unit
);