added function for extracting a grid of tile surfaces from a larger SDL surface
This commit is contained in:
parent
d882e111b3
commit
9a6b80f443
|
@ -403,8 +403,6 @@ void Box::center(const glm::vec2& center)
|
|||
move(center - this->center());
|
||||
}
|
||||
|
||||
/* Called when a Box instance is converted into an SDL_Rect. Allows passing Box to SDL functions that
|
||||
* expect an SDL_Rect instead of an SDL_FRect */
|
||||
Box::operator SDL_Rect() const
|
||||
{
|
||||
return {static_cast<int>(left()), static_cast<int>(top()), static_cast<int>(width()), static_cast<int>(height())};
|
||||
|
|
|
@ -135,7 +135,16 @@ public:
|
|||
void sw(const glm::vec2&);
|
||||
void west(const glm::vec2&);
|
||||
void center(const glm::vec2&);
|
||||
|
||||
/*!
|
||||
* Called when a Box instance is converted into an SDL_Rect. Allows passing Box to SDL functions that
|
||||
* expect an SDL_Rect instead of an SDL_FRect. Corner values are converted from float to int using a static
|
||||
* cast.
|
||||
*
|
||||
* @return box with all corners cast to int and converted to SDL_Rect
|
||||
*/
|
||||
operator SDL_Rect() const;
|
||||
|
||||
void clear();
|
||||
|
||||
/*!
|
||||
|
|
|
@ -707,6 +707,76 @@ fs::path sb::copy_file(fs::path from, fs::path to, bool overwrite_ok)
|
|||
return destination;
|
||||
}
|
||||
|
||||
std::shared_ptr<SDL_Surface> sb::extract_area(const std::shared_ptr<SDL_Surface>& source, const sb::Box& area)
|
||||
{
|
||||
/* Create a destination surface with the same format as the source and is the size of the tile. */
|
||||
std::shared_ptr<SDL_Surface> destination {
|
||||
SDL_CreateRGBSurfaceWithFormat(source->flags, area.w, area.h, source->format->BitsPerPixel, source->format->format), SDL_FreeSurface};
|
||||
|
||||
if (destination.get() != nullptr)
|
||||
{
|
||||
/* Blit the source onto the destination */
|
||||
SDL_Rect rect = area;
|
||||
if (SDL_BlitSurface(source.get(), &rect, destination.get(), nullptr) == 0)
|
||||
{
|
||||
/* Rotate and mirror the tile for compatibility with OpenGL */
|
||||
std::shared_ptr<SDL_Surface> flipped {rotozoomSurfaceXY(destination.get(), 0, 1, -1, 0), SDL_FreeSurface};
|
||||
if (flipped.get() != nullptr)
|
||||
{
|
||||
return flipped;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream message;
|
||||
message << "Could not rotate source surface for tile extraction. " << SDL_GetError();
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream message;
|
||||
message << "Could not blit source pixels to destination surface for tile extraction. " << SDL_GetError();
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream message;
|
||||
message << "Could not create destination surface for tile extraction. " << SDL_GetError();
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<SDL_Surface>> sb::extract_tiles_by_count(const std::shared_ptr<SDL_Surface>& source, const glm::ivec2& count)
|
||||
{
|
||||
glm::ivec2 source_size {source->w, source->h};
|
||||
|
||||
/* Get the floor of the division of the source size by the tile count. Then add one to each dimension that has any remainder. This leads
|
||||
* to a tile size that may be bigger than the available pixels in the last tile in the dimension. In that case, the last tile will contain
|
||||
* the rest of the available pixels and will be smaller than the tile size. */
|
||||
glm::ivec2 tile_size {source_size / count + glm::ivec2(glm::bvec2(glm::mod(glm::fvec2(source_size), glm::fvec2(count))))};
|
||||
|
||||
/* Use SDL coordinate system for the area box, meaning at the top of the image y=0, and at the bottom y=height. The Box class uses GL
|
||||
* coordinates by default, but for image manipulation and SDL surfaces, the Box class supports flipping the Y-axis to use SDL coordinates. */
|
||||
sb::Box area {{0.0f, 0.0f}, tile_size, false};
|
||||
|
||||
/* Iterate over rows and columns, moving the area by one tile size each iteration and copying that area into a new surface in the vector
|
||||
* of tiles. */
|
||||
std::vector<std::shared_ptr<SDL_Surface>> tiles;
|
||||
for (int y = 0; y < count.y; y++)
|
||||
{
|
||||
area.left(0.0f);
|
||||
for (int x = 0; x < count.x; x++)
|
||||
{
|
||||
tiles.push_back(sb::extract_area(source, area));
|
||||
area.move({tile_size.x, 0.0f});
|
||||
}
|
||||
area.move({0.0f, tile_size.y});
|
||||
}
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
int SDL_SetRenderDrawColor(SDL_Renderer* renderer, const Color& color)
|
||||
{
|
||||
return SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
|
||||
/* SDL */
|
||||
/* SDL shared libraries */
|
||||
#include "SDL.h"
|
||||
#include "SDL_image.h"
|
||||
#include "SDL_pixels.h"
|
||||
|
@ -42,6 +42,7 @@
|
|||
#include "glm/gtx/integer.hpp"
|
||||
#include "json/json.hpp"
|
||||
#include "sdl2-gfx/SDL2_gfxPrimitives.h"
|
||||
#include "sdl2-gfx/SDL2_rotozoom.h"
|
||||
|
||||
/* SPACEBOX */
|
||||
#include "Box.hpp"
|
||||
|
@ -85,7 +86,6 @@ namespace sb
|
|||
std::vector<fs::path> glob(fs::path);
|
||||
fs::path get_next_file_name(fs::path, int = 0, std::string = "", std::string = "");
|
||||
|
||||
|
||||
/*!
|
||||
* Read the file at path into a string and return the string.
|
||||
*
|
||||
|
@ -118,6 +118,38 @@ namespace sb
|
|||
*/
|
||||
fs::path copy_file(fs::path to, fs::path from, bool overwrite_ok = false);
|
||||
|
||||
/*!
|
||||
* Copy the pixel data in a given rectangular area of an SDL surface into a new SDL surface.
|
||||
*
|
||||
* @warning The coordinate system of the rectangular area is expected to be in SDL format, meaning y=0 at the top of the surface, and y=height
|
||||
* at the bottom. However, the Box class uses GL format by default. To set the Y-axis to SDL format, pass false to Box::invert_y(bool).
|
||||
*
|
||||
* @param source surface to copy pixels from
|
||||
* @param area rectangular area to copy
|
||||
* @return shared pointer to a new SDL surface
|
||||
*/
|
||||
std::shared_ptr<SDL_Surface> extract_area(const std::shared_ptr<SDL_Surface>& surface, const sb::Box& area);
|
||||
|
||||
/*!
|
||||
* Copy pixel data from a given SDL surface into a vector of new SDL surfaces, transforming the original surface into equally sized tiles.
|
||||
* The number of tiles and size of each tile is determined by the count parameter. Count is a 2D vector indicating how many tiles per row
|
||||
* and how many rows. For example, if count is {4, 3}, 12 tiles total will be created, 4 per row.
|
||||
*
|
||||
* The tiles will be in order from the top left tile, going row by row until the bottom right tile. Note that y=0 at the top of image, not
|
||||
* the bottom, which is common in image manipulation, although is not the coordinate system GL uses, so tile 0 on the Y-axis is the top of
|
||||
* the image in this function.
|
||||
*
|
||||
* If the count of tiles does not divide evenly into the number of pixels in the surface in either dimension, the rightmost and/or
|
||||
* bottommost tiles will be smaller than the rest.
|
||||
*
|
||||
* The pixel data in the passed surface is copied into new shared pointers to new SDL surfaces.
|
||||
*
|
||||
* @param source surface to split into tiles
|
||||
* @param count number of tiles as columns x rows
|
||||
* @return vector of shared pointers to new SDL surfaces
|
||||
*/
|
||||
std::vector<std::shared_ptr<SDL_Surface>> extract_tiles_by_count(const std::shared_ptr<SDL_Surface>& source, const glm::ivec2& count);
|
||||
|
||||
/*!
|
||||
* Return an unsorted vector of keys from the passed map.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue