Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Want MuPDF to be faster at rendering archive.org ebooks?
- What you will need:
- - MuPDF source code (I used 1.7a)
- - Kakadu 2.2.3 (find and/or buy it yourself)
- - C and C++ compiler (MSVC works fine)
- Steps:
- - Find jbig2_image.c and add optimised case for JBIG2_COMPOSE_XOR. Easiest way is to copy-paste
- the code for JBIG2_COMPOSE_OR and replace all |= with ^=.
- - Compile MuPDF. Save the object files.
- - Compile load-jpx-kakadu.cpp (see below)
- - Replace load-jpx.obj with load-jpx-kakadu.obj and relink. Remember to add Kakadu to linker path.
- - Enjoy faster MuPDF!
- Notes:
- Distributing the binary is probably against several licenses, but I can tell you how to make it
- yourself. Fuck licensing/copyright --- this is why we can't have nice things. This posting is in
- public domain.
- ---BEGIN FILE load-jpx-kakadu.cpp---
- #include <math.h>
- #ifdef __cplusplus
- extern "C" {
- #endif
- #include "mupdf/fitz.h"
- #ifdef __cplusplus
- }
- #endif
- #include <sstream>
- #include <winsock2.h>
- // Kakadu core includes
- #include "kdu_elementary.h"
- #include "kdu_messaging.h"
- #include "kdu_params.h"
- #include "kdu_compressed.h"
- #include "kdu_sample_processing.h"
- // Application level includes
- #include "kdu_file_io.h"
- /*****************************************************************************/
- /* kdu_simple_mem_source */
- /*****************************************************************************/
- class kdu_simple_mem_source : public kdu_compressed_source {
- public: // Member functions
- kdu_simple_mem_source(unsigned char *data, int size)
- { this->data = data; this->size = size; this->pos = 0; }
- ~kdu_simple_mem_source()
- { }
- bool exists()
- { return !!data; }
- bool operator!()
- { return !data; }
- int read(kdu_byte *buf, int num_bytes)
- {
- if(num_bytes > size - pos)
- num_bytes = size - pos;
- memcpy(buf, data + pos, num_bytes);
- pos += num_bytes;
- return num_bytes;
- }
- void close()
- {
- }
- private: // Data
- unsigned char *data;
- int size;
- int pos;
- };
- /******************************************************************************/
- /* transfer_bytes */
- /******************************************************************************/
- void
- transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision)
- /* Transfers source samples from the supplied line buffer into the output
- byte buffer, spacing successive output samples apart by `gap' bytes
- (to allow for interleaving of colour components). The function performs
- all necessary level shifting, type conversion, rounding and truncation. */
- {
- int width = src.get_width();
- if (src.get_buf32() != NULL)
- { // Decompressed samples have a 32-bit representation (integer or float)
- assert(precision >= 8); // Else would have used 16 bit representation
- kdu_sample32 *sp = src.get_buf32();
- if (!src.is_absolute())
- { // Transferring normalized floating point data.
- float scale16 = (float)(1<<16);
- kdu_int32 val;
- for (; width > 0; width--, sp++, dest+=gap)
- {
- val = (kdu_int32)(sp->fval*scale16);
- val = (val+128)>>8; // May be faster than true rounding
- val += 128;
- if (val & ((-1)<<8))
- val = (val<0)?0:255;
- *dest = (kdu_byte) val;
- }
- }
- else
- { // Transferring 32-bit absolute integers.
- kdu_int32 val;
- kdu_int32 downshift = precision-8;
- kdu_int32 offset = (1<<downshift)>>1;
- for (; width > 0; width--, sp++, dest+=gap)
- {
- val = sp->ival;
- val = (val+offset)>>downshift;
- val += 128;
- if (val & ((-1)<<8))
- val = (val<0)?0:255;
- *dest = (kdu_byte) val;
- }
- }
- }
- else
- { // Source data is 16 bits.
- kdu_sample16 *sp = src.get_buf16();
- if (!src.is_absolute())
- { // Transferring 16-bit fixed point quantities
- kdu_int16 val;
- if (precision >= 8)
- { // Can essentially ignore the bit-depth.
- for (; width > 0; width--, sp++, dest+=gap)
- {
- val = (sp->ival + 4112)>>5;
- if (val & ((-1)<<8))
- val = (val<0)?0:255;
- *dest = (kdu_byte) val;
- }
- }
- else
- { // Need to force zeros into one or more least significant bits.
- kdu_int16 downshift = KDU_FIX_POINT-precision;
- kdu_int16 upshift = 8-precision;
- kdu_int16 offset = 1<<(downshift-1);
- for (; width > 0; width--, sp++, dest+=gap)
- {
- val = sp->ival;
- val = (val+offset)>>downshift;
- val <<= upshift;
- val += 128;
- if (val & ((-1)<<8))
- val = (val<0)?0:(256-(1<<upshift));
- *dest = (kdu_byte) val;
- }
- }
- }
- else
- { // Transferring 16-bit absolute integers.
- kdu_int16 val;
- if (precision >= 8)
- {
- kdu_int16 downshift = precision-8;
- kdu_int16 offset = (1<<downshift)>>1;
- for (; width > 0; width--, sp++, dest+=gap)
- {
- val = sp->ival;
- val = (val+offset)>>downshift;
- val += 128;
- if (val & ((-1)<<8))
- val = (val<0)?0:255;
- *dest = (kdu_byte) val;
- }
- }
- else
- {
- kdu_int16 upshift = 8-precision;
- for (; width > 0; width--, sp++, dest+=gap)
- {
- val = sp->ival;
- val <<= upshift;
- val += 128;
- if (val & ((-1)<<8))
- val = (val<0)?0:(256-(1<<upshift));
- *dest = (kdu_byte) val;
- }
- }
- }
- }
- }
- /*****************************************************************************/
- /* STATIC process_tile */
- /*****************************************************************************/
- static void
- process_tile(kdu_tile tile, kdu_byte *buf, int row_gap)
- /* Decompresses a tile, writing the data into the supplied byte buffer.
- The buffer contains interleaved image components, if there are any.
- Although you may think of the buffer as belonging entirely to this tile,
- the `buf' pointer may actually point into a larger buffer representing
- multiple tiles. For this reason, `row_gap' is needed to identify the
- separation between consecutive rows in the real buffer. */
- {
- int c, num_components = tile.get_num_components(); assert(num_components<=3);
- bool use_ycc = tile.get_ycc();
- // Open tile-components and create processing engines and resources
- kdu_dims dims;
- kdu_sample_allocator allocator;
- kdu_tile_comp comps[3];
- kdu_line_buf lines[3];
- kdu_pull_ifc engines[3];
- bool reversible[3]; // Some components may be reversible and others not.
- int bit_depths[3]; // Original bit-depth may be quite different from 8.
- for (c=0; c < num_components; c++)
- {
- comps[c] = tile.access_component(c);
- reversible[c] = comps[c].get_reversible();
- bit_depths[c] = comps[c].get_bit_depth();
- kdu_resolution res = comps[c].access_resolution(); // Get top resolution
- kdu_dims comp_dims; res.get_dims(comp_dims);
- if (c == 0)
- dims = comp_dims;
- else
- assert(dims == comp_dims); // Safety check; the caller has ensured this
- bool use_shorts = (comps[c].get_bit_depth(true) <= 16);
- lines[c].pre_create(&allocator,dims.size.x,reversible[c],use_shorts);
- if (res.which() == 0) // No DWT levels used
- engines[c] =
- kdu_decoder(res.access_subband(LL_BAND),&allocator,use_shorts);
- else
- engines[c] = kdu_synthesis(res,&allocator,use_shorts);
- }
- allocator.finalize(); // Actually creates buffering resources
- for (c=0; c < num_components; c++)
- lines[c].create(); // Grabs resources from the allocator.
- // Now walk through the lines of the buffer, recovering them from the
- // relevant tile-component processing engines.
- while (dims.size.y--)
- {
- for (c=0; c < num_components; c++)
- engines[c].pull(lines[c],true);
- if ((num_components == 3) && use_ycc)
- kdu_convert_ycc_to_rgb(lines[0],lines[1],lines[2]);
- for (c=0; c < num_components; c++)
- transfer_bytes(buf+c,lines[c],num_components+1,bit_depths[c]);
- buf += row_gap;
- }
- // Cleanup
- for (c=0; c < num_components; c++)
- engines[c].destroy(); // engines are interfaces; no default destructors
- }
- static fz_context *kdu_fz_ctx;
- static std::ostringstream kdu_error_msgs;
- static std::ostringstream kdu_warning_msgs;
- void kdu_warn_callback() { fz_warn(kdu_fz_ctx, "kakadu warning: %s", kdu_warning_msgs.str().c_str()); kdu_warning_msgs.str(""); }
- void kdu_error_callback(){ fz_warn(kdu_fz_ctx, "kakadu error: %s", kdu_error_msgs.str().c_str()); kdu_warning_msgs.str(""); }
- extern "C"
- fz_pixmap *
- fz_load_jpx(fz_context *ctx, unsigned char *data, int size, fz_colorspace *defcs, int indexed)
- {
- fz_pixmap *img;
- fz_colorspace *colorspace;
- int a, n, w, h, depth, sgnd;
- int x, y, k, v;
- // Custom messaging services
- kdu_fz_ctx = ctx;
- kdu_customize_warnings(&kdu_warning_msgs, 79, kdu_warn_callback);
- kdu_customize_errors(&kdu_error_msgs, 79, kdu_error_callback);
- if (size < 2)
- fz_throw(ctx, FZ_ERROR_GENERIC, "not enough data to determine image format");
- /* Check for SOC marker -- if found we have a bare J2K stream
- else it may be a JP2, so seek to the first codestream box */
- if (data[0] != 0xFF || data[1] != 0x4F) {
- if(size < 12 || memcmp(data, "\0\0\0\xCjP \r\n\x87\n", 12))
- fz_throw(ctx, FZ_ERROR_GENERIC, "Invalid JP2 file format");
- data += 12;
- size -= 12;
- for(;;) {
- unsigned int boxlen;
- unsigned int boxtype;
- if(size < 8)
- fz_throw(ctx, FZ_ERROR_GENERIC, "Could not find JP2 codestream box");
- boxlen = ntohl(*(int*)data);
- boxtype = *(int*)(data + 4);
- data += 8;
- size -= 8;
- if(boxlen == 1) {
- if(size < 8)
- fz_throw(ctx, FZ_ERROR_GENERIC, "The JP2 box size is invalid (truncated)");
- if(*(int*)data) /* can't be > 4GB here... */
- fz_throw(ctx, FZ_ERROR_GENERIC, "The JP2 box hugesize is surely malformed");
- boxlen = ntohl(*(int*)(data + 4));
- data += 8;
- size -= 8;
- if(boxlen < 16 || boxlen - 16 > size)
- fz_throw(ctx, FZ_ERROR_GENERIC, "The JP2 box size is invalid (truncated)");
- boxlen -= 16;
- } else if(!boxlen)
- boxlen = size;
- else if(boxlen < 8)
- fz_throw(ctx, FZ_ERROR_GENERIC, "The JP2 box size is invalid (2-7)");
- else if(boxlen < 8 || boxlen - 8 > size)
- fz_throw(ctx, FZ_ERROR_GENERIC, "The JP2 box size is invalid (truncated)");
- else
- boxlen -= 8;
- if(boxtype == 0x6332706a) {
- size = boxlen;
- break;
- }
- data += boxlen;
- size -= boxlen;
- }
- }
- // Construct code-stream object
- kdu_simple_mem_source input(data, size);
- kdu_codestream codestream; codestream.create(&input);
- codestream.set_resilient(); // Set the parsing error tolerance.
- kdu_dims dims; codestream.get_dims(0,dims);
- // Determine number of components to decompress
- int num_components = codestream.get_num_components();
- if (num_components == 2)
- num_components = 1;
- else if (num_components >= 3)
- { // Check that components have consistent dimensions (for PPM file)
- num_components = 3;
- kdu_dims dims1; codestream.get_dims(1,dims1);
- kdu_dims dims2; codestream.get_dims(2,dims2);
- if ((dims1 != dims) || (dims2 != dims))
- num_components = 1;
- }
- codestream.apply_input_restrictions(0,num_components,0,0,NULL);
- n = num_components;
- w = dims.size.x;
- h = dims.size.y;
- a = 1;
- if (defcs)
- {
- if (defcs->n == n)
- {
- colorspace = defcs;
- }
- else
- {
- fz_warn(ctx, "jpx file and dict colorspaces do not match");
- defcs = NULL;
- }
- }
- if (!defcs)
- {
- switch (n)
- {
- case 1: colorspace = fz_device_gray(ctx); break;
- case 3: colorspace = fz_device_rgb(ctx); break;
- case 4: colorspace = fz_device_cmyk(ctx); break;
- }
- }
- // Now we are ready to walk through the tiles processing them one-by-one.
- fz_try(ctx)
- {
- img = fz_new_pixmap(ctx, colorspace, w, h);
- }
- fz_catch(ctx)
- {
- codestream.destroy();
- fz_rethrow_message(ctx, "out of memory loading jpx");
- }
- kdu_byte *buffer = img->samples;
- memset(buffer, 255, w*h*(num_components+a));
- try {
- kdu_dims tile_indices; codestream.get_valid_tiles(tile_indices);
- kdu_coords tpos;
- for (tpos.y=0; tpos.y < tile_indices.size.y; tpos.y++)
- for (tpos.x=0; tpos.x < tile_indices.size.x; tpos.x++)
- {
- kdu_tile tile = codestream.open_tile(tpos+tile_indices.pos);
- kdu_resolution res = tile.access_component(0).access_resolution();
- kdu_dims tile_dims; res.get_dims(tile_dims);
- kdu_coords offset = tile_dims.pos - dims.pos;
- int row_gap = (num_components+a)*dims.size.x; // inter-row separation
- kdu_byte *buf = buffer + offset.y*row_gap + offset.x*(num_components+a);
- process_tile(tile,buf,row_gap);
- tile.close();
- }
- // Write image buffer and clean up
- codestream.destroy();
- input.close(); // Not really necessary here.
- // buffer now contains (num_components+a)*width*height bytes, ready to use
- } catch(kdu_error) {
- codestream.destroy();
- fz_throw(ctx, FZ_ERROR_GENERIC, "The JPEG2000 decoder errored.");
- }
- return img;
- }
- ---END FILE load-jpx-kakadu.cpp---
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement