/* This function completes a rendering job. * * The thread is added to the pool of idle threads. */ static void finish_page_render(struct least_thread *render) { union _dispose_volatile { volatile fz_pixmap *volatile_pixmap; fz_pixmap *pixmap; } d; d.volatile_pixmap = render->pixmap; /* XXX Error handling ? */ if (render->pre_refresh) printf("finish_page: Discarding pre-refresh render " "of page %d by thread %d\n", render->pagenum, render->id); else { /* Page is complete and no longer rendering */ pages[render->pagenum].rendering = 0; /* Convert to texture */ pages[render->pagenum].texture = pixmap_to_texture( (void*)fz_pixmap_samples(render->context, d.pixmap), fz_pixmap_width(render->context, d.pixmap), fz_pixmap_height(render->context, d.pixmap), 0, 0); } /* XXX Using the threads context might not be a gr8 idea */ fz_drop_pixmap(render->context, d.pixmap); /* Place thread into idle pool */ idle_threads[idle_thread_count++] = render; }
void winresize(pdfapp_t *app, int w, int h) { int image_w = fz_pixmap_width(gapp.ctx, gapp.image); int image_h = fz_pixmap_height(gapp.ctx, gapp.image); XWindowChanges values; int mask, width, height; mask = CWWidth | CWHeight; values.width = w; values.height = h; XConfigureWindow(xdpy, xwin, mask, &values); reqw = w; reqh = h; if (!mapped) { gapp.winw = w; gapp.winh = h; width = -1; height = -1; XMapWindow(xdpy, xwin); XFlush(xdpy); while (1) { XNextEvent(xdpy, &xevt); if (xevt.type == ConfigureNotify) { width = xevt.xconfigure.width; height = xevt.xconfigure.height; } if (xevt.type == MapNotify) break; } XSetForeground(xdpy, xgc, WhitePixel(xdpy, xscr)); XFillRectangle(xdpy, xwin, xgc, 0, 0, image_w, image_h); XFlush(xdpy); if (width != reqw || height != reqh) { gapp.shrinkwrap = 0; dirty = 1; pdfapp_onresize(&gapp, width, height); } mapped = 1; } }
QImage Document::RenderPage (int num, double xRes, double yRes) { auto page = WrapPage (pdf_load_page (MuDoc_, num), MuDoc_); if (!page) return QImage (); #if MUPDF_VERSION < 0x0102 const auto& rect = pdf_bound_page (MuDoc_, page.get ()); #else fz_rect rect; pdf_bound_page (MuDoc_, page.get (), &rect); #endif auto px = fz_new_pixmap (MuCtx_, fz_device_bgr, xRes * (rect.x1 - rect.x0), yRes * (rect.y1 - rect.y0)); fz_clear_pixmap (MuCtx_, px); auto dev = fz_new_draw_device (MuCtx_, px); #if MUPDF_VERSION < 0x0102 pdf_run_page (MuDoc_, page.get (), dev, fz_scale (xRes, yRes), NULL); #else fz_matrix matrix; pdf_run_page (MuDoc_, page.get (), dev, fz_scale (&matrix, xRes, yRes), NULL); #endif fz_free_device (dev); const int pxWidth = fz_pixmap_width (MuCtx_, px); const int pxHeight = fz_pixmap_height (MuCtx_, px); auto samples = fz_pixmap_samples (MuCtx_, px); QImage temp (samples, pxWidth, pxHeight, QImage::Format_ARGB32); QImage img (QSize (pxWidth, pxHeight), QImage::Format_ARGB32); for (int y = 0; y < pxHeight; ++y) { auto target = reinterpret_cast<QRgb*> (img.scanLine (y)); const auto source = reinterpret_cast<QRgb*> (temp.scanLine (y)); std::memcpy (target, source, sizeof (source [0]) * pxWidth); } fz_drop_pixmap (MuCtx_, px); temp = QImage (QSize (pxWidth, pxHeight), QImage::Format_ARGB32); QPainter p; p.begin (&temp); p.fillRect (QRect (QPoint (0, 0), temp.size ()), Qt::white); p.drawImage (0, 0, img); p.end (); return temp; }
void PDFDocument::Render( Document::PixelWriter* pw, int page, float zoom, int rotation) { assert((page >= 0) && (page < GetNumPages())); std::unique_lock<std::mutex> lock(_render_mutex); // 1. Init MuPDF structures. const fz_matrix& m = Transform(zoom, rotation); pdf_page* page_struct = GetPage(page); const fz_irect& bbox = GetBoundingBox(page_struct, m); fz_pixmap* pixmap = fz_new_pixmap_with_bbox( _fz_context, fz_device_rgb(_fz_context), FZ_OBJ(bbox), nullptr, 1); fz_device* dev = fz_new_draw_device(_fz_context, FZ_OBJ(fz_identity), pixmap); // 2. Render page. fz_clear_pixmap_with_value(_fz_context, pixmap, 0xff); pdf_run_page( _fz_context, _pdf_document, page_struct, dev, FZ_OBJ(m), nullptr); // 3. Write pixmap to buffer. The page is vertically divided into n equal // stripes, each copied to pw by one thread. assert(fz_pixmap_components(_fz_context, pixmap) == 4); uint8_t* buffer = reinterpret_cast<uint8_t*>(fz_pixmap_samples(_fz_context, pixmap)); const int num_cols = fz_pixmap_width(_fz_context, pixmap); const int num_rows = fz_pixmap_height(_fz_context, pixmap); ExecuteInParallel([=](int num_threads, int i) { const int num_rows_per_thread = num_rows / num_threads; const int y_begin = i * num_rows_per_thread; const int y_end = (i == num_threads - 1) ? num_rows : (i + 1) * num_rows_per_thread; uint8_t* p = buffer + y_begin * num_cols * 4; for (int y = y_begin; y < y_end; ++y) { for (int x = 0; x < num_cols; ++x) { pw->Write(x, y, p[0], p[1], p[2]); p += 4; } } }); // 4. Clean up. fz_close_device(_fz_context, dev); fz_drop_device(_fz_context, dev); fz_drop_pixmap(_fz_context, pixmap); }
static int bmpmupdf_pixmap_to_bmp(WILLUSBITMAP *bmp,fz_context *ctx,fz_pixmap *pixmap) { unsigned char *p; int ncomp,i,row,col; bmp->width=fz_pixmap_width(ctx,pixmap); bmp->height=fz_pixmap_height(ctx,pixmap); ncomp=fz_pixmap_components(ctx,pixmap); /* Has to be 8-bit or RGB */ if (ncomp != 2 && ncomp != 4) return(-1); bmp->bpp=(ncomp==2) ? 8 : 24; bmp_alloc(bmp); if (ncomp==2) for (i=0;i<256;i++) bmp->red[i]=bmp->green[i]=bmp->blue[i]=i; p = fz_pixmap_samples(ctx,pixmap); if (ncomp==1) for (row=0;row<bmp->height;row++) { unsigned char *dest; dest=bmp_rowptr_from_top(bmp,row); memcpy(dest,p,bmp->width); p+=bmp->width; } else if (ncomp==2) for (row=0;row<bmp->height;row++) { unsigned char *dest; dest=bmp_rowptr_from_top(bmp,row); for (col=0;col<bmp->width;col++,dest++,p+=2) dest[0]=p[0]; } else for (row=0;row<bmp->height;row++) { unsigned char *dest; dest=bmp_rowptr_from_top(bmp,row); for (col=0;col<bmp->width;col++,dest+=ncomp-1,p+=ncomp) memcpy(dest,p,ncomp-1); } return(0); }
static int page_to_texture(fz_context *context, fz_document *doc, int pagenum) { fz_pixmap *image; /* Since this function is only called initially, this is an excellent place * to lock the window height/width. * Afterwards this can be changed using the 'F5' key. */ lw = w; lh = h; /* Convert page to pixmap */ image = page_to_pixmap(context, doc, pagenum); /* Convert to texture here */ pages[pagenum].texture = pixmap_to_texture((void*)fz_pixmap_samples(context, image), fz_pixmap_width(context, image), fz_pixmap_height(context, image), 0, 0); fz_drop_pixmap(context, image); return pages[pagenum].texture; }
static void drawpage(fz_context *ctx, fz_document *doc, int pagenum) { fz_page *page; fz_display_list *list = NULL; fz_device *dev = NULL; int start; fz_cookie cookie = { 0 }; fz_var(list); fz_var(dev); fz_try(ctx) { page = fz_load_page(doc, pagenum - 1); } fz_catch(ctx) { fz_throw(ctx, "cannot load page %d in file '%s'", pagenum, filename); } float zoom; fz_matrix ctm; fz_rect bounds, bounds2; fz_bbox bbox; fz_pixmap *pix = NULL; int w, h; fz_var(pix); bounds = fz_bound_page(doc, page); zoom = resolution / 72; ctm = fz_scale(zoom, zoom); ctm = fz_concat(ctm, fz_rotate(rotation)); bounds2 = fz_transform_rect(ctm, bounds); bbox = fz_round_rect(bounds2); /* Make local copies of our width/height */ w = width; h = height; /* If a resolution is specified, check to see whether w/h are * exceeded; if not, unset them. */ if (res_specified) { int t; t = bbox.x1 - bbox.x0; if (w && t <= w) w = 0; t = bbox.y1 - bbox.y0; if (h && t <= h) h = 0; } /* Now w or h will be 0 unless then need to be enforced. */ if (w || h) { float scalex = w/(bounds2.x1-bounds2.x0); float scaley = h/(bounds2.y1-bounds2.y0); if (fit) { if (w == 0) scalex = 1.0f; if (h == 0) scaley = 1.0f; } else { if (w == 0) scalex = scaley; if (h == 0) scaley = scalex; } if (!fit) { if (scalex > scaley) scalex = scaley; else scaley = scalex; } ctm = fz_concat(ctm, fz_scale(scalex, scaley)); bounds2 = fz_transform_rect(ctm, bounds); } bbox = fz_round_rect(bounds2); /* TODO: banded rendering and multi-page ppm */ fz_try(ctx) { pix = fz_new_pixmap_with_bbox(ctx, colorspace, bbox); fz_clear_pixmap_with_value(ctx, pix, 255); dev = fz_new_draw_device(ctx, pix); if (list) fz_run_display_list(list, dev, ctm, bbox, &cookie); else fz_run_page(doc, page, dev, ctm, &cookie); fz_free_device(dev); dev = NULL; int size = fz_pixmap_height(ctx, pix) * fz_pixmap_width(ctx, pix); inplace_reorder(fz_pixmap_samples(ctx, pix), size); size_t x_offset = (width - fz_pixmap_width(ctx, pix)) / 2; size_t y_offset = (height - fz_pixmap_height(ctx, pix)) / 2;; if (toggle_decors) { x_offset += decor_left_width; y_offset += decor_top_height; } for (int i = 0; i < fz_pixmap_height(ctx, pix); ++i) { memcpy(&GFX(gfx_ctx, x_offset, y_offset + i), &fz_pixmap_samples(ctx, pix)[fz_pixmap_width(ctx, pix) * i * 4], fz_pixmap_width(ctx, pix) * 4); } } fz_always(ctx) { fz_free_device(dev); dev = NULL; fz_drop_pixmap(ctx, pix); } fz_catch(ctx) { fz_free_display_list(ctx, list); fz_free_page(doc, page); fz_rethrow(ctx); } if (list) fz_free_display_list(ctx, list); fz_free_page(doc, page); fz_flush_warnings(ctx); if (cookie.errors) errored = 1; }
void winblit() { int image_w = fz_pixmap_width(gapp.ctx, gapp.image); int image_h = fz_pixmap_height(gapp.ctx, gapp.image); int image_n = fz_pixmap_components(gapp.ctx, gapp.image); unsigned char *samples = fz_pixmap_samples(gapp.ctx, gapp.image); int x0 = gapp.panx; int y0 = gapp.pany; int x1 = gapp.panx + image_w; int y1 = gapp.pany + image_h; RECT r; if (gapp.image) { if (gapp.iscopying || justcopied) { pdfapp_invert(&gapp, &gapp.selr); justcopied = 1; } pdfapp_inverthit(&gapp); dibinf->bmiHeader.biWidth = image_w; dibinf->bmiHeader.biHeight = -image_h; dibinf->bmiHeader.biSizeImage = image_h * 4; if (image_n == 2) { int i = image_w * image_h; unsigned char *color = malloc(i*4); unsigned char *s = samples; unsigned char *d = color; for (; i > 0 ; i--) { d[2] = d[1] = d[0] = *s++; d[3] = *s++; d += 4; } SetDIBitsToDevice(hdc, gapp.panx, gapp.pany, image_w, image_h, 0, 0, 0, image_h, color, dibinf, DIB_RGB_COLORS); free(color); } if (image_n == 4) { SetDIBitsToDevice(hdc, gapp.panx, gapp.pany, image_w, image_h, 0, 0, 0, image_h, samples, dibinf, DIB_RGB_COLORS); } pdfapp_inverthit(&gapp); if (gapp.iscopying || justcopied) { pdfapp_invert(&gapp, &gapp.selr); justcopied = 1; } } /* Grey background */ r.top = 0; r.bottom = gapp.winh; r.left = 0; r.right = x0; FillRect(hdc, &r, bgbrush); r.left = x1; r.right = gapp.winw; FillRect(hdc, &r, bgbrush); r.left = 0; r.right = gapp.winw; r.top = 0; r.bottom = y0; FillRect(hdc, &r, bgbrush); r.top = y1; r.bottom = gapp.winh; FillRect(hdc, &r, bgbrush); /* Drop shadow */ r.left = x0 + 2; r.right = x1 + 2; r.top = y1; r.bottom = y1 + 2; FillRect(hdc, &r, shbrush); r.left = x1; r.right = x1 + 2; r.top = y0 + 2; r.bottom = y1; FillRect(hdc, &r, shbrush); winblitsearch(); }
static void winblit(pdfapp_t *app) { int image_w = fz_pixmap_width(gapp.ctx, gapp.image); int image_h = fz_pixmap_height(gapp.ctx, gapp.image); int image_n = fz_pixmap_components(gapp.ctx, gapp.image); unsigned char *image_samples = fz_pixmap_samples(gapp.ctx, gapp.image); int x0 = gapp.panx; int y0 = gapp.pany; int x1 = gapp.panx + image_w; int y1 = gapp.pany + image_h; XSetForeground(xdpy, xgc, xbgcolor.pixel); fillrect(0, 0, x0, gapp.winh); fillrect(x1, 0, gapp.winw - x1, gapp.winh); fillrect(0, 0, gapp.winw, y0); fillrect(0, y1, gapp.winw, gapp.winh - y1); XSetForeground(xdpy, xgc, xshcolor.pixel); fillrect(x0+2, y1, image_w, 2); fillrect(x1, y0+2, 2, image_h); if (gapp.iscopying || justcopied) { pdfapp_invert(&gapp, &gapp.selr); justcopied = 1; } pdfapp_inverthit(&gapp); if (image_n == 4) ximage_blit(xwin, xgc, x0, y0, image_samples, 0, 0, image_w, image_h, image_w * image_n); else if (image_n == 2) { int i = image_w*image_h; unsigned char *color = malloc(i*4); if (color) { unsigned char *s = image_samples; unsigned char *d = color; for (; i > 0 ; i--) { d[2] = d[1] = d[0] = *s++; d[3] = *s++; d += 4; } ximage_blit(xwin, xgc, x0, y0, color, 0, 0, image_w, image_h, image_w * 4); free(color); } } pdfapp_inverthit(&gapp); if (gapp.iscopying || justcopied) { pdfapp_invert(&gapp, &gapp.selr); justcopied = 1; } winblitsearch(app); if (showingpage) { char buf[42]; snprintf(buf, sizeof buf, "Page %d/%d", gapp.pageno, gapp.pagecount); windrawstringxor(&gapp, 10, 20, buf); } }
QImage Pdf::page(int i) { fz_page* page = fz_load_page(doc, i); if (page == 0) { printf("cannot load page %d\n", i); return QImage(); } static const float resolution = 300.0; const float zoom = resolution / 72.0; fz_rect bounds = fz_bound_page(doc, page); fz_matrix ctm = fz_scale(zoom, zoom); fz_bbox bbox = fz_round_rect(fz_transform_rect(ctm, bounds)); fz_pixmap* pix = fz_new_pixmap_with_bbox(ctx, fz_device_gray, bbox); fz_clear_pixmap_with_value(ctx, pix, 255); fz_device* dev = fz_new_draw_device(ctx, pix); fz_run_page(doc, page, dev, ctm, NULL); fz_free_device(dev); dev = NULL; int w = fz_pixmap_width(ctx, pix); int h = fz_pixmap_height(ctx, pix); if (fz_pixmap_components(ctx, pix) != 2) { printf("omg: pixmap not bw? %d\n", fz_pixmap_components(ctx, pix)); return QImage(); } QImage image(w, h, QImage::Format_MonoLSB); QVector<QRgb> ct(2); ct[0] = qRgb(255, 255, 255); ct[1] = qRgb(0, 0, 0); image.setColorTable(ct); uchar* s = fz_pixmap_samples(ctx, pix); int bytes = w / 8; int bits = w % 8; for (int line = 0; line < h; ++line) { uchar* d = image.scanLine(line); for (int col = 0; col < bytes; ++col) { uchar data = 0; for (int i = 0; i < 8; ++i) { uchar v = *s; s += 2; data >>= 1; if (v < 128) { // convert grayscale to bw data |= 0x80; } } *d++ = data; } uchar data = 0; for (int col = 0; col < bits; ++col) { uchar v = *s; s += 2; data >>= 1; if (v < 128) data |= 0x80; } } fz_drop_pixmap(ctx, pix); return image; }
cairo_surface_t* pdf_page_image_get_cairo(zathura_page_t* page, void* data, zathura_image_t* image, zathura_error_t* error) { mupdf_page_t* mupdf_page = data; if (page == NULL || mupdf_page == NULL || image == NULL || image->data == NULL) { if (error != NULL) { *error = ZATHURA_ERROR_INVALID_ARGUMENTS; } goto error_ret; } fz_image* mupdf_image = (fz_image*) image->data; fz_pixmap* pixmap = NULL; cairo_surface_t* surface = NULL; pixmap = fz_get_pixmap_from_image(mupdf_page->ctx, mupdf_image, NULL, NULL, 0, 0); if (pixmap == NULL) { goto error_free; } surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, mupdf_image->w, mupdf_image->h); if (surface == NULL) { goto error_free; } unsigned char* surface_data = cairo_image_surface_get_data(surface); int rowstride = cairo_image_surface_get_stride(surface); unsigned char* s = fz_pixmap_samples(mupdf_page->ctx, pixmap); unsigned int n = fz_pixmap_components(mupdf_page->ctx, pixmap); const int height = fz_pixmap_height(mupdf_page->ctx, pixmap); const int width = fz_pixmap_width(mupdf_page->ctx, pixmap); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { guchar* p = surface_data + y * rowstride + x * 4; // RGB if (n == 4) { p[0] = s[2]; p[1] = s[1]; p[2] = s[0]; // Gray-scale or mask } else { p[0] = s[0]; p[1] = s[0]; p[2] = s[0]; } s += n; } } fz_drop_pixmap(mupdf_page->ctx, pixmap); return surface; error_free: if (pixmap != NULL) { fz_drop_pixmap(mupdf_page->ctx, pixmap); } if (surface != NULL) { cairo_surface_destroy(surface); } error_ret: return NULL; }