mirror of
https://github.com/SFML/SFML.git
synced 2024-11-24 20:31:05 +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 <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <cstddef>
|
||||
@ -54,6 +53,18 @@ class InputStream;
|
||||
class SFML_GRAPHICS_API Image
|
||||
{
|
||||
public:
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Supported formats for saving
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
enum class SaveFormat
|
||||
{
|
||||
BMP,
|
||||
TGA,
|
||||
PNG,
|
||||
JPG
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Default constructor
|
||||
///
|
||||
@ -220,13 +231,14 @@ public:
|
||||
/// if it already exists. This function fails if the image is empty.
|
||||
///
|
||||
/// \param filename Path of the file to save
|
||||
/// \param format Encoding format to use
|
||||
///
|
||||
/// \return True if saving was successful
|
||||
///
|
||||
/// \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
|
||||
@ -244,7 +256,7 @@ public:
|
||||
/// \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
|
||||
|
@ -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
|
||||
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
|
||||
const std::filesystem::path extension = filename.extension();
|
||||
const Vector2i convertedSize = Vector2i(m_size);
|
||||
|
||||
if (extension == ".bmp")
|
||||
switch (format)
|
||||
{
|
||||
// BMP format
|
||||
if (stbi_write_bmp(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
||||
return true;
|
||||
}
|
||||
else if (extension == ".tga")
|
||||
{
|
||||
// TGA format
|
||||
if (stbi_write_tga(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
||||
return true;
|
||||
}
|
||||
else if (extension == ".png")
|
||||
{
|
||||
// PNG format
|
||||
if (stbi_write_png(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data(), 0))
|
||||
return true;
|
||||
}
|
||||
else if (extension == ".jpg" || extension == ".jpeg")
|
||||
{
|
||||
// JPG format
|
||||
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";
|
||||
case SaveFormat::BMP:
|
||||
if (stbi_write_bmp(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
||||
return true;
|
||||
break;
|
||||
case SaveFormat::TGA:
|
||||
if (stbi_write_tga(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
||||
return true;
|
||||
break;
|
||||
case SaveFormat::PNG:
|
||||
if (stbi_write_png(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data(), 0))
|
||||
return true;
|
||||
break;
|
||||
case SaveFormat::JPG:
|
||||
if (stbi_write_jpg(filename.string().c_str(), convertedSize.x, convertedSize.y, 4, m_pixels.data(), 90))
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
assert(false && "Invalid format enumeration provided");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
if (!m_pixels.empty() && m_size.x > 0 && m_size.y > 0)
|
||||
{
|
||||
// Choose function based on format
|
||||
const std::string specified = toLower(std::string(format));
|
||||
const Vector2i convertedSize = Vector2i(m_size);
|
||||
|
||||
const Vector2i convertedSize = Vector2i(m_size);
|
||||
std::vector<std::uint8_t> buffer;
|
||||
|
||||
if (specified == "bmp")
|
||||
switch (format)
|
||||
{
|
||||
// BMP format
|
||||
if (stbi_write_bmp_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
||||
return buffer;
|
||||
}
|
||||
else if (specified == "tga")
|
||||
{
|
||||
// TGA format
|
||||
if (stbi_write_tga_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
||||
return buffer;
|
||||
}
|
||||
else if (specified == "png")
|
||||
{
|
||||
// PNG format
|
||||
if (stbi_write_png_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data(), 0))
|
||||
return buffer;
|
||||
}
|
||||
else if (specified == "jpg" || specified == "jpeg")
|
||||
{
|
||||
// JPG format
|
||||
if (stbi_write_jpg_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data(), 90))
|
||||
return buffer;
|
||||
case SaveFormat::BMP:
|
||||
if (stbi_write_bmp_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
||||
return buffer;
|
||||
break;
|
||||
case SaveFormat::TGA:
|
||||
if (stbi_write_tga_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data()))
|
||||
return buffer;
|
||||
break;
|
||||
case SaveFormat::PNG:
|
||||
if (stbi_write_png_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data(), 0))
|
||||
return buffer;
|
||||
break;
|
||||
case SaveFormat::JPG:
|
||||
if (stbi_write_jpg_to_func(bufferFromCallback, &buffer, convertedSize.x, convertedSize.y, 4, m_pixels.data(), 90))
|
||||
return buffer;
|
||||
break;
|
||||
default:
|
||||
assert(false && "Invalid format enumeration provided");
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
err() << "Failed to save image with format " << std::quoted(format) << std::endl;
|
||||
err() << "Failed to save image" << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ TEST_CASE("[Graphics] sf::Image")
|
||||
|
||||
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());
|
||||
CHECK(image.getSize() == sf::Vector2u(24, 24));
|
||||
CHECK(image.getPixelsPtr() != nullptr);
|
||||
@ -344,13 +344,7 @@ TEST_CASE("[Graphics] sf::Image")
|
||||
|
||||
SECTION("Successful load")
|
||||
{
|
||||
const auto memory = []()
|
||||
{
|
||||
sf::Image savedImage;
|
||||
savedImage.resize({24, 24}, sf::Color::Green);
|
||||
return savedImage.saveToMemory("png").value();
|
||||
}();
|
||||
|
||||
const auto memory = sf::Image({24, 24}, sf::Color::Green).saveToMemory(sf::Image::SaveFormat::PNG).value();
|
||||
CHECK(image.loadFromMemory(memory.data(), memory.size()));
|
||||
CHECK(image.getSize() == sf::Vector2u(24, 24));
|
||||
CHECK(image.getPixelsPtr() != nullptr);
|
||||
@ -384,24 +378,12 @@ TEST_CASE("[Graphics] sf::Image")
|
||||
{
|
||||
SECTION("Invalid size")
|
||||
{
|
||||
CHECK(!sf::Image({10, 0}, sf::Color::Magenta).saveToFile("test.jpg"));
|
||||
CHECK(!sf::Image({0, 10}, 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", sf::Image::SaveFormat::JPG));
|
||||
}
|
||||
|
||||
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")
|
||||
{
|
||||
auto filename = std::filesystem::temp_directory_path();
|
||||
@ -409,27 +391,32 @@ TEST_CASE("[Graphics] sf::Image")
|
||||
SECTION("To .bmp")
|
||||
{
|
||||
filename /= "test.bmp";
|
||||
CHECK(image.saveToFile(filename));
|
||||
CHECK(image.saveToFile(filename, sf::Image::SaveFormat::BMP));
|
||||
}
|
||||
|
||||
SECTION("To .tga")
|
||||
{
|
||||
filename /= "test.tga";
|
||||
CHECK(image.saveToFile(filename));
|
||||
CHECK(image.saveToFile(filename, sf::Image::SaveFormat::TGA));
|
||||
}
|
||||
|
||||
SECTION("To .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
|
||||
|
||||
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.getPixelsPtr() != nullptr);
|
||||
|
||||
CHECK(std::filesystem::remove(filename));
|
||||
}
|
||||
}
|
||||
@ -437,71 +424,49 @@ TEST_CASE("[Graphics] sf::Image")
|
||||
SECTION("saveToMemory()")
|
||||
{
|
||||
SECTION("Invalid size")
|
||||
{
|
||||
CHECK(!sf::Image({10, 0}, sf::Color::Magenta).saveToMemory("test.jpg"));
|
||||
CHECK(!sf::Image({0, 10}, sf::Color::Magenta).saveToMemory("test.jpg"));
|
||||
}
|
||||
CHECK(!sf::Image({10, 0}, sf::Color::Magenta).saveToMemory(sf::Image::SaveFormat::JPG));
|
||||
CHECK(!sf::Image({0, 10}, sf::Color::Magenta).saveToMemory(sf::Image::SaveFormat::JPG));
|
||||
}
|
||||
|
||||
SECTION("Successful save")
|
||||
{
|
||||
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("."));
|
||||
CHECK(!image.saveToMemory("gif"));
|
||||
CHECK(!image.saveToMemory(".jpg")); // Supposed to be "jpg"
|
||||
const auto memory = image.saveToMemory(sf::Image::SaveFormat::TGA).value();
|
||||
REQUIRE(memory.size() == 98);
|
||||
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;
|
||||
|
||||
SECTION("To bmp")
|
||||
{
|
||||
maybeOutput = image.saveToMemory("bmp");
|
||||
REQUIRE(maybeOutput.has_value());
|
||||
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
|
||||
const auto memory = image.saveToMemory(sf::Image::SaveFormat::PNG).value();
|
||||
REQUIRE(memory.size() == 92);
|
||||
CHECK(memory[0] == 137);
|
||||
CHECK(memory[1] == 80);
|
||||
CHECK(memory[2] == 78);
|
||||
CHECK(memory[3] == 71);
|
||||
}
|
||||
|
||||
// Cannot test JPEG encoding due to it triggering UB in stbiw__jpg_writeBits
|
||||
}
|
||||
|
||||
SECTION("Set/get pixel")
|
||||
|
Loading…
Reference in New Issue
Block a user