Finally got blackman-harris to work correctly.
This commit is contained in:
parent
e40b7c1a29
commit
94f6085e36
5 changed files with 337 additions and 15 deletions
|
@ -6,6 +6,7 @@ add_executable(${PROJECT_NAME}
|
||||||
block.cpp
|
block.cpp
|
||||||
icon_fetch.cpp
|
icon_fetch.cpp
|
||||||
grid.cpp
|
grid.cpp
|
||||||
|
resize_harris.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME}
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
#include "icon_fetch.hpp"
|
#include "icon_fetch.hpp"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const constexpr int g_icon_width = 16 * 5;
|
||||||
|
} //unnamed namespace
|
||||||
|
|
||||||
namespace implem {
|
namespace implem {
|
||||||
void draw_icons (
|
void draw_icons (
|
||||||
int icon_size,
|
int icon_size,
|
||||||
|
@ -52,8 +56,8 @@ void grid_drawer_trigger::attached (
|
||||||
tabstop(wd);
|
tabstop(wd);
|
||||||
|
|
||||||
for (int z = 0; z < static_cast<int>(m_icons.size()); ++z) {
|
for (int z = 0; z < static_cast<int>(m_icons.size()); ++z) {
|
||||||
const int x = (16 + 2) * (z % 3);
|
const int x = (g_icon_width + 2) * (z % 3);
|
||||||
const int y = (16 + 2) * (z / 3);
|
const int y = (g_icon_width + 2) * (z / 3);
|
||||||
m_icons[z].output(*m_widget, nana::point(x, y));
|
m_icons[z].output(*m_widget, nana::point(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,6 +93,6 @@ void grid_drawer_trigger::refresh (nana::paint::graphics& graph) {
|
||||||
|
|
||||||
if (m_memorycard and not m_icons_added) {
|
if (m_memorycard and not m_icons_added) {
|
||||||
m_icons_added = true;
|
m_icons_added = true;
|
||||||
implem::draw_icons(16, *m_memorycard, m_icons, *m_widget);
|
implem::draw_icons(g_icon_width, *m_memorycard, m_icons, *m_widget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "icon_fetch.hpp"
|
#include "icon_fetch.hpp"
|
||||||
|
#include "resize_harris.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
@ -63,10 +64,6 @@ namespace {
|
||||||
std::transform(data.begin(), data.end(), data.begin(), &to_srgb);
|
std::transform(data.begin(), data.end(), data.begin(), &to_srgb);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> bilinear_resize (int in_w, int in_h, const std::vector<float>& data, int out_w, int out_h) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<float> rgb4_to_float (const std::vector<char>& data, const std::vector<uint8_t>& palette, int w, int h) {
|
std::vector<float> rgb4_to_float (const std::vector<char>& data, const std::vector<uint8_t>& palette, int w, int h) {
|
||||||
assert(w * h);
|
assert(w * h);
|
||||||
std::vector<float> retval(w * h * 3);
|
std::vector<float> retval(w * h * 3);
|
||||||
|
@ -105,11 +102,12 @@ namespace {
|
||||||
const int scanl_sz = scanline_size(w, 24);
|
const int scanl_sz = scanline_size(w, 24);
|
||||||
std::vector<char> retval(scanl_sz * h);
|
std::vector<char> retval(scanl_sz * h);
|
||||||
assert(retval.size() >= data.size());
|
assert(retval.size() >= data.size());
|
||||||
|
assert(data.size() == w * h * 3);
|
||||||
|
|
||||||
for (int y = 0; y < h; ++y) {
|
for (int y = 0; y < h; ++y) {
|
||||||
for (int x = 0; x < w; ++x) {
|
for (int x = 0; x < w; ++x) {
|
||||||
for (int c = 0; c < 3; ++c) {
|
for (int c = 0; c < 3; ++c) {
|
||||||
retval[y * scanl_sz + x * 3 + c] = static_cast<char>(static_cast<uint8_t>(data[y * scanl_sz + x * 3 + c] * 255.0f));
|
retval[y * scanl_sz + x * 3 + c] = static_cast<char>(static_cast<uint8_t>(data[y * w * 3 + x * 3 + c] * 255.0f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,11 +170,10 @@ std::vector<std::vector<char>> icon_fetch (const ConstBlock& block, int width, i
|
||||||
std::vector<char> resized_rgb;
|
std::vector<char> resized_rgb;
|
||||||
if (scale) {
|
if (scale) {
|
||||||
std::vector<float> float_data = rgb4_to_float(orig_rgb, palette, in_width, in_height);
|
std::vector<float> float_data = rgb4_to_float(orig_rgb, palette, in_width, in_height);
|
||||||
//srgb_to_linear(float_data, in_width, in_height);
|
srgb_to_linear(float_data, in_width, in_height);
|
||||||
//auto float_resized_rgb = bilinear_resize(in_width, in_height, float_data, width, height);
|
auto float_resized_rgb = blackmanharris_resize(in_width, in_height, float_data, width, height);
|
||||||
//linear_to_srgb(float_resized_rgb, width, height);
|
linear_to_srgb(float_resized_rgb, width, height);
|
||||||
//resized_rgb = float_to_rgb24(float_resized_rgb, width, height);
|
resized_rgb = float_to_rgb24(float_resized_rgb, width, height);
|
||||||
resized_rgb = float_to_rgb24(float_data, width, height);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::swap(resized_rgb, orig_rgb);
|
std::swap(resized_rgb, orig_rgb);
|
||||||
|
@ -185,9 +182,9 @@ std::vector<std::vector<char>> icon_fetch (const ConstBlock& block, int width, i
|
||||||
|
|
||||||
assert(frame.size() == data_offset);
|
assert(frame.size() == data_offset);
|
||||||
frame.resize(data_offset + scan_sz * height);
|
frame.resize(data_offset + scan_sz * height);
|
||||||
for (int32_t y = 0; y < in_height; ++y) {
|
for (int32_t y = 0; y < height; ++y) {
|
||||||
std::copy_n(
|
std::copy_n(
|
||||||
resized_rgb.begin() + scan_sz * (in_height - 1 - y),
|
resized_rgb.begin() + scan_sz * (height - 1 - y),
|
||||||
scan_sz,
|
scan_sz,
|
||||||
frame.begin() + data_offset + y * scan_sz
|
frame.begin() + data_offset + y * scan_sz
|
||||||
);
|
);
|
||||||
|
|
309
src/gui/resize_harris.cpp
Normal file
309
src/gui/resize_harris.cpp
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
/*
|
||||||
|
Copyright 2008 Larry Gritz and the other authors and contributors.
|
||||||
|
All Rights Reserved.
|
||||||
|
Based on BSD-licensed software Copyright 2004 NVIDIA Corp.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the software's owners nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
(This is the Modified BSD License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "resize_harris.hpp"
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cassert>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template <typename T>
|
||||||
|
[[gnu::pure]]
|
||||||
|
T clamp (T v, T m, T M) {
|
||||||
|
if (v < m)
|
||||||
|
return m;
|
||||||
|
else if (v > M)
|
||||||
|
return M;
|
||||||
|
else
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClampedCoords {
|
||||||
|
public:
|
||||||
|
ClampedCoords (int tw, int th) :
|
||||||
|
m_xpos(0), m_ypos(0),
|
||||||
|
m_xbegin(0),
|
||||||
|
m_xend(0),
|
||||||
|
m_total_width(tw), m_total_height(th)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset (int xbegin, int xend, int ybegin) {
|
||||||
|
m_xpos = m_xbegin = xbegin;
|
||||||
|
m_ypos = ybegin;
|
||||||
|
m_xend = xend;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClampedCoords& operator++() {
|
||||||
|
if (++m_xpos < m_xend) {
|
||||||
|
// Special case: we only incremented x, didn't change y
|
||||||
|
// or z, and the previous position was within the data
|
||||||
|
// window. Call a shortcut version of pos.
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Wrap to the next scanline
|
||||||
|
m_xpos = m_xbegin;
|
||||||
|
++m_ypos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void advance_row() { ++m_ypos; }
|
||||||
|
int index (int nchannels) const { return xpos() * nchannels + ypos() * m_total_width * nchannels; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int xpos() const { return clamp(m_xpos, 0, m_total_width - 1); }
|
||||||
|
int ypos() const { return clamp(m_ypos, 0, m_total_height - 1); }
|
||||||
|
|
||||||
|
int m_xpos, m_ypos;
|
||||||
|
int m_xbegin;
|
||||||
|
int m_xend;
|
||||||
|
int m_total_width, m_total_height;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[gnu::pure]]
|
||||||
|
/// Return (x-floor(x)) and put (int)floor(x) in *xint. This is similar
|
||||||
|
/// to the built-in modf, but returns a true int, always rounds down
|
||||||
|
/// (compared to modf which rounds toward 0), and always returns
|
||||||
|
/// frac >= 0 (comapred to modf which can return <0 if x<0).
|
||||||
|
float floorfrac (float x, int *xint)
|
||||||
|
{
|
||||||
|
#if 1
|
||||||
|
float f = std::floor(x);
|
||||||
|
*xint = int(f);
|
||||||
|
return x - f;
|
||||||
|
#else /* vvv This idiom is slower */
|
||||||
|
int i = ifloor(x);
|
||||||
|
*xint = i;
|
||||||
|
return x - static_cast<float>(i); // Return the fraction left over
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[[gnu::pure]]
|
||||||
|
int ifloor (float x) {
|
||||||
|
return static_cast<int>(std::floor(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[gnu::pure]]
|
||||||
|
float blackman_harris_bh1d (float x) {
|
||||||
|
if (x < -1.0f || x > 1.0f) // Early out if outside filter range
|
||||||
|
return 0.0f;
|
||||||
|
// Compute BH. Straight from classic BH paper, but the usual
|
||||||
|
// formula assumes that the filter is centered at 0.5, so scale:
|
||||||
|
x = (x + 1.0f) * 0.5f;
|
||||||
|
const float A0 = 0.35875f;
|
||||||
|
const float A1 = -0.48829f;
|
||||||
|
const float A2 = 0.14128f;
|
||||||
|
const float A3 = -0.01168f;
|
||||||
|
const float m_pi(M_PI);
|
||||||
|
#if 0
|
||||||
|
// original -- three cos calls!
|
||||||
|
return A0 + A1 * cosf(2.f * m_pi * x)
|
||||||
|
+ A2 * cosf(4.f * m_pi * x) + A3 * cosf(6.f * m_pi * x);
|
||||||
|
#else
|
||||||
|
// Use trig identintities to reduce to just one cos.
|
||||||
|
// https://en.wikipedia.org/wiki/List_of_trigonometric_identities
|
||||||
|
// cos(2x) = 2 cos^2(x) - 1
|
||||||
|
// cos(3x) = 4 cos^3(x) − 3 cos(x)
|
||||||
|
float cos2pix = std::cos(2.f * m_pi * x);
|
||||||
|
float cos4pix = 2.0f * cos2pix * cos2pix - 1.0f;
|
||||||
|
float cos6pix = cos2pix * (2.0f * cos4pix - 1.0f);
|
||||||
|
return A0 + A1 * cos2pix + A2 * cos4pix + A3 * cos6pix;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[[gnu::pure]]
|
||||||
|
float blackman_harris_xfilt (float x, float wrad_inv) {
|
||||||
|
return blackman_harris_bh1d(x * wrad_inv);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IT, typename T>
|
||||||
|
void normalize (IT beg, IT end, T div) {
|
||||||
|
while (beg != end) {
|
||||||
|
*beg /= div;
|
||||||
|
++beg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //unnamed namespace
|
||||||
|
|
||||||
|
std::vector<float> blackmanharris_resize (
|
||||||
|
int in_w,
|
||||||
|
int in_h,
|
||||||
|
const std::vector<float>& data,
|
||||||
|
int out_w,
|
||||||
|
int out_h
|
||||||
|
) {
|
||||||
|
assert(in_w > 0 and in_h > 0);
|
||||||
|
assert(out_w > 0 and out_h > 0);
|
||||||
|
//if (in_w == out_w and in_h == out_h)
|
||||||
|
// return data;
|
||||||
|
|
||||||
|
const constexpr int nchannels = 3;
|
||||||
|
|
||||||
|
// Local copies of the source image window, converted to float
|
||||||
|
const constexpr float srcfx = 0.0f;
|
||||||
|
const constexpr float srcfy = 0.0f;
|
||||||
|
const float srcfw = static_cast<float>(in_w);
|
||||||
|
const float srcfh = static_cast<float>(in_h);
|
||||||
|
|
||||||
|
// Ratios of dst/src size. Values larger than 1 indicate that we
|
||||||
|
// are maximizing (enlarging the image), and thus want to smoothly
|
||||||
|
// interpolate. Values less than 1 indicate that we are minimizing
|
||||||
|
// (shrinking the image), and thus want to properly filter out the
|
||||||
|
// high frequencies.
|
||||||
|
const float xratio = static_cast<float>(out_w) / srcfw; // 2 upsize, 0.5 downsize
|
||||||
|
const float yratio = static_cast<float>(out_h) / srcfh;
|
||||||
|
|
||||||
|
const float filter_width = 3.0f * std::max(1.0f, xratio);
|
||||||
|
const float& filter_height = filter_width;
|
||||||
|
const constexpr float dstfx = 0.0f;
|
||||||
|
const constexpr float dstfy = 0.0f;
|
||||||
|
const float dstfw = static_cast<float>(out_w);
|
||||||
|
const float dstfh = static_cast<float>(out_h);
|
||||||
|
const float dstpixelwidth = 1.0f / dstfw;
|
||||||
|
const float dstpixelheight = 1.0f / dstfh;
|
||||||
|
std::array<float, nchannels> pel;
|
||||||
|
const float filterrad = filter_width / 2.0f;
|
||||||
|
|
||||||
|
// radi,radj is the filter radius, as an integer, in source pixels. We
|
||||||
|
// will filter the source over [x-radi, x+radi] X [y-radj,y+radj].
|
||||||
|
const int radi = static_cast<int>(std::ceil (filterrad/xratio));
|
||||||
|
const int radj = static_cast<int>(std::ceil (filterrad/yratio));
|
||||||
|
const int xtaps = 2*radi + 1;
|
||||||
|
const int ytaps = 2*radj + 1;
|
||||||
|
std::vector<float> yfiltval(ytaps);
|
||||||
|
|
||||||
|
// For separable filters, horizontal tap weights will be the same
|
||||||
|
// for every column. So we precompute all the tap weights for every
|
||||||
|
// x position we'll need. We do the same thing in y, but row by row
|
||||||
|
// inside the loop (since we never revisit a y row). This
|
||||||
|
// substantially speeds up resize.
|
||||||
|
std::vector<float> xfiltval_all(xtaps * out_w);
|
||||||
|
for (int x = 0; x < out_w; ++x) {
|
||||||
|
float * const xfiltval = xfiltval_all.data() + x * xtaps;
|
||||||
|
const float s = (x-dstfx+0.5f)*dstpixelwidth;
|
||||||
|
const float src_xf = srcfx + s * srcfw;
|
||||||
|
int src_x;
|
||||||
|
float src_xf_frac = floorfrac (src_xf, &src_x);
|
||||||
|
std::fill(pel.begin(), pel.end(), 0.0f);
|
||||||
|
for (int i = 0; i < xtaps; ++i) {
|
||||||
|
const float w = blackman_harris_xfilt (xratio * (i-radi-(src_xf_frac-0.5f)), 2.0f / filter_width);
|
||||||
|
xfiltval[i] = w;
|
||||||
|
}
|
||||||
|
const float totalweight_x = std::accumulate(xfiltval, xfiltval + xtaps, 0.0f);
|
||||||
|
if (totalweight_x != 0.0f) {
|
||||||
|
normalize(xfiltval, xfiltval + xtaps, totalweight_x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're going to loop over all output pixels we're interested in.
|
||||||
|
//
|
||||||
|
// (s,t) = NDC space coordinates of the output sample we are computing.
|
||||||
|
// This is the "sample point".
|
||||||
|
// (src_xf, src_xf) = source pixel space float coordinates of the
|
||||||
|
// sample we're computing. We want to compute the weighted sum
|
||||||
|
// of all the source image pixels that fall under the filter when
|
||||||
|
// centered at that location.
|
||||||
|
// (src_x, src_y) = image space integer coordinates of the floor,
|
||||||
|
// i.e., the closest pixel in the source image.
|
||||||
|
// src_xf_frac and src_yf_frac are the position within that pixel
|
||||||
|
// of our sample.
|
||||||
|
//
|
||||||
|
// Separate cases for separable and non-separable filters.
|
||||||
|
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
std::vector<float> retval(nchannels * out_w * out_h, 1.0f);
|
||||||
|
#else
|
||||||
|
std::vector<float> retval(nchannels * out_w * out_h);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto out = retval.begin();
|
||||||
|
ClampedCoords srcpel(in_w, in_h);
|
||||||
|
|
||||||
|
for (int y = 0; y < out_h; ++y) {
|
||||||
|
const float t = (y-dstfy+0.5f)*dstpixelheight;
|
||||||
|
const float src_yf = srcfy + t * srcfh;
|
||||||
|
int src_y;
|
||||||
|
const float src_yf_frac = floorfrac (src_yf, &src_y);
|
||||||
|
// If using separable filters, our vertical set of filter tap
|
||||||
|
// weights will be the same for the whole scanline we're on. Just
|
||||||
|
// compute and normalize them once.
|
||||||
|
for (int j = 0; j < ytaps; ++j) {
|
||||||
|
const float w = blackman_harris_xfilt(yratio * (j-radj-(src_yf_frac-0.5f)), 2.0f / filter_height);
|
||||||
|
yfiltval[j] = w;
|
||||||
|
}
|
||||||
|
const float totalweight_y = std::accumulate(yfiltval.begin(), yfiltval.end(), 0.0f);
|
||||||
|
if (totalweight_y != 0.0f) {
|
||||||
|
normalize(yfiltval.begin(), yfiltval.end(), totalweight_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int x = 0; x < out_w; ++x, out += nchannels) {
|
||||||
|
const float s = (x-dstfx+0.5f)*dstpixelwidth;
|
||||||
|
const float src_xf = srcfx + s * srcfw;
|
||||||
|
const int src_x = ifloor (src_xf);
|
||||||
|
std::fill_n(pel.begin(), pel.size(), 0.0f);
|
||||||
|
const float * const xfiltval = xfiltval_all.data() + x * xtaps;
|
||||||
|
const float totalweight_x = std::accumulate(xfiltval, xfiltval + xtaps, 0.0f);
|
||||||
|
if (totalweight_x != 0.0f) {
|
||||||
|
srcpel.reset(src_x-radi, src_x+radi+1, src_y-radj/*, src_y+radj+1*/);
|
||||||
|
for (int j = -radj; j <= radj; ++j) {
|
||||||
|
float wy = yfiltval[j+radj];
|
||||||
|
if (wy == 0.0f) {
|
||||||
|
// 0 weight for this y tap -- move to next line
|
||||||
|
//srcpel.pos (srcpel.x(), srcpel.y()+1, srcpel.z());
|
||||||
|
srcpel.advance_row();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < xtaps; ++i, ++srcpel) {
|
||||||
|
const float w = wy * xfiltval[i];
|
||||||
|
if (w) {
|
||||||
|
const auto index = srcpel.index(nchannels);
|
||||||
|
for (int c = 0; c < nchannels; ++c) {
|
||||||
|
pel[c] += w * data[index + c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Copy the pixel value (already normalized) to the output.
|
||||||
|
if (totalweight_y == 0.0f) {
|
||||||
|
std::fill_n(out, nchannels, 0.0f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(pel.size() == nchannels);
|
||||||
|
std::copy_n(pel.begin(), pel.size(), out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
11
src/gui/resize_harris.hpp
Normal file
11
src/gui/resize_harris.hpp
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
std::vector<float> blackmanharris_resize (
|
||||||
|
int in_w,
|
||||||
|
int in_h,
|
||||||
|
const std::vector<float>& data,
|
||||||
|
int out_w,
|
||||||
|
int out_h
|
||||||
|
);
|
Loading…
Add table
Reference in a new issue