Question

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?

Was it helpful?

Solution

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);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top