SFML community forums
General => Feature requests => Topic started by: lzr on April 27, 2008, 11:49:35 pm
-
There are many times in the game I'm porting in which the engine loads a part of an image. The image file to be loaded maybe 256x256, but the game may only load a 64x64 chunk located at (32,128). I can't find any way currently for the engine to do this. The function that comes closest is sf::Image::Resize, but this only let's one specify a size, not a position. This would be a very useful, and fairly easy, feature to program for the future.
Edit: I decided to code the feature myself. If you want, you can add it to the actual code.
////////////////////////////////////////////////////////////
/// Resize the image (no offset) - warning : this function does not scale the image,
/// it just adjusts size (add padding or remove pixels)
////////////////////////////////////////////////////////////
bool Image::Resize(unsigned int Width, unsigned int Height, const Color& Col)
{
return Resize(0, 0, Width, Height, Col);
}
////////////////////////////////////////////////////////////
/// Resize the image (with offset) - warning : this function does not scale the image,
/// it just adjusts size (add padding or remove pixels) and translates the image based on an offset
////////////////////////////////////////////////////////////
bool Image::Resize(unsigned int OffsetX, unsigned int OffsetY, unsigned int Width, unsigned int Height, const Color& Col)
{
// Check size
if ((Width == 0) || (Height == 0))
{
std::cerr << "Invalid new size for image (width = " << Width << ", height = " << Height << ")" << std::endl;
return false;
}
// Create a new pixel array with the desired size
std::vector<Color> Pixels(Width * Height, Col);
// Copy the old pixel buffer into the new one
for (unsigned int i = OffsetX; i < std::min(Width+OffsetX, myWidth); ++i)
for (unsigned int j = OffsetY; j < std::min(Height+OffsetY, myHeight); ++j)
Pixels[(i-OffsetX) + (j-OffsetY) * Width] = myPixels[i + j * myWidth];
Pixels.swap(myPixels);
// Store the new texture dimensions
myWidth = Width;
myHeight = Height;
// We can create the texture
if (CreateTexture())
{
return true;
}
else
{
// Oops... something failed
Reset();
return false;
}
}
-
Yep, I'm planning to add such function.
But I think I'd rather provide a subrect to the Load functions ; it would be more efficient.
-
What you've done here Izr is I guess not RESIZE, but CLIP. Besides, your function has to load the whole image first and then it clips a part of it.
I guess it would be much more efficient and useful if we could specify the coordinates to extract from an image on the disk.
Would it be hard to implement huge image support in SFML? I mean an engine that would cut the image into rectangles for the openGL and take care of keeping all pieces in the right place when rotating or zooming?
Either loading part of an image or adding large image support would help me a lot. Thanks.
-
Would it be hard to implement huge image support in SFML? I mean an engine that would cut the image into rectangles for the openGL and take care of keeping all pieces in the right place when rotating or zooming?
I won't be simple, but this is an important feature and I'll have to find a solution. This will be one of the priorities for the next version.
-
Yeah, I know that clips not resizes. But the original Resize function also clipped, it just didn't let you choose which part of the image to clip from. The syntax of the load function and the Resize function is very similar, yet for some reason, I can't get the code functioning properly in the load function. There's probably some stupid thing I'm missing, maybe you could take a look at it and figure it out. Giving a value of 0 for the Width or the Height gives the image the original width and height.
////////////////////////////////////////////////////////////
/// Load pixels from an image file in memory
////////////////////////////////////////////////////////////
bool ImageLoader::LoadImageFromMemory(const char* Data, std::size_t SizeInBytes, std::vector<Color>& Pixels, unsigned int X, unsigned int Y, unsigned int& Width, unsigned int& Height)
{
// Clear the array (just in case)
Pixels.clear();
// Load the image and get a pointer to the pixels in memory
const unsigned char* Buffer = reinterpret_cast<const unsigned char*>(Data);
int Size = static_cast<int>(SizeInBytes);
int ImgWidth, ImgHeight, ImgChannels;
unsigned int uimgWidth,uimgHeight;
unsigned char* PixelsPtr = SOIL_load_image_from_memory(Buffer, Size, &ImgWidth, &ImgHeight, &ImgChannels, SOIL_LOAD_RGBA);
if (PixelsPtr)
{
// Assign the image properties
uimgWidth=ImgWidth, uimgHeight=ImgHeight;
if(Width==0) Width = ImgWidth-X;
if(Height==0) Height = ImgHeight-Y;
// Copy the loaded pixels to the pixel buffer
Pixels.reserve(Width * Height);
for (unsigned int x = X; x < std::min<unsigned int>(Width+X, uimgWidth); ++x)
for (unsigned int y = Y; y < std::min<unsigned int>(Height+Y, uimgHeight); ++y)
{
Uint8* Pixel = PixelsPtr + (y + x * uimgHeight) * 4;
Pixels.push_back(Color(Pixel[0], Pixel[1], Pixel[2], Pixel[3]));
}
// Free the loaded pixels (they are now in our own pixel buffer)
SOIL_free_image_data(PixelsPtr);
return true;
}
else
{
// Error, failed to load the image
std::cerr << "Failed to load image from memory. Reason : " << SOIL_last_result() << std::endl;
return false;
}
}
In my old engine, which was DirectX based, I made a class that did basically what you described. It was a separate class from the regular Picture class and it would load an image of any size and split it into an Array of pictures, which could then be drawn.