使用像素阵列在 SFML 中操作图像

发布于 2024-11-15 11:25:43 字数 362 浏览 2 评论 0原文

嘿,所以我正在尝试创建一个接受图像引用和 sf::IntRect 的函数

这有 4 个整数,代表矩形的顶部、底部、左侧和右侧。这个矩形代表正在显示的图像,但实际上并没有切除矩形之外的像素)

,然后通过切除 IntRect 之外的像素来创建一个新的压缩图像。我想我能做到这一点的唯一方法是制作一个与 IntRect 尺寸相同的二维像素数组,然后通过迭代图像来填充它,但由于我不知道 IntRect 的尺寸,所以我无法制作一个常量数组......

这个问题不断出现,我假设做一堆向量工作和转换到 c_style 数组会消耗大量性能..

是否有一些简单的解决方案来大量操作/更改维度和颜色图像?

Hey so i'm trying to make a function that takes in an image refrence and sf::IntRect

(this has 4 ints representing the top,bottom,left, and right sides of a rectangle. this rectangle represents the part of the image being displayed, but does not actually cut off the pixels outside the rectangle)

and then creates a new condensed image by cutting off the pixels outside the IntRect. I figured the only way i could do this was by making a 2d pixel array of the same dimensions as the IntRect, and then filling it by iterating through the image, but since i do not know the IntRect's dimensions i can't make a constant array......

This problem continually comes up, i assume doing a bunch of vector work and convesions to c_style arrays would cost a lot of performance..

Is there some simple sollution to heavily manipulating/ changing the dimensions and colors of images?

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

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

发布评论

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

评论(1

太阳哥哥 2024-11-22 11:25:44

存储和访问二维图像(或任何具有可变大小的二维矩阵)的常用方法是分配合适大小的一维数组(宽度*高度,或(width + padding) * height),并“手动”计算数组的索引 (y * stride + x,其中 stride宽度+内边距)。

(我通常使用 std::vector 来表示一维数组,但这是一个不同的故事)

引用 2D 图像数据的常用方法(按引用传递中的引用)是传递元组:

  • void* imageData -- 左上角像素的地址
  • size_t stride -- 添加到 imageData 的字节数到达下一行
  • size_t width
  • size_t height
  • SomeEnum PixelFormat --图像的像素格式(如果只有一个可以省略)

当然如果有的话仅是一种像素格式,您可以使用类型指针,并以该类型(而不是字节)为单位指定stride

使用这样的引用,您可以非常轻松且高效地访问循环中的像素:以

size_t const bytesPerPixel = GetBytesPerPixel(ref->pixelFormat);

for (size_t y = 0; y < ref->height; y++)
{
    unsigned char* currentLine =
        static_cast<unsigned char*>(ref->imageData) + ref->stride * y;

    for (size_t x = 0; x < ref->width; x++)
    {
        // any modern compiler will optimize the multiplication below to
        // an incremental addition
        unsigned char* currentPixel = currentLine + bytesPerPixel * y;

        // do something to the pixel data at
        // currentPixel[0] ... currentPixel[bytesPerPixel - 1]
    }
}

这种方式传递图像引用的好处是,您始终可以“调整”这样的引用以指向原始图像的子矩形。您只需相应地调整值:使 imageData 指向子矩形的左上角像素,并将 widthheight 设置为子矩形的宽度和高度。 stride 保持不变。

这意味着您不必“具体化”裁剪后的图像,您只需将对子矩形的引用传递给任何函数,它将对该子矩形进行操作,就像在“完整”图像上一样。

如果您确实想“具体化”裁剪后的图像,那么您现在也应该有足够的信息来做到这一点。至少我希望如此:)

编辑:由于您对 sf::IntRect 部分非常明确,但随后只写了“image”而不是 sf::Image,我认为您在谈论某些内容您管理自己,而不是 sf::Image。好吧...

如果您只想将 sf::Image 的子矩形复制到另一个 sf::Image,您可以这样做:

sf::Image sourceImage = ...;
sf::IntRect subRect = ...;

// construct an empty sf::Image with the appropriate dimensions
sf::Image newImage(subRect.GetWidth(), subRect.GetHeight());

// copy the pixel data into the new image
newImage.Copy(sourceImage, 0, 0, subRect);

The usual way to store and access 2-dimensional images (or any 2-dimensional matrix with variable size), is to allocate a 1-dimensional array of suitable size (width * height, or (width + padding) * height), and calculate the index into the array "manually" (y * stride + x, where stride is width + padding).

(I normally use std::vector for that 1-dimensional array, but that's a different story)

The usual way to reference 2D image data (reference as in pass-by-reference), is to pass a tuple of:

  • void* imageData -- the address of the top-left pixel
  • size_t stride -- the number of bytes to add to imageData to get to the next line
  • size_t width
  • size_t height
  • SomeEnum pixelFormat -- the pixel format of the image (can be omitted if there is only one)

Of course, if there is only one Pixel-Format, you can use a typed pointer, and specify the stride in units of that type (instead of bytes).

Using such a reference, you can access pixels in a loop quite easily and quite efficient:

size_t const bytesPerPixel = GetBytesPerPixel(ref->pixelFormat);

for (size_t y = 0; y < ref->height; y++)
{
    unsigned char* currentLine =
        static_cast<unsigned char*>(ref->imageData) + ref->stride * y;

    for (size_t x = 0; x < ref->width; x++)
    {
        // any modern compiler will optimize the multiplication below to
        // an incremental addition
        unsigned char* currentPixel = currentLine + bytesPerPixel * y;

        // do something to the pixel data at
        // currentPixel[0] ... currentPixel[bytesPerPixel - 1]
    }
}

The nice thing about passing image references that way is, that you can always "adjust" such a reference to point to a sub-rect of the original image. You just have to adjust the values accordingly: make imageData point to the top-left pixel of the sub-rect and set width and height to the width and height of the sub-rect. stride stays the same.

That means you don't have to "materialize" the cropped image, you can just pass a reference to a sub-rect to any function, and it will operate on that sub-rect just as it would on a "complete" image.

And if you really want to "materialize" a cropped image, you should now have enough information to do that too. At least I hope so :)

EDIT: Since you were very explicit about the sf::IntRect part, but then only wrote "image" instead of sf::Image, I assumed you were talking about something you manage yourself, not an sf::Image. Well...

If you just want to copy a sub-rect of an sf::Image to another sf::Image, you can just do this:

sf::Image sourceImage = ...;
sf::IntRect subRect = ...;

// construct an empty sf::Image with the appropriate dimensions
sf::Image newImage(subRect.GetWidth(), subRect.GetHeight());

// copy the pixel data into the new image
newImage.Copy(sourceImage, 0, 0, subRect);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文