| diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc |
| index 9a0f3be..42ac3a8 100644 |
| --- a/poppler/CairoOutputDev.cc |
| +++ b/poppler/CairoOutputDev.cc |
| @@ -58,6 +58,7 @@ |
| #include <splash/SplashBitmap.h> |
| #include "CairoOutputDev.h" |
| #include "CairoFontEngine.h" |
| +#include "CairoRescaleBox.h" |
| //------------------------------------------------------------------------ |
| |
| // #define LOG_CAIRO |
| @@ -1331,6 +1332,82 @@ void CairoOutputDev::endMaskClip(GfxState *state) { |
| clearSoftMask(state); |
| } |
| |
| +cairo_surface_t *CairoOutputDev::downscaleSurface(cairo_surface_t *orig_surface) { |
| + cairo_surface_t *dest_surface; |
| + unsigned char *dest_buffer; |
| + int dest_stride; |
| + unsigned char *orig_buffer; |
| + int orig_width, orig_height; |
| + int orig_stride; |
| + GBool res; |
| + |
| + if (printing) |
| + return NULL; |
| + |
| + cairo_matrix_t matrix; |
| + cairo_get_matrix(cairo, &matrix); |
| + |
| + /* this whole computation should be factored out */ |
| + double xScale = matrix.xx; |
| + double yScale = matrix.yy; |
| + int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */ |
| + int scaledHeight; |
| + int scaledWidth; |
| + if (xScale >= 0) { |
| + tx = splashRound(matrix.x0 - 0.01); |
| + tx2 = splashRound(matrix.x0 + xScale + 0.01) - 1; |
| + } else { |
| + tx = splashRound(matrix.x0 + 0.01) - 1; |
| + tx2 = splashRound(matrix.x0 + xScale - 0.01); |
| + } |
| + scaledWidth = abs(tx2 - tx) + 1; |
| + //scaledWidth = splashRound(fabs(xScale)); |
| + if (scaledWidth == 0) { |
| + // technically, this should draw nothing, but it generally seems |
| + // better to draw a one-pixel-wide stripe rather than throwing it |
| + // away |
| + scaledWidth = 1; |
| + } |
| + if (yScale >= 0) { |
| + ty = splashFloor(matrix.y0 + 0.01); |
| + ty2 = splashCeil(matrix.y0 + yScale - 0.01); |
| + } else { |
| + ty = splashCeil(matrix.y0 - 0.01); |
| + ty2 = splashFloor(matrix.y0 + yScale + 0.01); |
| + } |
| + scaledHeight = abs(ty2 - ty); |
| + if (scaledHeight == 0) { |
| + scaledHeight = 1; |
| + } |
| + |
| + orig_width = cairo_image_surface_get_width (orig_surface); |
| + orig_height = cairo_image_surface_get_height (orig_surface); |
| + if (scaledWidth >= orig_width || scaledHeight >= orig_height) |
| + return NULL; |
| + |
| + dest_surface = cairo_surface_create_similar (orig_surface, |
| + cairo_surface_get_content (orig_surface), |
| + scaledWidth, scaledHeight); |
| + dest_buffer = cairo_image_surface_get_data (dest_surface); |
| + dest_stride = cairo_image_surface_get_stride (dest_surface); |
| + |
| + orig_buffer = cairo_image_surface_get_data (orig_surface); |
| + orig_stride = cairo_image_surface_get_stride (orig_surface); |
| + |
| + res = downscale_box_filter((uint32_t *)orig_buffer, |
| + orig_stride, orig_width, orig_height, |
| + scaledWidth, scaledHeight, 0, 0, |
| + scaledWidth, scaledHeight, |
| + (uint32_t *)dest_buffer, dest_stride); |
| + if (!res) { |
| + cairo_surface_destroy (dest_surface); |
| + return NULL; |
| + } |
| + |
| + return dest_surface; |
| + |
| +} |
| + |
| void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, |
| int width, int height, GBool invert, |
| GBool interpolate, GBool inlineImg) { |
| @@ -2094,6 +2171,18 @@ void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, |
| } |
| gfree(lookup); |
| |
| + cairo_surface_t *scaled_surface; |
| + |
| + scaled_surface = downscaleSurface (image); |
| + if (scaled_surface) { |
| + if (cairo_surface_status (scaled_surface)) |
| + goto cleanup; |
| + cairo_surface_destroy (image); |
| + image = scaled_surface; |
| + width = cairo_image_surface_get_width (image); |
| + height = cairo_image_surface_get_height (image); |
| + } |
| + |
| cairo_surface_mark_dirty (image); |
| pattern = cairo_pattern_create_for_surface (image); |
| cairo_surface_destroy (image); |
| diff --git a/poppler/CairoOutputDev.h b/poppler/CairoOutputDev.h |
| index fb9c0d7..266f0cb 100644 |
| --- a/poppler/CairoOutputDev.h |
| +++ b/poppler/CairoOutputDev.h |
| @@ -268,6 +268,7 @@ public: |
| |
| protected: |
| void doPath(cairo_t *cairo, GfxState *state, GfxPath *path); |
| + cairo_surface_t *downscaleSurface(cairo_surface_t *orig_surface); |
| |
| GfxRGB fill_color, stroke_color; |
| cairo_pattern_t *fill_pattern, *stroke_pattern; |
| diff --git a/poppler/CairoRescaleBox.cc b/poppler/CairoRescaleBox.cc |
| new file mode 100644 |
| index 0000000..dce5ddd |
| --- /dev/null |
| +++ b/poppler/CairoRescaleBox.cc |
| @@ -0,0 +1,352 @@ |
| +/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ |
| +/* |
| + * Copyright © 2009 Mozilla Corporation |
| + * |
| + * Permission to use, copy, modify, distribute, and sell this software and its |
| + * documentation for any purpose is hereby granted without fee, provided that |
| + * the above copyright notice appear in all copies and that both that |
| + * copyright notice and this permission notice appear in supporting |
| + * documentation, and that the name of Mozilla Corporation not be used in |
| + * advertising or publicity pertaining to distribution of the software without |
| + * specific, written prior permission. Mozilla Corporation makes no |
| + * representations about the suitability of this software for any purpose. It |
| + * is provided "as is" without express or implied warranty. |
| + * |
| + * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
| + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT |
| + * SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
| + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
| + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
| + * OF THIS SOFTWARE. |
| + * |
| + * Author: Jeff Muizelaar, Mozilla Corp. |
| + */ |
| + |
| +/* This implements a box filter that supports non-integer box sizes */ |
| + |
| +#ifdef HAVE_CONFIG_H |
| +#include <config.h> |
| +#endif |
| + |
| +#include <stdint.h> |
| +#include <stdio.h> |
| +#include <assert.h> |
| +#include <stdlib.h> |
| +#include <math.h> |
| +#include "goo/gmem.h" |
| +#include "CairoRescaleBox.h" |
| + |
| +typedef unsigned short int uint16_t; |
| +typedef unsigned int uint32_t; |
| + |
| +/* we work in fixed point where 1. == 1 << 24 */ |
| +#define FIXED_SHIFT 24 |
| + |
| +static void downsample_row_box_filter ( |
| + int start, int width, |
| + uint32_t *src, uint32_t *dest, |
| + int coverage[], int pixel_coverage) |
| +{ |
| + /* we need an array of the pixel contribution of each destination pixel on the boundaries. |
| + * we invert the value to get the value on the other size of the box */ |
| + /* |
| + |
| + value = a * contribution * 1/box_size |
| + value += a * 1/box_size |
| + value += a * 1/box_size |
| + value += a * 1/box_size |
| + value += a * (1 - contribution) * 1/box_size |
| + a * (1/box_size - contribution * 1/box_size) |
| + |
| + box size is constant |
| + |
| + |
| + value = a * contribtion_a * 1/box_size + b * contribution_b * 1/box_size |
| + contribution_b = (1 - contribution_a) |
| + = (1 - contribution_a_next) |
| + */ |
| + |
| + /* box size = ceil(src_width/dest_width) */ |
| + int x = 0; |
| + |
| + /* skip to start */ |
| + /* XXX: it might be possible to do this directly instead of iteratively, however |
| + * the iterative solution is simple */ |
| + while (x < start) |
| + { |
| + int box = 1 << FIXED_SHIFT; |
| + int start_coverage = coverage[x]; |
| + box -= start_coverage; |
| + src++; |
| + while (box >= pixel_coverage) |
| + { |
| + src++; |
| + box -= pixel_coverage; |
| + } |
| + x++; |
| + } |
| + |
| + while (x < start + width) |
| + { |
| + uint32_t a = 0; |
| + uint32_t r = 0; |
| + uint32_t g = 0; |
| + uint32_t b = 0; |
| + int box = 1 << FIXED_SHIFT; |
| + int start_coverage = coverage[x]; |
| + |
| + a = ((*src >> 24) & 0xff) * start_coverage; |
| + r = ((*src >> 16) & 0xff) * start_coverage; |
| + g = ((*src >> 8) & 0xff) * start_coverage; |
| + b = ((*src >> 0) & 0xff) * start_coverage; |
| + src++; |
| + x++; |
| + box -= start_coverage; |
| + |
| + while (box >= pixel_coverage) |
| + { |
| + a += ((*src >> 24) & 0xff) * pixel_coverage; |
| + r += ((*src >> 16) & 0xff) * pixel_coverage; |
| + g += ((*src >> 8) & 0xff) * pixel_coverage; |
| + b += ((*src >> 0) & 0xff) * pixel_coverage; |
| + src++; |
| + |
| + box -= pixel_coverage; |
| + } |
| + |
| + /* multiply by whatever is leftover |
| + * this ensures that we don't bias down. |
| + * i.e. start_coverage + n*pixel_coverage + box == 1 << 24 */ |
| + if (box > 0) |
| + { |
| + a += ((*src >> 24) & 0xff) * box; |
| + r += ((*src >> 16) & 0xff) * box; |
| + g += ((*src >> 8) & 0xff) * box; |
| + b += ((*src >> 0) & 0xff) * box; |
| + } |
| + |
| + a >>= FIXED_SHIFT; |
| + r >>= FIXED_SHIFT; |
| + g >>= FIXED_SHIFT; |
| + b >>= FIXED_SHIFT; |
| + |
| + *dest = (a << 24) | (r << 16) | (g << 8) | b; |
| + dest++; |
| + } |
| +} |
| + |
| +static void downsample_columns_box_filter ( |
| + int n, |
| + int start_coverage, |
| + int pixel_coverage, |
| + uint32_t *src, uint32_t *dest) |
| +{ |
| + int stride = n; |
| + while (n--) { |
| + uint32_t a = 0; |
| + uint32_t r = 0; |
| + uint32_t g = 0; |
| + uint32_t b = 0; |
| + uint32_t *column_src = src; |
| + int box = 1 << FIXED_SHIFT; |
| + |
| + a = ((*column_src >> 24) & 0xff) * start_coverage; |
| + r = ((*column_src >> 16) & 0xff) * start_coverage; |
| + g = ((*column_src >> 8) & 0xff) * start_coverage; |
| + b = ((*column_src >> 0) & 0xff) * start_coverage; |
| + column_src += stride; |
| + box -= start_coverage; |
| + |
| + while (box >= pixel_coverage) |
| + { |
| + a += ((*column_src >> 24) & 0xff) * pixel_coverage; |
| + r += ((*column_src >> 16) & 0xff) * pixel_coverage; |
| + g += ((*column_src >> 8) & 0xff) * pixel_coverage; |
| + b += ((*column_src >> 0) & 0xff) * pixel_coverage; |
| + column_src += stride; |
| + box -= pixel_coverage; |
| + } |
| + |
| + if (box > 0) { |
| + a += ((*column_src >> 24) & 0xff) * box; |
| + r += ((*column_src >> 16) & 0xff) * box; |
| + g += ((*column_src >> 8) & 0xff) * box; |
| + b += ((*column_src >> 0) & 0xff) * box; |
| + } |
| + |
| + a >>= FIXED_SHIFT; |
| + r >>= FIXED_SHIFT; |
| + g >>= FIXED_SHIFT; |
| + b >>= FIXED_SHIFT; |
| + |
| + *dest = (a << 24) | (r << 16) | (g << 8) | b; |
| + dest++; |
| + src++; |
| + } |
| +} |
| + |
| +static int compute_coverage (int coverage[], int src_length, int dest_length) |
| +{ |
| + int i; |
| + /* num = src_length/dest_length |
| + total = sum(pixel) / num |
| + |
| + pixel * 1/num == pixel * dest_length / src_length |
| + */ |
| + /* the average contribution of each source pixel */ |
| + int ratio = ((1 << 24)*(long long int)dest_length)/src_length; |
| + /* because ((1 << 24)*(long long int)dest_length) won't always be divisible by src_length |
| + * we'll need someplace to put the other bits. |
| + * |
| + * We want to ensure a + n*ratio < 1<<24 |
| + * |
| + * 1<<24 |
| + * */ |
| + |
| + double scale = (double)src_length/dest_length; |
| + |
| + /* for each destination pixel compute the coverage of the left most pixel included in the box */ |
| + /* I have a proof of this, which this margin is too narrow to contain */ |
| + for (i=0; i<dest_length; i++) |
| + { |
| + float left_side = i*scale; |
| + float right_side = (i+1)*scale; |
| + float right_fract = right_side - floor (right_side); |
| + float left_fract = ceil (left_side) - left_side; |
| + int overage; |
| + /* find out how many source pixels will be used to fill the box */ |
| + int count = floor (right_side) - ceil (left_side); |
| + /* what's the maximum value this expression can become? |
| + floor((i+1)*scale) - ceil(i*scale) |
| + |
| + (i+1)*scale - i*scale == scale |
| + |
| + since floor((i+1)*scale) <= (i+1)*scale |
| + and ceil(i*scale) >= i*scale |
| + |
| + floor((i+1)*scale) - ceil(i*scale) <= scale |
| + |
| + further since: floor((i+1)*scale) - ceil(i*scale) is an integer |
| + |
| + therefore: |
| + floor((i+1)*scale) - ceil(i*scale) <= floor(scale) |
| + */ |
| + |
| + if (left_fract == 0.) |
| + count--; |
| + |
| + /* compute how much the right-most pixel contributes */ |
| + overage = ratio*(right_fract); |
| + |
| + /* the remainder is the the amount that the left-most pixel |
| + * contributes */ |
| + coverage[i] = (1<<24) - (count * ratio + overage); |
| + } |
| + |
| + return ratio; |
| +} |
| + |
| +GBool downscale_box_filter(uint32_t *orig, int orig_stride, unsigned orig_width, unsigned orig_height, |
| + signed scaled_width, signed scaled_height, |
| + uint16_t start_column, uint16_t start_row, |
| + uint16_t width, uint16_t height, |
| + uint32_t *dest, int dst_stride) |
| +{ |
| + int pixel_coverage_x, pixel_coverage_y; |
| + int dest_y; |
| + int src_y = 0; |
| + uint32_t *scanline = orig; |
| + int *x_coverage = NULL; |
| + int *y_coverage = NULL; |
| + uint32_t *temp_buf = NULL; |
| + GBool retval = gFalse; |
| + |
| + x_coverage = (int *)gmallocn3 (orig_width, 1, sizeof(int)); |
| + y_coverage = (int *)gmallocn3 (orig_height, 1, sizeof(int)); |
| + |
| + /* we need to allocate enough room for ceil(src_height/dest_height)+1 |
| + Example: |
| + src_height = 140 |
| + dest_height = 50 |
| + src_height/dest_height = 2.8 |
| + |
| + |-------------| 2.8 pixels |
| + |----|----|----|----| 4 pixels |
| + need to sample 3 pixels |
| + |
| + |-------------| 2.8 pixels |
| + |----|----|----|----| 4 pixels |
| + need to sample 4 pixels |
| + */ |
| + |
| + temp_buf = (uint32_t *)gmallocn3 ((orig_height + scaled_height-1)/scaled_height+1, scaled_width, sizeof(uint32_t)); |
| + |
| + if (!x_coverage || !y_coverage || !scanline || !temp_buf) |
| + goto cleanup; |
| + |
| + pixel_coverage_x = compute_coverage (x_coverage, orig_width, scaled_width); |
| + pixel_coverage_y = compute_coverage (y_coverage, orig_height, scaled_height); |
| + |
| + assert (width + start_column <= scaled_width); |
| + |
| + /* skip the rows at the beginning */ |
| + for (dest_y = 0; dest_y < start_row; dest_y++) |
| + { |
| + int box = 1 << FIXED_SHIFT; |
| + int start_coverage_y = y_coverage[dest_y]; |
| + box -= start_coverage_y; |
| + src_y++; |
| + while (box >= pixel_coverage_y) |
| + { |
| + box -= pixel_coverage_y; |
| + src_y++; |
| + } |
| + } |
| + |
| + for (; dest_y < start_row + height; dest_y++) |
| + { |
| + int columns = 0; |
| + int box = 1 << FIXED_SHIFT; |
| + int start_coverage_y = y_coverage[dest_y]; |
| + |
| + scanline = orig + src_y * orig_stride / 4; |
| + downsample_row_box_filter (start_column, width, scanline, temp_buf + width * columns, x_coverage, pixel_coverage_x); |
| + columns++; |
| + src_y++; |
| + box -= start_coverage_y; |
| + |
| + while (box >= pixel_coverage_y) |
| + { |
| + scanline = orig + src_y * orig_stride / 4; |
| + downsample_row_box_filter (start_column, width, scanline, temp_buf + width * columns, x_coverage, pixel_coverage_x); |
| + columns++; |
| + src_y++; |
| + box -= pixel_coverage_y; |
| + } |
| + |
| + /* downsample any leftovers */ |
| + if (box > 0) |
| + { |
| + scanline = orig + src_y * orig_stride / 4; |
| + downsample_row_box_filter (start_column, width, scanline, temp_buf + width * columns, x_coverage, pixel_coverage_x); |
| + columns++; |
| + } |
| + |
| + /* now scale the rows we just downsampled in the y direction */ |
| + downsample_columns_box_filter (width, start_coverage_y, pixel_coverage_y, temp_buf, dest); |
| + dest += dst_stride / 4; |
| + |
| +// assert(width*columns <= ((orig_height + scaled_height-1)/scaled_height+1) * width); |
| + } |
| +// assert (src_y<=orig_height); |
| + |
| + retval = gTrue; |
| + |
| +cleanup: |
| + free (x_coverage); |
| + free (y_coverage); |
| + free (temp_buf); |
| + |
| + return gTrue; |
| +} |
| diff --git a/poppler/CairoRescaleBox.h b/poppler/CairoRescaleBox.h |
| new file mode 100644 |
| index 0000000..5349c87 |
| --- /dev/null |
| +++ b/poppler/CairoRescaleBox.h |
| @@ -0,0 +1,12 @@ |
| +#ifndef CAIRO_RESCALE_BOX_H |
| +#define CAIRO_RESCALE_BOX_H |
| + |
| +#include "goo/gtypes.h" |
| + |
| +GBool downscale_box_filter(unsigned int *orig, int orig_stride, unsigned orig_width, unsigned orig_height, |
| + signed scaled_width, signed scaled_height, |
| + unsigned short int start_column, unsigned short int start_row, |
| + unsigned short int width, unsigned short int height, |
| + unsigned int *dest, int dst_stride); |
| + |
| +#endif /* CAIRO_RESCALE_BOX_H */ |
| diff --git a/poppler/Makefile.am b/poppler/Makefile.am |
| index ec79e31..096ea76 100644 |
| --- a/poppler/Makefile.am |
| +++ b/poppler/Makefile.am |
| @@ -47,7 +47,9 @@ libpoppler_cairo_la_SOURCES = \ |
| CairoFontEngine.cc \ |
| CairoFontEngine.h \ |
| CairoOutputDev.cc \ |
| - CairoOutputDev.h |
| + CairoOutputDev.h \ |
| + CairoRescaleBox.cc \ |
| + CairoRescaleBox.h |
| |
| endif |
| |
| diff --git a/glib/CMakeLists.txt b/glib/CMakeLists.txt |
| index 6ed9523..ceef25e 100644 |
| --- a/glib/CMakeLists.txt |
| +++ b/glib/CMakeLists.txt |
| @@ -90,6 +90,7 @@ if (CAIRO_FOUND) |
| set(poppler_glib_SRCS ${poppler_glib_SRCS} |
| ${CMAKE_SOURCE_DIR}/poppler/CairoFontEngine.cc |
| ${CMAKE_SOURCE_DIR}/poppler/CairoOutputDev.cc |
| + ${CMAKE_SOURCE_DIR}/poppler/CairoRescaleBox.cc |
| ) |
| endif (CAIRO_FOUND) |
| add_library(poppler-glib SHARED ${poppler_glib_SRCS}) |