Manipulate Images in SFML using Pixel Arrays
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 th开发者_运维知识库is 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?
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 pixelsize_t stride
-- the number of bytes to add toimageData
to get to the next linesize_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);
精彩评论