如何在不造成像素重叠的情况下缩小块

How to scale down blocks without causing pixel overlapping

本文关键字:重叠 情况下 缩小 像素 成像      更新时间:2023-10-16

我有一堆需要绘制成网格的块。现在显示它们没有缩放,一切都很好,但是当我试图缩小它们以适应一个窗口时,我得到了"缩放伪影",因为我使用了正常的缩放比例公式和浮动。

有办法避免这些问题吗?

常用示例数据:

Original length: 200000

缩小到25x25 pixel grid(对于开发和调试来说只有这么小)缩放后的最大长度:625 (25 * 25)

Scale-ratio: (625 / 200000) = 0,003125

示例数据1 -重叠,缩放块相互覆盖[Start, end]

1: 2100 => 2800
2: 2800 => 3600
3: 3600 => 4500
4: 4500 => 5500

跳过这个例子的输出,因为我认为例子2和3会让你明白这一点。为了完整留了下来。

示例数据2 - 2和3之间空格错误[Start, end]

1: 960 => 1440
2: 1440 => 1920
3: 1920 => 2400
1: 960 => 1440, length: 480, scaled length: 1.5:
2: 1440 => 1920, length: 480, scaled length: 1.5:
3: 1920 => 2400, length: 480, scaled length: 1.5:

像素起始,结束,长度

1: 3, 0, 1
2: 4, 0, 1
3: 6, 0, 1

显示网格:

