static fz_draw_state * fz_knockout_begin(fz_draw_device *dev) { fz_context *ctx = dev->ctx; fz_bbox bbox; fz_pixmap *dest, *shape; fz_draw_state *state = &dev->stack[dev->top]; int isolated = state->blendmode & FZ_BLEND_ISOLATED; if ((state->blendmode & FZ_BLEND_KNOCKOUT) == 0) return state; state = push_stack(dev); bbox = fz_pixmap_bbox(dev->ctx, state->dest); bbox = fz_intersect_bbox(bbox, state->scissor); dest = fz_new_pixmap_with_bbox(dev->ctx, state->dest->colorspace, bbox); if (isolated) { fz_clear_pixmap(ctx, dest); } else { /* Find the last but one destination to copy */ int i = dev->top-1; /* i = the one on entry (i.e. the last one) */ fz_pixmap *prev = state->dest; while (i > 0) { prev = dev->stack[--i].dest; if (prev != state->dest) break; } if (prev) fz_copy_pixmap_rect(ctx, dest, prev, bbox); else fz_clear_pixmap(ctx, dest); } if (state->blendmode == 0 && isolated) { /* We can render direct to any existing shape plane. If there * isn't one, we don't need to make one. */ shape = state->shape; } else { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Knockout begin\n"); #endif state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].blendmode &= ~FZ_BLEND_MODEMASK; return &state[1]; }
static void fz_draw_clip_path(fz_device *devp, fz_path *path, fz_rect rect, int even_odd, fz_matrix ctm) { fz_draw_device *dev = devp->user; float expansion = fz_matrix_expansion(ctm); float flatness = 0.3f / expansion; fz_bbox bbox; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model; fz_context *ctx = dev->ctx; fz_reset_gel(dev->gel, state->scissor); fz_flatten_fill_path(dev->gel, path, ctm, flatness); fz_sort_gel(dev->gel); state = push_stack(dev); model = state->dest->colorspace; bbox = fz_bound_gel(dev->gel); bbox = fz_intersect_bbox(bbox, state->scissor); bbox = fz_intersect_bbox(bbox, fz_bbox_from_rect(rect)); /* SumatraPDF: try to match rendering with and without display list */ if (fz_is_infinite_rect(rect)) bbox = fz_intersect_bbox(bbox, fz_bbox_from_rect(fz_bound_path(ctx, path, NULL, ctm))); if (fz_is_empty_rect(bbox) || fz_is_rect_gel(dev->gel)) { state[1].scissor = bbox; state[1].mask = NULL; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (rectangular) begin\n"); #endif return; } fz_try(ctx) { state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].mask); state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, state[1].dest); if (state[1].shape) { state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].shape); } fz_scan_convert(dev->gel, even_odd, bbox, state[1].mask, NULL); state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (non-rectangular) begin\n"); #endif } fz_catch(ctx) { emergency_pop_stack(dev, state); } }
static void fz_draw_clip_stroke_path(fz_device *devp, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm) { fz_draw_device *dev = devp->user; float expansion = fz_matrix_expansion(ctm); float flatness = 0.3f / expansion; float linewidth = stroke->linewidth; fz_bbox bbox; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model; fz_context *ctx = dev->ctx; if (linewidth * expansion < 0.1f) linewidth = 1 / expansion; fz_reset_gel(dev->gel, state->scissor); if (stroke->dash_len > 0) fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth); else fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth); fz_sort_gel(dev->gel); state = push_stack(dev); model = state->dest->colorspace; bbox = fz_bound_gel(dev->gel); bbox = fz_intersect_bbox(bbox, state->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(*rect)); /* SumatraPDF: try to match rendering with and without display list */ else bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(fz_bound_path(ctx, path, stroke, ctm))); fz_try(ctx) { state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].mask); state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, state[1].dest); if (state->shape) { state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].shape); } if (!fz_is_empty_rect(bbox)) fz_scan_convert(dev->gel, 0, bbox, state[1].mask, NULL); state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (stroke) begin\n"); #endif } fz_catch(ctx) { emergency_pop_stack(dev, state); } }
static void fz_draw_begin_tile(fz_device *devp, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm) { fz_draw_device *dev = devp->user; fz_pixmap *dest = NULL; fz_pixmap *shape; fz_bbox bbox; fz_context *ctx = dev->ctx; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; /* area, view, xstep, ystep are in pattern space */ /* ctm maps from pattern space to device space */ if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_begin(dev); state = push_stack(dev); bbox = fz_bbox_covering_rect(fz_transform_rect(ctm, view)); /* We should never have a bbox that entirely covers our destination. * If we do, then the check for only 1 tile being visible above has * failed. Actually, this *can* fail due to the round_rect, at extreme * resolutions, so disable this assert. * assert(bbox.x0 > state->dest->x || bbox.x1 < state->dest->x + state->dest->w || * bbox.y0 > state->dest->y || bbox.y1 < state->dest->y + state->dest->h); */ dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(ctx, dest); shape = state[0].shape; if (shape) { fz_var(shape); fz_try(ctx) { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(ctx, shape); } fz_catch(ctx) { fz_drop_pixmap(ctx, dest); fz_rethrow(ctx); } } state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].xstep = xstep; state[1].ystep = ystep; state[1].area = area; state[1].ctm = ctm; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Tile begin\n"); #endif state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; }
static void fz_draw_end_mask(fz_device *devp) { fz_draw_device *dev = devp->user; fz_pixmap *temp, *dest; fz_bbox bbox; int luminosity; fz_context *ctx = dev->ctx; fz_draw_state *state; if (dev->top == 0) { fz_warn(ctx, "Unexpected draw_end_mask"); return; } state = &dev->stack[dev->top-1]; /* pop soft mask buffer */ luminosity = state[1].luminosity; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Mask -> Clip\n"); #endif /* convert to alpha mask */ temp = fz_alpha_from_gray(dev->ctx, state[1].dest, luminosity); if (state[1].dest != state[0].dest) fz_drop_pixmap(dev->ctx, state[1].dest); state[1].dest = NULL; if (state[1].shape != state[0].shape) fz_drop_pixmap(dev->ctx, state[1].shape); state[1].shape = NULL; if (state[1].mask != state[0].mask) fz_drop_pixmap(dev->ctx, state[1].mask); state[1].mask = NULL; /* create new dest scratch buffer */ bbox = fz_pixmap_bbox(ctx, temp); dest = fz_new_pixmap_with_bbox(dev->ctx, state->dest->colorspace, bbox); fz_clear_pixmap(dev->ctx, dest); /* push soft mask as clip mask */ state[1].mask = temp; state[1].dest = dest; state[1].blendmode |= FZ_BLEND_ISOLATED; /* If we have a shape, then it'll need to be masked with the * clip mask when we pop. So create a new shape now. */ if (state[0].shape) { state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].shape); } state[1].scissor = bbox; }
void fz_write_pixmap(fz_context *ctx, fz_pixmap *img, char *file, int rgb) { char name[1024]; fz_pixmap *converted = NULL; if (!img) return; if (rgb && img->colorspace && img->colorspace != fz_device_rgb) { fz_irect bbox; converted = fz_new_pixmap_with_bbox(ctx, fz_device_rgb, fz_pixmap_bbox(ctx, img, &bbox)); fz_convert_pixmap(ctx, converted, img); img = converted; } if (img->n <= 4) { sprintf(name, "%s.png", file); printf("extracting image %s\n", name); fz_write_png(ctx, img, name, 0); } else { sprintf(name, "%s.pam", file); printf("extracting image %s\n", name); fz_write_pam(ctx, img, name, 0); } fz_drop_pixmap(ctx, converted); }
fz_pixmap * fz_new_pixmap_from_page_contents(fz_context *ctx, fz_page *page, const fz_matrix *ctm, fz_colorspace *cs) { fz_rect rect; fz_irect irect; fz_pixmap *pix; fz_device *dev; fz_bound_page(ctx, page, &rect); fz_transform_rect(&rect, ctm); fz_round_rect(&irect, &rect); pix = fz_new_pixmap_with_bbox(ctx, cs, &irect); fz_clear_pixmap_with_value(ctx, pix, 0xFF); fz_try(ctx) { dev = fz_new_draw_device(ctx, pix); fz_run_page_contents(ctx, page, dev, ctm, NULL); } fz_always(ctx) { fz_drop_device(ctx, dev); } fz_catch(ctx) { fz_drop_pixmap(ctx, pix); fz_rethrow(ctx); } return pix; }
fz_pixmap * fz_new_pixmap_from_display_list(fz_context *ctx, fz_display_list *list, const fz_matrix *ctm, fz_colorspace *cs, int background) { fz_rect rect; fz_irect irect; fz_pixmap *pix; fz_device *dev; fz_bound_display_list(ctx, list, &rect); fz_transform_rect(&rect, ctm); fz_round_rect(&irect, &rect); pix = fz_new_pixmap_with_bbox(ctx, cs, &irect); if (background) fz_clear_pixmap_with_value(ctx, pix, 0xFF); else fz_clear_pixmap(ctx, pix); fz_try(ctx) { dev = fz_new_draw_device(ctx, pix); fz_run_display_list(ctx, list, dev, ctm, NULL, NULL); } fz_always(ctx) { fz_drop_device(ctx, dev); } fz_catch(ctx) { fz_drop_pixmap(ctx, pix); fz_rethrow(ctx); } return pix; }
void Mpdf::showPage() { fz_page *page = fz_load_page(doc, currentPage - 1); fz_matrix transform; fz_rotate(&transform, 0); fz_pre_scale(&transform, currentZoom / 100.0f, currentZoom / 100.0f); fz_rect bounds; fz_bound_page(doc, page, &bounds); fz_transform_rect(&bounds, &transform); fz_irect bbox; fz_round_rect(&bbox, &bounds); fz_pixmap *pix = fz_new_pixmap_with_bbox(ctx, fz_device_rgb, &bbox); fz_clear_pixmap_with_value(ctx, pix, 0xff); fz_device *dev = fz_new_draw_device(ctx, pix); fz_run_page(doc, page, dev, &transform, NULL); fz_free_device(dev); QString qpng = QString("%1.png").arg(currentPage); const char *ccpng = qpng.toStdString().c_str(); char *cpng = new char[strlen(ccpng) + 1]; strcpy(cpng, ccpng); fz_write_png(ctx, pix, cpng, 0); QPixmap qpix(qpng); pdfLabel->setPixmap(qpix); fz_drop_pixmap(ctx, pix); fz_free_page(doc, page); }
fz_pixmap * fz_new_pixmap_from_annot(fz_context *ctx, fz_annot *annot, const fz_matrix *ctm, fz_colorspace *cs, int alpha) { fz_rect rect; fz_irect irect; fz_pixmap *pix; fz_device *dev; fz_bound_annot(ctx, annot, &rect); fz_transform_rect(&rect, ctm); fz_round_rect(&irect, &rect); pix = fz_new_pixmap_with_bbox(ctx, cs, &irect, alpha); if (alpha) fz_clear_pixmap(ctx, pix); else fz_clear_pixmap_with_value(ctx, pix, 0xFF); fz_try(ctx) { dev = fz_new_draw_device(ctx, ctm, pix); fz_run_annot(ctx, annot, dev, &fz_identity, NULL); } fz_always(ctx) { fz_drop_device(ctx, dev); } fz_catch(ctx) { fz_drop_pixmap(ctx, pix); fz_rethrow(ctx); } return pix; }
void benchrenderpage(fz_context *ctx, pdf_document *xref, pdf_page *page, int pagenum) { fz_device *dev; fz_pixmap *pix; fz_bbox bbox; mstimer timer; timerstart(&timer); bbox = fz_round_rect(pdf_bound_page(xref, page)); pix = fz_new_pixmap_with_bbox(ctx, fz_device_rgb, bbox); fz_clear_pixmap_with_value(ctx, pix, 0xFF); dev = fz_new_draw_device(ctx, pix); fz_try(ctx) { pdf_run_page(xref, page, dev, fz_identity, NULL); timerstop(&timer); logbench("pagerender %3d: %.2f ms\n", pagenum, timeinms(&timer)); } fz_catch(ctx) { logbench("Error: pdf_run_page() failed\n"); } fz_drop_pixmap(ctx, pix); fz_free_device(dev); }
static void writepixmap(fz_context *ctx, fz_pixmap *pix, char *file, int rgb) { char buf[1024]; fz_pixmap *converted = NULL; if (!pix) return; if (rgb && pix->colorspace && pix->colorspace != fz_device_rgb(ctx)) { fz_irect bbox; converted = fz_new_pixmap_with_bbox(ctx, fz_device_rgb(ctx), fz_pixmap_bbox(ctx, pix, &bbox), pix->alpha); fz_convert_pixmap(ctx, converted, pix); pix = converted; } if (pix->n <= 4) { snprintf(buf, sizeof(buf), "%s.png", file); printf("extracting image %s\n", buf); fz_save_pixmap_as_png(ctx, pix, buf); } else { snprintf(buf, sizeof(buf), "%s.pam", file); printf("extracting image %s\n", buf); fz_save_pixmap_as_pam(ctx, pix, buf); } fz_drop_pixmap(ctx, converted); }
static void fz_draw_clip_path(fz_device *devp, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm) { fz_draw_device *dev = devp->user; float expansion = fz_matrix_expansion(ctm); float flatness = 0.3f / expansion; fz_bbox bbox; fz_draw_state *state = push_stack(dev); fz_colorspace *model = state->dest->colorspace; fz_reset_gel(dev->gel, state->scissor); fz_flatten_fill_path(dev->gel, path, ctm, flatness); fz_sort_gel(dev->gel); bbox = fz_bound_gel(dev->gel); bbox = fz_intersect_bbox(bbox, state->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(*rect)); if (fz_is_empty_rect(bbox) || fz_is_rect_gel(dev->gel)) { state[1].scissor = bbox; state[1].mask = NULL; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (rectangular) begin\n"); #endif return; } state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].mask); state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, state[1].dest); if (state[1].shape) { state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].shape); } fz_scan_convert(dev->gel, even_odd, bbox, state[1].mask, NULL); state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (non-rectangular) begin\n"); #endif }
static fz_pixmap *page_to_pixmap(fz_context *context, fz_document *doc, int pagenum) { fz_page *page; fz_pixmap *image; fz_device *dev; fz_rect bounds; fz_bbox bbox; fz_matrix ctm; float scale; printf("Rendering page %d\n", pagenum); page = fz_load_page(doc, pagenum); bounds = fz_bound_page(doc, page); /* XXX: There is a small risk of lw/lh being incorrect * due to a race condition during a refresh. * This shouldn't affect any visible pages though, as it causes renders * that will be discarded by finish_page_render to be faulty. */ ims = scale = lw / bounds.x1; printf("W, H: (%f, %f)\n", lw, lh); printf("Scale: %f\n", scale); ctm = fz_scale(scale, scale); pages[pagenum].w = bounds.x1; pages[pagenum].h = bounds.y1; bounds.x1 *= scale; bounds.y1 *= scale; pages[pagenum].sw = bounds.x1; pages[pagenum].sh = bounds.y1; bbox = fz_round_rect(bounds); printf("Size: (%d, %d)\n", bbox.x1, bbox.y1); image = fz_new_pixmap_with_bbox(context, fz_device_rgb, bbox); dev = fz_new_draw_device(context, image); fz_clear_pixmap_with_value(context, image, 255); fz_run_page(doc, page, dev, ctm, NULL); fz_free_device(dev); fz_free_page(doc, page); return image; }
static void fz_draw_begin_mask(fz_device *devp, fz_rect rect, int luminosity, fz_colorspace *colorspace, float *colorfv) { fz_draw_device *dev = devp->user; fz_pixmap *dest; fz_bbox bbox; fz_draw_state *state = push_stack(dev); fz_pixmap *shape = state->shape; bbox = fz_bbox_covering_rect(rect); bbox = fz_intersect_bbox(bbox, state->scissor); dest = fz_new_pixmap_with_bbox(dev->ctx, fz_device_gray, bbox); if (state->shape) { /* FIXME: If we ever want to support AIS true, then we * probably want to create a shape pixmap here, using: * shape = fz_new_pixmap_with_bbox(NULL, bbox); * then, in the end_mask code, we create the mask from this * rather than dest. */ shape = NULL; } if (luminosity) { float bc; if (!colorspace) colorspace = fz_device_gray; fz_convert_color(dev->ctx, fz_device_gray, &bc, colorspace, colorfv); fz_clear_pixmap_with_value(dev->ctx, dest, bc * 255); if (shape) fz_clear_pixmap_with_value(dev->ctx, shape, 255); } else { fz_clear_pixmap(dev->ctx, dest); if (shape) fz_clear_pixmap(dev->ctx, shape); } #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Mask begin\n"); #endif state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].luminosity = luminosity; }
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 void svg_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) { svg_device *sdev = (svg_device*)dev; fz_output *out = sdev->out; fz_rect rect; fz_irect bbox; fz_pixmap *pix; fz_buffer *buf = NULL; fz_var(buf); if (dev->container_len == 0) return; fz_round_rect(&bbox, fz_intersect_rect(fz_bound_shade(ctx, shade, ctm, &rect), &dev->container[dev->container_len-1].scissor)); if (fz_is_empty_irect(&bbox)) return; pix = fz_new_pixmap_with_bbox(ctx, fz_device_rgb(ctx), &bbox); fz_clear_pixmap(ctx, pix); fz_try(ctx) { fz_paint_shade(ctx, shade, ctm, pix, &bbox); buf = fz_new_buffer_from_pixmap_as_png(ctx, pix); if (alpha != 1.0f) fz_printf(ctx, out, "<g opacity=\"%g\">", alpha); fz_printf(ctx, out, "<image x=\"%dpx\" y=\"%dpx\" width=\"%dpx\" height=\"%dpx\" xlink:href=\"data:image/png;base64,", pix->x, pix->y, pix->w, pix->h); send_data_base64(ctx, out, buf); fz_printf(ctx, out, "\"/>\n"); if (alpha != 1.0f) fz_printf(ctx, out, "</g>"); } fz_always(ctx) { fz_drop_buffer(ctx, buf); fz_drop_pixmap(ctx, pix); } fz_catch(ctx) { fz_rethrow(ctx); } }
void j_mu_render_page(void* p_ctx, void* p_page) { fz_context* ctx = (fz_context*) p_ctx; pdf_page* page = (pdf_page*)p_page; fz_matrix transform; fz_rotate(&transform, 0); fz_pre_scale(&transform, 100 / 100.0f, 100 / 100.0f); fz_rect bounds; pdf_bound_page(ctx, page, &bounds); fz_transform_rect(&bounds, &transform); fz_irect bbox; fz_round_rect(&bbox, &bounds); fz_pixmap *pix = fz_new_pixmap_with_bbox(ctx, fz_device_rgb(ctx), &bbox); fz_pixmap_set_resolution(pix, 300); fz_clear_pixmap_with_value(ctx, pix, 0xff); }
fz_pixmap * pdf_expand_indexed_pixmap(fz_context *ctx, fz_pixmap *src) { struct indexed *idx; fz_pixmap *dst; unsigned char *s, *d; int y, x, k, n, high; unsigned char *lookup; fz_irect bbox; assert(src->colorspace->to_rgb == indexed_to_rgb); assert(src->n == 2); idx = src->colorspace->data; high = idx->high; lookup = idx->lookup; n = idx->base->n; dst = fz_new_pixmap_with_bbox(ctx, idx->base, fz_pixmap_bbox(ctx, src, &bbox)); s = src->samples; d = dst->samples; for (y = 0; y < src->h; y++) { for (x = 0; x < src->w; x++) { int v = *s++; int a = *s++; v = fz_mini(v, high); for (k = 0; k < n; k++) *d++ = fz_mul255(lookup[v * n + k], a); *d++ = a; } } dst->interpolate = src->interpolate; return dst; }
fz_pixmap * fz_alpha_from_gray(fz_context *ctx, fz_pixmap *gray, int luminosity) { fz_pixmap *alpha; unsigned char *sp, *dp; int len; assert(gray->n == 2); alpha = fz_new_pixmap_with_bbox(ctx, NULL, fz_pixmap_bbox(ctx, gray)); dp = alpha->samples; sp = gray->samples; if (!luminosity) sp ++; len = gray->w * gray->h; while (len--) { *dp++ = sp[0]; sp += 2; } return alpha; }
static void fz_draw_fill_shade(fz_device *devp, fz_shade *shade, fz_matrix ctm, float alpha) { fz_draw_device *dev = devp->user; fz_rect bounds; fz_bbox bbox, scissor; fz_pixmap *dest, *shape; float colorfv[FZ_MAX_COLORS]; unsigned char colorbv[FZ_MAX_COLORS + 1]; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; bounds = fz_bound_shade(dev->ctx, shade, ctm); scissor = state->scissor; bbox = fz_intersect_bbox(fz_bbox_covering_rect(bounds), scissor); if (fz_is_empty_rect(bbox)) return; if (!model) { fz_warn(dev->ctx, "cannot render shading directly to an alpha mask"); return; } if (state->blendmode & FZ_BLEND_KNOCKOUT) state = fz_knockout_begin(dev); dest = state->dest; shape = state->shape; if (alpha < 1) { dest = fz_new_pixmap_with_bbox(dev->ctx, state->dest->colorspace, bbox); fz_clear_pixmap(dev->ctx, dest); if (shape) { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } } if (shade->use_background) { unsigned char *s; int x, y, n, i; fz_convert_color(dev->ctx, model, colorfv, shade->colorspace, shade->background); for (i = 0; i < model->n; i++) colorbv[i] = colorfv[i] * 255; colorbv[i] = 255; n = dest->n; for (y = scissor.y0; y < scissor.y1; y++) { s = dest->samples + (unsigned int)(((scissor.x0 - dest->x) + (y - dest->y) * dest->w) * dest->n); for (x = scissor.x0; x < scissor.x1; x++) { for (i = 0; i < n; i++) *s++ = colorbv[i]; } } if (shape) { for (y = scissor.y0; y < scissor.y1; y++) { s = shape->samples + (unsigned int)((scissor.x0 - shape->x) + (y - shape->y) * shape->w); for (x = scissor.x0; x < scissor.x1; x++) { *s++ = 255; } } } } fz_paint_shade(dev->ctx, shade, ctm, dest, bbox); if (shape) fz_clear_pixmap_rect_with_value(dev->ctx, shape, 255, bbox); if (alpha < 1) { fz_paint_pixmap(state->dest, dest, alpha * 255); fz_drop_pixmap(dev->ctx, dest); if (shape) { fz_paint_pixmap(state->shape, shape, alpha * 255); fz_drop_pixmap(dev->ctx, shape); } } if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); }
static void fz_draw_begin_group(fz_device *devp, fz_rect rect, int isolated, int knockout, int blendmode, float alpha) { fz_draw_device *dev = devp->user; fz_bbox bbox; fz_pixmap *dest, *shape; fz_context *ctx = dev->ctx; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_begin(dev); state = push_stack(dev); bbox = fz_bbox_covering_rect(rect); bbox = fz_intersect_bbox(bbox, state->scissor); dest = fz_new_pixmap_with_bbox(ctx, model, bbox); #ifndef ATTEMPT_KNOCKOUT_AND_ISOLATED knockout = 0; isolated = 1; #endif if (isolated) { fz_clear_pixmap(dev->ctx, dest); } else { fz_copy_pixmap_rect(dev->ctx, dest, state[0].dest, bbox); } if (blendmode == 0 && alpha == 1.0 && isolated) { /* We can render direct to any existing shape plane. If there * isn't one, we don't need to make one. */ shape = state[0].shape; } else { fz_try(ctx) { shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } fz_catch(ctx) { fz_drop_pixmap(ctx, dest); fz_rethrow(ctx); } } state[1].alpha = alpha; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Group begin\n"); #endif state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].blendmode = blendmode | (isolated ? FZ_BLEND_ISOLATED : 0) | (knockout ? FZ_BLEND_KNOCKOUT : 0); }
int main(int argc, char **argv) { char *filename = argv[1]; pthread_t *thread = NULL; fz_locks_context locks; pthread_mutex_t mutex[FZ_LOCK_MAX]; int i; // Initialize FZ_LOCK_MAX number of non-recursive mutexes. for (i = 0; i < FZ_LOCK_MAX; i++) { if (pthread_mutex_init(&mutex[i], NULL) < 0) fail("pthread_mutex_init()"); } // Initialize the locking structure with function pointers to // the locking functions and to the user data. In this case // the user data is a pointer to the array of mutexes so the // locking functions can find the relevant lock to change when // they are called. This way we avoid global variables. locks.user = mutex; locks.lock = lock_mutex; locks.unlock = unlock_mutex; // This is the main threads context function, so supply the // locking structure. This context will be used to parse all // the pages from the document. fz_context *ctx = fz_new_context(NULL, &locks, FZ_STORE_UNLIMITED); // Open the PDF, XPS or CBZ document. fz_document *doc = fz_open_document(ctx, filename); // Retrieve the number of pages, which translates to the // number of threads used for rendering pages. int threads = fz_count_pages(doc); fprintf(stderr, "spawning %d threads, one per page...\n", threads); thread = malloc(threads * sizeof (pthread_t)); for (i = 0; i < threads; i++) { // Load the relevant page for each thread. fz_page *page = fz_load_page(doc, i); // Compute the bounding box for each page. fz_rect rect = fz_bound_page(doc, page); fz_bbox bbox = fz_round_rect(rect); // Create a display list that will hold the drawing // commands for the page. fz_display_list *list = fz_new_display_list(ctx); // Run the loaded page through a display list device // to populate the page's display list. fz_device *dev = fz_new_list_device(ctx, list); fz_run_page(doc, page, dev, fz_identity, NULL); fz_free_device(dev); // The page is no longer needed, all drawing commands // are now in the display list. fz_free_page(doc, page); // Create a white pixmap using the correct dimensions. fz_pixmap *pix = fz_new_pixmap_with_bbox(ctx, fz_device_rgb, bbox); fz_clear_pixmap_with_value(ctx, pix, 0xff); // Populate the data structure to be sent to the // rendering thread for this page. struct data *data = malloc(sizeof (struct data)); data->pagenumber = i + 1; data->ctx = ctx; data->list = list; data->bbox = bbox; data->pix = pix; // Create the thread and pass it the data structure. if (pthread_create(&thread[i], NULL, renderer, data) < 0) fail("pthread_create()"); } // Now each thread is rendering pages, so wait for each thread // to complete its rendering. fprintf(stderr, "joining %d threads...\n", threads); for (i = threads - 1; i >= 0; i--) { char filename[42]; struct data *data; if (pthread_join(thread[i], (void **) &data) < 0) fail("pthread_join"); sprintf(filename, "out%04d.png", i); fprintf(stderr, "\tSaving %s...\n", filename); // Write the rendered image to a PNG file fz_write_png(ctx, data->pix, filename, 0); // Free the thread's pixmap and display list since // they were allocated by the main thread above. fz_drop_pixmap(ctx, data->pix); fz_free_display_list(ctx, data->list); // Free the data structured passed back and forth // between the main thread and rendering thread. free(data); } fprintf(stderr, "finally!\n"); fflush(NULL); free(thread); // Finally the document is closed and the main thread's // context is freed. fz_close_document(doc); fz_free_context(ctx); return 0; }
void render(char *filename, int pagenumber, int zoom, int rotation) { // Create a context to hold the exception stack and various caches. fz_context *ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); // Open the PDF, XPS or CBZ document. fz_document *doc = fz_open_document(ctx, filename); // Retrieve the number of pages (not used in this example). int pagecount = fz_count_pages(doc); // Load the page we want. Page numbering starts from zero. fz_page *page = fz_load_page(doc, pagenumber - 1); // Calculate a transform to use when rendering. This transform // contains the scale and rotation. Convert zoom percentage to a // scaling factor. Without scaling the resolution is 72 dpi. fz_matrix transform; fz_rotate(&transform, rotation); fz_pre_scale(&transform, zoom / 100.0f, zoom / 100.0f); // Take the page bounds and transform them by the same matrix that // we will use to render the page. fz_rect bounds; fz_bound_page(doc, page, &bounds); fz_transform_rect(&bounds, &transform); // Create a blank pixmap to hold the result of rendering. The // pixmap bounds used here are the same as the transformed page // bounds, so it will contain the entire page. The page coordinate // space has the origin at the top left corner and the x axis // extends to the right and the y axis extends down. fz_irect bbox; fz_round_rect(&bbox, &bounds); fz_pixmap *pix = fz_new_pixmap_with_bbox(ctx, fz_device_rgb(ctx), &bbox); fz_clear_pixmap_with_value(ctx, pix, 0xff); // A page consists of a series of objects (text, line art, images, // gradients). These objects are passed to a device when the // interpreter runs the page. There are several devices, used for // different purposes: // // draw device -- renders objects to a target pixmap. // // text device -- extracts the text in reading order with styling // information. This text can be used to provide text search. // // list device -- records the graphic objects in a list that can // be played back through another device. This is useful if you // need to run the same page through multiple devices, without // the overhead of parsing the page each time. // Create a draw device with the pixmap as its target. // Run the page with the transform. fz_device *dev = fz_new_draw_device(ctx, pix); fz_run_page(doc, page, dev, &transform, NULL); fz_free_device(dev); // Save the pixmap to a file. fz_write_png(ctx, pix, "out.png", 0); // Clean up. fz_drop_pixmap(ctx, pix); fz_free_page(doc, page); fz_close_document(doc); fz_free_context(ctx); }
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 fz_paint_shade(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_pixmap *dest, const fz_irect *bbox) { unsigned char clut[256][FZ_MAX_COLORS]; fz_pixmap *temp = NULL; fz_pixmap *conv = NULL; float color[FZ_MAX_COLORS]; struct paint_tri_data ptd; int i, k; fz_matrix local_ctm; fz_var(temp); fz_var(conv); fz_try(ctx) { fz_concat(&local_ctm, &shade->matrix, ctm); if (shade->use_function) { fz_color_converter cc; fz_find_color_converter(&cc, ctx, dest->colorspace, shade->colorspace); for (i = 0; i < 256; i++) { cc.convert(&cc, color, shade->function[i]); for (k = 0; k < dest->colorspace->n; k++) clut[i][k] = color[k] * 255; clut[i][k] = shade->function[i][shade->colorspace->n] * 255; } conv = fz_new_pixmap_with_bbox(ctx, dest->colorspace, bbox); temp = fz_new_pixmap_with_bbox(ctx, fz_device_gray, bbox); fz_clear_pixmap(ctx, temp); } else { temp = dest; } ptd.ctx = ctx; ptd.dest = temp; ptd.shade = shade; ptd.bbox = bbox; fz_process_mesh(ctx, shade, &local_ctm, &do_paint_tri, &ptd); if (shade->use_function) { unsigned char *s = temp->samples; unsigned char *d = conv->samples; int len = temp->w * temp->h; while (len--) { int v = *s++; int a = fz_mul255(*s++, clut[v][conv->n - 1]); for (k = 0; k < conv->n - 1; k++) *d++ = fz_mul255(clut[v][k], a); *d++ = a; } fz_paint_pixmap(dest, conv, 255); fz_drop_pixmap(ctx, conv); fz_drop_pixmap(ctx, temp); } } fz_catch(ctx) { fz_drop_pixmap(ctx, conv); fz_drop_pixmap(ctx, temp); fz_rethrow(ctx); } }
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 }; int needshot = 0; fz_var(list); fz_var(dev); if (showtime) { start = gettime(); } 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); } if (mujstest_file) { fz_interactive *inter = fz_interact(doc); fz_widget *widget = NULL; if (inter) widget = fz_first_widget(inter, page); if (widget) { fprintf(mujstest_file, "GOTO %d\n", pagenum); needshot = 1; } for (;widget; widget = fz_next_widget(inter, widget)) { fz_rect rect = fz_widget_bbox(widget); int w = (rect.x1-rect.x0); int h = (rect.y1-rect.y0); int len; int type = fz_widget_get_type(widget); ++mujstest_count; switch (type) { default: fprintf(mujstest_file, "%% UNKNOWN %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); break; case FZ_WIDGET_TYPE_PUSHBUTTON: fprintf(mujstest_file, "%% PUSHBUTTON %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); break; case FZ_WIDGET_TYPE_CHECKBOX: fprintf(mujstest_file, "%% CHECKBOX %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); break; case FZ_WIDGET_TYPE_RADIOBUTTON: fprintf(mujstest_file, "%% RADIOBUTTON %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); break; case FZ_WIDGET_TYPE_TEXT: { int maxlen = fz_text_widget_max_len(inter, widget); int texttype = fz_text_widget_content_type(inter, widget); /* If height is low, assume a single row, and base * the width off that. */ if (h < 10) { w = (w+h-1) / (h ? h : 1); h = 1; } /* Otherwise, if width is low, work off height */ else if (w < 10) { h = (w+h-1) / (w ? w : 1); w = 1; } else { w = (w+9)/10; h = (h+9)/10; } len = w*h; if (len < 2) len = 2; if (len > maxlen) len = maxlen; fprintf(mujstest_file, "%% TEXT %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); switch (texttype) { default: case FZ_WIDGET_CONTENT_UNRESTRAINED: fprintf(mujstest_file, "TEXT %d ", mujstest_count); escape_string(mujstest_file, len-3, lorem); fprintf(mujstest_file, "\n"); break; case FZ_WIDGET_CONTENT_NUMBER: fprintf(mujstest_file, "TEXT %d\n", mujstest_count); break; case FZ_WIDGET_CONTENT_SPECIAL: fprintf(mujstest_file, "TEXT %lld\n", 46702919800LL + mujstest_count); break; case FZ_WIDGET_CONTENT_DATE: fprintf(mujstest_file, "TEXT Jun %d 1979\n", 1 + ((13 + mujstest_count) % 30)); break; case FZ_WIDGET_CONTENT_TIME: ++mujstest_count; fprintf(mujstest_file, "TEXT %02d:%02d\n", ((mujstest_count/60) % 24), mujstest_count % 60); break; } break; } case FZ_WIDGET_TYPE_LISTBOX: fprintf(mujstest_file, "%% LISTBOX %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); break; case FZ_WIDGET_TYPE_COMBOBOX: fprintf(mujstest_file, "%% COMBOBOX %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); break; } fprintf(mujstest_file, "CLICK %0.2f %0.2f\n", (rect.x0+rect.x1)/2, (rect.y0+rect.y1)/2); } } if (uselist) { fz_try(ctx) { list = fz_new_display_list(ctx); dev = fz_new_list_device(ctx, list); fz_run_page(doc, page, dev, fz_identity, &cookie); } fz_always(ctx) { fz_free_device(dev); dev = NULL; } fz_catch(ctx) { fz_free_display_list(ctx, list); fz_free_page(doc, page); fz_throw(ctx, "cannot draw page %d in file '%s'", pagenum, filename); } } if (showxml) { fz_try(ctx) { dev = fz_new_trace_device(ctx); fz_printf(out, "<page number=\"%d\">\n", pagenum); if (list) fz_run_display_list(list, dev, fz_identity, fz_infinite_rect, &cookie); else fz_run_page(doc, page, dev, fz_identity, &cookie); fz_printf(out, "</page>\n"); } fz_always(ctx) { fz_free_device(dev); dev = NULL; } fz_catch(ctx) { fz_free_display_list(ctx, list); fz_free_page(doc, page); fz_rethrow(ctx); } } if (showtext) { fz_text_page *text = NULL; fz_var(text); fz_try(ctx) { text = fz_new_text_page(ctx, fz_bound_page(doc, page)); dev = fz_new_text_device(ctx, sheet, text); if (list) fz_run_display_list(list, dev, fz_identity, fz_infinite_rect, &cookie); else fz_run_page(doc, page, dev, fz_identity, &cookie); fz_free_device(dev); dev = NULL; if (showtext == TEXT_XML) { fz_print_text_page_xml(ctx, out, text); } else if (showtext == TEXT_HTML) { fz_print_text_page_html(ctx, out, text); } else if (showtext == TEXT_PLAIN) { fz_print_text_page(ctx, out, text); fz_printf(out, "\f\n"); } } fz_always(ctx) { fz_free_device(dev); dev = NULL; fz_free_text_page(ctx, text); } fz_catch(ctx) { fz_free_display_list(ctx, list); fz_free_page(doc, page); fz_rethrow(ctx); } } if (showmd5 || showtime) printf("page %s %d", filename, pagenum); #ifdef GDI_PLUS_BMP_RENDERER // hack: use -G0 to "enable GDI+" when saving as TGA if (output && (strstr(output, ".bmp") || strstr(output, ".tga") && !gamma_value)) drawbmp(ctx, doc, page, list, pagenum); else #endif if (output || showmd5 || showtime) { float zoom; fz_matrix ctm; fz_rect bounds, tbounds; fz_bbox ibounds; 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)); tbounds = fz_transform_rect(ctm, bounds); ibounds = fz_round_rect(tbounds); /* convert to integers */ /* 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 = ibounds.x1 - ibounds.x0; if (w && t <= w) w = 0; t = ibounds.y1 - ibounds.y0; if (h && t <= h) h = 0; } /* Now w or h will be 0 unless they need to be enforced. */ if (w || h) { float scalex = w / (tbounds.x1 - tbounds.x0); float scaley = h / (tbounds.y1 - tbounds.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)); tbounds = fz_transform_rect(ctm, bounds); } ibounds = fz_round_rect(tbounds); /* TODO: banded rendering and multi-page ppm */ fz_try(ctx) { pix = fz_new_pixmap_with_bbox(ctx, colorspace, ibounds); if (savealpha) fz_clear_pixmap(ctx, pix); else fz_clear_pixmap_with_value(ctx, pix, 255); dev = fz_new_draw_device(ctx, pix); if (list) fz_run_display_list(list, dev, ctm, tbounds, &cookie); else fz_run_page(doc, page, dev, ctm, &cookie); fz_free_device(dev); dev = NULL; if (invert) fz_invert_pixmap(ctx, pix); if (gamma_value != 1) fz_gamma_pixmap(ctx, pix, gamma_value); if (savealpha) fz_unmultiply_pixmap(ctx, pix); if (output) { char buf[512]; sprintf(buf, output, pagenum); if (strstr(output, ".pgm") || strstr(output, ".ppm") || strstr(output, ".pnm")) fz_write_pnm(ctx, pix, buf); else if (strstr(output, ".pam")) fz_write_pam(ctx, pix, buf, savealpha); else if (strstr(output, ".png")) fz_write_png(ctx, pix, buf, savealpha); else if (strstr(output, ".pbm")) { fz_bitmap *bit = fz_halftone_pixmap(ctx, pix, NULL); fz_write_pbm(ctx, bit, buf); fz_drop_bitmap(ctx, bit); } /* SumatraPDF: support TGA as output format */ else if (strstr(output, ".tga")) fz_write_tga(ctx, pix, buf, savealpha); } if (showmd5) { unsigned char digest[16]; int i; fz_md5_pixmap(pix, digest); printf(" "); for (i = 0; i < 16; i++) printf("%02x", digest[i]); } } 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); } }
static void fz_draw_clip_text(fz_device *devp, fz_text *text, fz_matrix ctm, int accumulate) { fz_draw_device *dev = devp->user; fz_context *ctx = dev->ctx; fz_bbox bbox; fz_pixmap *mask, *dest, *shape; fz_matrix tm, trm, trunc_trm; fz_pixmap *glyph; int i, x, y, gid; fz_draw_state *state; fz_colorspace *model; /* If accumulate == 0 then this text object is guaranteed complete */ /* If accumulate == 1 then this text object is the first (or only) in a sequence */ /* If accumulate == 2 then this text object is a continuation */ state = push_stack(dev); model = state->dest->colorspace; if (accumulate == 0) { /* make the mask the exact size needed */ bbox = fz_bbox_covering_rect(fz_bound_text(dev->ctx, text, ctm)); bbox = fz_intersect_bbox(bbox, state->scissor); } else { /* be conservative about the size of the mask needed */ bbox = state->scissor; } if (accumulate == 0 || accumulate == 1) { mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, mask); dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, dest); if (state->shape) { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } else shape = NULL; state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; state[1].dest = dest; state[1].mask = mask; state[1].shape = shape; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (text) begin\n"); #endif } else { mask = state->mask; dev->top--; } if (!fz_is_empty_rect(bbox) && mask) { tm = text->trm; for (i = 0; i < text->len; i++) { gid = text->items[i].gid; if (gid < 0) continue; tm.e = text->items[i].x; tm.f = text->items[i].y; trm = fz_concat(tm, ctm); x = floorf(trm.e); y = floorf(trm.f); trunc_trm = trm; trunc_trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX); trunc_trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX); glyph = fz_render_glyph(dev->ctx, text->font, gid, trunc_trm, model, bbox); if (glyph) { draw_glyph(NULL, mask, glyph, x, y, bbox); if (state[1].shape) draw_glyph(NULL, state[1].shape, glyph, x, y, bbox); fz_drop_pixmap(dev->ctx, glyph); } else { fz_path *path = fz_outline_glyph(dev->ctx, text->font, gid, trm); if (path) { fz_pixmap *old_dest; float white = 1; state = &dev->stack[dev->top]; old_dest = state[0].dest; state[0].dest = state[0].mask; state[0].mask = NULL; fz_try(ctx) { fz_draw_fill_path(devp, path, 0, fz_identity, fz_device_gray, &white, 1); } fz_always(ctx) { state[0].mask = state[0].dest; state[0].dest = old_dest; fz_free_path(dev->ctx, path); } fz_catch(ctx) { fz_rethrow(ctx); } } else { fz_warn(dev->ctx, "cannot render glyph for clipping"); } } }
static void fz_draw_clip_stroke_text(fz_device *devp, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm) { fz_draw_device *dev = devp->user; fz_bbox bbox; fz_pixmap *mask, *dest, *shape; fz_matrix tm, trm; fz_pixmap *glyph; int i, x, y, gid; fz_draw_state *state = push_stack(dev); fz_colorspace *model = state->dest->colorspace; /* make the mask the exact size needed */ bbox = fz_bbox_covering_rect(fz_bound_text(dev->ctx, text, ctm)); bbox = fz_intersect_bbox(bbox, state->scissor); mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, mask); dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, dest); if (state->shape) { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } else shape = state->shape; state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].mask = mask; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (stroke text) begin\n"); #endif if (!fz_is_empty_rect(bbox)) { tm = text->trm; for (i = 0; i < text->len; i++) { gid = text->items[i].gid; if (gid < 0) continue; tm.e = text->items[i].x; tm.f = text->items[i].y; trm = fz_concat(tm, ctm); x = floorf(trm.e); y = floorf(trm.f); trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX); trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX); glyph = fz_render_stroked_glyph(dev->ctx, text->font, gid, trm, ctm, stroke, bbox); if (glyph) { draw_glyph(NULL, mask, glyph, x, y, bbox); if (shape) draw_glyph(NULL, shape, glyph, x, y, bbox); fz_drop_pixmap(dev->ctx, glyph); } else { fz_warn(dev->ctx, "cannot draw glyph for clipping (unimplemented case)"); // TODO: outline/non-cached case } } } }
static void fz_draw_fill_image(fz_device *devp, fz_image *image, fz_matrix ctm, float alpha) { fz_draw_device *dev = devp->user; fz_pixmap *converted = NULL; fz_pixmap *scaled = NULL; fz_pixmap *pixmap; fz_pixmap *orig_pixmap; int after; int dx, dy; fz_context *ctx = dev->ctx; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; fz_bbox clip = fz_pixmap_bbox(ctx, state->dest); clip = fz_intersect_bbox(clip, state->scissor); fz_var(scaled); if (!model) { fz_warn(dev->ctx, "cannot render image directly to an alpha mask"); return; } if (image->w == 0 || image->h == 0) return; dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b); dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d); pixmap = fz_image_to_pixmap(ctx, image, dx, dy); orig_pixmap = pixmap; /* convert images with more components (cmyk->rgb) before scaling */ /* convert images with fewer components (gray->rgb after scaling */ /* convert images with expensive colorspace transforms after scaling */ fz_try(ctx) { if (state->blendmode & FZ_BLEND_KNOCKOUT) state = fz_knockout_begin(dev); after = 0; if (pixmap->colorspace == fz_device_gray) after = 1; if (pixmap->colorspace != model && !after) { converted = fz_new_pixmap_with_bbox(ctx, model, fz_pixmap_bbox(ctx, pixmap)); fz_convert_pixmap(ctx, converted, pixmap); pixmap = converted; } if (dx < pixmap->w && dy < pixmap->h) { int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); scaled = fz_transform_pixmap(ctx, pixmap, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); if (!scaled) { if (dx < 1) dx = 1; if (dy < 1) dy = 1; scaled = fz_scale_pixmap(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL); } if (scaled) pixmap = scaled; } if (pixmap->colorspace != model) { if ((pixmap->colorspace == fz_device_gray && model == fz_device_rgb) || (pixmap->colorspace == fz_device_gray && model == fz_device_bgr)) { /* We have special case rendering code for gray -> rgb/bgr */ } else { converted = fz_new_pixmap_with_bbox(ctx, model, fz_pixmap_bbox(ctx, pixmap)); fz_convert_pixmap(ctx, converted, pixmap); pixmap = converted; } } fz_paint_image(state->dest, state->scissor, state->shape, pixmap, ctm, alpha * 255); if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); } fz_always(ctx) { fz_drop_pixmap(ctx, scaled); fz_drop_pixmap(ctx, converted); fz_drop_pixmap(ctx, orig_pixmap); } fz_catch(ctx) { fz_rethrow(ctx); } }