Spaces:
Running
Running
/* -*- 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. | |
*/ | |
//======================================================================== | |
// | |
// Modified under the Poppler project - http://poppler.freedesktop.org | |
// | |
// All changes made under the Poppler project to this file are licensed | |
// under GPL version 2 or later | |
// | |
// Copyright (C) 2012 Hib Eris <[email protected]> | |
// Copyright (C) 2012, 2017 Adrian Johnson <[email protected]> | |
// Copyright (C) 2018 Adam Reichold <[email protected]> | |
// Copyright (C) 2019 Albert Astals Cid <[email protected]> | |
// Copyright (C) 2019 Marek Kasik <[email protected]> | |
// | |
// To see a description of the changes please see the Changelog file that | |
// came with your tarball or type make ChangeLog if you are building from git | |
// | |
//======================================================================== | |
/* This implements a box filter that supports non-integer box sizes */ | |
/* we work in fixed point where 1. == 1 << 24 */ | |
static void downsample_row_box_filter(int start, int width, uint32_t *src, const uint32_t *src_limit, uint32_t *dest, const 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 * contribution_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 && src < src_limit) { | |
int box = 1 << FIXED_SHIFT; | |
int start_coverage = coverage[x]; | |
box -= start_coverage; | |
src++; | |
while (box >= pixel_coverage && src < src_limit) { | |
src++; | |
box -= pixel_coverage; | |
} | |
x++; | |
} | |
while (x < start + width && src < src_limit) { | |
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 && src < src_limit) { | |
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 && src < src_limit) { | |
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++) { | |
double left_side = i * scale; | |
double right_side = (i + 1) * scale; | |
double right_fract = right_side - floor(right_side); | |
double 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 amount that the left-most pixel | |
* contributes */ | |
coverage[i] = (1 << 24) - (count * ratio + overage); | |
} | |
return ratio; | |
} | |
bool CairoRescaleBox::downScaleImage(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, | |
cairo_surface_t *dest_surface) | |
{ | |
int pixel_coverage_x, pixel_coverage_y; | |
int dest_y; | |
int src_y = 0; | |
uint32_t *scanline; | |
int *x_coverage = nullptr; | |
int *y_coverage = nullptr; | |
uint32_t *temp_buf = nullptr; | |
bool retval = false; | |
unsigned int *dest; | |
int dst_stride; | |
dest = reinterpret_cast<unsigned int *>(cairo_image_surface_get_data(dest_surface)); | |
dst_stride = cairo_image_surface_get_stride(dest_surface); | |
scanline = (uint32_t *)gmallocn(orig_width, sizeof(int)); | |
x_coverage = (int *)gmallocn(orig_width, sizeof(int)); | |
y_coverage = (int *)gmallocn(orig_height, 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]; | |
getRow(src_y, scanline); | |
downsample_row_box_filter(start_column, width, scanline, scanline + orig_width, temp_buf + width * columns, x_coverage, pixel_coverage_x); | |
columns++; | |
src_y++; | |
box -= start_coverage_y; | |
while (box >= pixel_coverage_y) { | |
getRow(src_y, scanline); | |
downsample_row_box_filter(start_column, width, scanline, scanline + orig_width, temp_buf + width * columns, x_coverage, pixel_coverage_x); | |
columns++; | |
src_y++; | |
box -= pixel_coverage_y; | |
} | |
/* downsample any leftovers */ | |
if (box > 0) { | |
getRow(src_y, scanline); | |
downsample_row_box_filter(start_column, width, scanline, scanline + orig_width, 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 = true; | |
cleanup: | |
free(x_coverage); | |
free(y_coverage); | |
free(temp_buf); | |
free(scanline); | |
return retval; | |
} | |