multi-threaded.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /*
  2. Multi-threaded rendering of all pages in a document to PNG images.
  3. First look at docs/example.c and make sure you understand it.
  4. Then, before coming back here to see an example of multi-threading,
  5. please read the multi-threading section in:
  6. https://mupdf.readthedocs.io/en/latest/using-mupdf.html#multi-threading
  7. This example will create one main thread for reading pages from the
  8. document, and one thread per page for rendering. After rendering
  9. the main thread will wait for each rendering thread to complete before
  10. writing that thread's rendered image to a PNG image. There is
  11. nothing in MuPDF requiring a rendering thread to only render a
  12. single page, this is just a design decision taken for this example.
  13. To build this example in a source tree and render every page as a
  14. separate PNG, run:
  15. make examples
  16. ./build/debug/multi-threaded document.pdf
  17. To build from installed sources, and render the same document, run:
  18. gcc -I/usr/local/include -o multi-threaded \
  19. /usr/local/share/doc/mupdf/examples/multi-threaded.c \
  20. /usr/local/lib/libmupdf.a \
  21. /usr/local/lib/libmupdfthird.a \
  22. -lpthread -lm
  23. ./multi-threaded document.pdf
  24. Caution! As all pages are rendered simultaneously, please choose a
  25. file with just a few pages to avoid stressing your machine too
  26. much. Also you may run in to a limitation on the number of threads
  27. depending on your environment.
  28. */
  29. //Include the MuPDF header file, and pthread's header file.
  30. #include <mupdf/fitz.h>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <pthread.h>
  34. // A convenience function for dying abruptly on pthread errors.
  35. void
  36. fail(const char *msg)
  37. {
  38. fprintf(stderr, "%s\n", msg);
  39. abort();
  40. }
  41. // The data structure passed between the requesting main thread and
  42. // each rendering thread.
  43. struct thread_data {
  44. // A pointer to the original context in the main thread sent
  45. // from main to rendering thread. It will be used to create
  46. // each rendering thread's context clone.
  47. fz_context *ctx;
  48. // Page number sent from main to rendering thread for printing
  49. int pagenumber;
  50. // The display list as obtained by the main thread and sent
  51. // from main to rendering thread. This contains the drawing
  52. // commands (text, images, etc.) for the page that should be
  53. // rendered.
  54. fz_display_list *list;
  55. // The area of the page to render as obtained by the main
  56. // thread and sent from main to rendering thread.
  57. fz_rect bbox;
  58. // This is the result, a pixmap containing the rendered page.
  59. // It is passed first from main thread to the rendering
  60. // thread, then its samples are changed by the rendering
  61. // thread, and then back from the rendering thread to the main
  62. // thread.
  63. fz_pixmap *pix;
  64. // This is a note of whether a given thread failed or not.
  65. int failed;
  66. };
  67. // This is the function run by each rendering function. It takes
  68. // pointer to an instance of the data structure described above and
  69. // renders the display list into the pixmap before exiting.
  70. void *
  71. renderer(void *data_)
  72. {
  73. struct thread_data *data = (struct thread_data *)data_;
  74. int pagenumber = data->pagenumber;
  75. fz_context *ctx = data->ctx;
  76. fz_display_list *list = data->list;
  77. fz_rect bbox = data->bbox;
  78. fz_device *dev = NULL;
  79. fprintf(stderr, "thread at page %d loading!\n", pagenumber);
  80. // The context pointer is pointing to the main thread's
  81. // context, so here we create a new context based on it for
  82. // use in this thread.
  83. ctx = fz_clone_context(ctx);
  84. // Next we run the display list through the draw device which
  85. // will render the request area of the page to the pixmap.
  86. fz_var(dev);
  87. fprintf(stderr, "thread at page %d rendering!\n", pagenumber);
  88. fz_try(ctx)
  89. {
  90. // Create a white pixmap using the correct dimensions.
  91. data->pix = fz_new_pixmap_with_bbox(ctx, fz_device_rgb(ctx), fz_round_rect(bbox), NULL, 0);
  92. fz_clear_pixmap_with_value(ctx, data->pix, 0xff);
  93. // Do the actual rendering.
  94. dev = fz_new_draw_device(ctx, fz_identity, data->pix);
  95. fz_run_display_list(ctx, list, dev, fz_identity, bbox, NULL);
  96. fz_close_device(ctx, dev);
  97. }
  98. fz_always(ctx)
  99. fz_drop_device(ctx, dev);
  100. fz_catch(ctx)
  101. data->failed = 1;
  102. // Free this thread's context.
  103. fz_drop_context(ctx);
  104. fprintf(stderr, "thread at page %d done!\n", pagenumber);
  105. return data;
  106. }
  107. // These are the two locking functions required by MuPDF when
  108. // operating in a multi-threaded environment. They each take a user
  109. // argument that can be used to transfer some state, in this case a
  110. // pointer to the array of mutexes.
  111. void lock_mutex(void *user, int lock)
  112. {
  113. pthread_mutex_t *mutex = (pthread_mutex_t *) user;
  114. if (pthread_mutex_lock(&mutex[lock]) != 0)
  115. fail("pthread_mutex_lock()");
  116. }
  117. void unlock_mutex(void *user, int lock)
  118. {
  119. pthread_mutex_t *mutex = (pthread_mutex_t *) user;
  120. if (pthread_mutex_unlock(&mutex[lock]) != 0)
  121. fail("pthread_mutex_unlock()");
  122. }
  123. int main(int argc, char **argv)
  124. {
  125. char *filename = argc >= 2 ? argv[1] : "";
  126. pthread_t *thread = NULL;
  127. fz_locks_context locks;
  128. pthread_mutex_t mutex[FZ_LOCK_MAX];
  129. fz_context *ctx;
  130. fz_document *doc = NULL;
  131. int threads;
  132. int i;
  133. // Initialize FZ_LOCK_MAX number of non-recursive mutexes.
  134. for (i = 0; i < FZ_LOCK_MAX; i++)
  135. {
  136. if (pthread_mutex_init(&mutex[i], NULL) != 0)
  137. fail("pthread_mutex_init()");
  138. }
  139. // Initialize the locking structure with function pointers to
  140. // the locking functions and to the user data. In this case
  141. // the user data is a pointer to the array of mutexes so the
  142. // locking functions can find the relevant lock to change when
  143. // they are called. This way we avoid global variables.
  144. locks.user = mutex;
  145. locks.lock = lock_mutex;
  146. locks.unlock = unlock_mutex;
  147. // This is the main thread's context function, so supply the
  148. // locking structure. This context will be used to parse all
  149. // the pages from the document.
  150. ctx = fz_new_context(NULL, &locks, FZ_STORE_UNLIMITED);
  151. fz_var(thread);
  152. fz_var(doc);
  153. fz_try(ctx)
  154. {
  155. // Register default file types.
  156. fz_register_document_handlers(ctx);
  157. // Open the PDF, XPS or CBZ document.
  158. doc = fz_open_document(ctx, filename);
  159. // Retrieve the number of pages, which translates to the
  160. // number of threads used for rendering pages.
  161. threads = fz_count_pages(ctx, doc);
  162. fprintf(stderr, "spawning %d threads, one per page...\n", threads);
  163. thread = malloc(threads * sizeof (*thread));
  164. for (i = 0; i < threads; i++)
  165. {
  166. fz_page *page;
  167. fz_rect bbox;
  168. fz_display_list *list;
  169. fz_device *dev = NULL;
  170. fz_pixmap *pix;
  171. struct thread_data *data;
  172. fz_var(dev);
  173. fz_try(ctx)
  174. {
  175. // Load the relevant page for each thread. Note, that this
  176. // cannot be done on the worker threads, as only one thread
  177. // at a time can ever be accessing the document.
  178. page = fz_load_page(ctx, doc, i);
  179. // Compute the bounding box for each page.
  180. bbox = fz_bound_page(ctx, page);
  181. // Create a display list that will hold the drawing
  182. // commands for the page. Once we have the display list
  183. // this can safely be used on any other thread.
  184. list = fz_new_display_list(ctx, bbox);
  185. // Create a display list device to populate the page's display list.
  186. dev = fz_new_list_device(ctx, list);
  187. // Run the page to that device.
  188. fz_run_page(ctx, page, dev, fz_identity, NULL);
  189. // Close the device neatly, so everything is flushed to the list.
  190. fz_close_device(ctx, dev);
  191. }
  192. fz_always(ctx)
  193. {
  194. // Throw away the device.
  195. fz_drop_device(ctx, dev);
  196. // The page is no longer needed, all drawing commands
  197. // are now in the display list.
  198. fz_drop_page(ctx, page);
  199. }
  200. fz_catch(ctx)
  201. fz_rethrow(ctx);
  202. // Populate the data structure to be sent to the
  203. // rendering thread for this page.
  204. data = malloc(sizeof (*data));
  205. data->pagenumber = i + 1;
  206. data->ctx = ctx;
  207. data->list = list;
  208. data->bbox = bbox;
  209. data->pix = NULL;
  210. data->failed = 0;
  211. // Create the thread and pass it the data structure.
  212. if (pthread_create(&thread[i], NULL, renderer, data) != 0)
  213. fail("pthread_create()");
  214. }
  215. // Now each thread is rendering pages, so wait for each thread
  216. // to complete its rendering.
  217. fprintf(stderr, "joining %d threads...\n", threads);
  218. for (i = 0; i < threads; i++)
  219. {
  220. char filename[42];
  221. struct thread_data *data;
  222. if (pthread_join(thread[i], (void **) &data) != 0)
  223. fail("pthread_join");
  224. if (data->failed)
  225. {
  226. fprintf(stderr, "\tRendering for page %d failed\n", i + 1);
  227. }
  228. else
  229. {
  230. sprintf(filename, "out%04d.png", i);
  231. fprintf(stderr, "\tSaving %s...\n", filename);
  232. // Write the rendered image to a PNG file
  233. fz_save_pixmap_as_png(ctx, data->pix, filename);
  234. }
  235. // Free the thread's pixmap and display list.
  236. fz_drop_pixmap(ctx, data->pix);
  237. fz_drop_display_list(ctx, data->list);
  238. // Free the data structure passed back and forth
  239. // between the main thread and rendering thread.
  240. free(data);
  241. }
  242. }
  243. fz_always(ctx)
  244. {
  245. // Free the thread structure
  246. free(thread);
  247. // Drop the document
  248. fz_drop_document(ctx, doc);
  249. }
  250. fz_catch(ctx)
  251. {
  252. fz_report_error(ctx);
  253. fail("error");
  254. }
  255. // Finally the main thread's context is freed.
  256. fz_drop_context(ctx);
  257. fprintf(stderr, "finally!\n");
  258. fflush(NULL);
  259. return 0;
  260. }