在 C++ 中拉伸和覆盖位图

发布于 2024-12-05 12:37:51 字数 655 浏览 2 评论 0原文

我正在和维斯一起工作。 Std 2010 Cpp 和我一直在尝试找出如何从两个现有位图创建位图。

我真的不想使用两个 gl_drawbitmap 函数,每个函数一个,而是创建第三个位图和 gl_drawbitmap 将新的一个放到我的窗口上。我还尝试将第二个位图(我们称之为 b2)放置在第一个位图(b1)的中间,因此伪代码看起来有点像:(假设 b2 是 80 x 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) 来查找当前 WINDOW 的特定测量值,但我不知道如何为位图查找它。我还没有找到它们可行的证据,但是 b1.getHeight() 和 b1.getWidth() 可以解决问题吗?

总的来说,我在弄清楚如何进行两者的实际组合方面遇到了很多麻烦。

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

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

I'm working with Vis. Std 2010 Cpp and I've been trying to figure out how to create a bitmap from two existing bitmaps.

I don't really want to use two gl_drawbitmap functions, one for each, but rather create a third bitmap and gl_drawbitmap THAT new one out onto my window. I also am attempting to get the second bitmap(let's call it b2) to be placed in the middle of the first(b1), so the pseudo code would look kind of like: (assuming b2 is lets say 80 by 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)

I know of glutGet(GLUT_WINDOW_WIDTH/HEIGHT) to find a current WINDOW's specific measurements, but I don't know how to find it for a bitmap. I haven't been able to find proof that they are viable, but would b1.getHeight() and b1.getWidth() do the trick?

I'm just overall having plenty of trouble figuring out how to do the actual COMBINATION of the two.

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

Function related, it's what I'm trying to put the code in.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

就是爱搞怪 2024-12-12 12:37:51

无论您使用什么函数来加载图像,都应该为您提供两件事:一是图像信息,例如宽度和高度。另一个是图像数据,它是一个数字数组。

假设您得到图像 b1b2b1b2 是 GLubyte 数组。假设 b1_wb1_hb2_wb2_hb1 的宽度和高度code> 和 b2 (应该由位图加载函数提供给您)。

你想要做的事情由两部分组成,首先,你需要拉伸b1。其次,你需要把它写在b2的中间。事实上你可以同时做这两件事,但现在我将把它们分开。

拉伸

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

例如,如果要将图像的宽度和高度从 w1h1 拉伸到 w2h2,这是转换:

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 在第二种情况下,您可以编写如下宏。 this:

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

)

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

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(您明白了)

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

所以,这就是我们的想法。想象一下 xs 和 ys 是完美的四舍五入整数。嗯,这很简单,您只需选择源图像中该像素的颜色并将其放入目标中即可。但是,如果 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} (上、下、左、右的缩写)为如图所示。要线性化位置 (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。如果您要在 b1 中写入 stretched_b2 的左下位置是 (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 为您拉伸图像。

Whatever function you have that loads the image, should give you two things: One is the image info, such as width and height. Another is the image data, which is an array of numbers.

Let's say you get images b1 and b2, b1 and b2 being arrays of GLubyte. And let's say b1_w, b1_h, b2_w and b2_h are the width and heights of b1 and b2 (which should be provided to you by the bitmap load function).

What you want to do consists of two parts, first, you need to stretch b1. Second, you need to write it in the middle of b2. In fact you can do both at the same time, but for now, I will keep them separated.

Stretching

To stretch an image, you need to be able to do some simple math. Before getting to the map though, I will tell you the basic idea in image transformation (rotation, stretch etc). The simplest thing you can do is to go through the pixels of the target image, transform them back to the original picture, and decide what color to take from the original picture and assign to that pixel.

For example, if you want to stretch an image from width and height w1 and h1 to w2 and h2, here's the transformation:

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

where xt and yt are coordinates in the target image and xs and ys are coordinates in the source image. The inverse transformation is:

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

Before continuing, let's define a macro to make life easier. Since the array of data is a 1d array, but we use x and y indices, we write a macro to do the conversion for us.

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

(Sidenote: Note that I assumed the image is stored in RGB (or BGR), that is it has three values per pixel. Also note that if your image width is not a multiple of 4, then there would be padding and each row width will not be width*3 bytes, but would be width*3+something_to_make_it_divisible_by_4. In the second case, you could write the macro like this:

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

)

So your code would look like this (all the width/height and x/y values are float unless otherwise specified)

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
    }

There are three value assignments, because there are three color components (R, G and B). If you have alpha two, make it 4. If you are working in grayscale, keep it 1 (you get the idea)

Next step is the actual assignment of the pixel color. This is where different stretching algorithms vary and the quality of your stretching is dependent on this step.

So, here's the idea. Imagine xs and ys to turn out to be perfect rounded integers. Well that's easy, you just pick the color of that pixel in the source image and put it in the target. BUT, what if xs and ys are not integers? Then which color would you take for the target pixel? Here are two common methods.

Nearest Neighbor

This one is easy. It says, choose the color of the pixel that is closest to the location you computed. For example, if (xs, ys) was (12.78, 98.2), you choose the color of the point (13, 98). That is in your code you have:

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)]

Linearization

This method needs a bit of math. What it says is, you take the linearized value in between the four pixels around (xs, ys) and select that as the target pixel's color. How is that? I'll show you. Imagine these four pixels and the computed (xs, ys)

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

The values of the four surrounding pixels are v_{t,b}{l,r} (abbreviations of top, bottom, left and right) as shown in the picture. To linearize the value for position (xs, ys), you could either get the linearized value of (floor(xs), ys) and (ceil(xs), ys) and again linearize for that, or do the same first over y then x which is the same. So you get:

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);

Finally, you get

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

Note that you should do this linearization for every channel of the image (R, G and B for example) and put the appropriate v in the appropriate index (INDEX(...), INDEX(...)+1 and INDEX(...)+2 for example).

Note that in both methods you MUST check for the computed indices in the source image so that you wouldn't go out of the array bounds. If you do, you can assume a color of black/white/midgray for those pixels that go outside the array bounds. Or, you can check for those and if the pixel was out of the array, you change your method to only work with the ones that are inside the source image.

Writing over the other image

This part is simple. You have the image stretched_b2 and you want to write it on b1. If the bottom left position where you want to write stretched_b2 in b1 is (x_start, y_start), you simply write:

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];
    }

You want to place the image here:

(width-wise middle of b1 -40, heightwise middle of b1-30)
to (widthwise middle of b1+40, heightwise middle of b1+30)

so easily, you have

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

Combining the two parts

Now remember in the stretching section, the part where you had

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

and in the placing section that you had

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

Well in the stretching section h2 and w2 were the height and width of the stretched target image, remember? That's what I called h2_stretched and w2_stretched in the placing section. So as you can see, these for loops iterate the same thing. Now in the stretching section, you compute the value of the target image, and in the placing section you copy it over b1. Well, instead of keeping it in a temporary target image, you can directly compute the value and write it over b1, so you'd have:

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;
    }

Final note: Image manipulation is although trivial when you say it, has a lot of index checking and it's hard to get it right the first time. Be ready for segmentation faults and don't feel upset if it took you a while to debug it.

Post-final note: I explained these for you to learn, but with OpenGL, if I were you I would turn both images into textures, draw QUADS in appropriate positions and have OpenGL stretch the image for you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文