diff --git a/uppsrc/Core/Cpu.cpp b/uppsrc/Core/Cpu.cpp index a6448c955..a75ff9bba 100644 --- a/uppsrc/Core/Cpu.cpp +++ b/uppsrc/Core/Cpu.cpp @@ -8,6 +8,9 @@ #endif #endif +#ifdef PLATFORM_FREEBSD +#include +#endif #ifdef PLATFORM_MACOS #include #include diff --git a/uppsrc/Draw/Draw.upp b/uppsrc/Draw/Draw.upp index e79473f58..922c69610 100644 --- a/uppsrc/Draw/Draw.upp +++ b/uppsrc/Draw/Draw.upp @@ -42,6 +42,7 @@ file ImageOp.h, ImageOp.cpp, ImageChOp.cpp, + Mify.cpp, ImageScale.cpp, RescaleFilter.cpp, MakeCache.cpp, diff --git a/uppsrc/Draw/ImageOp.cpp b/uppsrc/Draw/ImageOp.cpp index 1db8446b5..448dbe8c8 100644 --- a/uppsrc/Draw/ImageOp.cpp +++ b/uppsrc/Draw/ImageOp.cpp @@ -859,38 +859,6 @@ Image FlipImage(const Image& m, int mode) m); } -Image Magnify(const Image& img, int nx, int ny) -{ - if(nx == 1 && ny == 1) - return img; - if(nx == 0 || ny == 0) - return Image(); - Size sz = img.GetSize(); - bool xdown = nx < 0; - nx = abs(nx); - int ncx = xdown ? sz.cx / nx : sz.cx * nx; - ImageBuffer b(ncx, sz.cy * ny); - const RGBA *s = ~img; - const RGBA *e = s + img.GetLength(); - RGBA *t = ~b; - while(s < e) { - RGBA *q = t; - const RGBA *le = s + sz.cx; - while(s < le) { - Fill(q, *s, nx); - q += nx; - s++; - } - for(int n = ny - 1; n--;) { - memcpy(q, t, ncx * sizeof(RGBA)); - q += ncx; - } - t = q; - } - b.SetResolution(img.GetResolution()); - return b; -} - static Pointf Cvp(double x, double y, double sina, double cosa) { return Pointf(x * cosa + y * sina, -x * sina + y * cosa); diff --git a/uppsrc/Draw/ImageOp.h b/uppsrc/Draw/ImageOp.h index 631e3d1cb..00b8bfff6 100644 --- a/uppsrc/Draw/ImageOp.h +++ b/uppsrc/Draw/ImageOp.h @@ -158,8 +158,6 @@ struct ChPartMaker { ChPartMaker(const Image& m); }; -Image Magnify(const Image& img, int nx, int ny); - // Image cache struct ImageMaker { @@ -221,6 +219,10 @@ Image CachedRescalePaintOnly(const Image& m, Size sz, int filter = Null); Image CachedSetColorKeepAlpha(const Image& img, Color color); Image CachedSetColorKeepAlphaPaintOnly(const Image& img, Color color); +Image Magnify(const Image& img, int nx, int ny); +Image Minify(const Image& img, int nx, int ny, bool co = false); +Image MinifyCached(const Image& img, int nx, int ny, bool co); + Image Upscale2x(const Image& src); Image Downscale2x(const Image& src); diff --git a/uppsrc/Draw/Mify.cpp b/uppsrc/Draw/Mify.cpp new file mode 100644 index 000000000..41aea6327 --- /dev/null +++ b/uppsrc/Draw/Mify.cpp @@ -0,0 +1,236 @@ +#include "Draw.h" + +namespace Upp { + +#ifdef CPU_SSE2 + +Image Minify(const Image& img, int nx, int ny, bool co) +{ + ASSERT(nx > 0 && ny > 0); + Size ssz = img.GetSize(); + Size tsz = Size((ssz.cx + nx - 1) / nx, (ssz.cy + ny - 1) / ny); + ImageBuffer ib(tsz); + int scx0 = ssz.cx / nx * nx; + auto do_line = [&](int ty, __m128 *b, __m128 *div) { + memset(b, 0, tsz.cx * sizeof(__m128)); + memset(div, 0, tsz.cx * sizeof(__m128)); + __m128 v1 = _mm_set1_ps(1); + int yy = ny * ty; + for(int yi = 0; yi < ny; yi++) { + int y = yy + yi; + if(y < ssz.cy) { + const RGBA *s = img[yy + yi]; + const RGBA *e = s + scx0; + const RGBA *e2 = s + ssz.cx; + __m128 *t = b; + __m128 *d = div; + while(s < e) { + __m128 px = _mm_setzero_ps(); + __m128 dv = _mm_setzero_ps(); + for(int n = nx; n--;) { + px = _mm_add_ps(px, LoadRGBAF(s++)); + dv = _mm_add_ps(dv, v1); + } + *t++ = px; + *d++ = dv; + } + __m128 px = _mm_setzero_ps(); + __m128 dv = _mm_setzero_ps(); + while(s < e2) { + px = _mm_add_ps(px, LoadRGBAF(s++)); + dv = _mm_add_ps(px, v1); + } + *t++ = px; + *d++ = dv; + } + } + __m128 *s = b; + __m128 *d = div; + RGBA *t = ~ib + ty * tsz.cx; + RGBA *e = t + tsz.cx; + while(t < e) + StoreRGBAF(t++, _mm_div_ps(*s++, *d++)); + }; + if(co) { + CoWork cw; + cw * [&] { + Buffer<__m128> div(tsz.cx); + Buffer<__m128> b(tsz.cx); + for(;;) { + int y = cw.Next(); + if(y >= tsz.cy) + break; + do_line(y, b, div); + } + }; + } + else { + Buffer<__m128> div(tsz.cx, _mm_setzero_ps()); + Buffer<__m128> b(tsz.cx, _mm_setzero_ps()); + for(int y = 0; y < tsz.cy; y++) + do_line(y, b, div); + } + return ib; +} + +#else + +struct RGBAV { + dword r, g, b, a; + + void Set(dword v) { r = g = b = a = v; } + void Clear() { Set(0); } + void Put(dword weight, const RGBA& src) { + r += weight * src.r; + g += weight * src.g; + b += weight * src.b; + a += weight * src.a; + } + void Put(const RGBA& src) { + r += src.r; + g += src.g; + b += src.b; + a += src.a; + } + RGBA Get(int div) const { + RGBA c; + c.r = byte(r / div); + c.g = byte(g / div); + c.b = byte(b / div); + c.a = byte(a / div); + return c; + } +}; + +Image Minify(const Image& img, int nx, int ny, bool co) +{ + ASSERT(nx > 0 && ny > 0); + Size ssz = img.GetSize(); + Size tsz = Size((ssz.cx + nx - 1) / nx, (ssz.cy + ny - 1) / ny); + ImageBuffer ib(tsz); + int scx0 = ssz.cx / nx * nx; + auto do_line = [&](int ty, RGBAV *b, int *div) { + int yy = ny * ty; + for(int i = 0; i < tsz.cx; i++) + b[i].Clear(); + memset(div, 0, tsz.cx * sizeof(int)); + for(int yi = 0; yi < ny; yi++) { + int y = yy + yi; + if(y < ssz.cy) { + const RGBA *s = img[yy + yi]; + const RGBA *e = s + scx0; + const RGBA *e2 = s + ssz.cx; + RGBAV *t = b; + int *dv = div; + while(s < e) { + for(int n = nx; n--;) { + t->Put(*s++); + (*dv)++; + } + t++; + dv++; + } + while(s < e2) { + t->Put(*s++); + (*dv)++; + } + } + } + const RGBAV *s = b; + const int *dv = div; + RGBA *it = ~ib + ty * tsz.cx; + for(int x = 0; x < tsz.cx; x++) + *it++ = (s++)->Get(*dv++); + }; + if(co) { + CoWork cw; + cw * [&] { + Buffer div(tsz.cx); + Buffer b(tsz.cx); + for(;;) { + int y = cw.Next(); + if(y >= tsz.cy) + break; + do_line(y, b, div); + } + }; + } + else { + Buffer div(tsz.cx); + Buffer b(tsz.cx); + for(int y = 0; y < tsz.cy; y++) + do_line(y, b, div); + } + return ib; +} + +#endif + +struct MinifyImageMaker : public ImageMaker { + Image image; + int nx; + int ny; + bool co; + + virtual String Key() const; + virtual Image Make() const; +}; + +String MinifyImageMaker::Key() const +{ + String key; + RawCat(key, image.GetSerialId()); + RawCat(key, nx); + RawCat(key, ny); + // we are not adding co as that is just a hint for actual image making + return key; +} + +Image MinifyImageMaker::Make() const +{ + return Minify(image, nx, ny, co); +} + +Image MinifyCached(const Image& img, int nx, int ny, bool co) +{ + MinifyImageMaker m; + m.image = img; + m.nx = nx; + m.ny = ny; + m.co = co; + return MakeImage(m); +} + +Image Magnify(const Image& img, int nx, int ny) +{ + if(nx == 1 && ny == 1) + return img; + if(nx == 0 || ny == 0) + return Image(); + Size sz = img.GetSize(); + bool xdown = nx < 0; + nx = abs(nx); + int ncx = xdown ? sz.cx / nx : sz.cx * nx; + ImageBuffer b(ncx, sz.cy * ny); + const RGBA *s = ~img; + const RGBA *e = s + img.GetLength(); + RGBA *t = ~b; + while(s < e) { + RGBA *q = t; + const RGBA *le = s + sz.cx; + while(s < le) { + Fill(q, *s, nx); + q += nx; + s++; + } + for(int n = ny - 1; n--;) { + memcpy(q, t, ncx * sizeof(RGBA)); + q += ncx; + } + t = q; + } + b.SetResolution(img.GetResolution()); + return b; +} + +}; \ No newline at end of file diff --git a/uppsrc/Draw/src.tpp/ImageMaker_en-us.tpp b/uppsrc/Draw/src.tpp/ImageMaker_en-us.tpp index c177ca171..259393879 100644 --- a/uppsrc/Draw/src.tpp/ImageMaker_en-us.tpp +++ b/uppsrc/Draw/src.tpp/ImageMaker_en-us.tpp @@ -110,6 +110,30 @@ with results being cached, that can only be used with Draw`::DrawImage(this is optimization hint that can save some memory in certain situations).&] [s3;%% &] [s4; &] +[s5;:Upp`:`:Magnify`(const Upp`:`:Image`&`,int`,int`): [_^Upp`:`:Image^ Image]_[* Magnify +]([@(0.0.255) const]_[_^Upp`:`:Image^ Image][@(0.0.255) `&]_[*@3 img], +[@(0.0.255) int]_[*@3 nx], [@(0.0.255) int]_[*@3 ny])&] +[s2;%% Multiplies the resolution of [%-*@3 img] by factors [%-*@3 nx] +and [%-*@3 ny] by simply repeating the pixels (each pixel becomes +[%-*@3 nx] x [%-*@3 ny] same color block).&] +[s3;%% &] +[s4; &] +[s5;:Upp`:`:Minify`(const Upp`:`:Image`&`,int`,int`,bool`): [_^Upp`:`:Image^ Image]_[* Mi +nify]([@(0.0.255) const]_[_^Upp`:`:Image^ Image][@(0.0.255) `&]_[*@3 img], +[@(0.0.255) int]_[*@3 nx], [@(0.0.255) int]_[*@3 ny], [@(0.0.255) bool]_[*@3 co]_`=_[@(0.0.255) f +alse])&] +[s2;%% Reduces the resolution of [%-*@3 img] by factors [%-*@3 nx] and +[%-*@3 ny] by averaging pixel value (each [%-*@3 nx] x [%-*@3 ny] block +of pixels is averaged and produces single pixel in the resulting +image). If [%-*@3 co] is true, parallel processing is enabled.&] +[s3;%% &] +[s4; &] +[s5;:Upp`:`:MinifyCached`(const Upp`:`:Image`&`,int`,int`,bool`): [_^Upp`:`:Image^ Imag +e]_[* MinifyCached]([@(0.0.255) const]_[_^Upp`:`:Image^ Image][@(0.0.255) `&]_[*@3 img], +[@(0.0.255) int]_[*@3 nx], [@(0.0.255) int]_[*@3 ny], [@(0.0.255) bool]_[*@3 co])&] +[s2;%% [%-*@3 img] [%-*@3 nx] [%-*@3 ny] [%-*@3 co] .&] +[s3;%% &] +[s4; &] [s5;:Upp`:`:Upscale2x`(const Upp`:`:Image`&`): [_^Upp`:`:Image^ Image]_[* Upscale2x]([@(0.0.255) c onst]_[_^Upp`:`:Image^ Image][@(0.0.255) `&]_[*@3 src])&] [s2;%% Doubles the resolution of image, using smart heuristics algorithm diff --git a/uppsrc/Painter/Image.cpp b/uppsrc/Painter/Image.cpp index 4ed2ac29d..91cdb077d 100644 --- a/uppsrc/Painter/Image.cpp +++ b/uppsrc/Painter/Image.cpp @@ -2,146 +2,8 @@ namespace Upp { -struct RGBAV { - dword r, g, b, a; - - void Set(dword v) { r = g = b = a = v; } - void Clear() { Set(0); } - void Put(dword weight, const RGBA& src) { - r += weight * src.r; - g += weight * src.g; - b += weight * src.b; - a += weight * src.a; - } - void Put(const RGBA& src) { - r += src.r; - g += src.g; - b += src.b; - a += src.a; - } - RGBA Get(int div) const { - RGBA c; - c.r = byte(r / div); - c.g = byte(g / div); - c.b = byte(b / div); - c.a = byte(a / div); - return c; - } -}; - -Image DownScale(const Image& img, int nx, int ny, bool co) -{ - ASSERT(nx > 0 && ny > 0); - Size ssz = img.GetSize(); - Size tsz = Size((ssz.cx + nx - 1) / nx, (ssz.cy + ny - 1) / ny); - ImageBuffer ib(tsz); - int scx0 = ssz.cx / nx * nx; - auto do_line = [&](int ty, RGBAV *b, int *div) { - int yy = ny * ty; - for(int i = 0; i < tsz.cx; i++) - b[i].Clear(); - memset(div, 0, tsz.cx * sizeof(int)); - for(int yi = 0; yi < ny; yi++) { - int y = yy + yi; - if(y < ssz.cy) { - const RGBA *s = img[yy + yi]; - const RGBA *e = s + scx0; - const RGBA *e2 = s + ssz.cx; - RGBAV *t = b; - int *dv = div; - while(s < e) { - for(int n = nx; n--;) { - t->Put(*s++); - (*dv)++; - } - t++; - dv++; - } - while(s < e2) { - t->Put(*s++); - (*dv)++; - } - } - } - const RGBAV *s = b; - const int *dv = div; - RGBA *it = ~ib + ty * tsz.cx; - for(int x = 0; x < tsz.cx; x++) - *it++ = (s++)->Get(*dv++); - }; - if(co) { - CoWork cw; - cw * [&] { - Buffer div(tsz.cx); - Buffer b(tsz.cx); - for(;;) { - int y = cw.Next(); - if(y >= tsz.cy) - break; - do_line(y, b, div); - } - }; - } - else { - Buffer div(tsz.cx); - Buffer b(tsz.cx); - for(int y = 0; y < tsz.cy; y++) - do_line(y, b, div); - } - return ib; -} - -struct DownscaleImageMaker : public ImageMaker { - Image image; - int nx; - int ny; - bool co; - - virtual String Key() const; - virtual Image Make() const; -}; - -String DownscaleImageMaker::Key() const -{ - String key; - RawCat(key, image.GetSerialId()); - RawCat(key, nx); - RawCat(key, ny); - // we are not adding co as that is just a hint for actual image making - return key; -} - -Image DownscaleImageMaker::Make() const -{ - return DownScale(image, nx, ny, co); -} - -Image DownScaleCached(const Image& img, int nx, int ny, bool co) -{ - DownscaleImageMaker m; - m.image = img; - m.nx = nx; - m.ny = ny; - m.co = co; - return MakeImage(m); -} - #ifdef CPU_X86 -never_inline -String AsString(__m128 x) -{ - float f[4]; - memcpy(f, &x, 16); - return Sprintf("(%f, %f, %f, %f)", f[3], f[2], f[1], f[0]); -} - -#if 1 -#define DUMPS(x) RLOG(#x << " = " << AsString(x)); -#else -#define DUMPS(x) -#endif - force_inline int IntAndFraction(__m128 x, __m128& fraction) { @@ -185,7 +47,7 @@ struct PainterImageSpanData { xform = Inverse(m); else { if(!fast) - image = (imagecache ? DownScaleCached : DownScale)(image, nx, ny, co); + image = (imagecache ? MinifyCached : Minify)(image, nx, ny, co); xform = Inverse(m) * Xform2D::Scale(1.0 / nx, 1.0 / ny); } cx = image.GetWidth();