|
|
@ -193,6 +193,7 @@ int rfb_bytes_per_line;
|
|
|
|
|
|
|
|
|
|
|
|
/* scaling info */
|
|
|
|
/* scaling info */
|
|
|
|
int scaling = 0;
|
|
|
|
int scaling = 0;
|
|
|
|
|
|
|
|
int scaling_noblend = 0;
|
|
|
|
double scale_fac = 1.0;
|
|
|
|
double scale_fac = 1.0;
|
|
|
|
int scaled_x = 0, scaled_y = 0;
|
|
|
|
int scaled_x = 0, scaled_y = 0;
|
|
|
|
|
|
|
|
|
|
|
@ -3736,11 +3737,14 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
|
|
|
|
main_blue_mask = fb->blue_mask;
|
|
|
|
main_blue_mask = fb->blue_mask;
|
|
|
|
|
|
|
|
|
|
|
|
if (scaling) {
|
|
|
|
if (scaling) {
|
|
|
|
width = (int) (width * scale_fac);
|
|
|
|
double eps = 0.000001;
|
|
|
|
height = (int) (height * scale_fac);
|
|
|
|
width = (int) (width * scale_fac + eps);
|
|
|
|
|
|
|
|
height = (int) (height * scale_fac + eps);
|
|
|
|
scaled_x = width;
|
|
|
|
scaled_x = width;
|
|
|
|
scaled_y = height;
|
|
|
|
scaled_y = height;
|
|
|
|
rfb_bytes_per_line = (main_bytes_per_line / fb->width) * width;
|
|
|
|
rfb_bytes_per_line = (main_bytes_per_line / fb->width) * width;
|
|
|
|
|
|
|
|
rfbLog("scaling screen: %dx%d -> %dx%d scale_fac=%.5f\n",
|
|
|
|
|
|
|
|
fb->width, fb->height, scaled_x, scaled_y, scale_fac);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
rfb_bytes_per_line = main_bytes_per_line;
|
|
|
|
rfb_bytes_per_line = main_bytes_per_line;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -4643,18 +4647,17 @@ static void hint_updates(void) {
|
|
|
|
#define FLOOR(x) ( (double) ((int) (x)) )
|
|
|
|
#define FLOOR(x) ( (double) ((int) (x)) )
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Scaling.
|
|
|
|
* Scaling:
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* For shrinking, a destination (scaled) pixel will correspond to more
|
|
|
|
* For shrinking, a destination (scaled) pixel will correspond to more
|
|
|
|
* than one source (i.e. main fb) pixel. Think of an x-y plane made
|
|
|
|
* than one source (i.e. main fb) pixel. Think of an x-y plane made with
|
|
|
|
* with graph paper. Each square in the graph paper (i.e. collection
|
|
|
|
* graph paper. Each unit square in the graph paper (i.e. collection of
|
|
|
|
* of points (x,y) such that N < x < N+1 and M < y < M+1, N and M
|
|
|
|
* points (x,y) such that N < x < N+1 and M < y < M+1, N and M integers)
|
|
|
|
* integers) corresponds to one pixel in the unscaled fb. There is a
|
|
|
|
* corresponds to one pixel in the unscaled fb. There is a solid
|
|
|
|
* solid color filling the inside such a square. A scaled pixel has
|
|
|
|
* color filling the inside of such a square. A scaled pixel has width
|
|
|
|
* width 1/scale_fac, e.g. for "-scale 3/4" the width of the scaled
|
|
|
|
* 1/scale_fac, e.g. for "-scale 3/4" the width of the scaled pixel
|
|
|
|
* pixel is 1.333. The area of this scaled pixel is 1.333 * 1.333
|
|
|
|
* is 1.333. The area of this scaled pixel is 1.333 * 1.333 (so it
|
|
|
|
* (so it obviously overlaps more than one source pixel, each which
|
|
|
|
* obviously overlaps more than one source pixel, each which have area 1).
|
|
|
|
* have area 1).
|
|
|
|
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* We take the weight an unscaled pixel (source) contributes to a
|
|
|
|
* We take the weight an unscaled pixel (source) contributes to a
|
|
|
|
* scaled pixel (destination) as simply proportional to the overlap area
|
|
|
|
* scaled pixel (destination) as simply proportional to the overlap area
|
|
|
@ -4662,65 +4665,103 @@ static void hint_updates(void) {
|
|
|
|
* pixel as an integral over the portion of the graph paper it covers.
|
|
|
|
* pixel as an integral over the portion of the graph paper it covers.
|
|
|
|
* The thing being integrated is the color value of the unscaled source.
|
|
|
|
* The thing being integrated is the color value of the unscaled source.
|
|
|
|
* That color value is constant over a graph paper square (source pixel),
|
|
|
|
* That color value is constant over a graph paper square (source pixel),
|
|
|
|
* and changes discontinuously from one square to the next.
|
|
|
|
* and changes discontinuously from one unit square to the next.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Here is an example for -scale 3/4, the solid lines are the source pixels
|
|
|
|
|
|
|
|
(graph paper unit squares), while the dotted lines denote the scaled
|
|
|
|
|
|
|
|
pixels (destination pixels):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
0 1 4/3 2 8/3 3 4=12/3
|
|
|
|
|
|
|
|
|---------|--.------|------.--|---------|.
|
|
|
|
|
|
|
|
| | . | . | |.
|
|
|
|
|
|
|
|
| A | . B | . | |.
|
|
|
|
|
|
|
|
| | . | . | |.
|
|
|
|
|
|
|
|
| | . | . | |.
|
|
|
|
|
|
|
|
1 |---------|--.------|------.--|---------|.
|
|
|
|
|
|
|
|
4/3|.........|.........|.........|.........|.
|
|
|
|
|
|
|
|
| | . | . | |.
|
|
|
|
|
|
|
|
| C | . D | . | |.
|
|
|
|
|
|
|
|
| | . | . | |.
|
|
|
|
|
|
|
|
2 |---------|--.------|------.--|---------|.
|
|
|
|
|
|
|
|
| | . | . | |.
|
|
|
|
|
|
|
|
| | . | . | |.
|
|
|
|
|
|
|
|
8/3|.........|.........|.........|.........|.
|
|
|
|
|
|
|
|
| | . | . | |.
|
|
|
|
|
|
|
|
3 |---------|--.------|------.--|---------|.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
So we see the first scaled pixel (0 < x < 4/3 and 0 < y < 4/3) mostly
|
|
|
|
|
|
|
|
overlaps with unscaled source pixel "A". The integration (averaging)
|
|
|
|
|
|
|
|
weights for this scaled pixel are:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A 1
|
|
|
|
|
|
|
|
B 1/3
|
|
|
|
|
|
|
|
C 1/3
|
|
|
|
|
|
|
|
D 1/9
|
|
|
|
|
|
|
|
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* The Red, Green, and Blue color values must be averaged over separately
|
|
|
|
* The Red, Green, and Blue color values must be averaged over separately
|
|
|
|
* otherwise you can get a complete mess (except in solid regions).
|
|
|
|
* otherwise you can get a complete mess (except in solid regions),
|
|
|
|
|
|
|
|
* because high order bits are averaged differently from the low order bits.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* So the algorithm is roughly:
|
|
|
|
* So the algorithm is roughly:
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* - Given as input a rectangle in the unscaled source fb with changes,
|
|
|
|
* - Given as input a rectangle in the unscaled source fb with changes,
|
|
|
|
* find the rectangle of pixels this affects in the scaled destination
|
|
|
|
* find the rectangle of pixels this affects in the scaled destination fb.
|
|
|
|
* fb.
|
|
|
|
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* - For each of the affected scaled pixels, determine all of the
|
|
|
|
* - For each of the affected scaled (dest) pixels, determine all of the
|
|
|
|
* unscaled pixels it overlaps with.
|
|
|
|
* unscaled (source) pixels it overlaps with.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* - Average those unscaled values together, weighted by the area
|
|
|
|
* - Average those unscaled source values together, weighted by the area
|
|
|
|
* overlap with the destination pixel. Average R, G, B separately.
|
|
|
|
* overlap with the destination pixel. Average R, G, B separately.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* - Take this average value and convert to a valid pixel value if
|
|
|
|
* - Take this average value and convert to a valid pixel value if
|
|
|
|
* necessary (e.g. rounding, shifting), and then insert it into the
|
|
|
|
* necessary (e.g. rounding, shifting), and then insert it into the
|
|
|
|
* destination framebuffer as the pixel value.
|
|
|
|
* destination framebuffer as the pixel value.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
|
|
|
|
* - On to the next destination pixel...
|
|
|
|
|
|
|
|
*
|
|
|
|
* ========================================================================
|
|
|
|
* ========================================================================
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* For expanding (which we don't think people will do very often... or
|
|
|
|
* For expanding, e.g. -scale 1.1 (which we don't think people will do
|
|
|
|
* at least so we hope, the framebuffer can become huge) the situation
|
|
|
|
* very often... or at least so we hope, the framebuffer can become huge)
|
|
|
|
* is reversed and the destination pixel is smaller than a "graph paper"
|
|
|
|
* the situation is reversed and the destination pixel is smaller than a
|
|
|
|
* square (source pixel). Some destination pixels will be completely
|
|
|
|
* "graph paper" unit square (source pixel). Some destination pixels
|
|
|
|
* within a single unscaled source pixel.
|
|
|
|
* will be completely within a single unscaled source pixel.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* What we do here is a simple 4 point interpolation scheme:
|
|
|
|
* What we do here is a simple 4 point interpolation scheme:
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Let P00 be the source pixel closest to the destination pixel but with
|
|
|
|
* Let P00 be the source pixel closest to the destination pixel but with
|
|
|
|
* x and y values less than or equal to those of the destination pixel.
|
|
|
|
* x and y values less than or equal to those of the destination pixel.
|
|
|
|
* It is the source pixel immediately to the upper left of the destination
|
|
|
|
* (for simplicity, think of the upper left corner of a pixel defining the
|
|
|
|
|
|
|
|
* x,y location of the pixel, the center would work just as well). So it
|
|
|
|
|
|
|
|
* is the source pixel immediately to the upper left of the destination
|
|
|
|
* pixel. Let P10 be the source pixel one to the right of P00. Let P01
|
|
|
|
* pixel. Let P10 be the source pixel one to the right of P00. Let P01
|
|
|
|
* be one down from P00. And let P11 be one down and one to the right
|
|
|
|
* be one down from P00. And let P11 be one down and one to the right
|
|
|
|
* of P00. They form a 2x2 square we will interpolate inside of.
|
|
|
|
* of P00. They form a 2x2 square we will interpolate inside of.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Let V00, V10, V01, and V11 be the color values of those 4 source
|
|
|
|
* Let V00, V10, V01, and V11 be the color values of those 4 source
|
|
|
|
* pixels. Let dx be the distance along x the destination pixel is from
|
|
|
|
* pixels. Let dx be the displacement along x the destination pixel is
|
|
|
|
* P00. Note: 0 <= dx < 1. Similarly let dy be the distance along y.
|
|
|
|
* from P00. Note: 0 <= dx < 1 by definition of P00. Similarly let
|
|
|
|
* The weighted average is:
|
|
|
|
* dy be the displacement along y. The weighted average for the
|
|
|
|
|
|
|
|
* interpolation is:
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Vave = V00 * (1 - dx) * (1 - dy)
|
|
|
|
* V_ave = V00 * (1 - dx) * (1 - dy)
|
|
|
|
* + V10 * dx * (1 - dy)
|
|
|
|
* + V10 * dx * (1 - dy)
|
|
|
|
* + V01 * (1 - dx) * dy
|
|
|
|
* + V01 * (1 - dx) * dy
|
|
|
|
* + V11 * dx * dy
|
|
|
|
* + V11 * dx * dy
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Note that the weights (1-dx)*(1-dy) + dx(1-dy) + (1-dx)*dy + dx*dy
|
|
|
|
* Note that the weights (1-dx)*(1-dy) + dx*(1-dy) + (1-dx)*dy + dx*dy
|
|
|
|
* automatically add up to 1. It is also nice that all the weights
|
|
|
|
* automatically add up to 1. It is also nice that all the weights are
|
|
|
|
* are positive. The above formula can be motivated by doing two 1D
|
|
|
|
* positive (unsigned char stays unsigned char). The above formula can
|
|
|
|
* interpolations along x:
|
|
|
|
* be motivated by doing two 1D interpolations along x:
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* VA = V00 * (1 - dx) + V10 * dx
|
|
|
|
* VA = V00 * (1 - dx) + V10 * dx
|
|
|
|
* VB = V01 * (1 - dx) + V11 * dx
|
|
|
|
* VB = V01 * (1 - dx) + V11 * dx
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* and then interpolating VA and VB along y:
|
|
|
|
* and then interpolating VA and VB along y:
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Vave = VA * (1 - dy) + VB * dy
|
|
|
|
* V_ave = VA * (1 - dy) + VB * dy
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* VA
|
|
|
|
* VA
|
|
|
|
* v |<-dx->|
|
|
|
|
* v |<-dx->|
|
|
|
@ -4729,15 +4770,20 @@ static void hint_updates(void) {
|
|
|
|
* -- | o...|... "o" denotes the position of the desired
|
|
|
|
* -- | o...|... "o" denotes the position of the desired
|
|
|
|
* ^ | . | . destination pixel relative to the P00
|
|
|
|
* ^ | . | . destination pixel relative to the P00
|
|
|
|
* | . | . source pixel.
|
|
|
|
* | . | . source pixel.
|
|
|
|
* V10 ------ V11 .
|
|
|
|
* V10 ----.- V11 .
|
|
|
|
* ........
|
|
|
|
* ........
|
|
|
|
|
|
|
|
* |
|
|
|
|
* VB
|
|
|
|
* VB
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Of course R, G, B averages are done separately. This gives reasonable
|
|
|
|
* Of course R, G, B averages are done separately as in the shrinking
|
|
|
|
* results. I believe this is called bilinear scaling.
|
|
|
|
* case. This gives reasonable results, and the implementation for
|
|
|
|
|
|
|
|
* shrinking can simply be used with different choices for weights for
|
|
|
|
|
|
|
|
* the loop over the 4 pixels.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define FPTYPE double
|
|
|
|
|
|
|
|
|
|
|
|
static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) {
|
|
|
|
static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) {
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Notation:
|
|
|
|
* Notation:
|
|
|
@ -4753,23 +4799,22 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) {
|
|
|
|
int i, j, i1, i2, j1, j2; /* indices for scaled fb (dest) */
|
|
|
|
int i, j, i1, i2, j1, j2; /* indices for scaled fb (dest) */
|
|
|
|
int I, J, I1, I2, J1, J2; /* indices for main fb (source) */
|
|
|
|
int I, J, I1, I2, J1, J2; /* indices for main fb (source) */
|
|
|
|
|
|
|
|
|
|
|
|
double w, wx, wy, wtot; /* pixel weights */
|
|
|
|
FPTYPE w, wx, wy, wtot; /* pixel weights */
|
|
|
|
|
|
|
|
|
|
|
|
double x1, y1, x2, y2; /* x-y coords for destination pixels edges */
|
|
|
|
FPTYPE x1, y1, x2, y2; /* x-y coords for destination pixels edges */
|
|
|
|
double dx, dy; /* size of destination pixel */
|
|
|
|
FPTYPE dx, dy; /* size of destination pixel */
|
|
|
|
|
|
|
|
|
|
|
|
double ddx, ddy; /* for interpolation expansion */
|
|
|
|
FPTYPE ddx, ddy; /* for interpolation expansion */
|
|
|
|
|
|
|
|
|
|
|
|
char *src, *dest; /* pointers to the two framebuffers */
|
|
|
|
char *src, *dest; /* pointers to the two framebuffers */
|
|
|
|
|
|
|
|
|
|
|
|
double pixave[4]; /* for averaging pixel values */
|
|
|
|
FPTYPE pixave[4]; /* for averaging pixel values */
|
|
|
|
|
|
|
|
|
|
|
|
unsigned char uc; /* tmp pixel data holders */
|
|
|
|
|
|
|
|
unsigned short us;
|
|
|
|
unsigned short us;
|
|
|
|
|
|
|
|
|
|
|
|
int shrink; /* whether shrinking or expanding */
|
|
|
|
int shrink; /* whether shrinking or expanding */
|
|
|
|
|
|
|
|
static int constant_weights = -1;
|
|
|
|
|
|
|
|
|
|
|
|
int pseudocolor = 0; /* true if PseudoColor... */
|
|
|
|
|
|
|
|
if (scale_fac <= 1.0) {
|
|
|
|
if (scale_fac <= 1.0) {
|
|
|
|
shrink = 1;
|
|
|
|
shrink = 1;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@ -4777,7 +4822,10 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (! screen->rfbServerFormat.trueColour) {
|
|
|
|
if (! screen->rfbServerFormat.trueColour) {
|
|
|
|
pseudocolor = 1;
|
|
|
|
/*
|
|
|
|
|
|
|
|
* PseudoColor colormap... blending leads to random colors.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
scaling_noblend = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Bpp = bpp/8; /* Bytes per pixel */
|
|
|
|
Bpp = bpp/8; /* Bytes per pixel */
|
|
|
@ -4793,8 +4841,8 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) {
|
|
|
|
* both are > 1 (e.g. 1.333 for -scale 3/4)
|
|
|
|
* both are > 1 (e.g. 1.333 for -scale 3/4)
|
|
|
|
* they should also be equal but we don't assume it.
|
|
|
|
* they should also be equal but we don't assume it.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
dx = (double) Nx / nx;
|
|
|
|
dx = (FPTYPE) Nx / nx;
|
|
|
|
dy = (double) Ny / ny;
|
|
|
|
dy = (FPTYPE) Ny / ny;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* find the extent of the change the input rectangle induces in
|
|
|
|
* find the extent of the change the input rectangle induces in
|
|
|
@ -4807,11 +4855,11 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) {
|
|
|
|
/* Right edges: find smallest i such that (i+1) * dx >= X2+1 */
|
|
|
|
/* Right edges: find smallest i such that (i+1) * dx >= X2+1 */
|
|
|
|
i2 = CEIL( (X2+1)/dx ) - 1;
|
|
|
|
i2 = CEIL( (X2+1)/dx ) - 1;
|
|
|
|
|
|
|
|
|
|
|
|
/* to be safe, correct any overflows: */
|
|
|
|
/* To be safe, correct any overflows: */
|
|
|
|
i1 = nfix(i1, nx);
|
|
|
|
i1 = nfix(i1, nx);
|
|
|
|
i2 = nfix(i2, nx) + 1; /* add 1 to make a rectangle upper boundary */
|
|
|
|
i2 = nfix(i2, nx) + 1; /* add 1 to make a rectangle upper boundary */
|
|
|
|
|
|
|
|
|
|
|
|
/* repeat above for y direction: */
|
|
|
|
/* Repeat above for y direction: */
|
|
|
|
j1 = FLOOR(Y1/dy);
|
|
|
|
j1 = FLOOR(Y1/dy);
|
|
|
|
j2 = CEIL( (Y2+1)/dy ) - 1;
|
|
|
|
j2 = CEIL( (Y2+1)/dy ) - 1;
|
|
|
|
|
|
|
|
|
|
|
@ -4819,21 +4867,55 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) {
|
|
|
|
j2 = nfix(j2, ny) + 1;
|
|
|
|
j2 = nfix(j2, ny) + 1;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* loop over destination pixels in scaled fb:
|
|
|
|
* There is some speedup if the pixel weights are constant, so
|
|
|
|
|
|
|
|
* let's special case these.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* If scale = 1/n and n divides Nx and Ny, the pixel weights
|
|
|
|
|
|
|
|
* are constant.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (constant_weights < 0) {
|
|
|
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
constant_weights = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 2; i<=128; i++) {
|
|
|
|
|
|
|
|
double test = ((double) 1)/ i;
|
|
|
|
|
|
|
|
double diff, eps = 1.0e-9;
|
|
|
|
|
|
|
|
diff = scale_fac - test;
|
|
|
|
|
|
|
|
if (-eps < diff && diff < eps) {
|
|
|
|
|
|
|
|
n = i;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n != 0) {
|
|
|
|
|
|
|
|
if (! scaling_noblend && Nx % n == 0 && Ny % n == 0) {
|
|
|
|
|
|
|
|
rfbLog("scale_and_mark_rect: using constant "
|
|
|
|
|
|
|
|
"pixel weight speedup for 1/%d\n", n);
|
|
|
|
|
|
|
|
constant_weights = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set these all to 1.0 to begin with */
|
|
|
|
|
|
|
|
wx = 1.0;
|
|
|
|
|
|
|
|
wy = 1.0;
|
|
|
|
|
|
|
|
w = 1.0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* Loop over destination pixels in scaled fb:
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
for (j=j1; j<j2; j++) {
|
|
|
|
for (j=j1; j<j2; j++) {
|
|
|
|
y1 = j * dy; /* top edge */
|
|
|
|
y1 = j * dy; /* top edge */
|
|
|
|
y2 = y1 + dy; /* bottom edge */
|
|
|
|
y2 = y1 + dy; /* bottom edge */
|
|
|
|
|
|
|
|
|
|
|
|
/* find main fb indices covered by this dest pixel: */
|
|
|
|
/* Find main fb indices covered by this dest pixel: */
|
|
|
|
J1 = (int) FLOOR(y1);
|
|
|
|
J1 = (int) FLOOR(y1);
|
|
|
|
J2 = (int) CEIL(y2) - 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
J1 = nfix(J1, Ny);
|
|
|
|
J1 = nfix(J1, Ny);
|
|
|
|
J2 = nfix(J2, Ny);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!shrink) {
|
|
|
|
if (shrink) {
|
|
|
|
|
|
|
|
J2 = (int) CEIL(y2) - 1;
|
|
|
|
|
|
|
|
J2 = nfix(J2, Ny);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
J2 = J1 + 1; /* simple interpolation */
|
|
|
|
J2 = J1 + 1; /* simple interpolation */
|
|
|
|
|
|
|
|
ddy = y1 - J1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* destination char* pointer: */
|
|
|
|
/* destination char* pointer: */
|
|
|
@ -4843,41 +4925,45 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) {
|
|
|
|
x1 = i * dx; /* left edge */
|
|
|
|
x1 = i * dx; /* left edge */
|
|
|
|
x2 = x1 + dx; /* right edge */
|
|
|
|
x2 = x1 + dx; /* right edge */
|
|
|
|
|
|
|
|
|
|
|
|
/* find main fb indices covered by this dest pixel: */
|
|
|
|
/* Find main fb indices covered by this dest pixel: */
|
|
|
|
I1 = (int) FLOOR(x1);
|
|
|
|
I1 = (int) FLOOR(x1);
|
|
|
|
I2 = (int) CEIL(x2) - 1;
|
|
|
|
if (I1 >= Nx) I1 = Nx - 1;
|
|
|
|
|
|
|
|
|
|
|
|
I1 = nfix(I1, Nx);
|
|
|
|
|
|
|
|
I2 = nfix(I2, Nx);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!shrink) {
|
|
|
|
if (shrink) {
|
|
|
|
|
|
|
|
I2 = (int) CEIL(x2) - 1;
|
|
|
|
|
|
|
|
if (I2 >= Nx) I2 = Nx - 1;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
I2 = I1 + 1; /* simple interpolation */
|
|
|
|
I2 = I1 + 1; /* simple interpolation */
|
|
|
|
|
|
|
|
ddx = x1 - I1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* zero out accumulators for next pixel average: */
|
|
|
|
/* Zero out accumulators for next pixel average: */
|
|
|
|
for (b=0; b<4; b++) {
|
|
|
|
for (b=0; b<4; b++) {
|
|
|
|
pixave[b] = 0.0; /* for RGB weighted sums */
|
|
|
|
pixave[b] = 0.0; /* for RGB weighted sums */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* wtot is for accumulating the total weight.
|
|
|
|
* wtot is for accumulating the total weight.
|
|
|
|
* It should always be 1/(scale_fac * scale_fac),
|
|
|
|
* It should always sum to 1/(scale_fac * scale_fac).
|
|
|
|
* but we don't assume that.
|
|
|
|
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
wtot = 0.0;
|
|
|
|
wtot = 0.0;
|
|
|
|
|
|
|
|
|
|
|
|
if (!shrink) {
|
|
|
|
|
|
|
|
/* interpolation distances, see diagram above */
|
|
|
|
|
|
|
|
ddx = x1 - I1;
|
|
|
|
|
|
|
|
ddy = y1 - J1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* loop over source pixels covered by this dest pixel:
|
|
|
|
* Loop over source pixels covered by this dest pixel.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* These "extra" loops over "J" and "I" make
|
|
|
|
|
|
|
|
* the cache/cacheline performance unclear.
|
|
|
|
|
|
|
|
* For example, will the data brought in from
|
|
|
|
|
|
|
|
* src for j, i, and J=0 still be in the cache
|
|
|
|
|
|
|
|
* after the J > 0 data have been accessed and
|
|
|
|
|
|
|
|
* we are at j, i+1, J=0? The stride in J is
|
|
|
|
|
|
|
|
* main_bytes_per_line, and so ~4 KB.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
for (J=J1; J<=J2; J++) {
|
|
|
|
for (J=J1; J<=J2; J++) {
|
|
|
|
/* see comments for I, x1, x2, etc. below */
|
|
|
|
/* see comments for I, x1, x2, etc. below */
|
|
|
|
if (pseudocolor) {
|
|
|
|
if (constant_weights) {
|
|
|
|
|
|
|
|
;
|
|
|
|
|
|
|
|
} else if (scaling_noblend) {
|
|
|
|
if (J != J1) {
|
|
|
|
if (J != J1) {
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -4886,7 +4972,7 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) {
|
|
|
|
/* interpolation scheme: */
|
|
|
|
/* interpolation scheme: */
|
|
|
|
} else if (!shrink) {
|
|
|
|
} else if (!shrink) {
|
|
|
|
if (J >= Ny) {
|
|
|
|
if (J >= Ny) {
|
|
|
|
continue; /* off edge */
|
|
|
|
continue;
|
|
|
|
} else if (J == J1) {
|
|
|
|
} else if (J == J1) {
|
|
|
|
wy = 1.0 - ddy;
|
|
|
|
wy = 1.0 - ddy;
|
|
|
|
} else if (J != J1) {
|
|
|
|
} else if (J != J1) {
|
|
|
@ -4908,11 +4994,15 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) {
|
|
|
|
|
|
|
|
|
|
|
|
/* Work out the weight: */
|
|
|
|
/* Work out the weight: */
|
|
|
|
|
|
|
|
|
|
|
|
if (pseudocolor) {
|
|
|
|
if (constant_weights) {
|
|
|
|
|
|
|
|
;
|
|
|
|
|
|
|
|
} else if (scaling_noblend) {
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* ugh, colormap is bad news, to
|
|
|
|
* Ugh, PseudoColor colormap is
|
|
|
|
* avoid random colors just take
|
|
|
|
* bad news, to avoid random
|
|
|
|
* the first pixel.
|
|
|
|
* colors just take the first
|
|
|
|
|
|
|
|
* pixel. Or user may have
|
|
|
|
|
|
|
|
* specified :nb to fraction.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
if (I != I1) {
|
|
|
|
if (I != I1) {
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
@ -4958,22 +5048,22 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* we average the unsigned char value
|
|
|
|
* We average the unsigned char value
|
|
|
|
* instead of char value: otherwise
|
|
|
|
* instead of char value: otherwise
|
|
|
|
* the minimum (char 0) is right next
|
|
|
|
* the minimum (char 0) is right next
|
|
|
|
* to the maximum (char -1)! This way
|
|
|
|
* to the maximum (char -1)! This way
|
|
|
|
* they are spread between 0 and 255.
|
|
|
|
* they are spread between 0 and 255.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
if (Bpp == 4 || Bpp == 1) {
|
|
|
|
if (Bpp != 2) {
|
|
|
|
|
|
|
|
|
|
|
|
for (b=0; b<Bpp; b++) {
|
|
|
|
for (b=0; b<Bpp; b++) {
|
|
|
|
uc = (unsigned char) *(src + b);
|
|
|
|
pixave[b] += w *
|
|
|
|
pixave[b] += w * uc;
|
|
|
|
((unsigned char) *(src+b));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (Bpp == 2) {
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* trickier with green split over
|
|
|
|
* 16bpp: trickier with green
|
|
|
|
* two bytes, so we use the masks:
|
|
|
|
* split over two bytes, so we
|
|
|
|
|
|
|
|
* use the masks:
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
us = *( (unsigned short *) src );
|
|
|
|
us = *( (unsigned short *) src );
|
|
|
|
pixave[0] += w * (us & main_red_mask);
|
|
|
|
pixave[0] += w * (us & main_red_mask);
|
|
|
@ -4987,12 +5077,12 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) {
|
|
|
|
wtot = 1.0/wtot; /* normalization factor */
|
|
|
|
wtot = 1.0/wtot; /* normalization factor */
|
|
|
|
|
|
|
|
|
|
|
|
/* place weighted average pixel in the scaled fb: */
|
|
|
|
/* place weighted average pixel in the scaled fb: */
|
|
|
|
if (Bpp == 4 || Bpp == 1) {
|
|
|
|
if (Bpp != 2) {
|
|
|
|
for (b=0; b<Bpp; b++) {
|
|
|
|
for (b=0; b<Bpp; b++) {
|
|
|
|
*(dest + b) = (char) (wtot * pixave[b]);
|
|
|
|
*(dest + b) = (char) (wtot * pixave[b]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (Bpp == 2) {
|
|
|
|
} else {
|
|
|
|
/* 16bpp/565 */
|
|
|
|
/* 16bpp / 565 case: */
|
|
|
|
pixave[0] *= wtot;
|
|
|
|
pixave[0] *= wtot;
|
|
|
|
pixave[1] *= wtot;
|
|
|
|
pixave[1] *= wtot;
|
|
|
|
pixave[2] *= wtot;
|
|
|
|
pixave[2] *= wtot;
|
|
|
@ -6523,7 +6613,9 @@ static void print_help(void) {
|
|
|
|
" and response may be slower. If \"fraction\" contains\n"
|
|
|
|
" and response may be slower. If \"fraction\" contains\n"
|
|
|
|
" a decimal point \".\" it is taken as a floating point\n"
|
|
|
|
" a decimal point \".\" it is taken as a floating point\n"
|
|
|
|
" number, alternatively the notation \"m/n\" may be used\n"
|
|
|
|
" number, alternatively the notation \"m/n\" may be used\n"
|
|
|
|
" to denote fractions, e.g. -scale 2/3\n"
|
|
|
|
" to denote fractions, e.g. -scale 2/3. If you just want\n"
|
|
|
|
|
|
|
|
" a quick, rough scaling without blending, append \":nb\"\n"
|
|
|
|
|
|
|
|
" to \"fraction\" (e.g. -scale 1/3:nb)\n"
|
|
|
|
"-visual n Experimental option: probably does not do what you\n"
|
|
|
|
"-visual n Experimental option: probably does not do what you\n"
|
|
|
|
" think. It simply *forces* the visual used for the\n"
|
|
|
|
" think. It simply *forces* the visual used for the\n"
|
|
|
|
" framebuffer; this may be a bad thing... It is useful for\n"
|
|
|
|
" framebuffer; this may be a bad thing... It is useful for\n"
|
|
|
@ -7010,8 +7102,17 @@ int main(int argc, char* argv[]) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (!strcmp(arg, "-scale")) {
|
|
|
|
} else if (!strcmp(arg, "-scale")) {
|
|
|
|
int m, n;
|
|
|
|
int m, n;
|
|
|
|
|
|
|
|
char *p;
|
|
|
|
float f;
|
|
|
|
float f;
|
|
|
|
if (strchr(argv[++i], '.') != NULL) {
|
|
|
|
i++;
|
|
|
|
|
|
|
|
if ( (p = strchr(argv[i], ':')) != NULL) {
|
|
|
|
|
|
|
|
/* options */
|
|
|
|
|
|
|
|
if (strstr(p+1, "nb") != NULL) {
|
|
|
|
|
|
|
|
scaling_noblend = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strchr(argv[i], '.') != NULL) {
|
|
|
|
if (sscanf(argv[i], "%f", &f) != 1) {
|
|
|
|
if (sscanf(argv[i], "%f", &f) != 1) {
|
|
|
|
fprintf(stderr, "bad -scale arg: %s\n",
|
|
|
|
fprintf(stderr, "bad -scale arg: %s\n",
|
|
|
|
argv[i]);
|
|
|
|
argv[i]);
|
|
|
@ -7024,7 +7125,7 @@ int main(int argc, char* argv[]) {
|
|
|
|
argv[i]);
|
|
|
|
argv[i]);
|
|
|
|
exit(1);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
scale_fac = (double) m / n;
|
|
|
|
scale_fac = ((double) m)/ n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (scale_fac == 1.0) {
|
|
|
|
if (scale_fac == 1.0) {
|
|
|
|
fprintf(stderr, "scaling disabled for factor "
|
|
|
|
fprintf(stderr, "scaling disabled for factor "
|
|
|
|