448 lines
12 KiB
C
448 lines
12 KiB
C
#include "common.h"
|
|
#include "rotate.h"
|
|
#include "blend.h"
|
|
|
|
/*\ Linear interpolation functions \*/
|
|
/*\ Between two values \*/
|
|
#define INTERP(v1, v2, f) \
|
|
(((v1) << _ROTATE_PREC) + (((v2) - (v1)) * (f)))
|
|
|
|
/*\ Between two colour bytes \*/
|
|
#define INTERP_VAL1(x_VAL, dest, l, r, x) \
|
|
x_VAL(dest) = (INTERP(x_VAL(l), x_VAL(r), (x)) >> _ROTATE_PREC)
|
|
|
|
/*\ Alpha channel: between two values and two zeroes \*/
|
|
#define INTERP_VAL1_A0(dest, v1, v2, f1, f2) \
|
|
A_VAL(dest) = ((INTERP(A_VAL(v1), A_VAL(v2), (f1)) * \
|
|
(f2)) >> (2 * _ROTATE_PREC))
|
|
|
|
/*\ Between four values \*/
|
|
#define INTERP_VAL2(x_VAL, dest, ul, ur, ll, lr, x, y) \
|
|
x_VAL(dest) = (INTERP(INTERP(x_VAL(ul), x_VAL(ur), (x)), \
|
|
INTERP(x_VAL(ll), x_VAL(lr), (x)), \
|
|
(y)) >> (2 * _ROTATE_PREC))
|
|
|
|
/*\ Functions used in rotation routines.
|
|
|*| The do { } while(0) construction is to make it one statement.
|
|
\*/
|
|
/*\ Between four colours \*/
|
|
#define INTERP_ARGB(dest, src, sow, x, y) do { \
|
|
INTERP_VAL2(R_VAL, (dest), (src), (src) + 1, (src) + (sow), (src) + (sow) + 1, (x) & _ROTATE_PREC_BITS, (y) & _ROTATE_PREC_BITS); \
|
|
INTERP_VAL2(G_VAL, (dest), (src), (src) + 1, (src) + (sow), (src) + (sow) + 1, (x) & _ROTATE_PREC_BITS, (y) & _ROTATE_PREC_BITS); \
|
|
INTERP_VAL2(B_VAL, (dest), (src), (src) + 1, (src) + (sow), (src) + (sow) + 1, (x) & _ROTATE_PREC_BITS, (y) & _ROTATE_PREC_BITS); \
|
|
INTERP_VAL2(A_VAL, (dest), (src), (src) + 1, (src) + (sow), (src) + (sow) + 1, (x) & _ROTATE_PREC_BITS, (y) & _ROTATE_PREC_BITS); \
|
|
} while (0)
|
|
|
|
/*\ Between two colours, alpha between two values and zeroes \*/
|
|
#define INTERP_RGB_A0(dest, v1, v2, f, f2) do { \
|
|
INTERP_VAL1(R_VAL, (dest), (v1), (v2), (f) & _ROTATE_PREC_BITS); \
|
|
INTERP_VAL1(G_VAL, (dest), (v1), (v2), (f) & _ROTATE_PREC_BITS); \
|
|
INTERP_VAL1(B_VAL, (dest), (v1), (v2), (f) & _ROTATE_PREC_BITS); \
|
|
INTERP_VAL1_A0(dest, (v1), (v2), (f) & _ROTATE_PREC_BITS, (f2) & _ROTATE_PREC_BITS); \
|
|
} while (0)
|
|
|
|
/*\ One colour, alpha between one value and three zeroes \*/
|
|
#define INTERP_A000(dest, v, f1, f2) do { \
|
|
*(dest) = *(v); \
|
|
A_VAL(dest) = (A_VAL(dest) * \
|
|
((f1) & _ROTATE_PREC_BITS) * ((f2) & _ROTATE_PREC_BITS)) >> (2 * _ROTATE_PREC); \
|
|
} while (0)
|
|
|
|
/*\ Rotate by pixel sampling only, target inside source \*/
|
|
static void
|
|
__imlib_RotateSampleInside(DATA32 *src, DATA32 *dest, int sow, int dow,
|
|
int dw, int dh, int x, int y,
|
|
int dxh, int dyh, int dxv, int dyv)
|
|
{
|
|
int i;
|
|
|
|
if ((dw < 1) || (dh < 1)) return;
|
|
|
|
while (1) {
|
|
i = dw - 1;
|
|
do {
|
|
*dest = src[(x >> _ROTATE_PREC) + ((y >> _ROTATE_PREC) * sow)];
|
|
/*\ RIGHT; \*/
|
|
x += dxh;
|
|
y += dyh;
|
|
dest++;
|
|
} while (--i >= 0);
|
|
if (--dh <= 0) break;
|
|
/*\ DOWN/LEFT; \*/
|
|
x += dxv - dw * dxh;
|
|
y += dyv - dw * dyh;
|
|
dest += (dow - dw);
|
|
}
|
|
}
|
|
|
|
/*\ Same as last function, but with antialiasing \*/
|
|
static void
|
|
__imlib_RotateAAInside(DATA32 *src, DATA32 *dest, int sow, int dow,
|
|
int dw, int dh, int x, int y,
|
|
int dxh, int dyh, int dxv, int dyv)
|
|
{
|
|
int i;
|
|
|
|
if ((dw < 1) || (dh < 1)) return;
|
|
|
|
while (1) {
|
|
i = dw - 1;
|
|
do {
|
|
DATA32 *src_x_y = (src + (x >> _ROTATE_PREC) +
|
|
((y >> _ROTATE_PREC) * sow));
|
|
INTERP_ARGB(dest, src_x_y, sow, x, y);
|
|
/*\ RIGHT; \*/
|
|
x += dxh;
|
|
y += dyh;
|
|
dest++;
|
|
} while (--i >= 0);
|
|
if (--dh <= 0) break;
|
|
/*\ DOWN/LEFT; \*/
|
|
x += dxv - dw * dxh;
|
|
y += dyv - dw * dyh;
|
|
dest += (dow - dw);
|
|
}
|
|
}
|
|
|
|
/*\ NOTE: To check if v is in [b .. t) ((v >= b) && (v < t))
|
|
|*| it's quicker to do ((unsigned)(v - b) < (t - b))
|
|
|*| as negative values, cast to unsigned, become large positive
|
|
|*| values, and fall through the compare.
|
|
|*| v in [0 .. t) is a special case: ((unsigned)v < t)
|
|
|*| v in [-t .. 0) is also special, as its the same as ~v in [0 .. t)
|
|
\*/
|
|
static int
|
|
__check_inside_coords(int x, int y, int dxh, int dyh, int dxv, int dyv,
|
|
int dw, int dh, int sw, int sh)
|
|
{
|
|
sw <<= _ROTATE_PREC;
|
|
sh <<= _ROTATE_PREC;
|
|
|
|
if (((unsigned)x >= sw) || ((unsigned)y >= sh))
|
|
return 0;
|
|
x += dxh * dw; y += dyh * dw;
|
|
if (((unsigned)x >= sw) || ((unsigned)y >= sh))
|
|
return 0;
|
|
x += dxv * dh; y += dyv * dh;
|
|
if (((unsigned)x >= sw) || ((unsigned)y >= sh))
|
|
return 0;
|
|
x -= dxh * dw; y -= dyh * dw;
|
|
if (((unsigned)x >= sw) || ((unsigned)y >= sh))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*\ These ones don't need the target to be inside the source \*/
|
|
void
|
|
__imlib_RotateSample(DATA32 *src, DATA32 *dest, int sow, int sw, int sh,
|
|
int dow, int dw, int dh, int x, int y,
|
|
int dxh, int dyh, int dxv, int dyv)
|
|
{
|
|
int i;
|
|
|
|
if ((dw < 1) || (dh < 1)) return;
|
|
|
|
if (__check_inside_coords(x, y, dxh, dyh, dxv, dyv, dw, dh, sw, sh)) {
|
|
__imlib_RotateSampleInside(src, dest, sow, dow, dw, dh, x, y,
|
|
dxh, dyh, dxv, dyv);
|
|
return;
|
|
|
|
}
|
|
|
|
sw <<= _ROTATE_PREC;
|
|
sh <<= _ROTATE_PREC;
|
|
while (1) {
|
|
i = dw - 1;
|
|
do {
|
|
if (((unsigned)x < sw) && ((unsigned)y < sh))
|
|
*dest = src[(x >> _ROTATE_PREC) + ((y >> _ROTATE_PREC) * sow)];
|
|
else *dest = 0;
|
|
/*\ RIGHT; \*/
|
|
x += dxh;
|
|
y += dyh;
|
|
dest++;
|
|
|
|
} while (--i >= 0);
|
|
if (--dh <= 0) break;
|
|
/*\ DOWN/LEFT; \*/
|
|
x += dxv - dw * dxh;
|
|
y += dyv - dw * dyh;
|
|
dest += (dow - dw);
|
|
|
|
}
|
|
}
|
|
|
|
/*\ With antialiasing.
|
|
|*| NB: The function 'sees' a transparent border around the source,
|
|
|*| with colour channels matching the edge, so there is no need to do
|
|
|*| anything special, but remember to account for this when calculating
|
|
|*| the bounding box.
|
|
\*/
|
|
void
|
|
__imlib_RotateAA(DATA32 *src, DATA32 *dest, int sow, int sw, int sh,
|
|
int dow, int dw, int dh, int x, int y,
|
|
int dxh, int dyh, int dxv, int dyv)
|
|
{
|
|
int i;
|
|
|
|
if ((dw < 1) || (dh < 1)) return;
|
|
|
|
if (__check_inside_coords(x, y, dxh, dyh, dxv, dyv, dw, dh, sw-1, sh-1)) {
|
|
__imlib_RotateAAInside(src, dest, sow, dow, dw, dh, x, y,
|
|
dxh, dyh, dxv, dyv);
|
|
return;
|
|
|
|
}
|
|
|
|
sw--; sh--;
|
|
sw <<= _ROTATE_PREC;
|
|
sh <<= _ROTATE_PREC;
|
|
while (1) {
|
|
i = dw - 1;
|
|
do {
|
|
DATA32 *src_x_y = (src + (x >> _ROTATE_PREC) +
|
|
((y >> _ROTATE_PREC) * sow));
|
|
if ((unsigned)x < sw) {
|
|
if ((unsigned)y < sh) {
|
|
/*\ 12
|
|
|*| 34
|
|
\*/
|
|
INTERP_ARGB(dest, src_x_y, sow, x, y);
|
|
} else if ((unsigned)(y - sh) < _ROTATE_PREC_MAX) {
|
|
/*\ 12
|
|
|*| ..
|
|
\*/
|
|
INTERP_RGB_A0(dest, src_x_y, src_x_y + 1, x, ~y);
|
|
} else if ((unsigned)(~y) < _ROTATE_PREC_MAX) {
|
|
/*\ ..
|
|
|*| 34
|
|
\*/
|
|
INTERP_RGB_A0(dest, src_x_y + sow, src_x_y + sow + 1, x, y);
|
|
} else *dest = 0;
|
|
} else if ((unsigned)(x - sw) < (_ROTATE_PREC_MAX)) {
|
|
if ((unsigned)y < sh) {
|
|
/*\ 1.
|
|
|*| 3.
|
|
\*/
|
|
INTERP_RGB_A0(dest, src_x_y, src_x_y + sow, y, ~x);
|
|
} else if ((unsigned)(y - sh) < _ROTATE_PREC_MAX) {
|
|
/*\ 1.
|
|
|*| ..
|
|
\*/
|
|
INTERP_A000(dest, src_x_y, ~x, ~y);
|
|
} else if ((unsigned)(~y) < _ROTATE_PREC_MAX) {
|
|
/*\ ..
|
|
|*| 3.
|
|
\*/
|
|
INTERP_A000(dest, src_x_y + sow, ~x, y);
|
|
} else *dest = 0;
|
|
} else if ((unsigned)(~x) < _ROTATE_PREC_MAX) {
|
|
if ((unsigned)y < sh) {
|
|
/*\ .2
|
|
|*| .4
|
|
\*/
|
|
INTERP_RGB_A0(dest, src_x_y + 1, src_x_y + sow + 1, y, x);
|
|
} else if ((unsigned)(y - sh) < _ROTATE_PREC_MAX) {
|
|
/*\ .2
|
|
|*| ..
|
|
\*/
|
|
INTERP_A000(dest, src_x_y + 1, x, ~y);
|
|
} else if ((unsigned)(~y) < _ROTATE_PREC_MAX) {
|
|
/*\ ..
|
|
|*| .4
|
|
\*/
|
|
INTERP_A000(dest, src_x_y + sow + 1, x, y);
|
|
} else *dest = 0;
|
|
} else *dest = 0;
|
|
/*\ RIGHT; \*/
|
|
x += dxh;
|
|
y += dyh;
|
|
dest++;
|
|
|
|
} while (--i >= 0);
|
|
if (--dh <= 0) break;
|
|
/*\ DOWN/LEFT; \*/
|
|
x += dxv - dw * dxh;
|
|
y += dyv - dw * dyh;
|
|
dest += (dow - dw);
|
|
|
|
}
|
|
}
|
|
|
|
/*\ Should this be in blend.c ?? \*/
|
|
#define LINESIZE 16
|
|
|
|
void
|
|
__imlib_BlendImageToImageSkewed(ImlibImage *im_src, ImlibImage *im_dst,
|
|
char aa, char blend, char merge_alpha,
|
|
int ssx, int ssy, int ssw, int ssh,
|
|
int ddx, int ddy,
|
|
int hsx, int hsy, int vsx, int vsy,
|
|
ImlibColorModifier *cm, ImlibOp op,
|
|
int clx, int cly, int clw, int clh)
|
|
{
|
|
int x, y, dxh, dyh, dxv, dyv, i;
|
|
double xy2;
|
|
DATA32 *data, *src;
|
|
int do_mmx;
|
|
|
|
if ((ssw < 0) || (ssh < 0))
|
|
return;
|
|
|
|
if ((!(im_src->data)) && (im_src->loader) && (im_src->loader->load))
|
|
im_src->loader->load(im_src, NULL, 0, 1);
|
|
if (!im_src->data)
|
|
return;
|
|
if ((!(im_dst->data)) && (im_dst->loader) && (im_src->loader->load))
|
|
im_dst->loader->load(im_dst, NULL, 0, 1);
|
|
if (!im_dst->data)
|
|
return;
|
|
|
|
/*\ Complicated gonio. Works on paper..
|
|
|*| Too bad it doesn't all fit into integer math..
|
|
\*/
|
|
if (vsx | vsy) {
|
|
xy2 = (double)(hsx * vsy - vsx * hsy) / _ROTATE_PREC_MAX;
|
|
if (xy2 == 0.0) return;
|
|
dxh = (double)(ssw * vsy) / xy2;
|
|
dxv = (double)-(ssw * vsx) / xy2;
|
|
dyh = (double)-(ssh * hsy) / xy2;
|
|
dyv = (double)(ssh * hsx) / xy2;
|
|
} else {
|
|
xy2 = (double)(hsx * hsx + hsy * hsy) / _ROTATE_PREC_MAX;
|
|
if (xy2 == 0.0) return;
|
|
dxh = (double)(ssw * hsx) / xy2;
|
|
dyh = (double)-(ssw * hsy) / xy2;
|
|
dxv = -dyh;
|
|
dyv = dxh;
|
|
}
|
|
x = - (ddx * dxh + ddy * dxv);
|
|
y = - (ddx * dyh + ddy * dyv);
|
|
|
|
if (ssx < 0) {
|
|
x += ssx * _ROTATE_PREC_MAX;
|
|
ssw += ssx;
|
|
ssx = 0;
|
|
|
|
}
|
|
if (ssy < 0) {
|
|
y += ssy * _ROTATE_PREC_MAX;
|
|
ssh += ssy;
|
|
ssy = 0;
|
|
|
|
}
|
|
if ((ssw + ssx) > im_src->w) ssw = im_src->w - ssx;
|
|
if ((ssh + ssy) > im_src->h) ssh = im_src->h - ssy;
|
|
|
|
src = im_src->data + ssx + ssy * im_src->w;
|
|
data = malloc(im_dst->w * LINESIZE * sizeof(DATA32));
|
|
if (!data)
|
|
return;
|
|
if (aa) {
|
|
/*\ Account for virtual transparent border \*/
|
|
x += _ROTATE_PREC_MAX;
|
|
y += _ROTATE_PREC_MAX;
|
|
}
|
|
#ifdef DO_MMX_ASM
|
|
do_mmx = __imlib_get_cpuid() & CPUID_MMX;
|
|
#endif
|
|
for (i = 0; i < im_dst->h; i += LINESIZE) {
|
|
int x2, y2, w, h, l, r;
|
|
|
|
h = MIN(LINESIZE, im_dst->h - i);
|
|
|
|
x2 = x + h * dxv;
|
|
y2 = y + h * dyv;
|
|
|
|
w = ssw << _ROTATE_PREC;
|
|
h = ssh << _ROTATE_PREC;
|
|
if (aa) {
|
|
/*\ Account for virtual transparent border \*/
|
|
w += 2 << _ROTATE_PREC;
|
|
h += 2 << _ROTATE_PREC;
|
|
}
|
|
/*\ Pretty similar code \*/
|
|
if (dxh > 0) {
|
|
if (dyh > 0) {
|
|
l = MAX(-MAX(y, y2) / dyh, -MAX(x, x2) / dxh);
|
|
r = MIN((h - MIN(y, y2)) / dyh, (w - MIN(x, x2)) / dxh);
|
|
|
|
} else if (dyh < 0) {
|
|
l = MAX(-MAX(x, x2) / dxh, (h - MIN(y, y2)) / dyh);
|
|
r = MIN(-MAX(y, y2) / dyh, (w - MIN(x, x2)) / dxh);
|
|
|
|
} else {
|
|
l = -MAX(x, x2) / dxh;
|
|
r = (w - MIN(x, x2)) / dxh;
|
|
|
|
}
|
|
} else if (dxh < 0) {
|
|
if (dyh > 0) {
|
|
l = MAX(-MAX(y, y2) / dyh, (w - MIN(x, x2)) / dxh);
|
|
r = MIN(-MAX(x, x2) / dxh, (h - MIN(y, y2)) / dyh);
|
|
|
|
} else if (dyh < 0) {
|
|
l = MAX((h - MIN(y, y2)) / dyh, (w - MIN(x, x2)) / dxh);
|
|
r = MIN(-MAX(y, y2) / dyh, -MAX(x, x2) / dxh);
|
|
|
|
} else {
|
|
l = (w - MIN(x, x2)) / dxh;
|
|
r = -MAX(x, x2) / dxh;
|
|
|
|
}
|
|
|
|
} else {
|
|
if (dyh > 0) {
|
|
l = -MAX(y, y2) / dyh;
|
|
r = (h - MIN(y, y2)) / dyh;
|
|
|
|
} else if (dyh < 0) {
|
|
l = (h - MIN(y, y2)) / dyh;
|
|
r = -MAX(y, y2) / dyh;
|
|
|
|
} else {
|
|
l = 0;
|
|
r = 0;
|
|
|
|
}
|
|
|
|
}
|
|
l--; r += 2; /*\ Be paranoid about roundoff errors \*/
|
|
if (l < 0) l = 0;
|
|
if (r > im_dst->w) r = im_dst->w;
|
|
if (r <= l) {
|
|
x = x2; y = y2;
|
|
continue;
|
|
|
|
}
|
|
|
|
w = r - l;
|
|
h = MIN(LINESIZE, im_dst->h - i);
|
|
x += l * dxh;
|
|
y += l * dyh;
|
|
if (aa) {
|
|
x -= _ROTATE_PREC_MAX; y -= _ROTATE_PREC_MAX;
|
|
#ifdef DO_MMX_ASM
|
|
if (do_mmx)
|
|
__imlib_mmx_RotateAA(src, data, im_src->w, ssw, ssh, w, w, h,
|
|
x, y, dxh, dyh, dxv, dyv);
|
|
else
|
|
#endif
|
|
__imlib_RotateAA(src, data, im_src->w, ssw, ssh, w, w, h,
|
|
x, y, dxh, dyh, dxv, dyv);
|
|
|
|
} else {
|
|
__imlib_RotateSample(src, data, im_src->w, ssw, ssh, w, w, h,
|
|
x, y, dxh, dyh, dxv, dyv);
|
|
|
|
}
|
|
__imlib_BlendRGBAToData(data, w, h, im_dst->data,
|
|
im_dst->w, im_dst->h, 0, 0, l, i, w, h,
|
|
blend, merge_alpha, cm, op, 0);
|
|
x = x2; y = y2;
|
|
|
|
}
|
|
free(data);
|
|
}
|