// // Lol Engine // // Copyright © 2004—2017 Sam Hocevar // // Lol Engine is free software. It comes without any warranty, to // the extent permitted by applicable law. You can redistribute it // and/or modify it under the terms of the Do What the Fuck You Want // to Public License, Version 2, as published by the WTFPL Task Force. // See http://www.wtfpl.net/ for more details. // #include /* * Image resizing functions */ namespace lol { static image ResizeBicubic(image &src, ivec2 size); static image ResizeBresenham(image &src, ivec2 size); image image::Resize(ivec2 size, ResampleAlgorithm algorithm) { switch (algorithm) { case ResampleAlgorithm::Bicubic: return ResizeBicubic(*this, size); case ResampleAlgorithm::Bresenham: default: return ResizeBresenham(*this, size); } } static image ResizeBicubic(image &src, ivec2 size) { image dst(size); ivec2 const oldsize = src.size(); vec4 const *srcp = src.lock(); vec4 *dstp = dst.lock(); float scalex = size.x > 1 ? (oldsize.x - 1.f) / (size.x - 1) : 1.f; float scaley = size.y > 1 ? (oldsize.y - 1.f) / (size.y - 1) : 1.f; for (int y = 0; y < size.y; ++y) { float yfloat = scaley * y; int yint = (int)yfloat; float y1 = yfloat - yint; vec4 const *p0 = srcp + oldsize.x * lol::min(lol::max(0, yint - 1), oldsize.y - 1); vec4 const *p1 = srcp + oldsize.x * lol::min(lol::max(0, yint ), oldsize.y - 1); vec4 const *p2 = srcp + oldsize.x * lol::min(lol::max(0, yint + 1), oldsize.y - 1); vec4 const *p3 = srcp + oldsize.x * lol::min(lol::max(0, yint + 2), oldsize.y - 1); for (int x = 0; x < size.x; ++x) { float xfloat = scalex * x; int xint = (int)xfloat; float x1 = xfloat - xint; int const i0 = lol::min(lol::max(0, xint - 1), oldsize.x - 1); int const i1 = lol::min(lol::max(0, xint ), oldsize.x - 1); int const i2 = lol::min(lol::max(0, xint + 1), oldsize.x - 1); int const i3 = lol::min(lol::max(0, xint + 2), oldsize.x - 1); vec4 a00 = p1[i1]; vec4 a01 = .5f * (p2[i1] - p0[i1]); vec4 a02 = p0[i1] - 2.5f * p1[i1] + 2.f * p2[i1] - .5f * p3[i1]; vec4 a03 = .5f * (p3[i1] - p0[i1]) + 1.5f * (p1[i1] - p2[i1]); vec4 a10 = .5f * (p1[i2] - p1[i0]); vec4 a11 = .25f * (p0[i0] - p2[i0] - p0[i2] + p2[i2]); vec4 a12 = .5f * (p0[i2] - p0[i0]) + 1.25f * (p1[i0] - p1[i2]) + .25f * (p3[i0] - p3[i2]) + p2[i2] - p2[i0]; vec4 a13 = .25f * (p0[i0] - p3[i0] - p0[i2] + p3[i2]) + .75f * (p2[i0] - p1[i0] + p1[i2] - p2[i2]); vec4 a20 = p1[i0] - 2.5f * p1[i1] + 2.f * p1[i2] - .5f * p1[i3]; vec4 a21 = .5f * (p2[i0] - p0[i0]) + 1.25f * (p0[i1] - p2[i1]) + .25f * (p0[i3] - p2[i3]) - p0[i2] + p2[i2]; vec4 a22 = p0[i0] - p3[i2] - 2.5f * (p1[i0] + p0[i1]) + 2.f * (p2[i0] + p0[i2]) - .5f * (p3[i0] + p0[i3]) + 6.25f * p1[i1] - 5.f * (p2[i1] + p1[i2]) + 1.25f * (p3[i1] + p1[i3]) + 4.f * p2[i2] - p2[i3] + .25f * p3[i3]; vec4 a23 = 1.5f * (p1[i0] - p2[i0]) + .5f * (p3[i0] - p0[i0]) + 1.25f * (p0[i1] - p3[i1]) + 3.75f * (p2[i1] - p1[i1]) + p3[i2] - p0[i2] + 3.f * (p1[i2] - p2[i2]) + .25f * (p0[i3] - p3[i3]) + .75f * (p2[i3] - p1[i3]); vec4 a30 = .5f * (p1[i3] - p1[i0]) + 1.5f * (p1[i1] - p1[i2]); vec4 a31 = .25f * (p0[i0] - p2[i0]) + .25f * (p2[i3] - p0[i3]) + .75f * (p2[i1] - p0[i1] + p0[i2] - p2[i2]); vec4 a32 = -.5f * p0[i0] + 1.25f * p1[i0] - p2[i0] + .25f * p3[i0] + 1.5f * p0[i1] - 3.75f * p1[i1] + 3.f * p2[i1] - .75f * p3[i1] - 1.5f * p0[i2] + 3.75f * p1[i2] - 3.f * p2[i2] + .75f * p3[i2] + .5f * p0[i3] - 1.25f * p1[i3] + p2[i3] - .25f * p3[i3]; vec4 a33 = .25f * p0[i0] - .75f * p1[i0] + .75f * p2[i0] - .25f * p3[i0] - .75f * p0[i1] + 2.25f * p1[i1] - 2.25f * p2[i1] + .75f * p3[i1] + .75f * p0[i2] - 2.25f * p1[i2] + 2.25f * p2[i2] - .75f * p3[i2] - .25f * p0[i3] + .75f * p1[i3] - .75f * p2[i3] + .25f * p3[i3]; float y2 = y1 * y1; float y3 = y2 * y1; float x2 = x1 * x1; float x3 = x2 * x1; vec4 p = a00 + a01 * y1 + a02 * y2 + a03 * y3 + a10 * x1 + a11 * x1 * y1 + a12 * x1 * y2 + a13 * x1 * y3 + a20 * x2 + a21 * x2 * y1 + a22 * x2 * y2 + a23 * x2 * y3 + a30 * x3 + a31 * x3 * y1 + a32 * x3 * y2 + a33 * x3 * y3; dstp[y * size.x + x] = lol::clamp(p, 0.f, 1.f); } } dst.unlock(dstp); src.unlock(srcp); return dst; } /* This is Bresenham resizing. I “rediscovered” it independently but * it was actually first described in 1995 by Tim Kientzle in “Scaling * Bitmaps with Bresenham”. */ /* FIXME: the algorithm does not handle alpha components properly. Resulting * alpha should be the mean alpha value of the neightbouring pixels, but * the colour components should be weighted with the alpha value. */ static image ResizeBresenham(image &src, ivec2 size) { image dst(size); ivec2 const oldsize = src.size(); float const invswsh = 1.0f / (oldsize.x * oldsize.y); vec4 const *srcp = src.lock(); vec4 *dstp = dst.lock(); array aline, line; aline.resize(size.x); line.resize(size.x); memset(line.data(), 0, line.bytes()); int remy = 0; for (int y = 0, y0 = 0; y < size.y; y++) { memset(aline.data(), 0, aline.bytes()); for (int toty = 0; toty < oldsize.y; ) { if (remy == 0) { vec4 color(0.f); int remx = 0; for (int x = 0, x0 = 0; x < size.x; x++) { vec4 acolor(0.f); for (int totx = 0; totx < oldsize.x; ) { if (remx == 0) { color = srcp[y0 * oldsize.x + x0]; x0++; remx = size.x; } int nx = lol::min(remx, oldsize.x - totx); acolor += (float)nx * color; totx += nx; remx -= nx; } line[x] = acolor; } y0++; remy = size.y; } int ny = lol::min(remy, oldsize.y - toty); for (int x = 0; x < size.x; x++) aline[x] += (float)ny * line[x]; toty += ny; remy -= ny; } for (int x = 0; x < size.x; x++) dstp[y * size.x + x] = aline[x] * invswsh; } dst.unlock(dstp); src.unlock(srcp); return dst; } } /* namespace lol */