mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 22:02:49 -06:00
568 lines
19 KiB
C++
568 lines
19 KiB
C++
#include "GeomDraw.h"
|
|
|
|
namespace Upp {
|
|
|
|
//#define BILINEAR_TIMING
|
|
|
|
#define RGBA_SET(d, s) *(dword *)&(d) = *(const dword *)&(s)
|
|
|
|
#define MINMAX0(x, hi) ((x) < 0 ? 0 : (x) > (hi) ? (hi) : (int)(x))
|
|
|
|
struct SideSegment
|
|
{
|
|
Point64 src16;
|
|
Size64 src16add;
|
|
int64 dest16;
|
|
int64 dest16add;
|
|
|
|
void operator -- () { src16 -= src16add; dest16 -= dest16add; }
|
|
void operator ++ () { src16 += src16add; dest16 += dest16add; }
|
|
void operator += (int i) { src16 += i * src16add; dest16 += i * dest16add; }
|
|
};
|
|
|
|
static SideSegment CalcSideSegment(Point d1, Point d2, Point s1, Point s2, int y1)
|
|
{
|
|
if(d2.y < d1.y) {
|
|
Swap(d1, d2);
|
|
Swap(s1, s2);
|
|
}
|
|
SideSegment sgm;
|
|
int ht = max<int>(d2.y - d1.y, 1);
|
|
sgm.dest16 = ((int64)d1.x << 16) + 0x8000;
|
|
sgm.dest16add = (int64)(d2.x - d1.x) * 0x10000 / ht;
|
|
sgm.src16 = ((Point64)s1 << 16) + (int64)0x8000;
|
|
sgm.src16add = (Size64)(s2 - s1) * (int64)0x10000 / (int64)ht;
|
|
if((y1 -= d1.y) > 0)
|
|
sgm += y1;
|
|
return sgm;
|
|
}
|
|
|
|
#ifdef BILINEAR_TIMING
|
|
int64 linsec_pixels = 0;
|
|
int linsec_msecs = 0;
|
|
EXITBLOCK {
|
|
RLOG("LinearSection: #pixels = " << linsec_pixels << ", " << linsec_msecs << " msecs, "
|
|
<< linsec_pixels / max<int64>(1000 * linsec_msecs, 1) << " MPixels/s");
|
|
}
|
|
#endif
|
|
|
|
typedef void (*TransformScanProc)(RGBA *dest, int count, Point64 sl16, Size64 sd16,
|
|
const RGBA *src, Size src_size, int enterclip, int leaveclip);
|
|
|
|
static void TransformScanOpaqueNearest(RGBA *dest, int count, Point64 sl16, Size64 sd16,
|
|
const RGBA *src, Size src_size, int enterclip, int leaveclip)
|
|
{
|
|
int xmax = (src_size.cx << 16) - 0x10001;
|
|
int ymax = (src_size.cy << 16) - 0x10001;
|
|
for(int i = enterclip; --i >= 0; dest++, sl16 += sd16)
|
|
RGBA_SET(*dest, src[(MINMAX0(sl16.x, xmax) >> 16)
|
|
+ ((int)MINMAX0(sl16.y, ymax) >> 16) * src_size.cx]);
|
|
if(enterclip < count) {
|
|
Point sl16i(sl16);
|
|
Size sd16i(sd16);
|
|
for(int i = leaveclip - enterclip; --i >= 0; dest++, sl16i += sd16i) {
|
|
ASSERT(sl16i.x >= 0 && sl16i.x <= xmax && sl16i.y >= 0 && sl16i.y <= ymax);
|
|
RGBA_SET(*dest, src[(sl16i.x >> 16) + (sl16i.y >> 16) * src_size.cx]);
|
|
}
|
|
sl16 = sl16i;
|
|
for(int i = count - leaveclip; --i >= 0; dest++, sl16 += sd16)
|
|
RGBA_SET(*dest, src[(MINMAX0(sl16.x, xmax) >> 16)
|
|
+ ((int)MINMAX0(sl16.y, ymax) >> 16) * src_size.cx]);
|
|
}
|
|
}
|
|
|
|
static void TransformScanAlphaNearest(RGBA *dest, int count, Point64 sl16, Size64 sd16,
|
|
const RGBA *src, Size src_size, int enterclip, int leaveclip)
|
|
{
|
|
int xmax = (src_size.cx << 16) - 0x10001;
|
|
int ymax = (src_size.cy << 16) - 0x10001;
|
|
for(int i = enterclip; --i >= 0; dest++, sl16 += sd16) {
|
|
RGBA pixel = src[(MINMAX0(sl16.x, xmax) >> 16)
|
|
+ ((int)MINMAX0(sl16.y, ymax) >> 16) * src_size.cx];
|
|
if(pixel.a) {
|
|
if(pixel.a > dest->a) dest->a = pixel.a;
|
|
int coef = pixel.a * 256 / dest->a;
|
|
dest->r += ((pixel.r - dest->r) * coef) >> 8;
|
|
dest->g += ((pixel.g - dest->g) * coef) >> 8;
|
|
dest->b += ((pixel.b - dest->b) * coef) >> 8;
|
|
}
|
|
}
|
|
if(enterclip < count) {
|
|
Point sl16i(sl16);
|
|
Size sd16i(sd16);
|
|
for(int i = leaveclip - enterclip; --i >= 0; dest++, sl16i += sd16i) {
|
|
ASSERT(sl16i.x >= 0 && sl16i.x <= xmax && sl16i.y >= 0 && sl16i.y <= ymax);
|
|
RGBA pixel = src[(sl16i.x >> 16) + (sl16i.y >> 16) * src_size.cx];
|
|
if(pixel.a) {
|
|
if(pixel.a > dest->a) dest->a = pixel.a;
|
|
int coef = pixel.a * 256 / dest->a;
|
|
dest->r += ((pixel.r - dest->r) * coef) >> 8;
|
|
dest->g += ((pixel.g - dest->g) * coef) >> 8;
|
|
dest->b += ((pixel.b - dest->b) * coef) >> 8;
|
|
}
|
|
}
|
|
sl16 = sl16i;
|
|
for(int i = count - leaveclip; --i >= 0; dest++, sl16 += sd16) {
|
|
RGBA pixel = src[(MINMAX0(sl16.x, xmax) >> 16)
|
|
+ ((int)MINMAX0(sl16.y, ymax) >> 16) * src_size.cx];
|
|
if(pixel.a) {
|
|
if(pixel.a > dest->a) dest->a = pixel.a;
|
|
int coef = pixel.a * 256 / dest->a;
|
|
dest->r += ((pixel.r - dest->r) * coef) >> 8;
|
|
dest->g += ((pixel.g - dest->g) * coef) >> 8;
|
|
dest->b += ((pixel.b - dest->b) * coef) >> 8;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void TransformScanOpaqueBilinear(RGBA *dest, int count, Point64 sl16, Size64 sd16,
|
|
const RGBA *src, Size src_size, int enterclip, int leaveclip)
|
|
{
|
|
int xmax = (src_size.cx << 16) - 0x10001;
|
|
int ymax = (src_size.cy << 16) - 0x10001;
|
|
for(; --count >= 0; dest++, sl16 += sd16) {
|
|
int x = MINMAX0(sl16.x, xmax) >> 8;
|
|
int y = MINMAX0(sl16.y, ymax) >> 8;
|
|
const RGBA *row1 = &src[(y >> 8) * src_size.cx + (x >> 8)];
|
|
const RGBA *row2 = row1 + src_size.cx;
|
|
int t1, t2;
|
|
t1 = row1[0].r * 256 + (row1[1].r - row1[0].r) * (byte)x;
|
|
t2 = row2[0].r * 256 + (row2[1].r - row2[0].r) * (byte)x;
|
|
dest->r = (t1 * 256 + (t2 - t1) * (byte)y) >> 16;
|
|
t1 = row1[0].g * 256 + (row1[1].g - row1[0].g) * (byte)x;
|
|
t2 = row2[0].g * 256 + (row2[1].g - row2[0].g) * (byte)x;
|
|
dest->g = (t1 * 256 + (t2 - t1) * (byte)y) >> 16;
|
|
t1 = row1[0].b * 256 + (row1[1].b - row1[0].b) * (byte)x;
|
|
t2 = row2[0].b * 256 + (row2[1].b - row2[0].b) * (byte)x;
|
|
dest->b = (t1 * 256 + (t2 - t1) * (byte)y) >> 16;
|
|
dest->a = 255;
|
|
}
|
|
}
|
|
|
|
static void TransformScanAlphaBilinear(RGBA *dest, int count, Point64 sl16, Size64 sd16,
|
|
const RGBA *src, Size src_size, int enterclip, int leaveclip)
|
|
{
|
|
int xmax = (src_size.cx << 16) - 0x10001;
|
|
int ymax = (src_size.cy << 16) - 0x10001;
|
|
for(; --count >= 0; dest++, sl16 += sd16) {
|
|
int x = MINMAX0(sl16.x, xmax) >> 8;
|
|
int y = MINMAX0(sl16.y, ymax) >> 8;
|
|
const RGBA *row1 = &src[(y >> 8) * src_size.cx + (x >> 8)];
|
|
const RGBA *row2 = row1 + src_size.cx;
|
|
int t1, t2;
|
|
t1 = row1[0].r * 256 + (row1[1].r - row1[0].r) * (byte)x;
|
|
t2 = row2[0].r * 256 + (row2[1].r - row2[0].r) * (byte)x;
|
|
byte r = (t1 * 256 + (t2 - t1) * (byte)y) >> 16;
|
|
t1 = row1[0].g * 256 + (row1[1].g - row1[0].g) * (byte)x;
|
|
t2 = row2[0].g * 256 + (row2[1].g - row2[0].g) * (byte)x;
|
|
byte g = (t1 * 256 + (t2 - t1) * (byte)y) >> 16;
|
|
t1 = row1[0].b * 256 + (row1[1].b - row1[0].b) * (byte)x;
|
|
t2 = row2[0].b * 256 + (row2[1].b - row2[0].b) * (byte)x;
|
|
byte b = (t1 * 256 + (t2 - t1) * (byte)y) >> 16;
|
|
t1 = row1[0].a * 256 + (row1[1].a - row1[0].a) * (byte)x;
|
|
t2 = row2[0].a * 256 + (row2[1].a - row2[0].a) * (byte)x;
|
|
if(byte a = (t1 * 256 + (t2 - t1) * (byte)y) >> 16) {
|
|
if(a > dest->a) dest->a = a;
|
|
int coef = a * 256 / dest->a;
|
|
dest->r += ((r - dest->r) * coef) >> 8;
|
|
dest->g += ((g - dest->g) * coef) >> 8;
|
|
dest->b += ((b - dest->b) * coef) >> 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int TransformSection(ImageBuffer& dest, const RGBA *src, Size src_size,
|
|
SideSegment& left, SideSegment& right, Rect clip, TransformScanProc scanproc)
|
|
{
|
|
int xmax = (src_size.cx << 16) - 0x10001;
|
|
int ymax = (src_size.cy << 16) - 0x10001;
|
|
int npixels = 0;
|
|
for(int y = clip.top; y < clip.bottom; ++y, ++left, ++right) {
|
|
int dl = (int)(left.dest16 >> 16), n = (int)((right.dest16 >> 16) - dl + 1);
|
|
if(n <= 0)
|
|
continue;
|
|
Size64 sd16 = (right.src16 - left.src16) / (int64)n;
|
|
Point64 sl16 = left.src16;
|
|
if(dl < clip.left) {
|
|
n += dl - clip.left;
|
|
sl16 += sd16 * (int64)(clip.left - dl);
|
|
dl = clip.left;
|
|
}
|
|
if(dl + n > clip.right)
|
|
n = clip.right - dl;
|
|
int enterclip = 0;
|
|
int leaveclip = n;
|
|
Point64 sr16 = sl16 + sd16 * (n - 1);
|
|
if(sd16.cx > 0) {
|
|
if(sl16.x < 0)
|
|
enterclip = (int)(~sl16.x / sd16.cx) + 1;
|
|
if(sr16.x > xmax)
|
|
leaveclip = n - 1 - (int)((sr16.x - xmax - 1) / sd16.cx);
|
|
}
|
|
else if(sd16.cx < 0) {
|
|
if(sl16.x > xmax)
|
|
enterclip = (int)((sl16.x - xmax - 1) / -sd16.cx) + 1;
|
|
if(sr16.x < 0)
|
|
leaveclip = n - 1 - (int)(~sr16.x / -sd16.cx);
|
|
}
|
|
else {
|
|
sl16.x = minmax<int64>(sl16.x, 0, xmax);
|
|
if(src_size.cy == 1)
|
|
enterclip = n;
|
|
}
|
|
|
|
if(sd16.cy > 0) {
|
|
if(sl16.y < 0)
|
|
enterclip = max(enterclip, (int)(~sl16.y / sd16.cy) + 1);
|
|
if(sr16.y > ymax)
|
|
leaveclip = min(leaveclip, n - 1 - (int)((sr16.y - ymax - 1) / sd16.cy));
|
|
}
|
|
else if(sd16.cy < 0) {
|
|
if(sl16.y > ymax)
|
|
enterclip = max(enterclip, (int)((sl16.y - ymax - 1) / -sd16.cy) + 1);
|
|
if(sr16.y < 0)
|
|
leaveclip = min(leaveclip, n - 1 - (int)(~sr16.y / -sd16.cy));
|
|
}
|
|
else {
|
|
sl16.y = minmax<int64>(sl16.y, 0, ymax);
|
|
if(src_size.cy == 1)
|
|
enterclip = n;
|
|
}
|
|
enterclip = minmax(enterclip, 0, n);
|
|
leaveclip = minmax(leaveclip, 0, n);
|
|
if(leaveclip <= enterclip)
|
|
enterclip = leaveclip = n;
|
|
|
|
npixels += n;
|
|
RGBA *d = &dest[y][dl];
|
|
scanproc(d, n, sl16, sd16, src, src_size, enterclip, leaveclip);
|
|
}
|
|
return npixels;
|
|
}
|
|
|
|
static int TransformSet(ImageBuffer& dest, RGBA color, SideSegment& left, SideSegment& right, Rect clip)
|
|
{
|
|
int npixels = 0;
|
|
for(int y = clip.top; y < clip.bottom; ++y, ++left, ++right) {
|
|
int dl = (int)(left.dest16 >> 16), n = (int)((right.dest16 >> 16) - dl + 1);
|
|
if(n <= 0)
|
|
continue;
|
|
if(dl < clip.left) {
|
|
n += dl - clip.left;
|
|
dl = clip.left;
|
|
}
|
|
if(dl + n > clip.right)
|
|
n = clip.right - dl;
|
|
npixels += n;
|
|
RGBA *d = &dest[y][dl];
|
|
if(color.a == 255)
|
|
Fill(d, color, n);
|
|
else {
|
|
for(; --n >= 0; d++) {
|
|
if(color.a > d->a) d->a = color.a;
|
|
int coef = color.a * 256 / d->a;
|
|
d->r += ((color.r - d->r) * coef) >> 8;
|
|
d->g += ((color.g - d->g) * coef) >> 8;
|
|
d->b += ((color.b - d->b) * coef) >> 8;
|
|
}
|
|
}
|
|
}
|
|
return npixels;
|
|
}
|
|
|
|
#ifdef BILINEAR_TIMING
|
|
static void TransformTiming(ImageBuffer& dest, const RGBA *src, Size src_size,
|
|
SideSegment& left, SideSegment& right, Rect clip, TransformScanProc scanproc)
|
|
{
|
|
SideSegment old_left = left, old_right = right;
|
|
int start = msecs(), total;
|
|
int npixels = 0;
|
|
do {
|
|
left = old_left;
|
|
right = old_right;
|
|
npixels += TransformSection(dest, src, src_size, left, right, clip, scanproc);
|
|
}
|
|
while((total = msecs(start)) < 20);
|
|
linsec_pixels += npixels;
|
|
linsec_msecs += total;
|
|
}
|
|
#else
|
|
#define TransformTiming TransformSection
|
|
#endif
|
|
|
|
static TransformScanProc GetTransformProc(int kind, bool interpolate)
|
|
{
|
|
if(kind == IMAGE_OPAQUE) return interpolate
|
|
? &TransformScanOpaqueBilinear : &TransformScanOpaqueNearest;
|
|
return interpolate ? &TransformScanAlphaBilinear : &TransformScanAlphaNearest;
|
|
}
|
|
|
|
void LinearSet(ImageBuffer& dest, Point d0, Point d1, Point d2, RGBA color, const Rect *opt_clip)
|
|
{
|
|
Rect clip(dest.GetSize());
|
|
if(opt_clip)
|
|
clip &= *opt_clip;
|
|
if(clip.IsEmpty())
|
|
return;
|
|
if(d0.y > d1.y) { Swap(d0, d1); }
|
|
if(d1.y > d2.y) { Swap(d1, d2); if(d0.y > d1.y) { Swap(d0, d1); } }
|
|
if(d0.y > clip.top) clip.top = d0.y;
|
|
if(d2.y < clip.bottom) clip.bottom = d2.y + 1;
|
|
Rect clip01, clip12;
|
|
clip01 = clip12 = clip;
|
|
clip01.bottom = clip12.top = minmax(d1.y, clip.top, clip.bottom);
|
|
SideSegment sg01 = CalcSideSegment(d0, d1, Point(0, 0), Point(0, 0), clip01.top);
|
|
SideSegment sg12 = CalcSideSegment(d1, d2, Point(0, 0), Point(0, 0), clip12.top);
|
|
SideSegment sg02 = CalcSideSegment(d0, d2, Point(0, 0), Point(0, 0), clip.top);
|
|
if(sg01.dest16add < sg02.dest16add) {
|
|
TransformSet(dest, color, sg01, sg02, clip01);
|
|
TransformSet(dest, color, sg12, sg02, clip12);
|
|
}
|
|
else {
|
|
TransformSet(dest, color, sg02, sg01, clip01);
|
|
TransformSet(dest, color, sg02, sg12, clip12);
|
|
}
|
|
}
|
|
|
|
void LinearCopy(ImageBuffer& dest, Point d0, Point d1, Point d2,
|
|
const Image& src, Point s0, Point s1, Point s2, const Rect *opt_clip, bool interpolate)
|
|
{
|
|
Rect clip(dest.GetSize());
|
|
if(opt_clip)
|
|
clip &= *opt_clip;
|
|
if(clip.IsEmpty())
|
|
return;
|
|
TransformScanProc scanproc = GetTransformProc(src.GetKind(), interpolate);
|
|
if(d0.y > d1.y) { Swap(d0, d1); Swap(s0, s1); }
|
|
if(d1.y > d2.y) { Swap(d1, d2); Swap(s1, s2); if(d0.y > d1.y) { Swap(d0, d1); Swap(s0, s1); } }
|
|
if(d0.y > clip.top) clip.top = d0.y;
|
|
if(d2.y < clip.bottom) clip.bottom = d2.y + 1;
|
|
Rect clip01, clip12;
|
|
clip01 = clip12 = clip;
|
|
clip01.bottom = clip12.top = minmax(d1.y, clip.top, clip.bottom);
|
|
SideSegment sg01 = CalcSideSegment(d0, d1, s0, s1, clip01.top);
|
|
SideSegment sg12 = CalcSideSegment(d1, d2, s1, s2, clip12.top);
|
|
SideSegment sg02 = CalcSideSegment(d0, d2, s0, s2, clip.top);
|
|
if(sg01.dest16add < sg02.dest16add) {
|
|
TransformTiming(dest, ~src, src.GetSize(), sg01, sg02, clip01, scanproc);
|
|
TransformTiming(dest, ~src, src.GetSize(), sg12, sg02, clip12, scanproc);
|
|
}
|
|
else {
|
|
TransformTiming(dest, ~src, src.GetSize(), sg02, sg01, clip01, scanproc);
|
|
TransformTiming(dest, ~src, src.GetSize(), sg02, sg12, clip12, scanproc);
|
|
}
|
|
}
|
|
|
|
void BilinearSet(ImageBuffer& dest, Point d1, Point d2, Point d3, Point d4, RGBA color, const Rect *clip)
|
|
{
|
|
Point destpos[] = { d1, d2, d3, d4 };
|
|
BilinearSet(dest, destpos, color, clip);
|
|
}
|
|
|
|
void BilinearSet(ImageBuffer& dest, Point destpos[4], RGBA color, const Rect *opt_clip)
|
|
{
|
|
Rect clip(dest.GetSize());
|
|
if(opt_clip)
|
|
clip &= *opt_clip;
|
|
if(clip.IsEmpty())
|
|
return;
|
|
|
|
int ybrk[4];
|
|
SideSegment sides[4];
|
|
int i;
|
|
for(i = 0; i < 4; i++) {
|
|
// ASSERT(src_pixel.GetRect().Contains(srcpos[i]));
|
|
sides[i].dest16 = Null;
|
|
ybrk[i] = destpos[i].y;
|
|
for(int j = i; --j >= 0 && ybrk[j] > ybrk[j + 1]; Swap(ybrk[j], ybrk[j + 1]))
|
|
;
|
|
}
|
|
ybrk[3]++;
|
|
|
|
for(i = 0; i < 3; i++) {
|
|
int top = ybrk[i], bottom = ybrk[i + 1];
|
|
if(top >= clip.bottom)
|
|
return;
|
|
if(bottom <= clip.top)
|
|
continue;
|
|
Rect subclip(clip.left, max(top, clip.top), clip.right, min(bottom, clip.bottom));
|
|
int half = subclip.Height() >> 1;
|
|
int active[4];
|
|
int64 mid[4];
|
|
int acount = 0;
|
|
Point prev = destpos[3];
|
|
for(int s = 0; s < 4; s++) {
|
|
Point next = destpos[s];
|
|
if(next.y < prev.y)
|
|
Swap(prev, next);
|
|
if(prev.y < subclip.bottom && next.y > subclip.top) {
|
|
if(IsNull(sides[s].dest16))
|
|
sides[s] = CalcSideSegment(prev, next, Point(0, 0), Point(0, 0), subclip.top);
|
|
mid[acount] = sides[s].dest16 + sides[s].dest16add * half;
|
|
active[acount] = s;
|
|
for(int t = acount++; --t >= 0 && mid[t] > mid[t + 1]; Swap(mid[t], mid[t + 1]), Swap(active[t], active[t + 1]))
|
|
;
|
|
}
|
|
prev = destpos[s];
|
|
}
|
|
if(acount != 2 && acount != 4)
|
|
continue;
|
|
SideSegment *sal = &sides[active[(acount >> 1) - 1]];
|
|
SideSegment *sar = &sides[active[(acount >> 1) - 0]];
|
|
int64 dt = sar->dest16 - sal->dest16, dw = sar->dest16add - sal->dest16add, db = dt + dw * (subclip.Height() - 1);
|
|
int ycross = Null;
|
|
Rect crossclip;
|
|
if(dt >= +0x10000 && db <= -0x10000 || dt <= -0x10000 && db >= +0x10000) {
|
|
ycross = (int)(subclip.top - dt / dw);
|
|
crossclip = subclip;
|
|
crossclip.top = subclip.bottom = ycross;
|
|
if(dt < 0)
|
|
Swap(sal, sar);
|
|
}
|
|
if(acount == 4) {
|
|
TransformSet(dest, color, sides[active[0]], *sal, subclip);
|
|
TransformSet(dest, color, *sar, sides[active[3]], subclip);
|
|
if(!IsNull(ycross)) {
|
|
TransformSet(dest, color, sides[active[0]], *sar, crossclip);
|
|
TransformSet(dest, color, *sal, sides[active[3]], crossclip);
|
|
}
|
|
}
|
|
else {
|
|
TransformSet(dest, color, *sal, *sar, subclip);
|
|
if(!IsNull(ycross))
|
|
TransformSet(dest, color, *sar, *sal, crossclip);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BilinearCopy(ImageBuffer& dest, Point destpos[4], const Image& src, Point srcpos[4],
|
|
const Rect *opt_clip, bool interpolate)
|
|
{
|
|
Rect clip(dest.GetSize());
|
|
if(opt_clip)
|
|
clip &= *opt_clip;
|
|
if(clip.IsEmpty())
|
|
return;
|
|
|
|
TransformScanProc scanproc = GetTransformProc(src.GetKind(), interpolate);
|
|
int ybrk[4];
|
|
SideSegment sides[4];
|
|
int i;
|
|
for(i = 0; i < 4; i++) {
|
|
// ASSERT(src_pixel.GetRect().Contains(srcpos[i]));
|
|
sides[i].dest16 = Null;
|
|
ybrk[i] = destpos[i].y;
|
|
for(int j = i; --j >= 0 && ybrk[j] > ybrk[j + 1]; Swap(ybrk[j], ybrk[j + 1]))
|
|
;
|
|
}
|
|
ybrk[3]++;
|
|
|
|
for(i = 0; i < 3; i++) {
|
|
int top = ybrk[i], bottom = ybrk[i + 1];
|
|
if(top >= clip.bottom)
|
|
return;
|
|
if(bottom <= clip.top)
|
|
continue;
|
|
Rect subclip(clip.left, max(top, clip.top), clip.right, min(bottom, clip.bottom));
|
|
int half = subclip.Height() >> 1;
|
|
int active[4];
|
|
int64 mid[4];
|
|
int acount = 0;
|
|
Point prev = destpos[3], sprev = srcpos[3];
|
|
for(int s = 0; s < 4; s++) {
|
|
Point next = destpos[s], snext = srcpos[s];
|
|
if(next.y < prev.y) {
|
|
Swap(prev, next);
|
|
Swap(sprev, snext);
|
|
}
|
|
if(prev.y < subclip.bottom && next.y > subclip.top) {
|
|
if(IsNull(sides[s].dest16))
|
|
sides[s] = CalcSideSegment(prev, next, sprev, snext, subclip.top);
|
|
mid[acount] = sides[s].dest16 + sides[s].dest16add * half;
|
|
active[acount] = s;
|
|
for(int t = acount++; --t >= 0 && mid[t] > mid[t + 1]; Swap(mid[t], mid[t + 1]), Swap(active[t], active[t + 1]))
|
|
;
|
|
}
|
|
prev = destpos[s];
|
|
sprev = srcpos[s];
|
|
}
|
|
if(acount != 2 && acount != 4)
|
|
continue;
|
|
SideSegment *sal = &sides[active[(acount >> 1) - 1]];
|
|
SideSegment *sar = &sides[active[(acount >> 1) - 0]];
|
|
int64 dt = sar->dest16 - sal->dest16, dw = sar->dest16add - sal->dest16add, db = dt + dw * (subclip.Height() - 1);
|
|
int ycross = Null;
|
|
Rect crossclip;
|
|
if(dt >= +0x10000 && db <= -0x10000 || dt <= -0x10000 && db >= +0x10000) {
|
|
ycross = (int)(subclip.top - dt / dw);
|
|
crossclip = subclip;
|
|
crossclip.top = subclip.bottom = ycross;
|
|
if(dt < 0)
|
|
Swap(sal, sar);
|
|
}
|
|
if(acount == 4) {
|
|
TransformTiming(dest, ~src, src.GetSize(), sides[active[0]], *sal, subclip, scanproc);
|
|
TransformTiming(dest, ~src, src.GetSize(), *sar, sides[active[3]], subclip, scanproc);
|
|
if(!IsNull(ycross)) {
|
|
TransformTiming(dest, ~src, src.GetSize(), sides[active[0]], *sar, crossclip, scanproc);
|
|
TransformTiming(dest, ~src, src.GetSize(), *sal, sides[active[3]], crossclip, scanproc);
|
|
}
|
|
}
|
|
else {
|
|
TransformTiming(dest, ~src, src.GetSize(), *sal, *sar, subclip, scanproc);
|
|
if(!IsNull(ycross))
|
|
TransformTiming(dest, ~src, src.GetSize(), *sar, *sal, crossclip, scanproc);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BilinearCopy(ImageBuffer& dest, Point d1, Point d2, Point d3, Point d4,
|
|
const Image& src, Point s1, Point s2, Point s3, Point s4, const Rect *opt_clip, bool interpolate)
|
|
{
|
|
Point destpos[4] = { d1, d2, d3, d4 }, srcpos[4] = { s1, s2, s3, s4 };
|
|
BilinearCopy(dest, destpos, src, srcpos, opt_clip, interpolate);
|
|
}
|
|
|
|
void BilinearCopy(ImageBuffer& dest, Point d1, Point d2, Point d3, Point d4,
|
|
const Image& src, Rect sr, const Rect *opt_clip, bool interpolate)
|
|
{
|
|
Point destpos[4] = { d1, d2, d3, d4 };
|
|
Point srcpos[4] = { Point(sr.left, sr.top), Point(sr.right - 1, sr.top),
|
|
Point(sr.right - 1, sr.bottom - 1), Point(sr.left, sr.bottom - 1) };
|
|
BilinearCopy(dest, destpos, src, srcpos, opt_clip, interpolate);
|
|
}
|
|
|
|
void BilinearCopy(ImageBuffer& dest, Point destpos[4], const Image& src, Rect sr,
|
|
const Rect *opt_clip, bool interpolate)
|
|
{
|
|
Point srcpos[4] = { Point(sr.left, sr.top), Point(sr.right - 1, sr.top),
|
|
Point(sr.right - 1, sr.bottom - 1), Point(sr.left, sr.bottom - 1) };
|
|
BilinearCopy(dest, destpos, src, srcpos, opt_clip, interpolate);
|
|
}
|
|
|
|
static inline Pointf Cvp(double x, double y, double sina, double cosa)
|
|
{
|
|
return Pointf(x * cosa + y * sina, -x * sina + y * cosa);
|
|
}
|
|
|
|
Image BilinearRotate(const Image& m, int angle)
|
|
{
|
|
Size isz = m.GetSize();
|
|
Point center = isz / 2;
|
|
Pointf centerf = Pointf(Point(isz)) / 2;
|
|
double sina, cosa;
|
|
Draw::SinCos(angle, sina, cosa);
|
|
Pointf p1 = Cvp(-centerf.x, -centerf.y, sina, cosa);
|
|
Pointf p2 = Cvp(centerf.x, -centerf.y, sina, cosa);
|
|
Size sz2 = Size(2 * (int)max(tabs(p1.x), tabs(p2.x)),
|
|
2 * (int)max(tabs(p1.y), tabs(p2.y)));
|
|
Point dcenter = sz2 / 2;
|
|
ImageBuffer ib(sz2);
|
|
Fill(~ib, RGBAZero(), ib.GetLength());
|
|
BilinearCopy(ib, dcenter + (Point)p1, dcenter + (Point)p2, dcenter - (Point)p1, dcenter - (Point)p2,
|
|
m, Point(0, 0), Point(isz.cx, 0), isz, Point(0, isz.cy));
|
|
return ib;
|
|
}
|
|
|
|
}
|