Advertisement
Guest User

Faster muPDF for archive.org ebooks (JPEG2000/JBIG2)

a guest
Sep 6th, 2017
159
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.16 KB | None | 0 0
  1. Want MuPDF to be faster at rendering archive.org ebooks?
  2.  
  3. What you will need:
  4. - MuPDF source code (I used 1.7a)
  5. - Kakadu 2.2.3 (find and/or buy it yourself)
  6. - C and C++ compiler (MSVC works fine)
  7.  
  8. Steps:
  9. - Find jbig2_image.c and add optimised case for JBIG2_COMPOSE_XOR. Easiest way is to copy-paste
  10. the code for JBIG2_COMPOSE_OR and replace all |= with ^=.
  11. - Compile MuPDF. Save the object files.
  12. - Compile load-jpx-kakadu.cpp (see below)
  13. - Replace load-jpx.obj with load-jpx-kakadu.obj and relink. Remember to add Kakadu to linker path.
  14. - Enjoy faster MuPDF!
  15.  
  16. Notes:
  17. Distributing the binary is probably against several licenses, but I can tell you how to make it
  18. yourself. Fuck licensing/copyright --- this is why we can't have nice things. This posting is in
  19. public domain.
  20.  
  21. ---BEGIN FILE load-jpx-kakadu.cpp---
  22. #include <math.h>
  23.  
  24. #ifdef __cplusplus
  25. extern "C" {
  26. #endif
  27.  
  28. #include "mupdf/fitz.h"
  29.  
  30. #ifdef __cplusplus
  31. }
  32. #endif
  33.  
  34. #include <sstream>
  35. #include <winsock2.h>
  36.  
  37. // Kakadu core includes
  38. #include "kdu_elementary.h"
  39. #include "kdu_messaging.h"
  40. #include "kdu_params.h"
  41. #include "kdu_compressed.h"
  42. #include "kdu_sample_processing.h"
  43. // Application level includes
  44. #include "kdu_file_io.h"
  45.  
  46. /*****************************************************************************/
  47. /* kdu_simple_mem_source */
  48. /*****************************************************************************/
  49.  
  50. class kdu_simple_mem_source : public kdu_compressed_source {
  51. public: // Member functions
  52. kdu_simple_mem_source(unsigned char *data, int size)
  53. { this->data = data; this->size = size; this->pos = 0; }
  54. ~kdu_simple_mem_source()
  55. { }
  56. bool exists()
  57. { return !!data; }
  58. bool operator!()
  59. { return !data; }
  60. int read(kdu_byte *buf, int num_bytes)
  61. {
  62. if(num_bytes > size - pos)
  63. num_bytes = size - pos;
  64. memcpy(buf, data + pos, num_bytes);
  65. pos += num_bytes;
  66. return num_bytes;
  67. }
  68. void close()
  69. {
  70. }
  71. private: // Data
  72. unsigned char *data;
  73. int size;
  74. int pos;
  75. };
  76.  
  77. /******************************************************************************/
  78. /* transfer_bytes */
  79. /******************************************************************************/
  80.  
  81. void
  82. transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision)
  83. /* Transfers source samples from the supplied line buffer into the output
  84. byte buffer, spacing successive output samples apart by `gap' bytes
  85. (to allow for interleaving of colour components). The function performs
  86. all necessary level shifting, type conversion, rounding and truncation. */
  87. {
  88. int width = src.get_width();
  89. if (src.get_buf32() != NULL)
  90. { // Decompressed samples have a 32-bit representation (integer or float)
  91. assert(precision >= 8); // Else would have used 16 bit representation
  92. kdu_sample32 *sp = src.get_buf32();
  93. if (!src.is_absolute())
  94. { // Transferring normalized floating point data.
  95. float scale16 = (float)(1<<16);
  96. kdu_int32 val;
  97.  
  98. for (; width > 0; width--, sp++, dest+=gap)
  99. {
  100. val = (kdu_int32)(sp->fval*scale16);
  101. val = (val+128)>>8; // May be faster than true rounding
  102. val += 128;
  103. if (val & ((-1)<<8))
  104. val = (val<0)?0:255;
  105. *dest = (kdu_byte) val;
  106. }
  107. }
  108. else
  109. { // Transferring 32-bit absolute integers.
  110. kdu_int32 val;
  111. kdu_int32 downshift = precision-8;
  112. kdu_int32 offset = (1<<downshift)>>1;
  113.  
  114. for (; width > 0; width--, sp++, dest+=gap)
  115. {
  116. val = sp->ival;
  117. val = (val+offset)>>downshift;
  118. val += 128;
  119. if (val & ((-1)<<8))
  120. val = (val<0)?0:255;
  121. *dest = (kdu_byte) val;
  122. }
  123. }
  124. }
  125. else
  126. { // Source data is 16 bits.
  127. kdu_sample16 *sp = src.get_buf16();
  128. if (!src.is_absolute())
  129. { // Transferring 16-bit fixed point quantities
  130. kdu_int16 val;
  131.  
  132. if (precision >= 8)
  133. { // Can essentially ignore the bit-depth.
  134. for (; width > 0; width--, sp++, dest+=gap)
  135. {
  136. val = (sp->ival + 4112)>>5;
  137. if (val & ((-1)<<8))
  138. val = (val<0)?0:255;
  139. *dest = (kdu_byte) val;
  140. }
  141. }
  142. else
  143. { // Need to force zeros into one or more least significant bits.
  144. kdu_int16 downshift = KDU_FIX_POINT-precision;
  145. kdu_int16 upshift = 8-precision;
  146. kdu_int16 offset = 1<<(downshift-1);
  147.  
  148. for (; width > 0; width--, sp++, dest+=gap)
  149. {
  150. val = sp->ival;
  151. val = (val+offset)>>downshift;
  152. val <<= upshift;
  153. val += 128;
  154. if (val & ((-1)<<8))
  155. val = (val<0)?0:(256-(1<<upshift));
  156. *dest = (kdu_byte) val;
  157. }
  158. }
  159. }
  160. else
  161. { // Transferring 16-bit absolute integers.
  162. kdu_int16 val;
  163.  
  164. if (precision >= 8)
  165. {
  166. kdu_int16 downshift = precision-8;
  167. kdu_int16 offset = (1<<downshift)>>1;
  168.  
  169. for (; width > 0; width--, sp++, dest+=gap)
  170. {
  171. val = sp->ival;
  172. val = (val+offset)>>downshift;
  173. val += 128;
  174. if (val & ((-1)<<8))
  175. val = (val<0)?0:255;
  176. *dest = (kdu_byte) val;
  177. }
  178. }
  179. else
  180. {
  181. kdu_int16 upshift = 8-precision;
  182.  
  183. for (; width > 0; width--, sp++, dest+=gap)
  184. {
  185. val = sp->ival;
  186. val <<= upshift;
  187. val += 128;
  188. if (val & ((-1)<<8))
  189. val = (val<0)?0:(256-(1<<upshift));
  190. *dest = (kdu_byte) val;
  191. }
  192. }
  193. }
  194. }
  195. }
  196.  
  197. /*****************************************************************************/
  198. /* STATIC process_tile */
  199. /*****************************************************************************/
  200.  
  201. static void
  202. process_tile(kdu_tile tile, kdu_byte *buf, int row_gap)
  203. /* Decompresses a tile, writing the data into the supplied byte buffer.
  204. The buffer contains interleaved image components, if there are any.
  205. Although you may think of the buffer as belonging entirely to this tile,
  206. the `buf' pointer may actually point into a larger buffer representing
  207. multiple tiles. For this reason, `row_gap' is needed to identify the
  208. separation between consecutive rows in the real buffer. */
  209. {
  210. int c, num_components = tile.get_num_components(); assert(num_components<=3);
  211. bool use_ycc = tile.get_ycc();
  212.  
  213. // Open tile-components and create processing engines and resources
  214. kdu_dims dims;
  215. kdu_sample_allocator allocator;
  216. kdu_tile_comp comps[3];
  217. kdu_line_buf lines[3];
  218. kdu_pull_ifc engines[3];
  219. bool reversible[3]; // Some components may be reversible and others not.
  220. int bit_depths[3]; // Original bit-depth may be quite different from 8.
  221. for (c=0; c < num_components; c++)
  222. {
  223. comps[c] = tile.access_component(c);
  224. reversible[c] = comps[c].get_reversible();
  225. bit_depths[c] = comps[c].get_bit_depth();
  226. kdu_resolution res = comps[c].access_resolution(); // Get top resolution
  227. kdu_dims comp_dims; res.get_dims(comp_dims);
  228. if (c == 0)
  229. dims = comp_dims;
  230. else
  231. assert(dims == comp_dims); // Safety check; the caller has ensured this
  232. bool use_shorts = (comps[c].get_bit_depth(true) <= 16);
  233. lines[c].pre_create(&allocator,dims.size.x,reversible[c],use_shorts);
  234. if (res.which() == 0) // No DWT levels used
  235. engines[c] =
  236. kdu_decoder(res.access_subband(LL_BAND),&allocator,use_shorts);
  237. else
  238. engines[c] = kdu_synthesis(res,&allocator,use_shorts);
  239. }
  240. allocator.finalize(); // Actually creates buffering resources
  241. for (c=0; c < num_components; c++)
  242. lines[c].create(); // Grabs resources from the allocator.
  243.  
  244. // Now walk through the lines of the buffer, recovering them from the
  245. // relevant tile-component processing engines.
  246.  
  247. while (dims.size.y--)
  248. {
  249. for (c=0; c < num_components; c++)
  250. engines[c].pull(lines[c],true);
  251. if ((num_components == 3) && use_ycc)
  252. kdu_convert_ycc_to_rgb(lines[0],lines[1],lines[2]);
  253. for (c=0; c < num_components; c++)
  254. transfer_bytes(buf+c,lines[c],num_components+1,bit_depths[c]);
  255. buf += row_gap;
  256. }
  257.  
  258. // Cleanup
  259. for (c=0; c < num_components; c++)
  260. engines[c].destroy(); // engines are interfaces; no default destructors
  261. }
  262.  
  263. static fz_context *kdu_fz_ctx;
  264. static std::ostringstream kdu_error_msgs;
  265. static std::ostringstream kdu_warning_msgs;
  266.  
  267. void kdu_warn_callback() { fz_warn(kdu_fz_ctx, "kakadu warning: %s", kdu_warning_msgs.str().c_str()); kdu_warning_msgs.str(""); }
  268. void kdu_error_callback(){ fz_warn(kdu_fz_ctx, "kakadu error: %s", kdu_error_msgs.str().c_str()); kdu_warning_msgs.str(""); }
  269.  
  270. extern "C"
  271. fz_pixmap *
  272. fz_load_jpx(fz_context *ctx, unsigned char *data, int size, fz_colorspace *defcs, int indexed)
  273. {
  274. fz_pixmap *img;
  275. fz_colorspace *colorspace;
  276. int a, n, w, h, depth, sgnd;
  277. int x, y, k, v;
  278.  
  279. // Custom messaging services
  280. kdu_fz_ctx = ctx;
  281. kdu_customize_warnings(&kdu_warning_msgs, 79, kdu_warn_callback);
  282. kdu_customize_errors(&kdu_error_msgs, 79, kdu_error_callback);
  283.  
  284.  
  285. if (size < 2)
  286. fz_throw(ctx, FZ_ERROR_GENERIC, "not enough data to determine image format");
  287.  
  288. /* Check for SOC marker -- if found we have a bare J2K stream
  289. else it may be a JP2, so seek to the first codestream box */
  290. if (data[0] != 0xFF || data[1] != 0x4F) {
  291. if(size < 12 || memcmp(data, "\0\0\0\xCjP \r\n\x87\n", 12))
  292. fz_throw(ctx, FZ_ERROR_GENERIC, "Invalid JP2 file format");
  293. data += 12;
  294. size -= 12;
  295. for(;;) {
  296. unsigned int boxlen;
  297. unsigned int boxtype;
  298. if(size < 8)
  299. fz_throw(ctx, FZ_ERROR_GENERIC, "Could not find JP2 codestream box");
  300. boxlen = ntohl(*(int*)data);
  301. boxtype = *(int*)(data + 4);
  302. data += 8;
  303. size -= 8;
  304. if(boxlen == 1) {
  305. if(size < 8)
  306. fz_throw(ctx, FZ_ERROR_GENERIC, "The JP2 box size is invalid (truncated)");
  307. if(*(int*)data) /* can't be > 4GB here... */
  308. fz_throw(ctx, FZ_ERROR_GENERIC, "The JP2 box hugesize is surely malformed");
  309. boxlen = ntohl(*(int*)(data + 4));
  310. data += 8;
  311. size -= 8;
  312. if(boxlen < 16 || boxlen - 16 > size)
  313. fz_throw(ctx, FZ_ERROR_GENERIC, "The JP2 box size is invalid (truncated)");
  314. boxlen -= 16;
  315. } else if(!boxlen)
  316. boxlen = size;
  317. else if(boxlen < 8)
  318. fz_throw(ctx, FZ_ERROR_GENERIC, "The JP2 box size is invalid (2-7)");
  319. else if(boxlen < 8 || boxlen - 8 > size)
  320. fz_throw(ctx, FZ_ERROR_GENERIC, "The JP2 box size is invalid (truncated)");
  321. else
  322. boxlen -= 8;
  323. if(boxtype == 0x6332706a) {
  324. size = boxlen;
  325. break;
  326. }
  327. data += boxlen;
  328. size -= boxlen;
  329. }
  330. }
  331.  
  332. // Construct code-stream object
  333. kdu_simple_mem_source input(data, size);
  334. kdu_codestream codestream; codestream.create(&input);
  335. codestream.set_resilient(); // Set the parsing error tolerance.
  336.  
  337. kdu_dims dims; codestream.get_dims(0,dims);
  338.  
  339. // Determine number of components to decompress
  340. int num_components = codestream.get_num_components();
  341. if (num_components == 2)
  342. num_components = 1;
  343. else if (num_components >= 3)
  344. { // Check that components have consistent dimensions (for PPM file)
  345. num_components = 3;
  346. kdu_dims dims1; codestream.get_dims(1,dims1);
  347. kdu_dims dims2; codestream.get_dims(2,dims2);
  348. if ((dims1 != dims) || (dims2 != dims))
  349. num_components = 1;
  350. }
  351.  
  352. codestream.apply_input_restrictions(0,num_components,0,0,NULL);
  353.  
  354. n = num_components;
  355. w = dims.size.x;
  356. h = dims.size.y;
  357. a = 1;
  358.  
  359. if (defcs)
  360. {
  361. if (defcs->n == n)
  362. {
  363. colorspace = defcs;
  364. }
  365. else
  366. {
  367. fz_warn(ctx, "jpx file and dict colorspaces do not match");
  368. defcs = NULL;
  369. }
  370. }
  371.  
  372. if (!defcs)
  373. {
  374. switch (n)
  375. {
  376. case 1: colorspace = fz_device_gray(ctx); break;
  377. case 3: colorspace = fz_device_rgb(ctx); break;
  378. case 4: colorspace = fz_device_cmyk(ctx); break;
  379. }
  380. }
  381.  
  382.  
  383. // Now we are ready to walk through the tiles processing them one-by-one.
  384. fz_try(ctx)
  385. {
  386. img = fz_new_pixmap(ctx, colorspace, w, h);
  387. }
  388. fz_catch(ctx)
  389. {
  390. codestream.destroy();
  391. fz_rethrow_message(ctx, "out of memory loading jpx");
  392. }
  393.  
  394. kdu_byte *buffer = img->samples;
  395. memset(buffer, 255, w*h*(num_components+a));
  396.  
  397. try {
  398. kdu_dims tile_indices; codestream.get_valid_tiles(tile_indices);
  399. kdu_coords tpos;
  400. for (tpos.y=0; tpos.y < tile_indices.size.y; tpos.y++)
  401. for (tpos.x=0; tpos.x < tile_indices.size.x; tpos.x++)
  402. {
  403. kdu_tile tile = codestream.open_tile(tpos+tile_indices.pos);
  404. kdu_resolution res = tile.access_component(0).access_resolution();
  405. kdu_dims tile_dims; res.get_dims(tile_dims);
  406. kdu_coords offset = tile_dims.pos - dims.pos;
  407. int row_gap = (num_components+a)*dims.size.x; // inter-row separation
  408. kdu_byte *buf = buffer + offset.y*row_gap + offset.x*(num_components+a);
  409.  
  410. process_tile(tile,buf,row_gap);
  411. tile.close();
  412. }
  413.  
  414. // Write image buffer and clean up
  415. codestream.destroy();
  416. input.close(); // Not really necessary here.
  417. // buffer now contains (num_components+a)*width*height bytes, ready to use
  418. } catch(kdu_error) {
  419. codestream.destroy();
  420. fz_throw(ctx, FZ_ERROR_GENERIC, "The JPEG2000 decoder errored.");
  421. }
  422. return img;
  423. }
  424.  
  425. ---END FILE load-jpx-kakadu.cpp---
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement