2

我正在与 Vis 合作。Std 2010 Cpp 和我一直试图弄清楚如何从两个现有位图创建位图。

我真的不想使用两个 gl_drawbitmap 函数,每个函数一个,而是创建第三个位图和 gl_drawbitmap 新的一个到我的窗口。我也试图将第二个位图(我们称之为 b2)放在第一个(b1)的中间,所以伪代码看起来有点像:(假设 b2 可以说是 80 乘 60)

place b2 on b1 stretched from 
(width-wise middle of b1 -40, heightwise middle of b1-30) 
to (widthwise middle of b1+40, heightwise middle of b1+30)

我知道 glutGet(GLUT_WINDOW_WIDTH/HEIGHT) 可以找到当前窗口的特定测量值,但我不知道如何为位图找到它。我还没有找到证明它们可行的证据,但是 b1.getHeight() 和 b1.getWidth() 能解决问题吗?

总的来说,我在弄清楚如何将两者结合起来时遇到了很多麻烦。

void drawbitmap(bitmap* b1, bitmap* b2)
{
...
}

功能相关,这就是我试图将代码放入的内容。

4

1 回答 1

4

无论您拥有加载图像的任何功能,都应该为您提供两件事:一是图像信息,例如宽度和高度。另一个是图像数据,它是一个数字数组。

假设你得到图像b1b2,b1b2是 GLubyte 的数组。假设b1_w,b1_h和是b2_wand的宽度和b2_h高度(应该由位图加载函数提供给您)。b1b2

你想做的包括两部分,首先,你需要伸展b1。其次,你需要把它写在b2. 事实上,你可以同时做这两件事,但现在,我会把它们分开。

拉伸

要拉伸图像,您需要能够进行一些简单的数学运算。不过,在进入地图之前,我将告诉您图像转换(旋转、拉伸等)的基本概念。您可以做的最简单的事情是遍历目标图像的像素,将它们转换回原始图片,然后决定从原始图片中获取什么颜色并分配给该像素。

例如,如果您想将图像从宽度和高度拉伸到w1and ,则转换如下:h1w2h2

xt = w2/w1*xs
yt = h2/h1*ys

其中xtyt是目标图像中的坐标,xsys是源图像中的坐标。逆变换为:

xs = w1/w2*xt
ys = h1/h2*yt

在继续之前,让我们定义一个宏以使生活更轻松。由于数据数组是一维数组,但我们使用 x 和 y 索引,因此我们编写了一个宏来为我们进行转换。

#define INDEX(x, y, width) (((y)*(width)+(x))*3)

旁注:请注意,我假设图像存储在 RGB(或 BGR)中,即每个像素具有三个值。另请注意,如果您的图像宽度不是 4 的倍数,那么会有填充和每行宽度不会是width*3字节,而是width*3+something_to_make_it_divisible_by_4. 在第二种情况下,您可以像这样编写宏:

#define INDEX(x, y, row_bytes) ((y)*(row_bytes)+(x)*3) // note the parentheses

)

所以你的代码看起来像这样(float除非另有说明,否则所有宽度/高度和 x/y 值都是)

for (unsigned int yt = 0; yt < h2; ++yt)
    for (unsigned int xt = 0; xt < w2; ++xt)
    {
        float xs = w1/w2*xt;
        float ys = h1/h2*yt;
        target[INDEX(xt, yt, w2)] = // COMPUTED IN THE NEXT STEP
        target[INDEX(xt, yt, w2)+1] = // COMPUTED IN THE NEXT STEP
        target[INDEX(xt, yt, w2)+2] = // COMPUTED IN THE NEXT STEP
    }

由于存在三个颜色分量(R、G 和 B),因此存在三个赋值。如果您有 alpha 2,请将其设为 4。如果您在灰度中工作,请保持 1(您明白了)

下一步是像素颜色的实际分配。这是不同的拉伸算法不同的地方,拉伸的质量取决于这一步。

所以,这就是这个想法。想象一下xsys结果是完美的舍入整数。这很简单,您只需在源图像中选择该像素的颜色并将其放入目标中。xs但是,如果ys不是整数怎么办?那么你会为目标像素采用哪种颜色呢?这里有两种常用的方法。

最近的邻居

这很容易。它说,选择最接近您计算的位置的像素的颜色。例如,如果(xs, ys)(12.78, 98.2),您选择点的颜色(13, 98)。那是在您的代码中:

unsigned int xs_int = (unsigned int)round(xs);
unsigned int ys_int = (unsigned int)round(ys);
target[INDEX(xt, yt, w2)] = source[INDEX(xs_int, ys_int, w1)]

线性化

这种方法需要一些数学知识。它说的是,你取周围四个像素之间的线性化值,(xs, ys)然后选择它作为目标像素的颜色。那个怎么样?我会给你看。想象一下这四个像素和计算出的(xs, ys)

v_tl         v_tr
+------------+
|            |
|  .(xs, ys) |
|            |
|            |
|            |
+------------+
v_bl         v_br

如图所示,周围四个像素的值为v_{t,b}{l,r}(top、bottom、left、right的缩写)。要对 position 的值进行线性化(xs, ys),您可以获取的线性化值(floor(xs), ys)(ceil(xs), ys)再次对其进行线性化,或者首先对 y 然后 x 执行相同的操作,这是相同的。所以你得到:

unsigned int x_bl = (unsigned int)floor(xs);
unsigned int y_bl = (unsigned int)floor(ys);
float v_l = v_bl+(v_tl-v_bl)*(ys-y_bl); // (floor(xs), ys)
    // note that in the end, there must be a (x_tl-x_bl) but this value is 1
float v_r = v_br+(v_tr-v_br)*(ys-y_bl); // (ceil(xs), ys)
float v = v_l+(v_r-v_l)*(xs-x_bl);

最后,你得到

target[INDEX(xt, yt, w2)] = v;

请注意,您应该对图像的每个通道(例如 R、G 和 B)进行这种线性化,并将适当的 v 放入适当的索引中(INDEX(...)、INDEX(...)+1 和 INDEX( ...)+2 例如)。

请注意,在这两种方法中,您必须检查源图像中的计算索引,以免超出数组范围。如果你这样做了,你可以为那些超出数组边界的像素假设黑色/白色/中灰色。或者,您可以检查那些像素,如果像素不在数组中,您可以将方法更改为仅适用于源图像内的像素。

在另一张图片上书写

这部分很简单。你有图像stretched_b2,你想把它写在b1. stretched_b2如果您要写入的左下角位置b1(x_start, y_start),您只需编写:

for (unsigned int y = 0; y < h2_stretched; ++y)
    for (unsigned int x = 0; x < w2_stretched; ++x)
    {
        b1[INDEX(x_start+x, y_start+y, w1)] =
            stretched_b2[INDEX(x, y, w2_stretched)];
        b1[INDEX(x_start+x, y_start+y, w1)+1] =
            stretched_b2[INDEX(x, y, w2_stretched)+1];
        b1[INDEX(x_start+x, y_start+y, w1)+2] =
            stretched_b2[INDEX(x, y, w2_stretched)+2];
    }

你想把图像放在这里:

(b1 -40 的宽度中间,b1-30 的高度中间) 到 (b1+40 的宽度中间,b1+30 的高度中间)

很容易,你有

w2_stretched = 80        // or +1 if you want to be really precise,
                         // but for now let's say 80 to keep it a multiple of 4
h2_stretched = 60
x_start = w1/2-40
y_start = h1/2-30

结合两个部分

现在记住在拉伸部分,你有的部分

for (unsigned int yt = 0; yt < h2; ++yt)
    for (unsigned int xt = 0; xt < w2; ++xt)

在你拥有的放置部分

for (unsigned int y = 0; y < h2_stretched; ++y)
    for (unsigned int x = 0; x < w2_stretched; ++x)

那么在拉伸部分h2w2是拉伸目标图像的高度和宽度,还记得吗?这就是我所说的h2_stretchedw2_stretched放置部分。如您所见,这些 for 循环迭代相同的东西。现在在拉伸部分,您计算目标图像的值,并在放置部分复制它b1。好吧,您可以直接计算该值并将其覆盖,而不是将其保存在临时目标图像中b1,因此您将拥有:

for (unsigned int yt = 0; yt < h2; ++yt)
    for (unsigned int xt = 0; xt < w2; ++xt)
    {
        float xs = w1/w2*xt;
        float ys = h1/h2*yt;
        b1[INDEX(x_start+xt, y_start+yt, w1)] = compute_with_mentioned_methods;
        b1[INDEX(x_start+xt, y_start+yt, w1)+1] = compute_with_mentioned_methods;
        b1[INDEX(x_start+xt, y_start+yt, w1)+2] = compute_with_mentioned_methods;
    }

最后一点:图像处理虽然是微不足道的,但有很多索引检查,而且很难在第一次就正确处理。为分段错误做好准备,如果您需要一段时间来调试它,请不要感到沮丧。

后期注意事项:我解释了这些供您学习,但是使用 OpenGL,如果我是您,我会将两个图像都转换为纹理,QUADS在适当的位置绘制并让 OpenGL 为您拉伸图像。

于 2011-09-19T22:11:01.940 回答