[ 0, 0, 0, 1, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
...

示例数据3 - 1错误地向后移动了一步[Start, end]

1: 896 => 1344
2: 1344 => 1792
3: 1792 => 2240
1: 896 => 1344, length: 448, scaled length: 1.4:
2: 1344 => 1792, length: 448, scaled length: 1.4:
3: 1792 => 2240, length: 448, scaled length: 1.4:

像素起始,结束,长度

1: 2, 0, 1
2: 4, 0, 1
3: 5, 0, 1

显示网格:

[ 0, 0, 1, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
...

数据2和3应该是什么样子?

[ 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
...

记住块值是[start, end)

先发制人(down-voters/trollers)记住:我不是通灵者或读心术者。

如果你想给负面的,以一种紧缩的方式去做,否则它是无用的(我不会学到任何东西),只会污染线程。

#include <iostream>
#include <math.h>
#include <limits.h>
#include <assert.h>
#include <vector>
#include <array>
#include <utility> // pair
#include <algorithm> // for_each
using namespace std;
const size_t width_size = 25; // 25 pixels
const size_t height_size = 25; // 25 pixels
const size_t grid_length = width_size * height_size; // width * height
array<size_t, grid_length> grid;
const size_t original_length = 200000;
typedef pair<unsigned long, unsigned long> block;
vector<block> test_values;
void show_grid()
{
    for (size_t y = 0; y < height_size; ++y) {
        const size_t start_pos_for_current_heigth = y * width_size;
        const size_t end_pos_for_current_heigth = start_pos_for_current_heigth + width_size;
        cout << "[ ";
        for (size_t i = start_pos_for_current_heigth; i < end_pos_for_current_heigth; ++i) {
            if (i + 1 < end_pos_for_current_heigth)
                cout << grid[i] << ", ";
            else
                cout << grid[i];
        };
        cout << " ]" << endl;
    }
}
void scale_and_add(const float scale)
{
    size_t test_value_id = 1;
    for_each(test_values.cbegin(), test_values.cend(), [&](const block &p) {
        const float s_f = p.first * scale;
        const unsigned long s = round(s_f);
        const float e_f = p.second * scale;
        const unsigned long e = round(e_f);
        const unsigned long block_length = p.second - p.first;
        const float block_length_scaled = block_length * scale;
        assert(s <= grid_length);
        assert(e <= grid_length);
        cout << test_value_id << ":" << endl;
        cout << " " << p.first << " => " << p.second << " length: " << block_length << endl;
        cout << " " << s << " (" << s_f << ") => " << e << " (" << e_f << ") length: " << (e - s) << " (" << block_length_scaled << ")" << " (scaled)" << endl;
        for (size_t i = s; i < e; ++i) {
            if (grid[i] != 0) {
                cout << "overlapp detected !" << endl;
            }
            grid[i] = test_value_id;
        }
        ++test_value_id;
    });
}
void reset_common()
{
    grid.fill(0);
    test_values.clear();
}
int main()
{
    const float scale = ((float)grid_length / (float)original_length);
    cout << "scale: " << scale << " length per pixel: " << ((float)original_length / (float)grid_length) << endl;
    // Example data 1
/*    cout << "Example data 1" << endl;
    test_values.push_back(make_pair(2100, 2800));
    test_values.push_back(make_pair(2800, 3600));
    test_values.push_back(make_pair(3600, 4500));
    test_values.push_back(make_pair(4500, 5500));
    scale_and_add(scale);
    show_grid();
    reset_common();
    // Example data 2
    cout << "Example data 2" << endl;
    test_values.push_back(make_pair(960, 1440));
    test_values.push_back(make_pair(1440, 1920));
    test_values.push_back(make_pair(1920, 2400));
    scale_and_add(scale);
    show_grid();
    reset_common();
    // Example data 3
    cout << endl << "Example data 3" << endl;
    test_values.push_back(make_pair(896, 1344));
    test_values.push_back(make_pair(1344, 1792));
    test_values.push_back(make_pair(1792, 2240));
    scale_and_add(scale);
    show_grid();
    reset_common();*/
    // Generated data - to quickly find the problem
    cout << "Generated data" << endl;
    auto to_op = [&](const size_t v) {
        return v * (original_length / grid_length) * 1.3; // 1.4 and 1.5 are also good values to show the problem
    };
    size_t pos = 0;
    size_t psize = 1; // Note this value (length) and check it with the displayed one, you'll be surprised !
    for (size_t g = 0; g < 10; ++g) {
        test_values.push_back(make_pair(to_op(pos), to_op(pos + psize)));
        pos += psize;
    }
    scale_and_add(scale);
    show_grid();
    return 0;
}
输出:

scale: 0.003125 length per pixel: 320
    Generated data
1:
 0 => 416 length: 416
 0 (0) => 1 (1.3) length: 1 (1.3) (scaled)
2:
 416 => 832 length: 416
 1 (1.3) => 3 (2.6) length: 2 (1.3) (scaled)
3:
 832 => 1248 length: 416
 3 (2.6) => 4 (3.9) length: 1 (1.3) (scaled)
4:
 1248 => 1664 length: 416
 4 (3.9) => 5 (5.2) length: 1 (1.3) (scaled)
5:
 1664 => 2080 length: 416
 5 (5.2) => 7 (6.5) length: 2 (1.3) (scaled)
6:
 2080 => 2496 length: 416
 7 (6.5) => 8 (7.8) length: 1 (1.3) (scaled)
7:
 2496 => 2912 length: 416
 8 (7.8) => 9 (9.1) length: 1 (1.3) (scaled)
8:
 2912 => 3328 length: 416
 9 (9.1) => 10 (10.4) length: 1 (1.3) (scaled)
9:
 3328 => 3744 length: 416
 10 (10.4) => 12 (11.7) length: 2 (1.3) (scaled)
10:
 3744 => 4160 length: 416
 12 (11.7) => 13 (13) length: 1 (1.3) (scaled)
[ 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]

这个代码示例更清楚地说明了我的问题。

有趣的事实:我用来编写这个例子的mingw-g++显示的值略有不同。我通常使用visual studio 2010,但这次不能,因为我不在家。

我不确定我是否得到了问题陈述,但我会尝试一下。您有连续显示的信息范围,范围通常是[a,b)。当a和b直接表示你想要绘制的像素时,一切都很好,但是当你想要缩放整个东西时,你就会遇到问题。

不处理多行像素,如果你有两个范围R1=[a,b)和R2=[b,c)未缩放,你只是从a绘制到b-1和从b绘制到c-1,你的范围被绘制,那么缩放情况下的问题是什么从(int)(a*scale)到((b*scale)-1),然后从(int)(b*scale)到((int)(c*scale)-1),你可以使用任何float到int转换,四舍五入,地板或天花板,你应该ok。

下一个问题是,如果缩放范围小于1个像素,在这种情况下,您可能需要检测缩放范围的大小是否为0,并在计算结束时添加一个校正因子(以像素为单位)。

伪代码
DrawRanges(List<Range> ranges, float scale)
  int carry = 0;
  foreach(Range range in ranges)
  {
      int newStart = ceiling(range.start*scale);
      int newEnd = ceiling(range.end*scale)-1;
      if (newStart <= newEnd)
      {
        newEnd = newStart;
        ++carry;
      }
      DrawRange(newStart+carry,newEnd+carry);
  }

这将最终失败,如果你有更多的范围比块在你的缩放网格,你将不得不弄清楚如何完全放弃范围。在drawrange中,你将索引映射到一个实际的块坐标。

这解决你的问题了吗?

对,再加1,问题没问题。我不知道为什么人们认为不留下评论就投反对票很有趣。

好吧,对于这个问题:-)

通常在绘图时你会遇到这种重叠问题,在3D计算机图形中使用扫描线渲染器(DirectX &在OpenGL中,它们通常会跳过一个像素(即右侧和下方的所有像素)。也许这能帮到你。

也有可能当除法是完美的时候,你没有人工制品,所以你可能必须处理它(即。如果值是一个'完全整数',例如185.000000,则不要删除最后一个像素)。

HTH