mirror of
https://github.com/SFML/SFML.git
synced 2024-11-28 22:31:09 +08:00
Specify sf::Image
's save format via a scoped enumeration
This commit is contained in:
parent
e185f6d53e
commit
40339ff06a
@ -36,7 +36,6 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
@ -54,6 +53,18 @@ class InputStream;
|
|||||||
class SFML_GRAPHICS_API Image
|
class SFML_GRAPHICS_API Image
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Supported formats for saving
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
enum class SaveFormat
|
||||||
|
{
|
||||||
|
BMP,
|
||||||
|
TGA,
|
||||||
|
PNG,
|
||||||
|
JPG
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Default constructor
|
/// \brief Default constructor
|
||||||
///
|
///
|
||||||
@ -220,13 +231,14 @@ public:
|
|||||||
/// if it already exists. This function fails if the image is empty.
|
/// if it already exists. This function fails if the image is empty.
|
||||||
///
|
///
|
||||||
/// \param filename Path of the file to save
|
/// \param filename Path of the file to save
|
||||||
|
/// \param format Encoding format to use
|
||||||
///
|
///
|
||||||
/// \return True if saving was successful
|
/// \return True if saving was successful
|
||||||
///
|
///
|
||||||
/// \see saveToMemory, loadFromFile
|
/// \see saveToMemory, loadFromFile
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
[[nodiscard]] bool saveToFile(const std::filesystem::path& filename) const;
|
[[nodiscard]] bool saveToFile(const std::filesystem::path& filename, SaveFormat format) const;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Save the image to a buffer in memory
|
/// \brief Save the image to a buffer in memory
|
||||||
@ -244,7 +256,7 @@ public:
|
|||||||
/// \see saveToFile, loadFromMemory
|
/// \see saveToFile, loadFromMemory
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
[[nodiscard]] std::optional<std::vector<std::uint8_t>> saveToMemory(std::string_view format) const;
|
[[nodiscard]] std::optional<std::vector<std::uint8_t>> saveToMemory(SaveFormat format) const;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Return the size (width and height) of the image
|
/// \brief Return the size (width and height) of the image
|
||||||
|
@ -317,44 +317,34 @@ bool Image::loadFromStream(InputStream& stream)
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
bool Image::saveToFile(const std::filesystem::path& filename) const
|
bool Image::saveToFile(const std::filesystem::path& filename, SaveFormat format) const
|
||||||
{
|
{
|
||||||
// Make sure the image is not empty
|
// Make sure the image is not empty
|
||||||
if (!m_pixels.empty() && m_size.x > 0 && m_size.y > 0)
|
if (!m_pixels.empty() && m_size.x > 0 && m_size.y > 0)
|
||||||
{
|
{
|
||||||
// Deduce the image type from its extension
|
const Vector2i convertedSize = Vector2i(m_size);
|
||||||
|
|
||||||
// Extract the extension
|
switch (format)
|
||||||
const std::filesystem::path extension = filename.extension();
|
|
||||||
const Vector2i convertedSize = Vector2i(m_size);
|
|
||||||
|
|
||||||
if (extension == ".bmp")
|
|
||||||
{
|
{
|
||||||
// BMP format
|
case SaveFormat::BMP:
|
||||||
if (stbi_write_bmp(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
if (stbi_write_bmp(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
||||||
return true;
|
return true;
|
||||||
}
|
break;
|
||||||
else if (extension == ".tga")
|
case SaveFormat::TGA:
|
||||||
{
|
if (stbi_write_tga(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
||||||
// TGA format
|
return true;
|
||||||
if (stbi_write_tga(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
break;
|
||||||
return true;
|
case SaveFormat::PNG:
|
||||||
}
|
if (stbi_write_png(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data(), 0))
|
||||||
else if (extension == ".png")
|
return true;
|
||||||
{
|
break;
|
||||||
// PNG format
|
case SaveFormat::JPG:
|
||||||
if (stbi_write_png(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data(), 0))
|
if (stbi_write_jpg(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data(), 90))
|
||||||
return true;
|
return true;
|
||||||
}
|
break;
|
||||||
else if (extension == ".jpg" || extension == ".jpeg")
|
default:
|
||||||
{
|
assert(false && "Invalid format enumeration provided");
|
||||||
// JPG format
|
return false;
|
||||||
if (stbi_write_jpg(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data(), 90))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
err() << "Image file extension " << extension << " not supported\n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,44 +354,39 @@ bool Image::saveToFile(const std::filesystem::path& filename) const
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
std::optional<std::vector<std::uint8_t>> Image::saveToMemory(std::string_view format) const
|
std::optional<std::vector<std::uint8_t>> Image::saveToMemory(SaveFormat format) const
|
||||||
{
|
{
|
||||||
// Make sure the image is not empty
|
// Make sure the image is not empty
|
||||||
if (!m_pixels.empty() && m_size.x > 0 && m_size.y > 0)
|
if (!m_pixels.empty() && m_size.x > 0 && m_size.y > 0)
|
||||||
{
|
{
|
||||||
// Choose function based on format
|
const Vector2i convertedSize = Vector2i(m_size);
|
||||||
const std::string specified = toLower(std::string(format));
|
|
||||||
const Vector2i convertedSize = Vector2i(m_size);
|
|
||||||
|
|
||||||
std::vector<std::uint8_t> buffer;
|
std::vector<std::uint8_t> buffer;
|
||||||
|
|
||||||
if (specified == "bmp")
|
switch (format)
|
||||||
{
|
{
|
||||||
// BMP format
|
case SaveFormat::BMP:
|
||||||
if (stbi_write_bmp_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
if (stbi_write_bmp_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
break;
|
||||||
else if (specified == "tga")
|
case SaveFormat::TGA:
|
||||||
{
|
if (stbi_write_tga_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
||||||
// TGA format
|
return buffer;
|
||||||
if (stbi_write_tga_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
break;
|
||||||
return buffer;
|
case SaveFormat::PNG:
|
||||||
}
|
if (stbi_write_png_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data(), 0))
|
||||||
else if (specified == "png")
|
return buffer;
|
||||||
{
|
break;
|
||||||
// PNG format
|
case SaveFormat::JPG:
|
||||||
if (stbi_write_png_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data(), 0))
|
if (stbi_write_jpg_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data(), 90))
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
break;
|
||||||
else if (specified == "jpg" || specified == "jpeg")
|
default:
|
||||||
{
|
assert(false && "Invalid format enumeration provided");
|
||||||
// JPG format
|
return std::nullopt;
|
||||||
if (stbi_write_jpg_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data(), 90))
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err() << "Failed to save image with format " << std::quoted(format) << std::endl;
|
err() << "Failed to save image" << std::endl;
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ TEST_CASE("[Graphics] sf::Image")
|
|||||||
|
|
||||||
SECTION("Successful load")
|
SECTION("Successful load")
|
||||||
{
|
{
|
||||||
const auto memory = sf::Image({24, 24}, sf::Color::Green).saveToMemory("png").value();
|
const auto memory = sf::Image({24, 24}, sf::Color::Green).saveToMemory(sf::Image::SaveFormat::PNG).value();
|
||||||
const sf::Image image(memory.data(), memory.size());
|
const sf::Image image(memory.data(), memory.size());
|
||||||
CHECK(image.getSize() == sf::Vector2u(24, 24));
|
CHECK(image.getSize() == sf::Vector2u(24, 24));
|
||||||
CHECK(image.getPixelsPtr() != nullptr);
|
CHECK(image.getPixelsPtr() != nullptr);
|
||||||
@ -344,13 +344,7 @@ TEST_CASE("[Graphics] sf::Image")
|
|||||||
|
|
||||||
SECTION("Successful load")
|
SECTION("Successful load")
|
||||||
{
|
{
|
||||||
const auto memory = []()
|
const auto memory = sf::Image({24, 24}, sf::Color::Green).saveToMemory(sf::Image::SaveFormat::PNG).value();
|
||||||
{
|
|
||||||
sf::Image savedImage;
|
|
||||||
savedImage.resize({24, 24}, sf::Color::Green);
|
|
||||||
return savedImage.saveToMemory("png").value();
|
|
||||||
}();
|
|
||||||
|
|
||||||
CHECK(image.loadFromMemory(memory.data(), memory.size()));
|
CHECK(image.loadFromMemory(memory.data(), memory.size()));
|
||||||
CHECK(image.getSize() == sf::Vector2u(24, 24));
|
CHECK(image.getSize() == sf::Vector2u(24, 24));
|
||||||
CHECK(image.getPixelsPtr() != nullptr);
|
CHECK(image.getPixelsPtr() != nullptr);
|
||||||
@ -384,24 +378,12 @@ TEST_CASE("[Graphics] sf::Image")
|
|||||||
{
|
{
|
||||||
SECTION("Invalid size")
|
SECTION("Invalid size")
|
||||||
{
|
{
|
||||||
CHECK(!sf::Image({10, 0}, sf::Color::Magenta).saveToFile("test.jpg"));
|
CHECK(!sf::Image({10, 0}, sf::Color::Magenta).saveToFile("test.jpg", sf::Image::SaveFormat::JPG));
|
||||||
CHECK(!sf::Image({0, 10}, sf::Color::Magenta).saveToFile("test.jpg"));
|
CHECK(!sf::Image({0, 10}, sf::Color::Magenta).saveToFile("test.jpg", sf::Image::SaveFormat::JPG));
|
||||||
}
|
}
|
||||||
|
|
||||||
const sf::Image image({256, 256}, sf::Color::Magenta);
|
const sf::Image image({256, 256}, sf::Color::Magenta);
|
||||||
|
|
||||||
SECTION("No extension")
|
|
||||||
{
|
|
||||||
CHECK(!image.saveToFile("wheresmyextension"));
|
|
||||||
CHECK(!image.saveToFile("pls/add/extension"));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Invalid extension")
|
|
||||||
{
|
|
||||||
CHECK(!image.saveToFile("test.ps"));
|
|
||||||
CHECK(!image.saveToFile("test.foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Successful save")
|
SECTION("Successful save")
|
||||||
{
|
{
|
||||||
auto filename = std::filesystem::temp_directory_path();
|
auto filename = std::filesystem::temp_directory_path();
|
||||||
@ -409,27 +391,32 @@ TEST_CASE("[Graphics] sf::Image")
|
|||||||
SECTION("To .bmp")
|
SECTION("To .bmp")
|
||||||
{
|
{
|
||||||
filename /= "test.bmp";
|
filename /= "test.bmp";
|
||||||
CHECK(image.saveToFile(filename));
|
CHECK(image.saveToFile(filename, sf::Image::SaveFormat::BMP));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("To .tga")
|
SECTION("To .tga")
|
||||||
{
|
{
|
||||||
filename /= "test.tga";
|
filename /= "test.tga";
|
||||||
CHECK(image.saveToFile(filename));
|
CHECK(image.saveToFile(filename, sf::Image::SaveFormat::TGA));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("To .png")
|
SECTION("To .png")
|
||||||
{
|
{
|
||||||
filename /= "test.png";
|
filename /= "test.png";
|
||||||
CHECK(image.saveToFile(filename));
|
CHECK(image.saveToFile(filename, sf::Image::SaveFormat::PNG));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cannot test JPEG encoding due to it triggering UB in stbiw__jpg_writeBits
|
// Cannot test JPEG encoding due to it triggering UB in stbiw__jpg_writeBits
|
||||||
|
|
||||||
const sf::Image loadedImage(filename);
|
SECTION("Filename without extension")
|
||||||
|
{
|
||||||
|
filename /= "test";
|
||||||
|
CHECK(image.saveToFile(filename, sf::Image::SaveFormat::PNG));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto loadedImage = sf::Image(filename);
|
||||||
CHECK(loadedImage.getSize() == sf::Vector2u(256, 256));
|
CHECK(loadedImage.getSize() == sf::Vector2u(256, 256));
|
||||||
CHECK(loadedImage.getPixelsPtr() != nullptr);
|
CHECK(loadedImage.getPixelsPtr() != nullptr);
|
||||||
|
|
||||||
CHECK(std::filesystem::remove(filename));
|
CHECK(std::filesystem::remove(filename));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -437,71 +424,49 @@ TEST_CASE("[Graphics] sf::Image")
|
|||||||
SECTION("saveToMemory()")
|
SECTION("saveToMemory()")
|
||||||
{
|
{
|
||||||
SECTION("Invalid size")
|
SECTION("Invalid size")
|
||||||
{
|
CHECK(!sf::Image({10, 0}, sf::Color::Magenta).saveToMemory(sf::Image::SaveFormat::JPG));
|
||||||
CHECK(!sf::Image({10, 0}, sf::Color::Magenta).saveToMemory("test.jpg"));
|
CHECK(!sf::Image({0, 10}, sf::Color::Magenta).saveToMemory(sf::Image::SaveFormat::JPG));
|
||||||
CHECK(!sf::Image({0, 10}, sf::Color::Magenta).saveToMemory("test.jpg"));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
SECTION("Successful save")
|
||||||
|
{
|
||||||
const sf::Image image({16, 16}, sf::Color::Magenta);
|
const sf::Image image({16, 16}, sf::Color::Magenta);
|
||||||
|
|
||||||
SECTION("No extension")
|
SECTION("To bmp")
|
||||||
{
|
{
|
||||||
CHECK(!image.saveToMemory(""));
|
const auto memory = image.saveToMemory(sf::Image::SaveFormat::BMP).value();
|
||||||
|
REQUIRE(memory.size() == 1146);
|
||||||
|
CHECK(memory[0] == 66);
|
||||||
|
CHECK(memory[1] == 77);
|
||||||
|
CHECK(memory[2] == 122);
|
||||||
|
CHECK(memory[3] == 4);
|
||||||
|
CHECK(memory[1000] == 255);
|
||||||
|
CHECK(memory[1001] == 255);
|
||||||
|
CHECK(memory[1002] == 255);
|
||||||
|
CHECK(memory[1003] == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Invalid extension")
|
SECTION("To tga")
|
||||||
{
|
{
|
||||||
CHECK(!image.saveToMemory("."));
|
const auto memory = image.saveToMemory(sf::Image::SaveFormat::TGA).value();
|
||||||
CHECK(!image.saveToMemory("gif"));
|
REQUIRE(memory.size() == 98);
|
||||||
CHECK(!image.saveToMemory(".jpg")); // Supposed to be "jpg"
|
CHECK(memory[0] == 0);
|
||||||
|
CHECK(memory[1] == 0);
|
||||||
|
CHECK(memory[2] == 10);
|
||||||
|
CHECK(memory[3] == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Successful save")
|
SECTION("To png")
|
||||||
{
|
{
|
||||||
std::optional<std::vector<std::uint8_t>> maybeOutput;
|
const auto memory = image.saveToMemory(sf::Image::SaveFormat::PNG).value();
|
||||||
|
REQUIRE(memory.size() == 92);
|
||||||
SECTION("To bmp")
|
CHECK(memory[0] == 137);
|
||||||
{
|
CHECK(memory[1] == 80);
|
||||||
maybeOutput = image.saveToMemory("bmp");
|
CHECK(memory[2] == 78);
|
||||||
REQUIRE(maybeOutput.has_value());
|
CHECK(memory[3] == 71);
|
||||||
const auto& output = *maybeOutput;
|
|
||||||
REQUIRE(output.size() == 1146);
|
|
||||||
CHECK(output[0] == 66);
|
|
||||||
CHECK(output[1] == 77);
|
|
||||||
CHECK(output[2] == 122);
|
|
||||||
CHECK(output[3] == 4);
|
|
||||||
CHECK(output[1000] == 255);
|
|
||||||
CHECK(output[1001] == 255);
|
|
||||||
CHECK(output[1002] == 255);
|
|
||||||
CHECK(output[1003] == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("To tga")
|
|
||||||
{
|
|
||||||
maybeOutput = image.saveToMemory("tga");
|
|
||||||
REQUIRE(maybeOutput.has_value());
|
|
||||||
const auto& output = *maybeOutput;
|
|
||||||
REQUIRE(output.size() == 98);
|
|
||||||
CHECK(output[0] == 0);
|
|
||||||
CHECK(output[1] == 0);
|
|
||||||
CHECK(output[2] == 10);
|
|
||||||
CHECK(output[3] == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("To png")
|
|
||||||
{
|
|
||||||
maybeOutput = image.saveToMemory("png");
|
|
||||||
REQUIRE(maybeOutput.has_value());
|
|
||||||
const auto& output = *maybeOutput;
|
|
||||||
REQUIRE(output.size() == 92);
|
|
||||||
CHECK(output[0] == 137);
|
|
||||||
CHECK(output[1] == 80);
|
|
||||||
CHECK(output[2] == 78);
|
|
||||||
CHECK(output[3] == 71);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cannot test JPEG encoding due to it triggering UB in stbiw__jpg_writeBits
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cannot test JPEG encoding due to it triggering UB in stbiw__jpg_writeBits
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Set/get pixel")
|
SECTION("Set/get pixel")
|
||||||
|
Loading…
Reference in New Issue
Block a user